]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
maint: Update after gnulib module 'readutmp' changed
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 4 Aug 2023 01:35:29 +0000 (18:35 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 4 Aug 2023 06:16:00 +0000 (23:16 -0700)
(This patch is coauthored with Bruno Haible,
with original version at <https://bugs.gnu.org/64937#>.)
This updates the gnulib submodule to latest.
For year-2038 safety on Linux/{x86,arm},
this adds an --enable-systemd option to ‘configure’.
The idea is that this sort of thing will become the default
after it has been tested more.
* configure.ac: Don't test whether struct utmp and struct utmpx
have the ut_host field; this is now done in gnulib's readutmp module.
* src/local.mk: Link the programs 'pinky', 'uptime', 'users',
'who' with $(READUTMP_LIB).
* src/pinky.c, src/who.c:
Test HAVE_STRUCT_XTMP_UT_HOST instead of HAVE_UT_HOST.
* src/pinky.c (print_entry):
* src/who.c (print_user, print_deadprocs, print_login)
(print_initspawn, scan_entries):
Support the situation where ut_line is a 'char *' rather than a
'char[]' of fixed size.  Likewise for ut_user and ut_host.
(make_id_equals_comment): Likewise for ut_id.
* src/pinky.c (print_entry):
* src/who.c (print_user):
Open /dev to simplify looking up its entries.
Don’t use printf if the output might in theory be longer than INT_MAX.
* src/pinky.c (scan_entries, short_pinky):
* src/uptime.c (print_uptime, uptime):
* src/users.c (list_entries_users, users):
* src/who.c (who):
Use idx_t where new read_utmp needs it.
* src/system.h (STREQ_LEN): Add comment that last arg can be -1.

NEWS
configure.ac
gnulib
src/local.mk
src/pinky.c
src/system.h
src/uptime.c
src/users.c
src/who.c

diff --git a/NEWS b/NEWS
index 2b8f984ba805f8ec965137a1c6224732bf5f1f0a..53a342a5ed9140cce3a7a2349be656f0430cb512 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -41,6 +41,10 @@ GNU coreutils NEWS                                    -*- outline -*-
 
   pinky, uptime, users, and who no longer misbehave on 32-bit GNU/Linux
   platforms like x86 and ARM where time_t was historically 32 bits.
+  A new configure-time option --enable-systemd enables experimental
+  support for using systemd, to let these programs continue to work on
+  these old platforms even after the year 2038, so long as systemd
+  is also installed.
   [bug introduced in coreutils-9.0]
 
   'pr --length=1 --double-space' no longer enters an infinite loop.
index 33441a82ffc1820d3afda5285199e422dfee59cc..afc1098f763c9562215243e9b516d7fbb1bbfb29 100644 (file)
@@ -406,36 +406,6 @@ AC_DEFUN([coreutils_DUMMY_1],
 ])
 coreutils_DUMMY_1
 
-AC_MSG_CHECKING([ut_host in struct utmp])
-AC_CACHE_VAL([su_cv_func_ut_host_in_utmp],
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
-                                   #include <utmp.h>
-                                   struct utmp ut;
-                                   int s = sizeof ut.ut_host;]])],
-  [su_cv_func_ut_host_in_utmp=yes],
-  [su_cv_func_ut_host_in_utmp=no])])
-AC_MSG_RESULT([$su_cv_func_ut_host_in_utmp])
-if test $su_cv_func_ut_host_in_utmp = yes; then
-  have_ut_host=1
-  AC_DEFINE([HAVE_UT_HOST], [1], [FIXME])
-fi
-
-if test -z "$have_ut_host"; then
-  AC_MSG_CHECKING([ut_host in struct utmpx])
-  AC_CACHE_VAL([su_cv_func_ut_host_in_utmpx],
-  [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
-                                     #include <utmpx.h>
-                                     struct utmpx ut;
-                                     int s = sizeof ut.ut_host;]])],
-    [su_cv_func_ut_host_in_utmpx=yes],
-    [su_cv_func_ut_host_in_utmpx=no])])
-  AC_MSG_RESULT([$su_cv_func_ut_host_in_utmpx])
-  if test $su_cv_func_ut_host_in_utmpx = yes; then
-    AC_DEFINE([HAVE_UTMPX_H], [1], [FIXME])
-    AC_DEFINE([HAVE_UT_HOST], [1], [FIXME])
-  fi
-fi
-
 GNULIB_BOOT_TIME([gl_ADD_PROG([optional_bin_progs], [uptime])])
 
 AC_SYS_POSIX_TERMIOS()
diff --git a/gnulib b/gnulib
index 0e7af0cff0011f25f5f842af5216325923017b34..f6aa86ea62cd60141e918142bf3ba9fda8ee2463 160000 (submodule)
--- a/gnulib
+++ b/gnulib
@@ -1 +1 @@
-Subproject commit 0e7af0cff0011f25f5f842af5216325923017b34
+Subproject commit f6aa86ea62cd60141e918142bf3ba9fda8ee2463
index cb9b392743c4722846669041ceab5d4b87e088c5..4d7df27894a9c3815cd1f75a427f3a9d6c448e46 100644 (file)
@@ -317,6 +317,12 @@ src_who_LDADD += $(GETADDRINFO_LIB)
 src_hostname_LDADD += $(GETHOSTNAME_LIB)
 src_uname_LDADD += $(GETHOSTNAME_LIB)
 
+# for read_utmp
+src_pinky_LDADD += $(READUTMP_LIB)
+src_uptime_LDADD += $(READUTMP_LIB)
+src_users_LDADD += $(READUTMP_LIB)
+src_who_LDADD += $(READUTMP_LIB)
+
 # for strsignal
 src_kill_LDADD += $(LIBTHREAD)
 
index b6d411c8586988408feb48bc0a160ca90d6e6e9d..38ceccbea67a56f96787d8668cca74baa8337b33 100644 (file)
@@ -62,7 +62,7 @@ static bool include_home_and_shell = true;
 static bool do_short_format = true;
 
 /* if true, display the ut_host field. */
-#ifdef HAVE_UT_HOST
+#if HAVE_STRUCT_XTMP_UT_HOST
 static bool include_where = true;
 #endif
 
@@ -203,20 +203,32 @@ print_entry (const STRUCT_UTMP *utmp_ent)
   time_t last_change;
   char mesg;
 
-#define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
-#define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
-
-  char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
-  char *p = line;
+#ifdef UT_LINE_SIZE
+  char line[UT_LINE_SIZE + 1];
+  stzncpy (line, utmp_ent->ut_line, UT_LINE_SIZE);
+#else
+  /* If ut_line contains a space, the device name starts after the space.  */
+  char *line = utmp_ent->ut_line;
+  char *space = strchr (line, ' ');
+  line = space ? space + 1 : line;
+#endif
 
-  /* Copy ut_line into LINE, prepending '/dev/' if ut_line is not
-     already an absolute file name.  Some system may put the full,
-     absolute file name in ut_line.  */
-  if ( ! IS_ABSOLUTE_FILE_NAME (utmp_ent->ut_line))
-    p = stpcpy (p, DEV_DIR_WITH_TRAILING_SLASH);
-  stzncpy (p, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
+  int dirfd;
+  if (IS_ABSOLUTE_FILE_NAME (line))
+    dirfd = AT_FDCWD;
+  else
+    {
+      static int dev_dirfd;
+      if (!dev_dirfd)
+        {
+          dev_dirfd = open ("/dev", O_PATHSEARCH | O_DIRECTORY);
+          if (dev_dirfd < 0)
+            dev_dirfd = AT_FDCWD - 1;
+        }
+      dirfd = dev_dirfd;
+    }
 
-  if (stat (line, &stats) == 0)
+  if (AT_FDCWD <= dirfd && fstatat (dirfd, line, &stats, 0) == 0)
     {
       mesg = (stats.st_mode & S_IWGRP) ? ' ' : '*';
       last_change = stats.st_atime;
@@ -227,15 +239,20 @@ print_entry (const STRUCT_UTMP *utmp_ent)
       last_change = 0;
     }
 
-  printf ("%-8.*s", UT_USER_SIZE, UT_USER (utmp_ent));
+  if (0 <= UT_USER_SIZE || strnlen (UT_USER (utmp_ent), 8) < 8)
+    printf ("%-8.*s", UT_USER_SIZE, UT_USER (utmp_ent));
+  else
+    fputs (UT_USER (utmp_ent), stdout);
 
   if (include_fullname)
     {
-      struct passwd *pw;
+#ifdef UT_USER_SIZE
       char name[UT_USER_SIZE + 1];
-
       stzncpy (name, UT_USER (utmp_ent), UT_USER_SIZE);
-      pw = getpwnam (name);
+#else
+      char *name = UT_USER (utmp_ent);
+#endif
+      struct passwd *pw = getpwnam (name);
       if (pw == nullptr)
         /* TRANSLATORS: Real name is unknown; at most 19 characters. */
         printf (" %19s", _("        ???"));
@@ -253,8 +270,12 @@ print_entry (const STRUCT_UTMP *utmp_ent)
         }
     }
 
-  printf (" %c%-8.*s",
-          mesg, (int) sizeof (utmp_ent->ut_line), utmp_ent->ut_line);
+  fputc (' ', stdout);
+  fputc (mesg, stdout);
+  if (0 <= UT_LINE_SIZE || strnlen (utmp_ent->ut_line, 8) < 8)
+    printf ("%-8.*s", UT_LINE_SIZE, utmp_ent->ut_line);
+  else
+    fputs (utmp_ent->ut_line, stdout);
 
   if (include_idle)
     {
@@ -267,15 +288,18 @@ print_entry (const STRUCT_UTMP *utmp_ent)
 
   printf (" %s", time_string (utmp_ent));
 
-#ifdef HAVE_UT_HOST
+#ifdef HAVE_STRUCT_XTMP_UT_HOST
   if (include_where && utmp_ent->ut_host[0])
     {
-      char ut_host[sizeof (utmp_ent->ut_host) + 1];
       char *host = nullptr;
       char *display = nullptr;
 
-      /* Copy the host name into UT_HOST, and ensure it's nul terminated. */
-      stzncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host));
+# ifdef UT_HOST_SIZE
+      char ut_host[UT_HOST_SIZE + 1];
+      stzncpy (ut_host, utmp_ent->ut_host, UT_HOST_SIZE);
+# else
+      char *ut_host = utmp_ent->ut_host;
+# endif
 
       /* Look for an X display.  */
       display = strchr (ut_host, ':');
@@ -288,10 +312,13 @@ print_entry (const STRUCT_UTMP *utmp_ent)
       if ( ! host)
         host = ut_host;
 
+      fputc (' ', stdout);
+      fputs (host, stdout);
       if (display)
-        printf (" %s:%s", host, display);
-      else
-        printf (" %s", host);
+        {
+          fputc (':', stdout);
+          fputs (display, stdout);
+        }
 
       if (host != ut_host)
         free (host);
@@ -408,17 +435,23 @@ print_heading (void)
   if (include_idle)
     printf (" %-6s", _("Idle"));
   printf (" %-*s", time_format_width, _("When"));
-#ifdef HAVE_UT_HOST
+#ifdef HAVE_STRUCT_XTMP_UT_HOST
   if (include_where)
     printf (" %s", _("Where"));
 #endif
   putchar ('\n');
 }
 
+/* Work around <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109614>,
+   triggered by STREQ_LEN with a negative length.  */
+#if 11 <= __GNUC__
+# pragma GCC diagnostic ignored "-Wstringop-overread"
+#endif
+
 /* Display UTMP_BUF, which should have N entries. */
 
 static void
-scan_entries (size_t n, const STRUCT_UTMP *utmp_buf,
+scan_entries (idx_t n, const STRUCT_UTMP *utmp_buf,
               const int argc_names, char *const argv_names[])
 {
   if (hard_locale (LC_TIME))
@@ -461,7 +494,7 @@ static void
 short_pinky (char const *filename,
              const int argc_names, char *const argv_names[])
 {
-  size_t n_users;
+  idx_t n_users;
   STRUCT_UTMP *utmp_buf = nullptr;
 
   if (read_utmp (filename, &n_users, &utmp_buf, 0) != 0)
@@ -550,14 +583,14 @@ main (int argc, char **argv)
 
         case 'i':
           include_fullname = false;
-#ifdef HAVE_UT_HOST
+#ifdef HAVE_STRUCT_XTMP_UT_HOST
           include_where = false;
 #endif
           break;
 
         case 'q':
           include_fullname = false;
-#ifdef HAVE_UT_HOST
+#ifdef HAVE_STRUCT_XTMP_UT_HOST
           include_where = false;
 #endif
           include_idle = false;
index b5ec074e7c3b7b002282644ba008dc54704c49df..7b736604b00c76a9c323d7666e7c8d42b17ef331 100644 (file)
@@ -192,7 +192,7 @@ select_plural (uintmax_t n)
 }
 
 #define STREQ(a, b) (strcmp (a, b) == 0)
-#define STREQ_LEN(a, b, n) (strncmp (a, b, n) == 0)
+#define STREQ_LEN(a, b, n) (strncmp (a, b, n) == 0) /* n==-1 means unbounded */
 #define STRPREFIX(a, b) (strncmp (a, b, strlen (b)) == 0)
 
 /* Just like strncmp, but the second argument must be a literal string
index bdbf1451a1b37826ba27a71c325399e20c071731..ad1bbb9a190b661e3f09a4136b52d0c995c40530 100644 (file)
@@ -45,7 +45,7 @@
   proper_name ("Kaveh Ghazi")
 
 static void
-print_uptime (size_t n, const STRUCT_UTMP *this)
+print_uptime (idx_t n, const STRUCT_UTMP *this)
 {
   idx_t entries = 0;
   time_t boot_time = 0;
@@ -169,7 +169,7 @@ print_uptime (size_t n, const STRUCT_UTMP *this)
 static _Noreturn void
 uptime (char const *filename, int options)
 {
-  size_t n_users;
+  idx_t n_users;
   STRUCT_UTMP *utmp_buf = nullptr;
 
   if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)
index e14f3fc3e7adb36635a1dc8c97f3575d7c231f81..353c1765decbad2bb8b6311f149288bbfc507fdf 100644 (file)
@@ -42,11 +42,11 @@ userid_compare (const void *v_a, const void *v_b)
 }
 
 static void
-list_entries_users (size_t n, const STRUCT_UTMP *this)
+list_entries_users (idx_t n, const STRUCT_UTMP *this)
 {
-  char **u = xnmalloc (n, sizeof *u);
-  size_t i;
-  size_t n_entries = 0;
+  char **u = xinmalloc (n, sizeof *u);
+  idx_t i;
+  idx_t n_entries = 0;
 
   while (n--)
     {
@@ -82,7 +82,7 @@ list_entries_users (size_t n, const STRUCT_UTMP *this)
 static void
 users (char const *filename, int options)
 {
-  size_t n_users;
+  idx_t n_users;
   STRUCT_UTMP *utmp_buf;
 
   if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)
index ec0dff7923963a7df19d1421ce4c15741d112eda..0e94d3c835f73a336251e839bf12c81d383663d6 100644 (file)
--- a/src/who.c
+++ b/src/who.c
@@ -333,26 +333,38 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)
   time_t last_change;
   char mesg;
   char idlestr[IDLESTR_LEN + 1];
+  PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
   static char *hoststr;
-#if HAVE_UT_HOST
-  static size_t hostlen;
+#if HAVE_STRUCT_XTMP_UT_HOST
+  static idx_t hostlen;
 #endif
 
-#define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
-#define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
-
-  char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
-  char *p = line;
-  PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
+#ifdef UT_LINE_SIZE
+  char line[UT_LINE_SIZE + 1];
+  stzncpy (line, utmp_ent->ut_line, UT_LINE_SIZE);
+#else
+  /* If ut_line contains a space, the device name starts after the space.  */
+  char *line = utmp_ent->ut_line;
+  char *space = strchr (line, ' ');
+  line = space ? space + 1 : line;
+#endif
 
-  /* Copy ut_line into LINE, prepending '/dev/' if ut_line is not
-     already an absolute file name.  Some systems may put the full,
-     absolute file name in ut_line.  */
-  if ( ! IS_ABSOLUTE_FILE_NAME (utmp_ent->ut_line))
-    p = stpcpy (p, DEV_DIR_WITH_TRAILING_SLASH);
-  stzncpy (p, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
+  int dirfd;
+  if (IS_ABSOLUTE_FILE_NAME (line))
+    dirfd = AT_FDCWD;
+  else
+    {
+      static int dev_dirfd;
+      if (!dev_dirfd)
+        {
+          dev_dirfd = open ("/dev", O_PATHSEARCH | O_DIRECTORY);
+          if (dev_dirfd < 0)
+            dev_dirfd = AT_FDCWD - 1;
+        }
+      dirfd = dev_dirfd;
+    }
 
-  if (stat (line, &stats) == 0)
+  if (AT_FDCWD <= dirfd && fstatat (dirfd, line, &stats, 0) == 0)
     {
       mesg = is_tty_writable (&stats) ? '+' : '-';
       last_change = stats.st_atime;
@@ -368,15 +380,18 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)
   else
     sprintf (idlestr, "  ?");
 
-#if HAVE_UT_HOST
+#if HAVE_STRUCT_XTMP_UT_HOST
   if (utmp_ent->ut_host[0])
     {
-      char ut_host[sizeof (utmp_ent->ut_host) + 1];
       char *host = nullptr;
       char *display = nullptr;
 
-      /* Copy the host name into UT_HOST, and ensure it's nul terminated. */
-      stzncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host));
+# ifdef UT_HOST_SIZE
+      char ut_host[UT_HOST_SIZE + 1];
+      stzncpy (ut_host, utmp_ent->ut_host, UT_HOST_SIZE);
+# else
+      char *ut_host = utmp_ent->ut_host;
+# endif
 
       /* Look for an X display.  */
       display = strchr (ut_host, ':');
@@ -394,23 +409,29 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)
 
       if (display)
         {
-          if (hostlen < strlen (host) + strlen (display) + 4)
+          idx_t needed = strlen (host) + strlen (display) + 4;
+          if (hostlen < needed)
             {
-              hostlen = strlen (host) + strlen (display) + 4;
               free (hoststr);
-              hoststr = xmalloc (hostlen);
+              hoststr = xpalloc (nullptr, &hostlen, needed - hostlen, -1, 1);
             }
-          sprintf (hoststr, "(%s:%s)", host, display);
+          char *p = hoststr;
+          *p++ = '(';
+          p = stpcpy (p, host);
+          *p++ = ':';
+          strcpy (stpcpy (p, display), ")");
         }
       else
         {
-          if (hostlen < strlen (host) + 3)
+          idx_t needed = strlen (host) + 3;
+          if (hostlen < needed)
             {
-              hostlen = strlen (host) + 3;
               free (hoststr);
-              hoststr = xmalloc (hostlen);
+              hoststr = xpalloc (nullptr, &hostlen, needed - hostlen, -1, 1);
             }
-          sprintf (hoststr, "(%s)", host);
+          char *p = hoststr;
+          *p++ = '(';
+          strcpy (stpcpy (p, host), ")");
         }
 
       if (host != ut_host)
@@ -419,17 +440,13 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)
   else
     {
       if (hostlen < 1)
-        {
-          hostlen = 1;
-          free (hoststr);
-          hoststr = xmalloc (hostlen);
-        }
+        hoststr = xpalloc (hoststr, &hostlen, 1, -1, 1);
       *hoststr = '\0';
     }
 #endif
 
-  print_line (sizeof UT_USER (utmp_ent), UT_USER (utmp_ent), mesg,
-              sizeof utmp_ent->ut_line, utmp_ent->ut_line,
+  print_line (UT_USER_SIZE, UT_USER (utmp_ent), mesg,
+              UT_LINE_SIZE, utmp_ent->ut_line,
               time_string (utmp_ent), idlestr, pidstr,
               hoststr ? hoststr : "", "");
 }
@@ -444,11 +461,14 @@ print_boottime (const STRUCT_UTMP *utmp_ent)
 static char *
 make_id_equals_comment (STRUCT_UTMP const *utmp_ent)
 {
-  size_t utmpsize = sizeof UT_ID (utmp_ent);
-  char *comment = xmalloc (strlen (_("id=")) + utmpsize + 1);
-
-  char *p = stpcpy (comment, _("id="));
-  stzncpy (p, UT_ID (utmp_ent), utmpsize);
+  char const *id = UT_ID (utmp_ent);
+  idx_t idlen = strnlen (id, UT_ID_SIZE);
+  char const *prefix = _("id=");
+  idx_t prefixlen = strlen (prefix);
+  char *comment = xmalloc (prefixlen + idlen + 1);
+  char *p = mempcpy (comment, prefix, prefixlen);
+  p = mempcpy (p, id, idlen);
+  *p = '\0';
   return comment;
 }
 
@@ -470,7 +490,7 @@ print_deadprocs (const STRUCT_UTMP *utmp_ent)
 
   /* FIXME: add idle time? */
 
-  print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
+  print_line (-1, "", ' ', UT_LINE_SIZE, utmp_ent->ut_line,
               time_string (utmp_ent), "", pidstr, comment, exitstr);
   free (comment);
 }
@@ -483,7 +503,7 @@ print_login (const STRUCT_UTMP *utmp_ent)
 
   /* FIXME: add idle time? */
 
-  print_line (-1, _("LOGIN"), ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
+  print_line (-1, _("LOGIN"), ' ', UT_LINE_SIZE, utmp_ent->ut_line,
               time_string (utmp_ent), "", pidstr, comment, "");
   free (comment);
 }
@@ -494,7 +514,7 @@ print_initspawn (const STRUCT_UTMP *utmp_ent)
   char *comment = make_id_equals_comment (utmp_ent);
   PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
 
-  print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
+  print_line (-1, "", ' ', UT_LINE_SIZE, utmp_ent->ut_line,
               time_string (utmp_ent), "", pidstr, comment, "");
   free (comment);
 }
@@ -531,9 +551,9 @@ print_runlevel (const STRUCT_UTMP *utmp_ent)
 /* Print the username of each valid entry and the number of valid entries
    in UTMP_BUF, which should have N elements. */
 static void
-list_entries_who (size_t n, const STRUCT_UTMP *utmp_buf)
+list_entries_who (idx_t n, const STRUCT_UTMP *utmp_buf)
 {
-  unsigned long int entries = 0;
+  idx_t entries = 0;
   char const *separator = "";
 
   while (n--)
@@ -551,7 +571,7 @@ list_entries_who (size_t n, const STRUCT_UTMP *utmp_buf)
         }
       utmp_buf++;
     }
-  printf (_("\n# users=%lu\n"), entries);
+  printf (_("\n# users=%td\n"), entries);
 }
 
 static void
@@ -561,9 +581,15 @@ print_heading (void)
               _("PID"), _("COMMENT"), _("EXIT"));
 }
 
+/* Work around <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109614>,
+   triggered by STREQ_LEN with a negative length.  */
+#if 11 <= __GNUC__
+# pragma GCC diagnostic ignored "-Wstringop-overread"
+#endif
+
 /* Display UTMP_BUF, which should have N entries. */
 static void
-scan_entries (size_t n, const STRUCT_UTMP *utmp_buf)
+scan_entries (idx_t n, const STRUCT_UTMP *utmp_buf)
 {
   char *ttyname_b IF_LINT ( = nullptr);
   time_t boottime = TYPE_MINIMUM (time_t);
@@ -576,15 +602,14 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf)
       ttyname_b = ttyname (STDIN_FILENO);
       if (!ttyname_b)
         return;
-      if (STRNCMP_LIT (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH) == 0)
-        ttyname_b += DEV_DIR_LEN;      /* Discard /dev/ prefix.  */
+      if (STRNCMP_LIT (ttyname_b, "/dev/") == 0)
+        ttyname_b += sizeof "/dev/" - 1;       /* Discard /dev/ prefix.  */
     }
 
   while (n--)
     {
       if (!my_line_only
-          || STREQ_LEN (ttyname_b, utmp_buf->ut_line,
-                        sizeof (utmp_buf->ut_line)))
+          || STREQ_LEN (ttyname_b, utmp_buf->ut_line, UT_LINE_SIZE))
         {
           if (need_users && IS_USER_PROCESS (utmp_buf))
             print_user (utmp_buf, boottime);
@@ -617,7 +642,7 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf)
 static void
 who (char const *filename, int options)
 {
-  size_t n_users;
+  idx_t n_users;
   STRUCT_UTMP *utmp_buf;
 
   if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)