With these two patches, read_utmp() and get_boot_time() succeed on Haiku.

Haiku has a get_system_info() call that is supposed to return the boot time.
But, like the sysctl on *BSD platforms, its value changes after the VM has
been put to sleep and resumed (and the date has changed, which happens
automatically after the network connection is restored). So this approach
is only usable as a fallback.

I did not find a file that is touched immediately after boot (other than
files that contains settings, likely to be touched at other occasions as
well). But some directory under /dev does the trick.


2023-08-12  Bruno Haible  <br...@clisp.org>

        readutmp, boot-time: On Haiku, return the boot time.
        * m4/readutmp.m4 (gl_PREREQ_READUTMP_H): Test whether <OS.h> exists.
        * lib/boot-time-aux.h (get_haiku_boot_time,
        get_haiku_boot_time_final_fallback): New functions.
        * lib/readutmp.c: Include <OS.h>.
        (read_utmp_from_file): If opening UTMP_FILE fails, continue processing
        instead of failing. Invoke get_haiku_boot_time and
        get_haiku_boot_time_final_fallback.
        * lib/boot-time.c: Include <OS.h>.
        (get_boot_time_uncached): Invoke get_haiku_boot_time and
        get_haiku_boot_time_final_fallback.

2023-08-12  Bruno Haible  <br...@clisp.org>

        readutmp: Reduce code duplication.
        * lib/readutmp.c (have_boot_time): New function.
        (read_utmp_from_file): Invoke it, instead of duplicating the same loop.

>From 826c19d3dc416244267d3d21f7a901a7163d7d22 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 12 Aug 2023 19:01:56 +0200
Subject: [PATCH 1/2] readutmp: Reduce code duplication.

* lib/readutmp.c (have_boot_time): New function.
(read_utmp_from_file): Invoke it, instead of duplicating the same loop.
---
 ChangeLog      |   6 +++
 lib/readutmp.c | 108 +++++++++++++++++++++----------------------------
 2 files changed, 52 insertions(+), 62 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d407d7b4c6..f1db83be86 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2023-08-12  Bruno Haible  <br...@clisp.org>
+
+	readutmp: Reduce code duplication.
+	* lib/readutmp.c (have_boot_time): New function.
+	(read_utmp_from_file): Invoke it, instead of duplicating the same loop.
+
 2023-08-12  Paul Eggert  <egg...@cs.ucla.edu>
 
 	c-file-type: new module
diff --git a/lib/readutmp.c b/lib/readutmp.c
index 9a5b34054a..d3ed04e242 100644
--- a/lib/readutmp.c
+++ b/lib/readutmp.c
@@ -297,6 +297,20 @@ finish_utmp (struct utmp_alloc a)
   return a;
 }
 
+/* Determine whether A already contains an entry of type BOOT_TIME.  */
+_GL_ATTRIBUTE_MAYBE_UNUSED
+static bool
+have_boot_time (struct utmp_alloc a)
+{
+  for (idx_t i = 0; i < a.filled; i++)
+    {
+      struct gl_utmp *ut = &a.utmp[i];
+      if (UT_TYPE_BOOT_TIME (ut))
+        return true;
+    }
+  return false;
+}
+
 # if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION && !HAVE_DECL_GETUTENT
 struct utmp *getutent (void);
 # endif
@@ -411,7 +425,6 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
       && file_is_utmp)
     {
-      bool have_boot_time = false;
       for (idx_t i = 0; i < a.filled; i++)
         {
           struct gl_utmp *ut = &a.utmp[i];
@@ -420,11 +433,10 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
               /* Workaround for Raspbian:  */
               if (ut->ut_ts.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
                 ut->ut_ts = runlevel_ts;
-              have_boot_time = true;
               break;
             }
         }
-      if (!have_boot_time)
+      if (!have_boot_time (a))
         {
           /* Workaround for Alpine Linux:  */
           struct timespec boot_time;
@@ -445,29 +457,17 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
      it produces wrong values after the date has been bumped in the running
      system.  */
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
-      && strcmp (file, UTMP_FILE) == 0)
+      && strcmp (file, UTMP_FILE) == 0
+      && !have_boot_time (a))
     {
-      bool have_boot_time = false;
-      for (idx_t i = 0; i < a.filled; i++)
-        {
-          struct gl_utmp *ut = &a.utmp[i];
-          if (UT_TYPE_BOOT_TIME (ut))
-            {
-              have_boot_time = true;
-              break;
-            }
-        }
-      if (!have_boot_time)
-        {
-          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);
-        }
+      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
 
@@ -557,21 +557,17 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
 
 #   if defined __OpenBSD__
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
-      && strcmp (file, UTMP_FILE) == 0)
+      && strcmp (file, UTMP_FILE) == 0
+      && !have_boot_time (a))
     {
-      /* OpenBSD's 'struct utmp' does not have an ut_type field.  */
-      bool have_boot_time = false;
-      if (!have_boot_time)
-        {
-          struct timespec boot_time;
-          if (get_openbsd_boot_time (&boot_time) >= 0)
-            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_openbsd_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
 
@@ -581,29 +577,17 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
       && defined CTL_KERN && defined KERN_BOOTTIME \
       && !defined __minix
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
-      && strcmp (file, UTMP_FILE) == 0)
+      && strcmp (file, UTMP_FILE) == 0
+      && !have_boot_time (a))
     {
-      bool have_boot_time = false;
-      for (idx_t i = 0; i < a.filled; i++)
-        {
-          struct gl_utmp *ut = &a.utmp[i];
-          if (UT_TYPE_BOOT_TIME (ut))
-            {
-              have_boot_time = true;
-              break;
-            }
-        }
-      if (!have_boot_time)
-        {
-          struct timespec boot_time;
-          if (get_bsd_boot_time_final_fallback (&boot_time) >= 0)
-            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_bsd_boot_time_final_fallback (&boot_time) >= 0)
+        a = add_utmp (a, options,
+                      "reboot", strlen ("reboot"),
+                      "", 0,
+                      "", 0,
+                      "", 0,
+                      0, BOOT_TIME, boot_time, 0, 0, 0);
     }
 #  endif
 
@@ -612,7 +596,7 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
 # if defined __CYGWIN__ || defined _WIN32
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
       && strcmp (file, UTMP_FILE) == 0
-      && a.filled == 0)
+      && !have_boot_time (a))
     {
       struct timespec boot_time;
       if (get_windows_boot_time (&boot_time) >= 0)
-- 
2.34.1

>From 92a6fbdeccc6373e554d014ba9465b8d1d9cd150 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 12 Aug 2023 19:40:28 +0200
Subject: [PATCH 2/2] readutmp, boot-time: On Haiku, return the boot time.

* m4/readutmp.m4 (gl_PREREQ_READUTMP_H): Test whether <OS.h> exists.
* lib/boot-time-aux.h (get_haiku_boot_time,
get_haiku_boot_time_final_fallback): New functions.
* lib/readutmp.c: Include <OS.h>.
(read_utmp_from_file): If opening UTMP_FILE fails, continue processing
instead of failing. Invoke get_haiku_boot_time and
get_haiku_boot_time_final_fallback.
* lib/boot-time.c: Include <OS.h>.
(get_boot_time_uncached): Invoke get_haiku_boot_time and
get_haiku_boot_time_final_fallback.
---
 ChangeLog           |  14 +++++
 lib/boot-time-aux.h |  45 ++++++++++++++
 lib/boot-time.c     |  20 +++++-
 lib/readutmp.c      | 148 +++++++++++++++++++++++++++++---------------
 m4/readutmp.m4      |   4 +-
 5 files changed, 178 insertions(+), 53 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f1db83be86..30bf3bef82 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2023-08-12  Bruno Haible  <br...@clisp.org>
+
+	readutmp, boot-time: On Haiku, return the boot time.
+	* m4/readutmp.m4 (gl_PREREQ_READUTMP_H): Test whether <OS.h> exists.
+	* lib/boot-time-aux.h (get_haiku_boot_time,
+	get_haiku_boot_time_final_fallback): New functions.
+	* lib/readutmp.c: Include <OS.h>.
+	(read_utmp_from_file): If opening UTMP_FILE fails, continue processing
+	instead of failing. Invoke get_haiku_boot_time and
+	get_haiku_boot_time_final_fallback.
+	* lib/boot-time.c: Include <OS.h>.
+	(get_boot_time_uncached): Invoke get_haiku_boot_time and
+	get_haiku_boot_time_final_fallback.
+
 2023-08-12  Bruno Haible  <br...@clisp.org>
 
 	readutmp: Reduce code duplication.
diff --git a/lib/boot-time-aux.h b/lib/boot-time-aux.h
index 8de834efd7..8006b72134 100644
--- a/lib/boot-time-aux.h
+++ b/lib/boot-time-aux.h
@@ -227,6 +227,51 @@ get_bsd_boot_time_final_fallback (struct timespec *p_boot_time)
 
 #endif
 
+#if defined __HAIKU__
+
+static int
+get_haiku_boot_time (struct timespec *p_boot_time)
+{
+  /* On Haiku, /etc/utmp does not exist.  During boot,
+       1. the current time is restored, but possibly with a wrong time zone,
+          that is, with an offset of a few hours,
+       2. some symlinks and files get created,
+       3. the various devices are brought up, in particular the network device,
+       4. the correct date and time is set,
+       5. some more device nodes get created.
+     The boot time can be retrieved by looking at a directory created during
+     phase 5, such as /dev/input.  */
+  const char * const boot_touched_file = "/dev/input";
+  struct stat statbuf;
+  if (stat (boot_touched_file, &statbuf) >= 0)
+    {
+      *p_boot_time = get_stat_mtime (&statbuf);
+      return 0;
+    }
+  return -1;
+}
+
+#endif
+
+#if HAVE_OS_H /* BeOS, Haiku */
+
+/* The following approach is only usable as a fallback, because it 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_haiku_boot_time_final_fallback (struct timespec *p_boot_time)
+{
+  system_info si;
+
+  get_system_info (&si);
+  p_boot_time->tv_sec = si.boot_time / 1000000;
+  p_boot_time->tv_nsec = (si.boot_time % 1000000) * 1000;
+  return 0;
+}
+
+#endif
+
 #if defined __CYGWIN__ || defined _WIN32
 
 static int
diff --git a/lib/boot-time.c b/lib/boot-time.c
index b27cb6c6b6..53bd8b148b 100644
--- a/lib/boot-time.c
+++ b/lib/boot-time.c
@@ -39,6 +39,10 @@
 # include <sys/sysctl.h>
 #endif
 
+#if HAVE_OS_H
+# include <OS.h>
+#endif
+
 #include "idx.h"
 #include "readutmp.h"
 #include "stat-time.h"
@@ -162,7 +166,7 @@ get_boot_time_uncached (struct timespec *p_boot_time)
     found_boot_time = runlevel_ts;
 #   endif
 
-#  else /* HP-UX */
+#  else /* HP-UX, Haiku */
 
   FILE *f = fopen (UTMP_FILE, "re");
 
@@ -216,6 +220,20 @@ get_boot_time_uncached (struct timespec *p_boot_time)
     }
 # endif
 
+# if defined __HAIKU__
+  if (found_boot_time.tv_sec == 0)
+    {
+      get_haiku_boot_time (&found_boot_time);
+    }
+# endif
+
+# if HAVE_OS_H
+  if (found_boot_time.tv_sec == 0)
+    {
+      get_haiku_boot_time_final_fallback (&found_boot_time);
+    }
+# endif
+
 # if defined __CYGWIN__ || defined _WIN32
   if (found_boot_time.tv_sec == 0)
     {
diff --git a/lib/readutmp.c b/lib/readutmp.c
index d3ed04e242..d0c6303466 100644
--- a/lib/readutmp.c
+++ b/lib/readutmp.c
@@ -47,6 +47,10 @@
 # include <sys/sysctl.h>
 #endif
 
+#if HAVE_OS_H
+# include <OS.h>
+#endif
+
 #include "stat-time.h"
 #include "xalloc.h"
 
@@ -495,64 +499,74 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     }
 #   endif
 
-#  else /* old FreeBSD, OpenBSD, HP-UX */
+#  else /* old FreeBSD, OpenBSD, HP-UX, Haiku */
 
   FILE *f = fopen (file, "re");
 
-  if (! f)
-    return -1;
-
-  for (;;)
+  if (f != NULL)
     {
-      struct UTMP_STRUCT_NAME ut;
+      for (;;)
+        {
+          struct UTMP_STRUCT_NAME ut;
 
-      if (fread (&ut, sizeof ut, 1, f) == 0)
-        break;
-      a = add_utmp (a, options,
-                    UT_USER (&ut), strnlen (UT_USER (&ut), UT_USER_SIZE),
-                    #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)
-                    ut.ut_id, strnlen (ut.ut_id, UT_ID_SIZE),
-                    #else
-                    "", 0,
-                    #endif
-                    ut.ut_line, strnlen (ut.ut_line, UT_LINE_SIZE),
-                    #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)
-                    ut.ut_host, strnlen (ut.ut_host, UT_HOST_SIZE),
-                    #else
-                    "", 0,
-                    #endif
-                    #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
-                    ut.ut_pid,
-                    #else
-                    0,
-                    #endif
-                    #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
-                    ut.ut_type,
-                    #else
-                    0,
-                    #endif
-                    #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
-                    (struct timespec) { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 },
-                    #else
-                    (struct timespec) { .tv_sec = ut.ut_time, .tv_nsec = 0 },
-                    #endif
-                    #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
-                    ut.ut_session,
-                    #else
-                    0,
-                    #endif
-                    UT_EXIT_E_TERMINATION (&ut), UT_EXIT_E_EXIT (&ut)
-                   );
-    }
+          if (fread (&ut, sizeof ut, 1, f) == 0)
+            break;
+          a = add_utmp (a, options,
+                        UT_USER (&ut), strnlen (UT_USER (&ut), UT_USER_SIZE),
+                        #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)
+                        ut.ut_id, strnlen (ut.ut_id, UT_ID_SIZE),
+                        #else
+                        "", 0,
+                        #endif
+                        ut.ut_line, strnlen (ut.ut_line, UT_LINE_SIZE),
+                        #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)
+                        ut.ut_host, strnlen (ut.ut_host, UT_HOST_SIZE),
+                        #else
+                        "", 0,
+                        #endif
+                        #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
+                        ut.ut_pid,
+                        #else
+                        0,
+                        #endif
+                        #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
+                        ut.ut_type,
+                        #else
+                        0,
+                        #endif
+                        #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
+                        (struct timespec) { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 },
+                        #else
+                        (struct timespec) { .tv_sec = ut.ut_time, .tv_nsec = 0 },
+                        #endif
+                        #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
+                        ut.ut_session,
+                        #else
+                        0,
+                        #endif
+                        UT_EXIT_E_TERMINATION (&ut), UT_EXIT_E_EXIT (&ut)
+                       );
+        }
 
-  int saved_errno = ferror (f) ? errno : 0;
-  if (fclose (f) != 0)
-    saved_errno = errno;
-  if (saved_errno != 0)
+      int saved_errno = ferror (f) ? errno : 0;
+      if (fclose (f) != 0)
+        saved_errno = errno;
+      if (saved_errno != 0)
+        {
+          free (a.utmp);
+          errno = saved_errno;
+          return -1;
+        }
+    }
+  else
     {
-      free (a.utmp);
-      errno = saved_errno;
-      return -1;
+      if (strcmp (file, UTMP_FILE) != 0)
+        {
+          int saved_errno = errno;
+          free (a.utmp);
+          errno = saved_errno;
+          return -1;
+        }
     }
 
 #   if defined __OpenBSD__
@@ -591,6 +605,38 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     }
 #  endif
 
+#  if defined __HAIKU__
+  if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
+      && strcmp (file, UTMP_FILE) == 0
+      && !have_boot_time (a))
+    {
+      struct timespec boot_time;
+      if (get_haiku_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
+
+#  if HAVE_OS_H /* BeOS, Haiku */
+  if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
+      && strcmp (file, UTMP_FILE) == 0
+      && !have_boot_time (a))
+    {
+      struct timespec boot_time;
+      if (get_haiku_boot_time_final_fallback (&boot_time) >= 0)
+        a = add_utmp (a, options,
+                      "reboot", strlen ("reboot"),
+                      "", 0,
+                      "", 0,
+                      "", 0,
+                      0, BOOT_TIME, boot_time, 0, 0, 0);
+    }
+#  endif
+
 # endif
 
 # if defined __CYGWIN__ || defined _WIN32
diff --git a/m4/readutmp.m4 b/m4/readutmp.m4
index 357bb8b3c8..827b573f46 100644
--- a/m4/readutmp.m4
+++ b/m4/readutmp.m4
@@ -1,4 +1,4 @@
-# readutmp.m4 serial 25
+# readutmp.m4 serial 26
 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,
@@ -112,4 +112,6 @@ AC_DEFUN_ONCE([gl_PREREQ_READUTMP_H]
      #endif
     ])
   AC_CHECK_FUNCS([sysctl])
+
+  AC_CHECK_HEADERS_ONCE([OS.h])
 ])
-- 
2.34.1

Reply via email to