From bea15dd14f8388a4b78b63ecfa0decad0414ec88 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 11 Aug 2023 23:30:01 +0200 Subject: [PATCH] boot-time: Simplify execution. * lib/boot-time.c: Include , , , , , , 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 | 21 ++++ lib/boot-time.c | 223 ++++++++++++++++++++++++++++++++++++++-- modules/boot-time | 14 ++- modules/boot-time-tests | 2 +- 4 files changed, 250 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 29d5c73907..bfc2954a80 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2023-08-11 Bruno Haible + + boot-time: Simplify execution. + * lib/boot-time.c: Include , , , + , , , 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 readutmp: Refactor boot time determination code. diff --git a/lib/boot-time.c b/lib/boot-time.c index fe1e13eccb..932ee22d69 100644 --- a/lib/boot-time.c +++ b/lib/boot-time.c @@ -22,22 +22,231 @@ #include "boot-time.h" #include +#include +#include +#include +#include + +#if defined __linux__ || defined __ANDROID__ +# include +# include +#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 diff --git a/modules/boot-time b/modules/boot-time index 99ae5c9fe7..ba8a2b4e6f 100644 --- a/modules/boot-time +++ b/modules/boot-time @@ -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 diff --git a/modules/boot-time-tests b/modules/boot-time-tests index 0ba9d467ca..bb56ee36e5 100644 --- a/modules/boot-time-tests +++ b/modules/boot-time-tests @@ -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) -- 2.39.5