Subject: bug#73928: Bug#1080330: coreutils: who no longer works

From: Bruno Haible <[email-masked]>
Date: Wed, 19 Feb 2025 23:51:52 +0100

Chris Hofstaedtler wrote:
> I don't really know my way around the sd-logind API, but it looks
> like filtering on the session class (returned by sd_session_get_class)
> might be fruitful.
> 
> pam_systemd(8) has an explanation on the classes.

Thanks for the hint. Let me try this commit to gnulib. Paul, does it
work for you (after installing package 'systemd-devel' and rebuilding
coreutils with --enable-systemd)?

2025-02-19  Bruno Haible  <[email-masked]>

	readutmp: Let callers distinguish LOGINs from USERs.
	Reported by Paul Eggert in
	<https://lists.gnu.org/archive/html/bug-gnulib/2025-02/msg00123.html>.
	* lib/readutmp.h (LOGIN_PROCESS, UT_TYPE_LOGIN_PROCESS): New macros.
	* lib/readutmp.c (read_utmp_from_systemd): Possibly use LOGIN_PROCESS
	instead of USER_PROCESS, depending on the session's class.

diff --git a/lib/readutmp.c b/lib/readutmp.c
index c5b6b3655b..9366dfa5c2 100644
--- a/lib/readutmp.c
+++ b/lib/readutmp.c
@@ -877,8 +877,8 @@ read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
                     }
                 }
 
-              /* Create up to two USER_PROCESS entries: one for the seat,
-                 one for the tty.  */
+              /* Create up to two USER_PROCESS or LOGIN_PROCESS entries:
+                 one for the seat, one for the tty.  */
               if (seat != NULL || tty != NULL)
                 {
                   char *user;
@@ -889,6 +889,13 @@ read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
                   if (sd_session_get_leader (session, &leader_pid) < 0)
                     leader_pid = 0;
 
+                  char *clasz;
+                  if (sd_session_get_class (session, &clasz) < 0)
+                    clasz = missing;
+                  short ctype =
+                    (strncmp (clasz, "manager", 7) == 0 ? LOGIN_PROCESS :
+                     USER_PROCESS);
+
                   char *host;
                   char *remote_host;
                   if (sd_session_get_remote_host (session, &remote_host) < 0)
@@ -932,7 +939,7 @@ read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
                                   seat, strlen (seat),
                                   host, strlen (host),
                                   leader_pid /* the best we have */,
-                                  USER_PROCESS, start_ts, leader_pid, 0, 0);
+                                  ctype, start_ts, leader_pid, 0, 0);
                   if (tty != NULL)
                     a = add_utmp (a, options,
                                   user, strlen (user),
@@ -940,10 +947,12 @@ read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
                                   tty, strlen (tty),
                                   host, strlen (host),
                                   leader_pid /* the best we have */,
-                                  USER_PROCESS, start_ts, leader_pid, 0, 0);
+                                  ctype, start_ts, leader_pid, 0, 0);
 
                   if (host != missing)
                     free (host);
+                  if (clasz != missing)
+                    free (clasz);
                   if (user != missing)
                     free (user);
                 }
diff --git a/lib/readutmp.h b/lib/readutmp.h
index b5e8133c7c..60d63df959 100644
--- a/lib/readutmp.h
+++ b/lib/readutmp.h
@@ -45,7 +45,7 @@
 # include <utmp.h>
 #endif
 
-/* Needed for BOOT_TIME and USER_PROCESS.  */
+/* Needed for BOOT_TIME, USER_PROCESS, LOGIN_PROCESS.  */
 #if HAVE_UTMPX_H
 # if defined _THREAD_SAFE && defined UTMP_DATA_INIT
     /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE
@@ -74,7 +74,8 @@ struct gl_utmp
   struct timespec ut_ts;        /* time */
   pid_t ut_pid;                 /* process ID of ? */
   pid_t ut_session;             /* process ID of session leader */
-  short ut_type;                /* BOOT_TIME, USER_PROCESS, or other */
+  short ut_type;                /* BOOT_TIME, USER_PROCESS, LOGIN_PROCESS,
+                                   or other */
   struct { int e_termination; int e_exit; } ut_exit;
 };
 
@@ -257,19 +258,21 @@ struct utmpx32
 # define WTMP_FILE "/etc/wtmp"
 #endif
 
-/* In early versions of Android, <utmp.h> did not define BOOT_TIME, only
-   USER_PROCESS.  We need to use the value that is defined in newer versions
-   of Android.  */
+/* In early versions of Android, <utmp.h> did not define BOOT_TIME or
+   LOGIN_PROCESS, only USER_PROCESS.  We need to use the value that is defined
+   in newer versions of Android.  */
 #if defined __ANDROID__ && !defined BOOT_TIME
 # define BOOT_TIME 2
+# define LOGIN_PROCESS 6
 #endif
 
 /* Some platforms, such as OpenBSD, don't have an ut_type field and don't have
-   the BOOT_TIME and USER_PROCESS macros.  But we want to support them in
-   'struct gl_utmp'.  */
+   the BOOT_TIME, USER_PROCESS, and LOGIN_PROCESS macros.  But we want to
+   support them in 'struct gl_utmp'.  */
 #if !(HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
 # define BOOT_TIME 2
 # define USER_PROCESS 0
+# define LOGIN_PROCESS 6
 #endif
 
 /* Macros that test (UT)->ut_type.  */
@@ -283,6 +286,11 @@ struct utmpx32
 #else
 # define UT_TYPE_USER_PROCESS(UT) 0
 #endif
+#ifdef LOGIN_PROCESS
+# define UT_TYPE_LOGIN_PROCESS(UT) ((UT)->ut_type == LOGIN_PROCESS)
+#else
+# define UT_TYPE_LOGIN_PROCESS(UT) 0
+#endif
 
 /* Determines whether an entry *UT corresponds to a user process.  */
 #define IS_USER_PROCESS(UT)                                    \