Paul Eggert wrote: > In the meantime I installed the attached to Gnulib, to > document the incompatibility, which also occurs with getlogin_r.
These two patches actually work around the bugs. With this, coreutils should be fine, since it already imports the 'getlogin' module from gnulib. 2025-03-09 Bruno Haible <br...@clisp.org> getlogin_r: Work around musl bug. * lib/getlogin_r.c (getlogin_r): Add implementation for Linux. * m4/getlogin_r.m4 (gl_FUNC_GETLOGIN_R): Test whether getlogin_r has the musl bug. * tests/test-getlogin_r.c (main): Add another test. * doc/posix-functions/getlogin_r.texi: Mention the workaround. 2025-03-09 Bruno Haible <br...@clisp.org> getlogin: Work around musl bug. * lib/unistd.in.h (getlogin): Consider REPLACE_GETLOGIN. * lib/getlogin.c: Change license header to GPL. (getlogin): Add implementation for Linux. * m4/getlogin.m4 (gl_FUNC_GETLOGIN): Test whether getlogin works. * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize REPLACE_GETLOGIN. * modules/unistd-h (Makefile.am): Substitute REPLACE_GETLOGIN. * modules/getlogin (Depends-on): Add readutmp. (configure.ac): Consider REPLACE_GETLOGIN. (License): Change to GPL. * tests/test-getlogin.c (main): Add another test. * doc/posix-functions/getlogin.texi: Mention the workaround.
>From 90840606e77dd24d87ad7f075d8f87c0ed814ef5 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sun, 9 Mar 2025 10:16:58 +0100 Subject: [PATCH 1/2] getlogin: Work around musl bug. * lib/unistd.in.h (getlogin): Consider REPLACE_GETLOGIN. * lib/getlogin.c: Change license header to GPL. (getlogin): Add implementation for Linux. * m4/getlogin.m4 (gl_FUNC_GETLOGIN): Test whether getlogin works. * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize REPLACE_GETLOGIN. * modules/unistd-h (Makefile.am): Substitute REPLACE_GETLOGIN. * modules/getlogin (Depends-on): Add readutmp. (configure.ac): Consider REPLACE_GETLOGIN. (License): Change to GPL. * tests/test-getlogin.c (main): Add another test. * doc/posix-functions/getlogin.texi: Mention the workaround. --- ChangeLog | 15 +++++ doc/posix-functions/getlogin.texi | 6 +- lib/getlogin.c | 99 +++++++++++++++++++++++++++++-- lib/unistd.in.h | 14 ++++- m4/getlogin.m4 | 49 ++++++++++++++- m4/unistd_h.m4 | 3 +- modules/getlogin | 6 +- modules/unistd-h | 1 + tests/test-getlogin.c | 6 ++ 9 files changed, 184 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 89940ddd4c..6d2ad66c83 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2025-03-09 Bruno Haible <br...@clisp.org> + + getlogin: Work around musl bug. + * lib/unistd.in.h (getlogin): Consider REPLACE_GETLOGIN. + * lib/getlogin.c: Change license header to GPL. + (getlogin): Add implementation for Linux. + * m4/getlogin.m4 (gl_FUNC_GETLOGIN): Test whether getlogin works. + * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize REPLACE_GETLOGIN. + * modules/unistd-h (Makefile.am): Substitute REPLACE_GETLOGIN. + * modules/getlogin (Depends-on): Add readutmp. + (configure.ac): Consider REPLACE_GETLOGIN. + (License): Change to GPL. + * tests/test-getlogin.c (main): Add another test. + * doc/posix-functions/getlogin.texi: Mention the workaround. + 2025-03-08 Paul Eggert <egg...@cs.ucla.edu> getlogin: document musl bug diff --git a/doc/posix-functions/getlogin.texi b/doc/posix-functions/getlogin.texi index 0de026f2ee..0ccce336ce 100644 --- a/doc/posix-functions/getlogin.texi +++ b/doc/posix-functions/getlogin.texi @@ -15,14 +15,14 @@ @item This function is not declared unless @code{_POSIX} is defined on some platforms: mingw. +@item +This function returns the value of the @env{LOGNAME} environment variable: +musl libc 1.2.5. @end itemize Portability problems not fixed by Gnulib: @itemize @item -This function returns the value of the @env{LOGNAME} environment variable: -musl libc 1.2.5. -@item This function returns an empty string even when standard input is a tty on some platforms: HP-UX 11.11. diff --git a/lib/getlogin.c b/lib/getlogin.c index 72c25acbc7..4772111567 100644 --- a/lib/getlogin.c +++ b/lib/getlogin.c @@ -3,19 +3,19 @@ Copyright (C) 2010-2025 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU General Public License for more details. - You should have received a copy of the GNU Lesser General Public License + You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ -/* Written by Bruno Haible, 2010. */ +/* Written by Bruno Haible, 2010, 2025. */ #include <config.h> @@ -30,15 +30,102 @@ # define GetUserName GetUserNameA #endif +#if defined __linux__ || defined __ANDROID__ +# include <fcntl.h> +# include <pwd.h> +# include <stdlib.h> +# include <string.h> +# include <sys/stat.h> +# include "readutmp.h" +#endif + char * getlogin (void) { #if defined _WIN32 && ! defined __CYGWIN__ + /* Native Windows platform. */ static char login_name[1024]; DWORD sz = sizeof (login_name); if (GetUserName (login_name, &sz)) return login_name; +#elif defined __linux__ || defined __ANDROID__ + /* Linux. */ + { + /* Read the login uid from the /proc file system. */ + int fd = open ("/proc/self/loginuid", O_RDONLY); + if (fd >= 0) + { + char buf[20 + 1]; + int n = read (fd, buf, sizeof (buf) - 1); + if (n > 0) + { + buf[n] = '\0'; + char *endptr; + unsigned long uid = strtoul (buf, &endptr, 10); + if (endptr == buf + n && uid != (uid_t) -1) + { + /* Convert the uid to a user name. */ + struct passwd *p = getpwuid (uid); + if (p != NULL && strlen (p->pw_name) < 64) + { + static char resultbuf[64]; + strcpy (resultbuf, p->pw_name); + close (fd); + return resultbuf; + } + } + } + close (fd); + } + } + { + /* Find the tty connected to the current process. */ + char *tty = ttyname (STDIN_FILENO); + if (tty != NULL) + { + /* Try to see to which user it is allocated, via utmp. */ + if (strncmp (tty, "/dev/tty", 8) == 0) + { + idx_t n; + STRUCT_UTMP *entries; + if (read_utmp (UTMP_FILE, &n, &entries, READ_UTMP_USER_PROCESS) + == 0) + { + idx_t i; + for (i = 0; i < n; i++) + if (strcmp (entries[i].ut_line, tty + 5) == 0) + { + if (strlen (entries[i].ut_user) < 64) + { + static char resultbuf[64]; + strcpy (resultbuf, entries[i].ut_user); + free (entries); + return resultbuf; + } + break; + } + free (entries); + } + } + /* Fallback for systems which don't maintain an utmp database + or for ttys that are not recorded in that the utmp database: + Look at the owner of that tty. */ + struct stat statbuf; + if (stat (tty, &statbuf) >= 0) + { + uid_t uid = statbuf.st_uid; + /* Convert the uid to a user name. */ + struct passwd *p = getpwuid (uid); + if (p != NULL && strlen (p->pw_name) < 64) + { + static char resultbuf[64]; + strcpy (resultbuf, p->pw_name); + return resultbuf; + } + } + } + } #endif return NULL; } diff --git a/lib/unistd.in.h b/lib/unistd.in.h index acabdf8c68..bfb4817726 100644 --- a/lib/unistd.in.h +++ b/lib/unistd.in.h @@ -1365,11 +1365,21 @@ _GL_WARN_ON_USE (gethostname, "gethostname is unportable - " ${LOGNAME-$USER} on Unix platforms, $USERNAME on native Windows platforms. */ -# if !@HAVE_DECL_GETLOGIN@ +# if @REPLACE_GETLOGIN@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# define getlogin rpl_getlogin +# endif +_GL_FUNCDECL_RPL (getlogin, char *, (void), ); +_GL_CXXALIAS_RPL (getlogin, char *, (void)); +# else +# if !@HAVE_DECL_GETLOGIN@ _GL_FUNCDECL_SYS (getlogin, char *, (void), ); -# endif +# endif _GL_CXXALIAS_SYS (getlogin, char *, (void)); +# endif +# if __GLIBC__ >= 2 _GL_CXXALIASWARN (getlogin); +# endif #elif defined GNULIB_POSIXCHECK # undef getlogin # if HAVE_RAW_DECL_GETLOGIN diff --git a/m4/getlogin.m4 b/m4/getlogin.m4 index e876fad5ea..7f1f07319d 100644 --- a/m4/getlogin.m4 +++ b/m4/getlogin.m4 @@ -1,5 +1,5 @@ # getlogin.m4 -# serial 7 +# serial 8 dnl Copyright (C) 2010-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -16,6 +16,53 @@ AC_DEFUN([gl_FUNC_GETLOGIN] AC_CHECK_FUNCS_ONCE([getlogin]) if test $ac_cv_func_getlogin = no; then HAVE_GETLOGIN=0 + else + dnl On musl libc, getlogin returns getenv ("LOGNAME"). + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_CACHE_CHECK([whether getlogin works], + [gl_cv_func_getlogin_works], + [ + dnl Initial guess, used when cross-compiling. +changequote(,)dnl + case "$host_os" in + # Guess no on musl libc. + *-musl* | midipix*) gl_cv_func_getlogin_works="guessing no" ;; + # Guess yes otherwise. + *) gl_cv_func_getlogin_works="guessing yes" ;; + esac +changequote([,])dnl + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if !HAVE_DECL_GETLOGIN +extern +# ifdef __cplusplus +"C" +# endif +char *getlogin (void); +#endif +int +main (void) +{ + int result = 0; + char *buf; + + putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke"); + buf = getlogin (); + if (buf != NULL && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0) + result |= 1; + return result; +}]])], + [gl_cv_func_getlogin_works=yes], + [gl_cv_func_getlogin_works=no], + [:]) + ]) + case "$gl_cv_func_getlogin_works" in + *yes) ;; + *) REPLACE_GETLOGIN=1 ;; + esac fi ]) diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4 index 2c08d65e0d..6ec16286ef 100644 --- a/m4/unistd_h.m4 +++ b/m4/unistd_h.m4 @@ -1,5 +1,5 @@ # unistd_h.m4 -# serial 96 +# serial 97 dnl Copyright (C) 2006-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -244,6 +244,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS] REPLACE_GETDOMAINNAME=0; AC_SUBST([REPLACE_GETDOMAINNAME]) REPLACE_GETDTABLESIZE=0; AC_SUBST([REPLACE_GETDTABLESIZE]) REPLACE_GETENTROPY=0; AC_SUBST([REPLACE_GETENTROPY]) + REPLACE_GETLOGIN=0; AC_SUBST([REPLACE_GETLOGIN]) REPLACE_GETLOGIN_R=0; AC_SUBST([REPLACE_GETLOGIN_R]) REPLACE_GETGROUPS=0; AC_SUBST([REPLACE_GETGROUPS]) REPLACE_GETPAGESIZE=0; AC_SUBST([REPLACE_GETPAGESIZE]) diff --git a/modules/getlogin b/modules/getlogin index 8725a7031e..7acee58a81 100644 --- a/modules/getlogin +++ b/modules/getlogin @@ -7,10 +7,12 @@ m4/getlogin.m4 Depends-on: unistd-h +readutmp [test $HAVE_GETLOGIN = 0 || test $REPLACE_GETLOGIN = 1] configure.ac: gl_FUNC_GETLOGIN -gl_CONDITIONAL([GL_COND_OBJ_GETLOGIN], [test $HAVE_GETLOGIN = 0]) +gl_CONDITIONAL([GL_COND_OBJ_GETLOGIN], + [test $HAVE_GETLOGIN = 0 || test $REPLACE_GETLOGIN = 1]) gl_UNISTD_MODULE_INDICATOR([getlogin]) AC_REQUIRE([gl_LIB_GETLOGIN]) @@ -26,7 +28,7 @@ Link: $(GETLOGIN_LIB) License: -LGPLv2+ +GPL Maintainer: all diff --git a/modules/unistd-h b/modules/unistd-h index 1a7376efdb..0fdb6368b3 100644 --- a/modules/unistd-h +++ b/modules/unistd-h @@ -201,6 +201,7 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's|@''REPLACE_GETDOMAINNAME''@|$(REPLACE_GETDOMAINNAME)|g' \ -e 's|@''REPLACE_GETDTABLESIZE''@|$(REPLACE_GETDTABLESIZE)|g' \ -e 's|@''REPLACE_GETENTROPY''@|$(REPLACE_GETENTROPY)|g' \ + -e 's|@''REPLACE_GETLOGIN''@|$(REPLACE_GETLOGIN)|g' \ -e 's|@''REPLACE_GETLOGIN_R''@|$(REPLACE_GETLOGIN_R)|g' \ -e 's|@''REPLACE_GETGROUPS''@|$(REPLACE_GETGROUPS)|g' \ -e 's|@''REPLACE_GETPAGESIZE''@|$(REPLACE_GETPAGESIZE)|g' \ diff --git a/tests/test-getlogin.c b/tests/test-getlogin.c index 1b980a152d..431844bd71 100644 --- a/tests/test-getlogin.c +++ b/tests/test-getlogin.c @@ -41,5 +41,11 @@ main (void) #endif test_getlogin_result (buf, err); + /* Check that getlogin() does not merely return getenv ("LOGNAME"). */ + putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke"); + buf = getlogin (); + ASSERT (!(buf != NULL + && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0)); + return test_exit_status; } -- 2.43.0
>From 0bca572030d26721cb81800ff4264b6f18dc7e40 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sun, 9 Mar 2025 10:19:20 +0100 Subject: [PATCH 2/2] getlogin_r: Work around musl bug. * lib/getlogin_r.c (getlogin_r): Add implementation for Linux. * m4/getlogin_r.m4 (gl_FUNC_GETLOGIN_R): Test whether getlogin_r has the musl bug. * tests/test-getlogin_r.c (main): Add another test. * doc/posix-functions/getlogin_r.texi: Mention the workaround. --- ChangeLog | 9 ++++ doc/posix-functions/getlogin_r.texi | 6 +-- lib/getlogin_r.c | 74 +++++++++++++++++++++++++++++ m4/getlogin_r.m4 | 20 +++++--- tests/test-getlogin_r.c | 5 ++ 5 files changed, 105 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6d2ad66c83..d8eb0e8df7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2025-03-09 Bruno Haible <br...@clisp.org> + + getlogin_r: Work around musl bug. + * lib/getlogin_r.c (getlogin_r): Add implementation for Linux. + * m4/getlogin_r.m4 (gl_FUNC_GETLOGIN_R): Test whether getlogin_r has the + musl bug. + * tests/test-getlogin_r.c (main): Add another test. + * doc/posix-functions/getlogin_r.texi: Mention the workaround. + 2025-03-09 Bruno Haible <br...@clisp.org> getlogin: Work around musl bug. diff --git a/doc/posix-functions/getlogin_r.texi b/doc/posix-functions/getlogin_r.texi index daafd9eaa0..df145ecfe2 100644 --- a/doc/posix-functions/getlogin_r.texi +++ b/doc/posix-functions/getlogin_r.texi @@ -20,6 +20,9 @@ This function returns a truncated result, instead of failing with error code @code{ERANGE}, when the buffer is not large enough, on some platforms: macOS 14. +@item +This function returns the value of the @env{LOGNAME} environment variable: +musl libc 1.2.5. @end itemize Portability problems not fixed by Gnulib: @@ -28,9 +31,6 @@ This function has an incompatible declaration on some platforms: FreeBSD 12.0, MidnightBSD 2.0, Solaris 11.4 (when @code{_POSIX_PTHREAD_SEMANTICS} is not defined). @item -This function returns the value of the @env{LOGNAME} environment variable: -musl libc 1.2.5. -@item This function fails even when standard input is a tty on some platforms: HP-UX 11.11. @item diff --git a/lib/getlogin_r.c b/lib/getlogin_r.c index 6a13e8e405..ade819940e 100644 --- a/lib/getlogin_r.c +++ b/lib/getlogin_r.c @@ -39,6 +39,14 @@ extern char *getlogin (void); # endif #endif +#if defined __linux__ || defined __ANDROID__ +# include <fcntl.h> +# include <pwd.h> +# include <stdlib.h> +# include <string.h> +# include <sys/stat.h> +#endif + /* See unistd.in.h for documentation. */ int getlogin_r (char *name, size_t size) @@ -64,6 +72,72 @@ getlogin_r (char *name, size_t size) return ENOENT; } return 0; +#elif defined __linux__ || defined __ANDROID__ + /* Linux. */ + { + /* Read the login uid from the /proc file system. */ + int fd = open ("/proc/self/loginuid", O_RDONLY); + if (fd >= 0) + { + char buf[20 + 1]; + int n = read (fd, buf, sizeof (buf) - 1); + if (n > 0) + { + buf[n] = '\0'; + char *endptr; + unsigned long uid = strtoul (buf, &endptr, 10); + if (endptr == buf + n && uid != (uid_t) -1) + { + /* Convert the uid to a user name. */ + struct passwd pbuf1; + char pbuf2[1024]; + struct passwd *p; + if (getpwuid_r (uid, &pbuf1, pbuf2, sizeof (pbuf2), &p) == 0) + { + if (strlen (p->pw_name) < size) + { + strcpy (name, p->pw_name); + close (fd); + return 0; + } + return ERANGE; + } + } + } + close (fd); + } + } + { + /* Find the tty connected to the current process. */ + char tty[1024]; + if (ttyname_r (STDIN_FILENO, tty, sizeof (tty)) == 0) + { + /* We cannot use read_utmp here, since it is not multithread-safe. */ + /* Fallback for systems which don't maintain an utmp database + or for ttys that are not recorded in that the utmp database: + Look at the owner of that tty. */ + struct stat statbuf; + if (stat (tty, &statbuf) >= 0) + { + uid_t uid = statbuf.st_uid; + /* Convert the uid to a user name. */ + struct passwd pbuf1; + char pbuf2[1024]; + struct passwd *p; + if (getpwuid_r (uid, &pbuf1, pbuf2, sizeof (pbuf2), &p) == 0) + { + if (strlen (p->pw_name) < size) + { + strcpy (name, p->pw_name); + return 0; + } + return ERANGE; + } + } + } + } + /* ENOENT is a reasonable errno value if getlogin returns NULL. */ + return ENOENT; #elif HAVE_GETLOGIN_R /* Platform with a getlogin_r() function. */ int ret = getlogin_r (name, size); diff --git a/m4/getlogin_r.m4 b/m4/getlogin_r.m4 index 26664e1fda..5ac8a89ff8 100644 --- a/m4/getlogin_r.m4 +++ b/m4/getlogin_r.m4 @@ -1,5 +1,5 @@ # getlogin_r.m4 -# serial 15 +# serial 16 dnl Copyright (C) 2005-2007, 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -35,22 +35,26 @@ AC_DEFUN([gl_FUNC_GETLOGIN_R] HAVE_GETLOGIN_R=1 dnl On Mac OS X 10.13 and OSF/1 5.1, getlogin_r returns a truncated result dnl if the buffer is not large enough. + dnl On musl libc, getlogin_r returns getenv ("LOGNAME"). AC_REQUIRE([AC_CANONICAL_HOST]) - AC_CACHE_CHECK([whether getlogin_r works with small buffers], + AC_CACHE_CHECK([whether getlogin_r works], [gl_cv_func_getlogin_r_works], [ dnl Initial guess, used when cross-compiling. changequote(,)dnl case "$host_os" in - # Guess no on Mac OS X, OSF/1. - darwin* | osf*) gl_cv_func_getlogin_r_works="guessing no" ;; - # Guess yes otherwise. - *) gl_cv_func_getlogin_r_works="guessing yes" ;; + # Guess no on Mac OS X, OSF/1. + darwin* | osf*) gl_cv_func_getlogin_r_works="guessing no" ;; + # Guess no on musl libc. + *-musl* | midipix*) gl_cv_func_getlogin_r_works="guessing no" ;; + # Guess yes otherwise. + *) gl_cv_func_getlogin_r_works="guessing yes" ;; esac changequote([,])dnl AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include <stddef.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> #if !HAVE_DECL_GETLOGIN_R @@ -76,6 +80,10 @@ AC_DEFUN([gl_FUNC_GETLOGIN_R] if (getlogin_r (buf, n) == 0) result |= 4; } + putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke"); + if (getlogin_r (buf, 100) == 0 + && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0) + result |= 8; return result; }]])], [gl_cv_func_getlogin_r_works=yes], diff --git a/tests/test-getlogin_r.c b/tests/test-getlogin_r.c index 4deddfbb01..3674195e55 100644 --- a/tests/test-getlogin_r.c +++ b/tests/test-getlogin_r.c @@ -68,5 +68,10 @@ main (void) ASSERT (strcmp (hugebuf, buf) == 0); } + /* Check that getlogin_r() does not merely return getenv ("LOGNAME"). */ + putenv ("LOGNAME=ygvfibmslhkmvoetbrcegzwydorcke"); + err = getlogin_r (buf, sizeof buf); + ASSERT (!(err == 0 && strcmp (buf, "ygvfibmslhkmvoetbrcegzwydorcke") == 0)); + return test_exit_status; } -- 2.43.0