From: Bruno Haible Date: Sun, 9 Mar 2025 09:19:20 +0000 (+0100) Subject: getlogin_r: Work around musl bug. X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=0bca572030d26721cb81800ff4264b6f18dc7e40;p=gnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index 6d2ad66c83..d8eb0e8df7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2025-03-09 Bruno Haible + + 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 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 @@ HP-UX 11. 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 @@ Portability problems not fixed by Gnulib: 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 +# include +# include +# include +# include +#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 +#include #include #include #if !HAVE_DECL_GETLOGIN_R @@ -76,6 +80,10 @@ main (void) 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; }