* 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.
+2025-03-09 Bruno Haible <bruno@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.
+
2025-03-09 Bruno Haible <bruno@clisp.org>
getlogin: Work around musl bug.
# 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)
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);
# 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,
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
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],
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;
}