]> Savannah Git Hosting - gnulib.git/commitdiff
readutmp: Refactor boot time determination code.
authorBruno Haible <bruno@clisp.org>
Fri, 11 Aug 2023 21:20:44 +0000 (23:20 +0200)
committerBruno Haible <bruno@clisp.org>
Fri, 11 Aug 2023 21:20:44 +0000 (23:20 +0200)
* lib/boot-time-aux.h: New file, extracted from lib/readutmp.c.
* lib/readutmp.c: On Linux, include <sys/sysinfo.h> even if
!READUTMP_USE_SYSTEMD.
Include boot-time-aux.h.
(SIZEOF): Remove macro, moved to boot-time-aux.h.
(get_linux_uptime): Remove function, moved to boot-time-aux.h.
(read_utmp_from_file): Invoke get_linux_boot_time_fallback,
get_android_boot_time, get_openbsd_boot_time, get_windows_boot_time.
Code moved to boot-time-aux.h.
(get_boot_time_uncached: Invoke get_linux_boot_time_final_fallback.
Code moved to boot-time-aux.h.
* m4/readutmp.m4 (gl_PREREQ_READUTMP_H): New macro, extracted from
gl_READUTMP.
(gl_READUTMP): Invoke it.
* modules/readutmp (Files): Add lib/boot-time-aux.h.

ChangeLog
lib/boot-time-aux.h [new file with mode: 0644]
lib/readutmp.c
m4/readutmp.m4
modules/readutmp

index 6836ca6308d1e1e27d2290256b95345ccb8c88c3..29d5c73907a699f7c1f802e6c66ca958adb73ce1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2023-08-11  Bruno Haible  <bruno@clisp.org>
+
+       readutmp: Refactor boot time determination code.
+       * lib/boot-time-aux.h: New file, extracted from lib/readutmp.c.
+       * lib/readutmp.c: On Linux, include <sys/sysinfo.h> even if
+       !READUTMP_USE_SYSTEMD.
+       Include boot-time-aux.h.
+       (SIZEOF): Remove macro, moved to boot-time-aux.h.
+       (get_linux_uptime): Remove function, moved to boot-time-aux.h.
+       (read_utmp_from_file): Invoke get_linux_boot_time_fallback,
+       get_android_boot_time, get_openbsd_boot_time, get_windows_boot_time.
+       Code moved to boot-time-aux.h.
+       (get_boot_time_uncached: Invoke get_linux_boot_time_final_fallback.
+       Code moved to boot-time-aux.h.
+       * m4/readutmp.m4 (gl_PREREQ_READUTMP_H): New macro, extracted from
+       gl_READUTMP.
+       (gl_READUTMP): Invoke it.
+       * modules/readutmp (Files): Add lib/boot-time-aux.h.
+
 2023-08-11  Bruno Haible  <bruno@clisp.org>
 
        readutmp: Make 'struct utmpx32' usable by other code.
diff --git a/lib/boot-time-aux.h b/lib/boot-time-aux.h
new file mode 100644 (file)
index 0000000..fc84086
--- /dev/null
@@ -0,0 +1,228 @@
+/* Auxiliary functions for determining the time when the machine last booted.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>.  */
+
+#define SIZEOF(a) (sizeof(a)/sizeof(a[0]))
+
+#if defined __linux__ || defined __ANDROID__
+
+/* Store the uptime counter, as managed by the Linux kernel, in *P_UPTIME.
+   Return 0 upon success, -1 upon failure.  */
+_GL_ATTRIBUTE_MAYBE_UNUSED
+static int
+get_linux_uptime (struct timespec *p_uptime)
+{
+  /* The clock_gettime facility returns the uptime with a resolution of 1 µsec.
+     It is available with glibc >= 2.14.  In glibc < 2.17 it required linking
+     with librt.  */
+# if (__GLIBC__ + (__GLIBC_MINOR__ >= 17) > 2) || defined __ANDROID__
+  if (clock_gettime (CLOCK_BOOTTIME, p_uptime) >= 0)
+    return 0;
+# endif
+
+  /* /proc/uptime contains the uptime with a resolution of 0.01 sec.
+     But it does not have read permissions on Android.  */
+# if !defined __ANDROID__
+  FILE *fp = fopen ("/proc/uptime", "re");
+  if (fp != NULL)
+    {
+      char buf[32 + 1];
+      size_t n = fread (buf, 1, sizeof (buf) - 1, fp);
+      fclose (fp);
+      if (n > 0)
+        {
+          buf[n] = '\0';
+          /* buf now contains two values: the uptime and the idle time.  */
+          char *endptr;
+          double uptime = strtod (buf, &endptr);
+          if (endptr > buf)
+            {
+              p_uptime->tv_sec = (time_t) uptime;
+              p_uptime->tv_nsec = (uptime - p_uptime->tv_sec) * 1e9 + 0.5;
+              return 0;
+            }
+        }
+    }
+# endif
+
+  /* The sysinfo call returns the uptime with a resolution of 1 sec only.  */
+  struct sysinfo info;
+  if (sysinfo (&info) >= 0)
+    {
+      p_uptime->tv_sec = info.uptime;
+      p_uptime->tv_nsec = 0;
+      return 0;
+    }
+
+  return -1;
+}
+
+#endif
+
+#if defined __linux__ && !defined __ANDROID__
+
+static int
+get_linux_boot_time_fallback (struct timespec *p_boot_time)
+{
+  /* On Alpine Linux, UTMP_FILE is not filled.  It is always empty.
+     So, get the time stamp of a file that gets touched only during the
+     boot process.  */
+
+  const char * const boot_touched_files[] =
+    {
+      "/var/lib/systemd/random-seed", /* seen on distros with systemd */
+      "/var/run/utmp",                /* seen on distros with OpenRC */
+      "/var/lib/random-seed"          /* seen on older distros */
+    };
+  for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
+    {
+      const char *filename = boot_touched_files[i];
+      struct stat statbuf;
+      if (stat (filename, &statbuf) >= 0)
+        {
+          *p_boot_time = get_stat_mtime (&statbuf);
+          return 0;
+        }
+    }
+  return -1;
+}
+
+# if NEED_BOOT_TIME_FINAL_FALLBACK
+
+/* The following approach is only usable as a fallback, because it is of
+   the form
+     boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
+   and therefore produces wrong values after the date has been bumped in the
+   running system, which happens frequently if the system is running in a
+   virtual machine and this VM has been put into "saved" or "sleep" state
+   and then resumed.  */
+static int
+get_linux_boot_time_final_fallback (struct timespec *p_boot_time)
+{
+  struct timespec uptime;
+  if (get_linux_uptime (&uptime) >= 0)
+    {
+      struct timespec result;
+      /* equivalent to:
+      if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
+      */
+      if (timespec_get (&result, TIME_UTC) >= 0)
+        {
+          if (result.tv_nsec < uptime.tv_nsec)
+            {
+              result.tv_nsec += 1000000000;
+              result.tv_sec -= 1;
+            }
+          result.tv_sec -= uptime.tv_sec;
+          result.tv_nsec -= uptime.tv_nsec;
+          *p_boot_time = result;
+          return 0;
+        }
+    }
+  return -1;
+}
+
+# endif
+
+#endif
+
+#if defined __ANDROID__
+
+static int
+get_android_boot_time (struct timespec *p_boot_time)
+{
+  /* On Android, there is no /var, and normal processes don't have access
+     to system files.  Therefore use the kernel's uptime counter, although
+     it produces wrong values after the date has been bumped in the running
+     system.  */
+  struct timespec uptime;
+  if (get_linux_uptime (&uptime) >= 0)
+    {
+      struct timespec result;
+      if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
+        {
+          if (result.tv_nsec < uptime.tv_nsec)
+            {
+              result.tv_nsec += 1000000000;
+              result.tv_sec -= 1;
+            }
+          result.tv_sec -= uptime.tv_sec;
+          result.tv_nsec -= uptime.tv_nsec;
+          *p_boot_time = result;
+          return 0;
+        }
+    }
+  return -1;
+}
+
+#endif
+
+#if defined __OpenBSD__
+
+static int
+get_openbsd_boot_time (struct timespec *p_boot_time)
+{
+  /* On OpenBSD, UTMP_FILE is not filled.  It contains only dummy entries.
+     So, get the time stamp of a file that gets touched only during the
+     boot process.  */
+  const char * const boot_touched_files[] =
+    {
+      "/var/db/host.random",
+      "/var/run/utmp"
+    };
+  for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
+    {
+      const char *filename = boot_touched_files[i];
+      struct stat statbuf;
+      if (stat (filename, &statbuf) >= 0)
+        {
+          *p_boot_time = get_stat_mtime (&statbuf);
+          return 0;
+        }
+    }
+  return -1;
+}
+
+#endif
+
+# if defined __CYGWIN__ || defined _WIN32
+
+static int
+get_windows_boot_time (struct timespec *p_boot_time)
+{
+  /* On Cygwin, /var/run/utmp is empty.
+     On native Windows, <utmpx.h> and <utmp.h> don't exist.
+     Instead, on Windows, the boot time can be retrieved by looking at the
+     time stamp of a file that (normally) gets touched only during the boot
+     process, namely C:\pagefile.sys.  */
+  const char * const boot_touched_file =
+    #if defined __CYGWIN__ && !defined _WIN32
+    "/cygdrive/c/pagefile.sys"
+    #else
+    "C:\\pagefile.sys"
+    #endif
+    ;
+  struct stat statbuf;
+  if (stat (boot_touched_file, &statbuf) >= 0)
+    {
+      *p_boot_time = get_stat_mtime (&statbuf);
+      return 0;
+    }
+  return -1;
+}
+
+# endif
index 00dbbcea483caaf79ed20a61b468973d71740cfe..f2e66899bb45816857d3054f08b7cc08f3079b77 100644 (file)
@@ -31,7 +31,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 
-#if READUTMP_USE_SYSTEMD || defined __ANDROID__
+#if defined __linux__ || defined __ANDROID__
 # include <sys/sysinfo.h>
 # include <time.h>
 #endif
 /* Each of the FILE streams in this file is only used in a single thread.  */
 #include "unlocked-io.h"
 
+/* Some helper functions.  */
+#define NEED_BOOT_TIME_FINAL_FALLBACK READUTMP_USE_SYSTEMD
+#include "boot-time-aux.h"
+
 /* The following macros describe the 'struct UTMP_STRUCT_NAME',
    *not* 'struct gl_utmp'.  */
 #undef UT_USER
 /* Size of the ut->ut_host member.  */
 #define UT_HOST_SIZE  sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_host)
 
-#define SIZEOF(a) (sizeof(a)/sizeof(a[0]))
-
 #if 8 <= __GNUC__
 # pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
 #endif
@@ -288,60 +290,6 @@ finish_utmp (struct utmp_alloc a)
   return a;
 }
 
-# if READUTMP_USE_SYSTEMD || defined __ANDROID__
-
-/* Store the uptime counter, as managed by the Linux kernel, in *P_UPTIME.
-   Return 0 upon success, -1 upon failure.  */
-static int
-get_linux_uptime (struct timespec *p_uptime)
-{
-  /* The clock_gettime facility returns the uptime with a resolution of 1 µsec.
-     It is available with glibc >= 2.14.  In glibc < 2.17 it required linking
-     with librt.  */
-#  if (__GLIBC__ + (__GLIBC_MINOR__ >= 17) > 2) || defined __ANDROID__
-  if (clock_gettime (CLOCK_BOOTTIME, p_uptime) >= 0)
-    return 0;
-#  endif
-
-  /* /proc/uptime contains the uptime with a resolution of 0.01 sec.
-     But it does not have read permissions on Android.  */
-#  if !defined __ANDROID__
-  FILE *fp = fopen ("/proc/uptime", "re");
-  if (fp != NULL)
-    {
-      char buf[32 + 1];
-      size_t n = fread (buf, 1, sizeof (buf) - 1, fp);
-      fclose (fp);
-      if (n > 0)
-        {
-          buf[n] = '\0';
-          /* buf now contains two values: the uptime and the idle time.  */
-          char *endptr;
-          double uptime = strtod (buf, &endptr);
-          if (endptr > buf)
-            {
-              p_uptime->tv_sec = (time_t) uptime;
-              p_uptime->tv_nsec = (uptime - p_uptime->tv_sec) * 1e9 + 0.5;
-              return 0;
-            }
-        }
-    }
-#  endif
-
-  /* The sysinfo call returns the uptime with a resolution of 1 sec only.  */
-  struct sysinfo info;
-  if (sysinfo (&info) >= 0)
-    {
-      p_uptime->tv_sec = info.uptime;
-      p_uptime->tv_nsec = 0;
-      return 0;
-    }
-
-  return -1;
-}
-
-# endif
-
 # if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION && !HAVE_DECL_GETUTENT
 struct utmp *getutent (void);
 # endif
@@ -472,28 +420,14 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
       if (!have_boot_time)
         {
           /* Workaround for Alpine Linux:  */
-          const char * const boot_touched_files[] =
-            {
-              "/var/lib/systemd/random-seed", /* seen on distros with systemd */
-              "/var/run/utmp",                /* seen on distros with OpenRC */
-              "/var/lib/random-seed"          /* seen on older distros */
-            };
-          for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
-            {
-              const char *filename = boot_touched_files[i];
-              struct stat statbuf;
-              if (stat (filename, &statbuf) >= 0)
-                {
-                  struct timespec boot_time = get_stat_mtime (&statbuf);
-                  a = add_utmp (a, options,
-                                "reboot", strlen ("reboot"),
-                                "", 0,
-                                "~", strlen ("~"),
-                                "", 0,
-                                0, BOOT_TIME, boot_time, 0, 0, 0);
-                  break;
-                }
-            }
+          struct timespec boot_time;
+          if (get_linux_boot_time_fallback (&boot_time) >= 0)
+            a = add_utmp (a, options,
+                          "reboot", strlen ("reboot"),
+                          "", 0,
+                          "~", strlen ("~"),
+                          "", 0,
+                          0, BOOT_TIME, boot_time, 0, 0, 0);
         }
     }
 #   endif
@@ -518,28 +452,14 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
         }
       if (!have_boot_time)
         {
-          struct timespec uptime;
-          if (get_linux_uptime (&uptime) >= 0)
-            {
-              struct timespec result;
-              if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
-                {
-                  if (result.tv_nsec < uptime.tv_nsec)
-                    {
-                      result.tv_nsec += 1000000000;
-                      result.tv_sec -= 1;
-                    }
-                  result.tv_sec -= uptime.tv_sec;
-                  result.tv_nsec -= uptime.tv_nsec;
-                  struct timespec boot_time = result;
-                  a = add_utmp (a, options,
-                                "reboot", strlen ("reboot"),
-                                "", 0,
-                                "", 0,
-                                "", 0,
-                                0, BOOT_TIME, boot_time, 0, 0, 0);
-                }
-            }
+          struct timespec boot_time;
+          if (get_android_boot_time (&boot_time) >= 0)
+            a = add_utmp (a, options,
+                          "reboot", strlen ("reboot"),
+                          "", 0,
+                          "", 0,
+                          "", 0,
+                          0, BOOT_TIME, boot_time, 0, 0, 0);
         }
     }
 #   endif
@@ -629,9 +549,6 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     }
 
 #   if defined __OpenBSD__
-  /* On OpenBSD, UTMP_FILE is not filled.  It contains only dummy entries.
-     So, fake a BOOT_TIME entry, by getting the time stamp of a file that
-     gets touched only during the boot process.  */
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
       && strcmp (file, UTMP_FILE) == 0)
     {
@@ -639,26 +556,16 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
       bool have_boot_time = false;
       if (!have_boot_time)
         {
-          const char * const boot_touched_files[] =
-            {
-              "/var/db/host.random",
-              "/var/run/utmp"
-            };
-          for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
+          struct timespec boot_time;
+          if (get_openbsd_boot_time (&boot_time) >= 0)
             {
-              const char *filename = boot_touched_files[i];
-              struct stat statbuf;
-              if (stat (filename, &statbuf) >= 0)
-                {
-                  struct timespec boot_time = get_stat_mtime (&statbuf);
-                  a = add_utmp (a, options,
-                                "reboot", strlen ("reboot"),
-                                "", 0,
-                                "", 0,
-                                "", 0,
-                                0, BOOT_TIME, boot_time, 0, 0, 0);
-                  break;
-                }
+              a = add_utmp (a, options,
+                            "reboot", strlen ("reboot"),
+                            "", 0,
+                            "", 0,
+                            "", 0,
+                            0, BOOT_TIME, boot_time, 0, 0, 0);
+              break;
             }
         }
     }
@@ -678,24 +585,14 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
       && strcmp (file, UTMP_FILE) == 0
       && a.filled == 0)
     {
-      const char * const boot_touched_file =
-        #if defined __CYGWIN__ && !defined _WIN32
-        "/cygdrive/c/pagefile.sys"
-        #else
-        "C:\\pagefile.sys"
-        #endif
-        ;
-      struct stat statbuf;
-      if (stat (boot_touched_file, &statbuf) >= 0)
-        {
-          struct timespec boot_time = get_stat_mtime (&statbuf);
-          a = add_utmp (a, options,
-                        "reboot", strlen ("reboot"),
-                        "", 0,
-                        "", 0,
-                        "", 0,
-                        0, BOOT_TIME, boot_time, 0, 0, 0);
-        }
+      struct timespec boot_time;
+      if (get_windows_boot_time (&boot_time) >= 0)
+        a = add_utmp (a, options,
+                      "reboot", strlen ("reboot"),
+                      "", 0,
+                      "", 0,
+                      "", 0,
+                      0, BOOT_TIME, boot_time, 0, 0, 0);
     }
 # endif
 
@@ -727,33 +624,10 @@ get_boot_time_uncached (void)
     free (utmp);
   }
 
-  /* The following approach is only usable as a fallback, because it is of
-     the form
-       boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
-     and therefore produces wrong values after the date has been bumped in the
-     running system, which happens frequently if the system is running in a
-     virtual machine and this VM has been put into "saved" or "sleep" state
-     and then resumed.  */
   {
-    struct timespec uptime;
-    if (get_linux_uptime (&uptime) >= 0)
-      {
-        struct timespec result;
-        /* equivalent to:
-        if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
-        */
-        if (timespec_get (&result, TIME_UTC) >= 0)
-          {
-            if (result.tv_nsec < uptime.tv_nsec)
-              {
-                result.tv_nsec += 1000000000;
-                result.tv_sec -= 1;
-              }
-            result.tv_sec -= uptime.tv_sec;
-            result.tv_nsec -= uptime.tv_nsec;
-            return result;
-          }
-      }
+    struct timespec boot_time;
+    if (get_linux_boot_time_final_fallback (&boot_time) >= 0)
+      return boot_time;
   }
 
   /* We shouldn't get here.  */
index 6ba5b2e225a985e967e9b3140ec6fa7d8d1b8027..07eebcd43d5cb4c36a91024026948aee7cb77aa8 100644 (file)
@@ -1,4 +1,4 @@
-# readutmp.m4 serial 23
+# readutmp.m4 serial 24
 dnl Copyright (C) 2002-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -6,9 +6,6 @@ dnl with or without modifications, as long as this notice is preserved.
 
 AC_DEFUN([gl_READUTMP],
 [
-  dnl Persuade utmpx.h to declare utmpxname
-  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
-
   AC_REQUIRE([gl_SYSTEMD_CHOICE])
 
   dnl Set READUTMP_LIB to '-lsystemd' or '', depending on whether use of
@@ -45,6 +42,15 @@ AC_DEFUN([gl_READUTMP],
   fi
   AC_SUBST([READUTMP_LIB])
 
+  gl_PREREQ_READUTMP_H
+])
+
+# Prerequisites of readutmp.h.
+AC_DEFUN_ONCE([gl_PREREQ_READUTMP_H],
+[
+  dnl Persuade utmpx.h to declare utmpxname
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
   AC_CHECK_HEADERS_ONCE([utmp.h utmpx.h])
   if test $ac_cv_header_utmp_h = yes || test $ac_cv_header_utmpx_h = yes; then
     dnl Prerequisites of lib/readutmp.h and lib/readutmp.c.
index 21f6de5777bd5fac11227e030fc8698bb54ccfbe..85bbf749545ce9c7c586609ca9ee410fdb8893e5 100644 (file)
@@ -4,6 +4,7 @@ Read entire utmp file into memory.
 Files:
 lib/readutmp.h
 lib/readutmp.c
+lib/boot-time-aux.h
 m4/readutmp.m4
 m4/systemd.m4