]> Savannah Git Hosting - gnulib.git/commitdiff
boot-time: Simplify execution.
authorBruno Haible <bruno@clisp.org>
Fri, 11 Aug 2023 21:30:01 +0000 (23:30 +0200)
committerBruno Haible <bruno@clisp.org>
Fri, 11 Aug 2023 21:30:01 +0000 (23:30 +0200)
* lib/boot-time.c: Include <stdio.h>, <string.h>, <sys/types.h>,
<sys/stat.h>, <sys/sysinfo.h>, <time.h>, stat-time.h, unlocked-io.h,
boot-time-aux.h.
(UT_USER): New macro, from lib/readutmp.c.
(getutent): New declaration.
(get_boot_time_uncached): New function, containing a simplified code
from lib/readutmp.c.
(get_boot_time): Don't invoke read_utmp. Instead, invoke
get_boot_time_uncached and cache the result.
* modules/boot-time (Files): Add lib/boot-time-aux.h, lib/readutmp.h,
m4/readutmp.m4.
(Depends-on): Remove readutmp. Add extensions, fopen-gnu, stat-time,
stdbool, time-h, timespec_get, unlocked-io-internal.
(configure.ac): Invoke gl_PREREQ_READUTMP_H.
(Link): Remove $(READUTMP_LIB). Add $(CLOCK_TIME_LIB).
* modules/boot-time-tests (Makefile.am): Link test-boot-time with
$(CLOCK_TIME_LIB), not with $(READUTMP_LIB).

ChangeLog
lib/boot-time.c
modules/boot-time
modules/boot-time-tests

index 29d5c73907a699f7c1f802e6c66ca958adb73ce1..bfc2954a8017c73bd8742f6ebfc1c336d818f5da 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2023-08-11  Bruno Haible  <bruno@clisp.org>
+
+       boot-time: Simplify execution.
+       * lib/boot-time.c: Include <stdio.h>, <string.h>, <sys/types.h>,
+       <sys/stat.h>, <sys/sysinfo.h>, <time.h>, stat-time.h, unlocked-io.h,
+       boot-time-aux.h.
+       (UT_USER): New macro, from lib/readutmp.c.
+       (getutent): New declaration.
+       (get_boot_time_uncached): New function, containing a simplified code
+       from lib/readutmp.c.
+       (get_boot_time): Don't invoke read_utmp. Instead, invoke
+       get_boot_time_uncached and cache the result.
+       * modules/boot-time (Files): Add lib/boot-time-aux.h, lib/readutmp.h,
+       m4/readutmp.m4.
+       (Depends-on): Remove readutmp. Add extensions, fopen-gnu, stat-time,
+       stdbool, time-h, timespec_get, unlocked-io-internal.
+       (configure.ac): Invoke gl_PREREQ_READUTMP_H.
+       (Link): Remove $(READUTMP_LIB). Add $(CLOCK_TIME_LIB).
+       * modules/boot-time-tests (Makefile.am): Link test-boot-time with
+       $(CLOCK_TIME_LIB), not with $(READUTMP_LIB).
+
 2023-08-11  Bruno Haible  <bruno@clisp.org>
 
        readutmp: Refactor boot time determination code.
index fe1e13eccbebfd1669ef9a098be8b3ae01180668..932ee22d69f5e64601bba6324a1fb2fcf51dd09a 100644 (file)
 #include "boot-time.h"
 
 #include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined __linux__ || defined __ANDROID__
+# include <sys/sysinfo.h>
+# include <time.h>
+#endif
 
 #include "idx.h"
 #include "readutmp.h"
+#include "stat-time.h"
+
+/* 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 1
+#include "boot-time-aux.h"
+
+/* The following macros describe the 'struct UTMP_STRUCT_NAME',
+   *not* 'struct gl_utmp'.  */
+#undef UT_USER
+
+/* Accessor macro for the member named ut_user or ut_name.  */
+#if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_NAME \
+     : HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_NAME)
+# define UT_USER(UT) ((UT)->ut_name)
+#else
+# define UT_USER(UT) ((UT)->ut_user)
+#endif
+
+#if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION && !HAVE_DECL_GETUTENT
+struct utmp *getutent (void);
+#endif
+
+#if defined __linux__ || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32
+
+static int
+get_boot_time_uncached (struct timespec *p_boot_time)
+{
+  struct timespec found_boot_time = {0};
+
+# if !(HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE) /* old FreeBSD, OpenBSD, native Windows */
+
+#  if defined __OpenBSD__
+  /* Workaround for OpenBSD:  */
+  get_openbsd_boot_time (&found_boot_time);
+#  endif
+
+# else
+
+  /* Try to find the boot time in the /var/run/utmp file.  */
+
+#  if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */
+
+  /* Ignore the return value for now.
+     Solaris' utmpname returns 1 upon success -- which is contrary
+     to what the GNU libc version does.  In addition, older GNU libc
+     versions are actually void.   */
+  UTMP_NAME_FUNCTION ((char *) UTMP_FILE);
+
+  SET_UTMP_ENT ();
+
+#   if (defined __linux__ && !defined __ANDROID__) || defined __minix
+  /* Timestamp of the "runlevel" entry, if any.  */
+  struct timespec runlevel_ts = {0};
+#   endif
+
+  void const *entry;
+
+  while ((entry = GET_UTMP_ENT ()) != NULL)
+    {
+      struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) entry;
+
+      struct timespec ts =
+        #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
+        { .tv_sec = ut->ut_tv.tv_sec, .tv_nsec = ut->ut_tv.tv_usec * 1000 };
+        #else
+        { .tv_sec = ut->ut_time, .tv_nsec = 0 };
+        #endif
+
+      if (ut->ut_type == BOOT_TIME)
+        found_boot_time = ts;
+
+#   if defined __linux__ && !defined __ANDROID__
+      if (memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0
+          && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0)
+        runlevel_ts = ts;
+#   endif
+#   if defined __minix
+      if (UT_USER (ut)[0] == '\0'
+          && memcmp (ut->ut_line, "run-level ", strlen ("run-level ")) == 0)
+        runlevel_ts = ts;
+#   endif
+    }
+
+  END_UTMP_ENT ();
+
+#   if defined __linux__ && !defined __ANDROID__
+  /* On Raspbian, which runs on hardware without a real-time clock, during boot,
+       1. the clock gets set to 1970-01-01 00:00:00,
+       2. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
+          ut_user = "reboot", ut_line = "~", time = 1970-01-01 00:00:05 or so,
+       3. the clock gets set to a correct value through NTP,
+       4. an entry gets written into /var/run/utmp, with
+          ut_user = "runlevel", ut_line = "~", time = correct value.
+     In this case, get the time from the "runlevel" entry.  */
+
+  /* Workaround for Raspbian:  */
+  if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
+    found_boot_time = runlevel_ts;
+  if (found_boot_time.tv_sec == 0)
+    {
+      /* Workaround for Alpine Linux:  */
+      get_linux_boot_time_fallback (&found_boot_time);
+    }
+#   endif
+
+#   if defined __ANDROID__
+  if (found_boot_time.tv_sec == 0)
+    {
+      /* Workaround for Android:  */
+      get_android_boot_time (&found_boot_time);
+    }
+#   endif
+
+#   if defined __minix
+  /* On Minix, during boot,
+       1. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
+          ut_user = "", ut_line = "system boot", time = 1970-01-01 00:00:00,
+       2. an entry gets written into /var/run/utmp, with
+          ut_user = "", ut_line = "run-level m", time = correct value.
+     In this case, copy the time from the "run-level m" entry to the
+     "system boot" entry.  */
+  if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
+    found_boot_time = runlevel_ts;
+#   endif
+
+#  else /* HP-UX */
+
+  FILE *f = fopen (UTMP_FILE, "re");
+
+  if (f != NULL)
+    {
+      for (;;)
+        {
+          struct UTMP_STRUCT_NAME ut;
+
+          if (fread (&ut, sizeof ut, 1, f) == 0)
+            break;
+
+          struct timespec ts =
+            #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
+            { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 };
+            #else
+            { .tv_sec = ut.ut_time, .tv_nsec = 0 };
+            #endif
+
+          if (ut.ut_type == BOOT_TIME)
+            found_boot_time = ts;
+        }
+
+      fclose (f);
+    }
+
+#  endif
+
+#  if defined __linux__ && !defined __ANDROID__
+  if (found_boot_time.tv_sec == 0)
+    {
+      get_linux_boot_time_final_fallback (&found_boot_time);
+    }
+#  endif
+
+# endif
+
+# if defined __CYGWIN__ || defined _WIN32
+  if (found_boot_time.tv_sec == 0)
+    {
+      /* Workaround for Windows:  */
+      get_windows_boot_time (&found_boot_time);
+    }
+# endif
+
+  if (found_boot_time.tv_sec != 0)
+    {
+      *p_boot_time = found_boot_time;
+      return 0;
+    }
+  else
+    return -1;
+}
 
 int
 get_boot_time (struct timespec *p_boot_time)
 {
-  idx_t n_entries = 0;
-  STRUCT_UTMP *utmp = NULL;
-  read_utmp (UTMP_FILE, &n_entries, &utmp, READ_UTMP_BOOT_TIME);
-  if (n_entries > 0)
+  /* Cache the result from get_boot_time_uncached.  */
+  static int volatile cached_result = -1;
+  static struct timespec volatile cached_boot_time;
+
+  if (cached_result < 0)
+    {
+      struct timespec boot_time;
+      int result = get_boot_time_uncached (&boot_time);
+      cached_boot_time = boot_time;
+      cached_result = result;
+    }
+
+  if (cached_result == 0)
     {
-      *p_boot_time = utmp[0].ut_ts;
-      free (utmp);
+      *p_boot_time = cached_boot_time;
       return 0;
     }
-  free (utmp);
+  else
+    return -1;
+}
+
+#else
+
+int
+get_boot_time (struct timespec *p_boot_time)
+{
   return -1;
 }
+
+#endif
index 99ae5c9fe773a193fdd2a321555afc7d22914048..ba8a2b4e6f4e1b112ce1f04da66a3dae77a80db1 100644 (file)
@@ -4,12 +4,22 @@ Determine the time when the machine last booted.
 Files:
 lib/boot-time.h
 lib/boot-time.c
+lib/boot-time-aux.h
+lib/readutmp.h
+m4/readutmp.m4
 
 Depends-on:
+extensions
+fopen-gnu
 idx
-readutmp
+stat-time
+stdbool
+time-h
+timespec_get
+unlocked-io-internal
 
 configure.ac:
+gl_PREREQ_READUTMP_H
 
 Makefile.am:
 lib_SOURCES += boot-time.c
@@ -18,7 +28,7 @@ Include:
 "boot-time.h"
 
 Link:
-$(READUTMP_LIB)
+$(CLOCK_TIME_LIB)
 
 License:
 GPL
index 0ba9d467ca647a90373909bf9c0d71294720c29e..bb56ee36e596ca11834057e4a82aa1b9f0a726c4 100644 (file)
@@ -8,4 +8,4 @@ configure.ac:
 Makefile.am:
 TESTS += test-boot-time
 check_PROGRAMS += test-boot-time
-test_boot_time_LDADD = $(LDADD) $(READUTMP_LIB)
+test_boot_time_LDADD = $(LDADD) $(CLOCK_TIME_LIB)