[PATCH 1/1] w: Prefer logind over utmp

  • From: Thorsten Kukuk <kukuk@xxxxxxxx>
  • Date: Fri, 23 Jun 2023 11:06:32 +0200

The utmp format of glibc is not Y2038 safe, not even on 64bit systems.
Query logind/elogind for all required informations instead.

Signed-off-by: Thorsten Kukuk <kukuk@xxxxxxxx>
---
 configure.ac |   7 ++
 src/w.c      | 178 ++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 169 insertions(+), 16 deletions(-)

diff --git a/configure.ac b/configure.ac
index e20a2c55..04e92757 100644
--- a/configure.ac
+++ b/configure.ac
@@ -280,6 +280,13 @@ AS_IF([test "x$with_systemd" != "xno"], [
     [PKG_CHECK_MODULES([SYSTEMD], [libsystemd-login])]
   )
   AC_DEFINE(WITH_SYSTEMD, 1, [enable systemd support])
+
+  # The functions needed to replace utmp with logind are only available
+  # with systemd v254 or later.
+  old_LIBS="$LIBS"
+  LIBS="$LIBS $SYSTEMD_LIBS"
+  AC_CHECK_FUNCS([sd_session_get_leader])
+  LIBS="$old_LIBS"
 ])
 AM_CONDITIONAL([WITH_SYSTEMD], [test x$with_systemd != xno])
 
diff --git a/src/w.c b/src/w.c
index 093cfbc7..22235b74 100644
--- a/src/w.c
+++ b/src/w.c
@@ -53,6 +53,14 @@
 #      include <utmp.h>
 #endif
 #include <arpa/inet.h>
+#ifdef WITH_SYSTEMD
+#      include <systemd/sd-login.h>
+#      include <systemd/sd-daemon.h>
+#endif
+#ifdef WITH_ELOGIND
+#      include <elogind/sd-login.h>
+#      include <elogind/sd-daemon.h>
+#endif
 
 #include "c.h"
 #include "fileutils.h"
@@ -196,7 +204,25 @@ static void print_display_or_interface(const char 
*restrict host, int len, int r
 
 
 /* This routine prints either the hostname or the IP address of the remote */
-static void print_from(const utmp_t *restrict const u, const int ip_addresses, 
const int fromlen) {
+static void print_from(
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+                      const char *session,
+#endif
+                      const utmp_t *restrict const u, const int ip_addresses, 
const int fromlen) {
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+        if (session) {
+           char *host = NULL;
+           int r;
+
+           r = sd_session_get_remote_host(session, &host);
+           if (r < 0 || host == NULL)
+               print_host("", 0, fromlen);
+           else {
+               print_host(host, strlen(host), fromlen);
+               free(host);
+           }
+       } else {
+#endif
        char buf[fromlen + 1];
        char buf_ipv6[INET6_ADDRSTRLEN];
        int len;
@@ -241,6 +267,9 @@ static void print_from(const utmp_t *restrict const u, 
const int ip_addresses, c
 #else
        print_host(u->ut_host, UT_HOSTSIZE, fromlen);
 #endif
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+       }
+#endif
 }
 
 
@@ -343,7 +372,7 @@ static int get_tty_device(const char *restrict const name)
 
     for (i=0; dev_paths[i] != NULL; i++) {
         snprintf(buf, 32, dev_paths[i], name);
-        if (stat(buf, &st) == 0)
+        if (stat(buf, &st) == 0 && (st.st_mode & S_IFMT) == S_IFCHR)
             return st.st_rdev;
     }
     return -1;
@@ -357,6 +386,9 @@ static int get_tty_device(const char *restrict const name)
  * essential core of 'w'.
  */
 static int find_best_proc(
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+        const char *session,
+#endif
         const utmp_t * restrict const u,
         const char *restrict const tty,
         unsigned long long *restrict const jcpu,
@@ -369,6 +401,7 @@ static int find_best_proc(
 #define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, reap->stacks[i], info)
 #define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i], info)
     unsigned uid = ~0U;
+    pid_t ut_pid = -1;
     int found_utpid = 0;
     int i, total_procs, line;
     unsigned long long best_time = 0;
@@ -394,6 +427,12 @@ static int find_best_proc(
     *jcpu = 0;
     *pcpu = 0;
     if (!ignoreuser) {
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+        if (session) {
+            if (sd_session_get_uid(session, &uid) < 0)
+                return 0;
+        } else {
+#endif
         char buf[UT_NAMESIZE + 1];
         struct passwd *passwd_data;
         strncpy(buf, u->ut_user, UT_NAMESIZE);
@@ -402,6 +441,9 @@ static int find_best_proc(
             return 0;
         uid = passwd_data->pw_uid;
         /* OK to have passwd_data go out of scope here */
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+       }
+#endif
     }
 
     line = get_tty_device(tty);
@@ -414,9 +456,16 @@ static int find_best_proc(
               _("Unable to load process information"));
     total_procs = reap->counts->total;
 
+    if (u)
+        ut_pid = u->ut_pid;
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+    else
+        sd_session_get_leader(session, &ut_pid);
+#endif
+
     for (i=0; i < total_procs; i++) {
         /* is this the login process? */
-        if (PIDS_GETINT(TGID) == u->ut_pid) {
+        if (PIDS_GETINT(TGID) == ut_pid) {
             found_utpid = 1;
             if (!best_time) {
                 best_time = PIDS_GETULL(START);
@@ -458,6 +507,9 @@ static int find_best_proc(
 }
 
 static void showinfo(
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+            const char *session, const char *name,
+#endif
             utmp_t * u, int formtype, int maxcmd, int from,
             const int userlen, const int fromlen, const int ip_addresses,
             const int pids)
@@ -473,14 +525,37 @@ static void showinfo(
     strcpy(cmdline, "-");
 
     hertz = procps_hertz_get();
+
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+    if (session) {
+        char *sd_tty;
+
+        if (sd_session_get_tty(session, &sd_tty) >= 0) {
+            for (i = 0; i < strlen (sd_tty); i++)
+                /* clean up tty if garbled */
+               if (isalnum(sd_tty[i]) || (sd_tty[i] == '/'))
+                   tty[i + 5] = sd_tty[i];
+               else
+                   tty[i + 5] = '\0';
+           free(sd_tty);
+       }
+    } else {
+#endif
     for (i = 0; i < UT_LINESIZE; i++)
         /* clean up tty if garbled */
         if (isalnum(u->ut_line[i]) || (u->ut_line[i] == '/'))
             tty[i + 5] = u->ut_line[i];
         else
             tty[i + 5] = '\0';
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+    }
+#endif
 
-    if (find_best_proc(u, tty + 5, &jcpu, &pcpu, cmdline, &best_pid) == 0)
+    if (find_best_proc(
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+                      session,
+#endif
+                      u, tty + 5, &jcpu, &pcpu, cmdline, &best_pid) == 0)
     /*
      * just skip if stale utmp entry (i.e. login proc doesn't
      * exist). If there is a desire a cmdline flag could be
@@ -489,20 +564,40 @@ static void showinfo(
      */
         return;
 
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+    if (name)
+      strncpy(uname, name, UT_NAMESIZE);
+    else
+#endif
+      strncpy(uname, u->ut_user, UT_NAMESIZE);
     /* force NUL term for printf */
-    strncpy(uname, u->ut_user, UT_NAMESIZE);
     uname[UT_NAMESIZE] = '\0';
 
     if (formtype) {
         printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, tty + 5);
-        if (from)
-            print_from(u, ip_addresses, fromlen);
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+        if (session) {
+            uint64_t ltime;
+
+            if (from)
+              print_from(session, NULL, ip_addresses, fromlen);
+
+            sd_session_get_start_time(session, &ltime);
+            print_logintime(ltime/((uint64_t) 1000000ULL), stdout);
+        } else {
+#endif
+            if (from)
+                print_from(NULL, u, ip_addresses, fromlen);
+
 #ifdef HAVE_UTMPX_H
-        print_logintime(u->ut_tv.tv_sec, stdout);
+            print_logintime(u->ut_tv.tv_sec, stdout);
 #else
-        print_logintime(u->ut_time, stdout);
+            print_logintime(u->ut_time, stdout);
+#endif
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+        }
 #endif
-        if (*u->ut_line == ':')
+        if (u && *u->ut_line == ':')
             /* idle unknown for xdm logins */
             printf(" ?xdm? ");
         else
@@ -518,15 +613,22 @@ static void showinfo(
     } else {
         printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, tty + 5);
         if (from)
-            print_from(u, ip_addresses, fromlen);
-        if (*u->ut_line == ':')
+           print_from(NULL, u, ip_addresses, fromlen);
+        if (u && *u->ut_line == ':')
             /* idle unknown for xdm logins */
             printf(" ?xdm? ");
         else
             print_time_ival7(idletime(tty), 0, stdout);
     }
     if (pids) {
-        pids_length = printf(" %d/%d", u->ut_pid, best_pid);
+        pid_t ut_pid = -1;
+        if (u)
+           ut_pid = u->ut_pid;
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+       else
+           sd_session_get_leader(session, &ut_pid);
+#endif
+        pids_length = printf(" %d/%d", ut_pid, best_pid);
         if (pids_length > maxcmd) {
             maxcmd = 0;
         } else if (pids_length > 0) {
@@ -692,7 +794,40 @@ int main(int argc, char **argv)
                else
                        printf(_("   IDLE WHAT\n"));
        }
-
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+       if (sd_booted() > 0) {
+               char **sessions_list;
+               int sessions;
+               int i;
+
+               sessions = sd_get_sessions (&sessions_list);
+               if (sessions < 0 && sessions != -ENOENT)
+                       error(EXIT_FAILURE, -sessions, _("error getting 
sessions"));
+
+               if (sessions >= 0) {
+                       for (int i = 0; i < sessions; i++) {
+                               char *name;
+                               int r;
+
+                               if ((r = 
sd_session_get_username(sessions_list[i], &name)) < 0)
+                                       error(EXIT_FAILURE, -r, _("get user 
name failed"));
+
+                               if (user) {
+                                       if (!strcmp(name, user))
+                                               showinfo(sessions_list[i], 
name, NULL, longform,
+                                                        maxcmd, from, userlen, 
fromlen,
+                                                        ip_addresses, pids);
+                               } else {
+                                       showinfo(sessions_list[i], name, NULL, 
longform, maxcmd,
+                                                from, userlen, fromlen, 
ip_addresses, pids);
+                               }
+                               free(name);
+                               free(sessions_list[i]);
+                       }
+                       free(sessions_list);
+               }
+       } else {
+#endif
 #ifdef HAVE_UTMPX_H
        setutxent();
 #else
@@ -711,7 +846,11 @@ int main(int argc, char **argv)
                        if (u->ut_type != USER_PROCESS)
                                continue;
                        if (!strncmp(u->ut_user, user, UT_NAMESIZE))
-                               showinfo(u, longform, maxcmd, from, userlen,
+                               showinfo(
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+                                        NULL, NULL,
+#endif
+                                        u, longform, maxcmd, from, userlen,
                                         fromlen, ip_addresses, pids);
                }
        } else {
@@ -726,7 +865,11 @@ int main(int argc, char **argv)
                        if (u->ut_type != USER_PROCESS)
                                continue;
                        if (*u->ut_user)
-                               showinfo(u, longform, maxcmd, from, userlen,
+                               showinfo(
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+                                        NULL, NULL,
+#endif
+                                        u, longform, maxcmd, from, userlen,
                                         fromlen, ip_addresses, pids);
                }
        }
@@ -735,6 +878,9 @@ int main(int argc, char **argv)
 #else
        endutent();
 #endif
+#if (defined(WITH_SYSTEMD) || defined(WITH_ELOGIND)) && 
defined(HAVE_SD_SESSION_GET_LEADER)
+       }
+#endif
 
        return EXIT_SUCCESS;
 }
-- 
2.41.0


--YiEDa0DAkWCtVeE4--

Other related posts:

  • » [PATCH 1/1] w: Prefer logind over utmp - Thorsten Kukuk