unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Bruno Haible <bruno@clisp.org>
To: Po Lu <luangruo@yahoo.com>
Cc: "Paul Eggert" <eggert@cs.ucla.edu>,
	"Robert Pluim" <rpluim@gmail.com>,
	bug-gnulib@gnu.org, "Pádraig Brady" <P@draigbrady.com>,
	"Sven Joachim" <svenjoac@gmx.de>,
	64937@debbugs.gnu.org, "Natanael Copa" <natanael.copa@gmail.com>,
	Emacs-devel@gnu.org, "Thorsten Kukuk" <kukuk@suse.com>
Subject: Re: boot time on Linux
Date: Thu, 10 Aug 2023 12:30:26 +0200	[thread overview]
Message-ID: <5521870.8Rdponrqg9@nimes> (raw)
In-Reply-To: <875y5nve2k.fsf@yahoo.com>

[-- Attachment #1: Type: text/plain, Size: 928 bytes --]

I wrote:
> > No, it isn't. The attached file, when compiled and run under Termux (which
> > doesn't have particular permissions), prints e.g.:
> >
> > from clock  : 1691616762.476870660 = 2023-08-09 21:32:42.476870660
> > from sysinfo: 1691616762.329261637 = 2023-08-09 21:32:42.329261637
> >
> > Note that this uses the kernel's uptime counter, so it will not work well
> > when the user changes the current time manually. But this is rare on Android.

It works well enough, that I'm adding it to Gnulib, through the attached patch.

Po Lu wrote:
> This uses the uptime counter (which also results in an SELinux denial
> for me, but different Android distributions have SELinux policies of
> varying strictness)

How did you run the program, and which of the two calls (clock_gettime, sysinfo)
failed for you? Maybe it depends not only on the Android version and device,
but also on the permissions required by the app?

Bruno

[-- Attachment #2: 0001-readutmp-Return-a-boot-time-also-on-Android.patch --]
[-- Type: text/x-patch, Size: 9546 bytes --]

From 9db6a91083ecca9c49bd5353c7624c6388f332fb Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 10 Aug 2023 07:59:19 +0200
Subject: [PATCH] readutmp: Return a boot time also on Android.

* lib/readutmp.c (get_linux_uptime): New function, extracted from
get_boot_time_uncached.
(read_utmp_from_file): Don't look for file time stamps on Android.
Instead, use get_linux_uptime.
(get_boot_time_uncached): Use get_linux_uptime.
---
 ChangeLog      |   9 +++
 lib/readutmp.c | 196 +++++++++++++++++++++++++++++--------------------
 2 files changed, 124 insertions(+), 81 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3c2256a7b2..b167189c03 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2023-08-10  Bruno Haible  <bruno@clisp.org>
+
+	readutmp: Return a boot time also on Android.
+	* lib/readutmp.c (get_linux_uptime): New function, extracted from
+	get_boot_time_uncached.
+	(read_utmp_from_file): Don't look for file time stamps on Android.
+	Instead, use get_linux_uptime.
+	(get_boot_time_uncached): Use get_linux_uptime.
+
 2023-08-09  Bruno Haible  <bruno@clisp.org>
 
 	readutmp: Fix a mistake (regression 2023-08-08).
diff --git a/lib/readutmp.c b/lib/readutmp.c
index ec21f5e16f..b344d8294d 100644
--- a/lib/readutmp.c
+++ b/lib/readutmp.c
@@ -31,11 +31,13 @@
 #include <stdlib.h>
 #include <stdint.h>
 
+#if READUTMP_USE_SYSTEMD || defined __ANDROID__
+# include <sys/sysinfo.h>
+# include <time.h>
+#endif
 #if READUTMP_USE_SYSTEMD
 # include <dirent.h>
-# include <sys/sysinfo.h>
 # include <systemd/sd-login.h>
-# include <time.h>
 #endif
 
 #include "stat-time.h"
@@ -284,6 +286,60 @@ 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
@@ -391,7 +447,7 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
 
   END_UTMP_ENT ();
 
-#  if defined __linux__
+#  if defined __linux__ && !defined __ANDROID__
   /* On Alpine Linux, UTMP_FILE is not filled.  It is always empty.
      So, fake a BOOT_TIME entry, by getting the time stamp of a file that
      gets touched only during the boot process.  */
@@ -436,6 +492,37 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     }
 #  endif
 
+#  if defined __ANDROID__
+  /* 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;
+            struct timespec boot_time = result;
+            a = add_utmp (a, options,
+                          "reboot", strlen ("reboot"),
+                          "", 0,
+                          "", 0,
+                          "", 0,
+                          0, BOOT_TIME, boot_time, 0, 0, 0);
+          }
+      }
+  }
+#  endif
+
 # else /* old FreeBSD, OpenBSD, HP-UX */
 
   FILE *f = fopen (file, "re");
@@ -522,7 +609,7 @@ read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
                   a = add_utmp (a, options,
                                 "reboot", strlen ("reboot"),
                                 "", 0,
-                                "", strlen (""),
+                                "", 0,
                                 "", 0,
                                 0, BOOT_TIME, boot_time, 0, 0, 0);
                   break;
@@ -562,87 +649,34 @@ get_boot_time_uncached (void)
     free (utmp);
   }
 
-  /* The following approaches are only usable as fallbacks, because they are
-     all of the form
+  /* 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 produce wrong values after the date has been bumped in the
+     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.  */
-
-  /* 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
-  struct timespec up;
-  if (clock_gettime (CLOCK_BOOTTIME, &up) >= 0)
-    {
-      struct timespec result;
-      /* equivalent to:
-      if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
-      */
-      if (timespec_get (&result, TIME_UTC) >= 0)
-        {
-          if (result.tv_nsec < up.tv_nsec)
-            {
-              result.tv_nsec += 1000000000;
-              result.tv_sec -= 1;
-            }
-          result.tv_sec -= up.tv_sec;
-          result.tv_nsec -= up.tv_nsec;
-          return result;
-        }
-    }
-#  endif
-
-  /* /proc/uptime contains the uptime with a resolution of 0.01 sec.  */
-  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)
-            {
-              struct timespec result;
-              if (0 <= timespec_get (&result, TIME_UTC))
-                {
-                  time_t uptime_sec = uptime;
-                  struct timespec up =
-                    {
-                      .tv_sec = uptime_sec,
-                      .tv_nsec = (uptime - uptime_sec) * 1e9 + 0.5
-                    };
-                  if (result.tv_nsec < up.tv_nsec)
-                    {
-                      result.tv_nsec += 1000000000;
-                      result.tv_sec -= 1;
-                    }
-                  result.tv_sec -= up.tv_sec;
-                  result.tv_nsec -= up.tv_nsec;
-                  return result;
-                }
-            }
-        }
-    }
-
-  /* The sysinfo call returns the uptime with a resolution of 1 sec only.  */
-  struct sysinfo info;
-  if (sysinfo (&info) >= 0)
-    {
-      struct timespec result;
-      if (0 <= timespec_get (&result, TIME_UTC))
-        {
-          result.tv_sec -= info.uptime;
-          return result;
-        }
-    }
+  {
+    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;
+          }
+      }
+  }
 
   /* We shouldn't get here.  */
   return (struct timespec) {0};
-- 
2.34.1


  parent reply	other threads:[~2023-08-10 10:30 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87tttmpt5h.fsf@turtle.gmx.de>
     [not found] ` <20230808173430.GA27131@suse.com>
     [not found]   ` <26226778.6c9BZvbsD2@nimes>
     [not found]     ` <3732835.vtg8X0x55z@nimes>
2023-08-09 19:31       ` boot time on Linux Paul Eggert
2023-08-09 21:06         ` Bruno Haible
2023-08-09 23:53         ` Po Lu
2023-08-10  0:14           ` Bruno Haible
2023-08-10  2:14             ` Po Lu
2023-08-10  6:22               ` Paul Eggert
2023-08-10  6:58                 ` Po Lu
2023-08-10 10:30               ` Bruno Haible [this message]
2023-08-10 12:23                 ` bug#64937: " Po Lu via GNU coreutils Bug Reports
2023-08-10 12:25                   ` Bruno Haible
2023-08-10 13:04                     ` Po Lu
2023-08-10 14:12                       ` Bruno Haible
2023-08-11  8:27                         ` Po Lu
2023-08-10  9:30         ` Natanael Copa
2023-08-10  9:38           ` Po Lu
2023-08-10 10:05             ` Natanael Copa
2023-08-10 15:30           ` Bruno Haible

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5521870.8Rdponrqg9@nimes \
    --to=bruno@clisp.org \
    --cc=64937@debbugs.gnu.org \
    --cc=Emacs-devel@gnu.org \
    --cc=P@draigbrady.com \
    --cc=bug-gnulib@gnu.org \
    --cc=eggert@cs.ucla.edu \
    --cc=kukuk@suse.com \
    --cc=luangruo@yahoo.com \
    --cc=natanael.copa@gmail.com \
    --cc=rpluim@gmail.com \
    --cc=svenjoac@gmx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).