]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
readutmp: Add option to make use of wtmpdb. master
authorBruno Haible <bruno@clisp.org>
Tue, 4 Aug 2026 15:03:56 +0000 (17:03 +0200)
committerBruno Haible <bruno@clisp.org>
Tue, 4 Aug 2026 15:03:56 +0000 (17:03 +0200)
* m4/wtmpdb.m4: New file.
* m4/readutmp.m4 (gl_READUTMP): Invoke gl_WTMPDB_CHOICE. Define
READUTMP_USE_WTMPDB. Take it into account for READUTMP_LIB.
* lib/readutmp.h (WTMPDB_FILE): New macro.
* lib/readutmp.c: Include <wtmpdb.h>.
(wtmpdb_time_to_timespec): New function.
(struct wtmpdb_locals): New type.
(wtmpdb_callback, read_utmp_from_wtmpdb): New functions.
(read_utmp): For WTMP_FILE, conditionally use read_utmp_from_wtmpdb.
* modules/readutmp (Files): Add m4/wtmpdb.m4.
(Depends-on): Add str_endswith.
* DEPENDENCIES: Mention libwtmpdb.

ChangeLog
DEPENDENCIES
lib/readutmp.c
lib/readutmp.h
m4/readutmp.m4
m4/wtmpdb.m4 [new file with mode: 0644]
modules/readutmp

index 0443b14d3019a075c0d8074157fc821477895265..521e65763195b03d4d93f257446f70d4a164f6ee 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2026-08-04  Bruno Haible  <bruno@clisp.org>
+
+       readutmp: Add option to make use of wtmpdb.
+       * m4/wtmpdb.m4: New file.
+       * m4/readutmp.m4 (gl_READUTMP): Invoke gl_WTMPDB_CHOICE. Define
+       READUTMP_USE_WTMPDB. Take it into account for READUTMP_LIB.
+       * lib/readutmp.h (WTMPDB_FILE): New macro.
+       * lib/readutmp.c: Include <wtmpdb.h>.
+       (wtmpdb_time_to_timespec): New function.
+       (struct wtmpdb_locals): New type.
+       (wtmpdb_callback, read_utmp_from_wtmpdb): New functions.
+       (read_utmp): For WTMP_FILE, conditionally use read_utmp_from_wtmpdb.
+       * modules/readutmp (Files): Add m4/wtmpdb.m4.
+       (Depends-on): Add str_endswith.
+       * DEPENDENCIES: Mention libwtmpdb.
+
 2026-08-04  Bruno Haible  <bruno@clisp.org>
 
        readutmp: Fix naming of configure option.
index 155101349b367c9ee86b5462ecb16e31b226d352..e8276a8b96a565dd0c697ded0d5669834cffc8bd 100644 (file)
@@ -416,6 +416,16 @@ For modules 'readutmp' and 'boot-time':
        - On Debian and Debian-based systems: libsystemd-dev,
        - On Red Hat distributions: systemd-devel.
        - Other: https://repology.org/project/systemd/versions
+  * wtmpdb libraries
+    + Recommended if wtmpdb is present on the system.
+      Needed for fetching login, logout, and boot times from
+      /var/log/wtmp.db instead of from the file /var/run/wtmp.
+    + Homepage:
+      https://github.com/thkukuk/wtmpdb
+    + Pre-built package name:
+       - On Debian and Debian-based systems: libwtmpdb-dev,
+       - On Red Hat distributions: --.
+       - Other: https://repology.org/project/wtmpdb/versions
 
 For modules 'terminfo' and 'termcap':
   * GNU ncurses (preferred)
index 6e43b5e956f3c66507684af46a3c96b5a648b28d..8e0fdcb0ea81c7c25578751b9ed4ca28a34d9d9e 100644 (file)
 
 #include <config.h>
 
+#if READUTMP_USE_WTMPDB
+# include <wtmpdb.h>
+/* Kill macro definitions that conflict with <utmp.h> and <utmpx.h>.  */
+# undef EMPTY
+# undef BOOT_TIME
+# undef RUNLEVEL
+# undef USER_PROCESS
+#endif
+
 #include "readutmp.h"
 
 #include <errno.h>
@@ -972,6 +981,166 @@ read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
 
 # endif
 
+# if READUTMP_USE_WTMPDB
+
+static int
+wtmpdb_time_to_timespec (const char *value, struct timespec *ts)
+{
+  if (value != NULL)
+    {
+      char *endptr;
+      unsigned long long login_usec = strtoull (value, &endptr, 10);
+      if (endptr != value && *endptr == '\0')
+        {
+          ts->tv_sec = login_usec / 1000000;
+          ts->tv_nsec = (login_usec % 1000000) * 1000;
+          return 0;
+        }
+    }
+  return -1;
+}
+
+struct wtmpdb_locals
+{
+  int options;
+  struct utmp_alloc a;
+};
+
+static int
+wtmpdb_callback (void *data, int argc, char **argv, char **column_names)
+{
+  struct wtmpdb_locals *l = (struct wtmpdb_locals *) data;
+
+  /* column_names  is typically
+     [ "ID", "Type", "User", "Login", "Logout", "TTY", "RemoteHost", "Service" ]
+   */
+
+  int id_index = -1;
+  int type_index = -1;
+  int user_index = -1;
+  int login_index = -1;
+  int logout_index = -1;
+  int tty_index = -1;
+  int remote_host_index = -1;
+  int service_index = -1;
+
+  for (int i = 0; i < argc; i++)
+    {
+      if (streq (column_names[i], "ID"))
+        id_index = i;
+      if (streq (column_names[i], "Type"))
+        type_index = i;
+      if (streq (column_names[i], "User"))
+        user_index = i;
+      if (streq (column_names[i], "Login"))
+        login_index = i;
+      if (streq (column_names[i], "Logout"))
+        logout_index = i;
+      if (streq (column_names[i], "TTY"))
+        tty_index = i;
+      if (streq (column_names[i], "RemoteHost"))
+        remote_host_index = i;
+      if (streq (column_names[i], "Service"))
+        service_index = i;
+    }
+
+  const char *id      = (id_index >= 0 ? argv[id_index] : NULL);
+  const char *type    = (type_index >= 0 ? argv[type_index] : NULL);
+  const char *user    = (user_index >= 0 ? argv[user_index] : NULL);
+  const char *login   = (login_index >= 0 ? argv[login_index] : NULL);
+  const char *logout  = (logout_index >= 0 ? argv[logout_index] : NULL);
+  const char *tty     = (tty_index >= 0 ? argv[tty_index] : NULL);
+  const char *host    = (remote_host_index >= 0 ? argv[remote_host_index] : NULL);
+  const char *service = (service_index >= 0 ? argv[service_index] : NULL);
+
+  if (type != NULL && streq (type, "1" /* BOOT_TIME */))
+    {
+      struct timespec login_ts;
+      if (wtmpdb_time_to_timespec (login, &login_ts) == 0)
+        {
+          l->a = add_utmp (l->a, l->options,
+                           "reboot", strlen ("reboot"),
+                           id, strlen (id),
+                           tty, strlen (tty),
+                           host, strlen (host),
+                           0, BOOT_TIME,
+                           login_ts, 0, 0, 0);
+
+          struct timespec logout_ts;
+          if (wtmpdb_time_to_timespec (logout, &logout_ts) == 0)
+            {
+              l->a = add_utmp (l->a, l->options,
+                               "shutdown", strlen ("shutdown"),
+                               id, strlen (id),
+                               tty, strlen (tty),
+                               host, strlen (host),
+                               0, EMPTY,
+                               logout_ts, 0, 0, 0);
+            }
+        }
+    }
+
+  if (type != NULL && streq (type, "3" /* USER_PROCESS */)
+      && login != NULL)
+    {
+      struct timespec login_ts;
+      if (wtmpdb_time_to_timespec (login, &login_ts) == 0)
+        {
+          short ctype =
+            (service != NULL && str_endswith (service, "-greeter")
+             ? LOGIN_PROCESS
+             : USER_PROCESS);
+          l->a = add_utmp (l->a, l->options,
+                           user, strlen (user),
+                           id, strlen (id),
+                           tty, strlen (tty),
+                           host, strlen (host),
+                           0, ctype,
+                           login_ts, 0, 0, 0);
+
+          struct timespec logout_ts;
+          if (wtmpdb_time_to_timespec (logout, &logout_ts) == 0)
+            {
+              l->a = add_utmp (l->a, l->options,
+                               "", 0,
+                               id, strlen (id),
+                               tty, strlen (tty),
+                               host, strlen (host),
+                               0, DEAD_PROCESS,
+                               logout_ts, 0, 0, 0);
+            }
+        }
+    }
+
+  return 0;
+}
+
+static int
+read_utmp_from_wtmpdb (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
+{
+  /* Fill entries, simulating what a utmp file would contain.  */
+  struct wtmpdb_locals locals = { options, { NULL, 0, 0, 0 } };
+
+  char *err = NULL;
+  int ret = wtmpdb_read_all_v2 (WTMPDB_FILE, wtmpdb_callback, &locals, &err);
+  free (err);
+  if (ret >= 0)
+    {
+      locals.a = finish_utmp (locals.a);
+      if (locals.a.filled > 0)
+        {
+          *n_entries = locals.a.filled;
+          *utmp_buf = locals.a.utmp;
+          return 0;
+        }
+      free (locals.a.utmp);
+    }
+
+  return -1;
+}
+
+# endif
+
 int
 read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
            int options)
@@ -982,6 +1151,17 @@ read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     return read_utmp_from_systemd (n_entries, utmp_buf, options);
 # endif
 
+# if READUTMP_USE_WTMPDB
+  if (streq (file, WTMP_FILE))
+    {
+      /* Imitate reading WTMP_FILE, using wtmpdb APIs.  */
+      int ret = read_utmp_from_wtmpdb (n_entries, utmp_buf, options);
+      if (ret >= 0)
+        return ret;
+      /* Could not read /var/log/wtmp.db.  Fall back to /var/log/wtmp.  */
+    }
+# endif
+
   return read_utmp_from_file (file, n_entries, utmp_buf, options);
 }
 
index badbbc4331e4c61713893e763db878408488fd9a..31a27c1dda2fb8400bc95d4be3fadafe31d67e9b 100644 (file)
@@ -246,7 +246,7 @@ struct utmpx32
 #endif
 
 /* Definition of WTMP_FILE.
-   On glibc systems, UTMP_FILE is "/var/log/wtmp".  */
+   On glibc systems, WTMP_FILE is "/var/log/wtmp".  */
 #if !defined WTMP_FILE && defined _PATH_WTMP
 # define WTMP_FILE _PATH_WTMP
 #endif
@@ -258,6 +258,12 @@ struct utmpx32
 # define WTMP_FILE "/etc/wtmp"
 #endif
 
+/* Definition of WTMPDB_FILE.
+   On Debian and Devuan systems, WTMPDB_FILE is "/var/log/wtmp.db".  */
+#ifndef WTMPDB_FILE
+# define WTMPDB_FILE "/var/log/wtmp.db"
+#endif
+
 /* 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.  */
index 3eeec0d08184ffdc8f933be9e6f90b0b07f84321..787fe4eaa15714fae72852feb9bd7eaf54490860 100644 (file)
@@ -1,5 +1,5 @@
 # readutmp.m4
-# serial 32
+# serial 33
 dnl Copyright (C) 2002-2026 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -9,11 +9,13 @@ dnl This file is offered as-is, without any warranty.
 AC_DEFUN([gl_READUTMP],
 [
   AC_REQUIRE([gl_SYSTEMD_CHOICE])
+  AC_REQUIRE([gl_WTMPDB_CHOICE])
 
-  dnl Set READUTMP_LIB to '-lsystemd' or '', depending on whether use of
-  dnl systemd APIs is possible and desired (only the systemd login API, here).
+  dnl Set READUTMP_LIB to '-lsystemd' or '-lwtmpdb' or '', depending on whether
+  dnl use of systemd APIs is possible and desired (only the systemd login API,
+  dnl here) and whether use of wtmpdb APIs is possible and desired.
   dnl AC_LIB_LINKFLAGS_BODY would be overkill here, since few people install
-  dnl libsystemd in non-system directories.
+  dnl libsystemd or libwtmpdb in non-system directories.
   READUTMP_LIB=
   if test "$SYSTEMD_CHOICE" = yes; then
     AC_CHECK_HEADER([systemd/sd-login.h])
@@ -42,6 +44,14 @@ AC_DEFUN([gl_READUTMP],
       fi
     fi
   fi
+  if test "$WTMPDB_CHOICE" = yes; then
+    AC_CHECK_HEADER([wtmpdb.h])
+    if test $ac_cv_header_wtmpdb_h = yes; then
+      AC_DEFINE([READUTMP_USE_WTMPDB], [1],
+        [Define if the readutmp module should use the wtmpdb API.])
+      READUTMP_LIB=$READUTMP_LIB${READUTMP_LIB:+ }'-lwtmpdb'
+    fi
+  fi
   AC_SUBST([READUTMP_LIB])
 
   gl_PREREQ_READUTMP_H
diff --git a/m4/wtmpdb.m4 b/m4/wtmpdb.m4
new file mode 100644 (file)
index 0000000..159524e
--- /dev/null
@@ -0,0 +1,25 @@
+# wtmpdb.m4
+# serial 1
+dnl Copyright (C) 2026 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl This file is offered as-is, without any warranty.
+
+# Sets WTMPDB_CHOICE to 'yes' or 'no', depending on the preferred use of
+# wtmpdb APIs.
+AC_DEFUN([gl_WTMPDB_CHOICE],
+[
+  AC_MSG_CHECKING([whether to use wtmpdb APIs])
+  AC_ARG_WITH([wtmpdb],
+    [  --with-wtmpdb           use wtmpdb APIs],
+    [WTMPDB_CHOICE="$withval"],
+    [WTMPDB_CHOICE=no])
+  AC_MSG_RESULT([$WTMPDB_CHOICE])
+  AC_SUBST([WTMPDB_CHOICE])
+])
+
+# Pre-built package name for the libwtmpdb library:
+# - On Debian and Debian-based systems: libwtmpdb-dev,
+# - On Red Hat distributions: --.
+# - Other: https://repology.org/project/wtmpdb/versions
index f74c533f782ff7005aa23fb46bb1ebc90f27b2a8..0ce43d8972a1babdaaf5e43dc15ca9c425885501 100644 (file)
@@ -7,6 +7,7 @@ lib/readutmp.c
 lib/boot-time-aux.h
 m4/readutmp.m4
 m4/systemd.m4
+m4/wtmpdb.m4
 
 Depends-on:
 extensions
@@ -19,6 +20,7 @@ stdint-h
 memeq
 streq
 strnlen
+str_endswith
 time-h
 unlocked-io-internal
 xalloc