bug#64937: boot time on Linux

2023-08-10 Thread Bruno Haible
Natanael Copa wrote:
> There are machines without RTC (Raspberry PI for example), and in
> this case the time stamp may end up to be the same every reboot (if
> correctly set up it should save the shutdown time for the reboot and set
> time to this on next boot, but there is no guarantee).

Indeed, on a Raspi with Raspbian (Debian derivative), /var/run/utmp contains
a BOOT_TIME entry with time = 1970-01-01 00:00:05. Which is unusable.

The clock gets set during the boot process, apparently through NTP. Then,
some files get touched:
  /var/lib/systemd/timers/stamp-apt-daily.timer
  /var/lib/systemd/timers/stamp-apt-daily-upgrade.timer
  /var/lib/apt/daily_lock
But I suspect that they may be touched at other moments than during boot.
Therefore here, the workaround with the file time stamps does not work.

But another workaround works:


2023-08-10  Bruno Haible  

readutmp: Fix the boot time returned on Raspbian.
* lib/readutmp.c (read_utmp_from_file): When the time of the BOOT_TIME
entry is very close to the Epoch, replace it with the time from the
"runlevel"/"~" entry.

diff --git a/lib/readutmp.c b/lib/readutmp.c
index b344d8294d..e383531474 100644
--- a/lib/readutmp.c
+++ b/lib/readutmp.c
@@ -369,6 +369,12 @@ read_utmp_from_file (char const *file, idx_t *n_entries, 
STRUCT_UTMP **utmp_buf,
 
   SET_UTMP_ENT ();
 
+#  if defined __linux__ && !defined __ANDROID__
+  bool file_is_utmp = (strcmp (file, UTMP_FILE) == 0);
+  /* Timestamp of the "runlevel" entry, if any.  */
+  struct timespec runlevel_ts = {0};
+#  endif
+
   void const *entry;
 
   while ((entry = GET_UTMP_ENT ()) != NULL)
@@ -408,6 +414,13 @@ read_utmp_from_file (char const *file, idx_t *n_entries, 
STRUCT_UTMP **utmp_buf,
   struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) 
entry;
 #  endif
 
+  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
+
   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)
@@ -431,11 +444,7 @@ read_utmp_from_file (char const *file, idx_t *n_entries, 
STRUCT_UTMP **utmp_buf,
 #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
+ts,
 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : 
HAVE_STRUCT_UTMP_UT_SESSION)
 ut->ut_session,
 #else
@@ -443,6 +452,12 @@ read_utmp_from_file (char const *file, idx_t *n_entries, 
STRUCT_UTMP **utmp_buf,
 #endif
 UT_EXIT_E_TERMINATION (ut), UT_EXIT_E_EXIT (ut)
);
+#  if defined __linux__ && !defined __ANDROID__
+  if (file_is_utmp
+  && memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0
+  && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0)
+runlevel_ts = ts;
+#  endif
 }
 
   END_UTMP_ENT ();
@@ -450,9 +465,19 @@ read_utmp_from_file (char const *file, idx_t *n_entries, 
STRUCT_UTMP **utmp_buf,
 #  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.  */
+ gets touched only during the boot process.
+
+ 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, copy the time from the "runlevel" entry to the "reboot"
+ entry.  */
   if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
-  && strcmp (file, UTMP_FILE) == 0)
+  && file_is_utmp)
 {
   bool have_boot_time = false;
   for (idx_t i = 0; i < a.filled; i++)
@@ -460,12 +485,16 @@ read_utmp_from_file (char const *file, idx_t *n_entries, 
STRUCT_UTMP **utmp_buf,
   struct gl_utmp *ut = [i];
   if (UT_TYPE_BOOT_TIME (ut))
 {
+  /* Workaround for Raspbian:  */
+  if (ut->ut_ts.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
+ 

bug#64937: boot time on Linux

2023-08-10 Thread Bruno Haible
Po Lu wrote:
> >> Both clock_gettime (CLOCK_BOOTIME, ... sysinfo fail with AVC denial
> >> errors and errno set to EACCESS.
> >
> > Was this inside Termux, or inside the Emacs app?
> 
> Inside the Emacs app.

Emacs does not have the following in AndroidManifest.xml, which Termux has:



Maybe one of these makes the difference?

Bruno








bug#64937: boot time on Linux

2023-08-10 Thread Natanael Copa
Hi, I had a quick look at the thread, and I have a few comments.

1) it is openrc's bootmisc (
https://github.com/OpenRC/openrc/blob/86efc43d0e0d7569f5d2e7a58b8c461ac9f7dae8/init.d/bootmisc.in#L197)
that creates /var/run/utmp. There is no guarantee that this exists. You
can for example run emacs in a alpine docker container.
2) Even if it does exist, there is no guarantee that the timestamp is
correct. There are machines without RTC (Raspberry PI for example), and in
this case the time stamp may end up to be the same every reboot (if
correctly set up it should save the shutdown time for the reboot and set
time to this on next boot, but there is no guarantee).

For emacs' use case I'd say the best solution would be to use uptime from
sysinfo(2) and assume system time is correct (NTP client should be running
at the time emacs is started).

-nc


On Wed, Aug 9, 2023 at 9:31 PM Paul Eggert  wrote:

> [For those cc'ed, the thread's at .]
>
> On 2023-08-09 07:29, Bruno Haible wrote:
>
> > And on Alpine Linux, while /var/run/utmp is empty, its time stamp is
> > essentially the boot time.
> >
> > The approach used by Emacs, namely to look at the time stamp of
> > /var/run/random-seed, is therefore essentially one of the best
> approaches.
> > It just needs to also look at /var/lib/systemd/random-seed and - on
> Alpine
> > Linux - /var/run/utmp .
>
> Thanks for looking into this. Clearly Emacs had some catching up to do,
> since it was using a location for the random-seed file that current
> GNU/Linux distros no longer use. To try to fix this I installed the
> attached patch to Emacs master on Savannah.
>
> This patch does not address the problem for Alpine, nor I suspect for
> Android. I suppose Alpine could use the timestamp of /var/run/utmp (or
> is that /run/utmp?) but I don't know how 'configure' would reliably
> detect it's being built or cross-built for Alpine. I'll cc this to
> Natanael Copa, who does the Alpine ports for Emacs, to see whether he
> can give advice.
>
> Also, I don't know how Android records boot time so I'll cc this to Po
> Lu, the main developer for Emacs on Android.



-- 
Natanael Copa


bug#64937: boot time on Linux

2023-08-10 Thread Po Lu via GNU coreutils Bug Reports
Bruno Haible  writes:

> Po Lu wrote:
>> Both clock_gettime (CLOCK_BOOTIME, ... sysinfo fail with AVC denial
>> errors and errno set to EACCESS.
>
> Was this inside Termux, or inside the Emacs app?

Inside the Emacs app.  I'll try Termux soon: maybe the target SDK
version is the culprit.





bug#64937: "who" reports funny dates

2023-08-10 Thread Thorsten Kukuk via GNU coreutils Bug Reports
On Thu, Aug 10, Bruno Haible wrote:

> This is merely a warning, and it's already gone after today's refactorings
> in Gnulib. To get past it, either remove '-Werror' from the Makefile, or
> bootstrap against the current Gnulib:

Thanks, I know how to get past it, else I couldn't have tested the result ;)
I only wanted to make aware of it, so that it does not go lost, since this 
warning doesn't happen in all configurations.

  Thorsten

-- 
Thorsten Kukuk, Distinguished Engineer, Senior Architect, Future Technologies
SUSE Software Solutions Germany GmbH, Frankenstraße 146, 90461 Nuernberg, 
Germany
Managing Director: Ivo Totev, Andrew McDonald, Werner Knoblich
(HRB 36809, AG Nürnberg)





bug#64937: "who" reports funny dates

2023-08-10 Thread Bruno Haible
Thorsten Kukuk wrote:
> Not sure how relevant this code still is, but currently I get with this:
> 
> lib/readutmp.c: In function 'get_boot_time_uncached':
> lib/readutmp.c:326:35: error: declaration of 'up' shadows a previous local 
> [-Werror=shadow]
>   326 |   struct timespec up =
>   |   ^~
> lib/readutmp.c:286:19: note: shadowed declaration is here
>   286 |   struct timespec up;
>   |   ^~
> cc1: all warnings being treated as errors

This is merely a warning, and it's already gone after today's refactorings
in Gnulib. To get past it, either remove '-Werror' from the Makefile, or
bootstrap against the current Gnulib:
  $ GNULIB_SRCDIR=
  $ ./bootstrap --no-git --gnulib-srcdir=$GNULIB_SRCDIR

Bruno








bug#64937: boot time on Linux

2023-08-10 Thread Bruno Haible
Po Lu wrote:
> Both clock_gettime (CLOCK_BOOTIME, ... sysinfo fail with AVC denial
> errors and errno set to EACCESS.

Was this inside Termux, or inside the Emacs app?

Bruno








bug#64937: boot time on Linux

2023-08-10 Thread Po Lu via GNU coreutils Bug Reports
Bruno Haible  writes:

> 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?

Both clock_gettime (CLOCK_BOOTIME, ... sysinfo fail with AVC denial
errors and errno set to EACCESS.

I think it is a bit of a stretch to ascribe this to an app's requested
permissions, though, since none of the listed permissions available to
user programs seems pertinent.





bug#64937: "who" reports funny dates

2023-08-10 Thread Thorsten Kukuk via GNU coreutils Bug Reports


Hi,

currently testing current coreutils git checkout on a utmp/wtmp free
machine, looks good so far. Except there is a compile problem with this
patch:

On Tue, Aug 08, Bruno Haible wrote:

> 2023-08-08  Bruno Haible  
> 
>   readutmp: Get the boot time with higher precision.
>   Suggested by Thorsten Kukuk  in
>   
> .
>   * lib/readutmp.c (get_boot_time_uncached): Try clock_gettime first.
> 
> diff --git a/lib/readutmp.c b/lib/readutmp.c
> index 7ef5bfe84c..f7e43eb4a6 100644
> --- a/lib/readutmp.c
> +++ b/lib/readutmp.c
> @@ -284,6 +284,31 @@ finish_utmp (struct utmp_alloc a)
>  static struct timespec
>  get_boot_time_uncached (void)
>  {
> +  /* 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;

Not sure how relevant this code still is, but currently I get with this:

lib/readutmp.c: In function 'get_boot_time_uncached':
lib/readutmp.c:326:35: error: declaration of 'up' shadows a previous local 
[-Werror=shadow]
  326 |   struct timespec up =
  |   ^~
lib/readutmp.c:286:19: note: shadowed declaration is here
  286 |   struct timespec up;
  |   ^~
cc1: all warnings being treated as errors


  Thorsten

-- 
Thorsten Kukuk, Distinguished Engineer, Senior Architect, Future Technologies
SUSE Software Solutions Germany GmbH, Frankenstraße 146, 90461 Nuernberg, 
Germany
Managing Director: Ivo Totev, Andrew McDonald, Werner Knoblich
(HRB 36809, AG Nürnberg)





bug#64937: boot time on Linux

2023-08-10 Thread Bruno Haible
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
From 9db6a91083ecca9c49bd5353c7624c6388f332fb Mon Sep 17 00:00:00 2001
From: Bruno Haible 
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  
+
+	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  
 
 	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 
 #include 
 
+#if READUTMP_USE_SYSTEMD || defined __ANDROID__
+# include 
+# include 
+#endif
 #if READUTMP_USE_SYSTEMD
 # include 
-# include 
 # include 
-# include 
 #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, );
+  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 () >= 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.  */
+  {

bug#64937: boot time on Linux

2023-08-10 Thread Natanael Copa
On Thu, 10 Aug 2023 17:38:10 +0800
Po Lu  wrote:

> Natanael Copa  writes:
> 
> > 2) Even if it does exist, there is no guarantee that the timestamp is
> > correct. There are machines without RTC (Raspberry PI for example),
> > and in this case the time stamp may end up to be the same every reboot
> > (if correctly set up it should save the shutdown time for the reboot
> > and set time to this on next boot, but there is no guarantee).  
> 
> If so, doesn't that discount the possibility of deriving the boot time
> from the timestamp of any particular file?  Since AFAIU these machines
> lacking a TOY clock are relatively widespread?

That was sort of my point. It is better to avoid if possible. In emacs
case I believe it is.

-nc





bug#64937: boot time on Linux

2023-08-10 Thread Po Lu via GNU coreutils Bug Reports
Natanael Copa  writes:

> 2) Even if it does exist, there is no guarantee that the timestamp is
> correct. There are machines without RTC (Raspberry PI for example),
> and in this case the time stamp may end up to be the same every reboot
> (if correctly set up it should save the shutdown time for the reboot
> and set time to this on next boot, but there is no guarantee).

If so, doesn't that discount the possibility of deriving the boot time
from the timestamp of any particular file?  Since AFAIU these machines
lacking a TOY clock are relatively widespread?





bug#64937: boot time on Linux

2023-08-10 Thread Po Lu via GNU coreutils Bug Reports
Paul Eggert  writes:

> On 2023-08-09 19:14, 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), which cannot establish the precise time the system
>> started
>
> Emacs doesn't need a precise boot time. All it really needs is an
> integer that uniquely identifies the current OS boot.
>
>>  since time elapses between the read from the uptime counter and
>> the read from the RTC.
>
> Emacs allows for up to one second of slop in computing the boot
> time. (In other words, it assumes that reboots are at least one second
> apart.) So if there are minor errors in computing the boot time it
> should be OK. If the errors are greater than one second, though,
> lock-file may assume that locks are stale when they're not.

OK, but the SELinux problem still stands in the way.  There's an uptime
counter in the Settings app though -- I'll try to establish how that
works.





bug#64937: boot time on Linux

2023-08-10 Thread Paul Eggert

On 2023-08-09 19:14, 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), which cannot establish the precise time the system
started


Emacs doesn't need a precise boot time. All it really needs is an 
integer that uniquely identifies the current OS boot.



 since time elapses between the read from the uptime counter and
the read from the RTC.


Emacs allows for up to one second of slop in computing the boot time. 
(In other words, it assumes that reboots are at least one second apart.) 
So if there are minor errors in computing the boot time it should be OK. 
If the errors are greater than one second, though, lock-file may assume 
that locks are stale when they're not.