From 5e736ca6ccfa131736ab0b3a298de2cb319e7dfb Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 12 Aug 2023 19:39:11 -0700 Subject: [PATCH] Improve boot-time gathering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify Emacs proper by using Gnulib’s boot-time module instead of doing it all by hand. This should port Emacs better to obscurish hosts, as Bruno Haible has merged the best of Emacs’s and Gnulib’s boot-time gathering. * lib/boot-time-aux.h, lib/boot-time.c, lib/boot-time.h: * lib/readutmp.h, m4/readutmp.m4: New files, copied from Gnulib. * admin/merge-gnulib (GNULIB_MODULES): Add boot-time. * configure.ac: Do not check for utmp.h; the boot-time module now does this. (BOOT_TIME_FILE): Remove; no longer used. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * src/filelock.c [__FreeBSD__]: Do not include . [HAVE_UTMP_H]: Do not include utmp.h. Include boot-time instead: boot-time does the work now. (BOOT_TIME) [HAVE_ANDROID && !ANDROID_STUBIFY]: Don’t undef. (WTMP_FILE): Don’t define. (boot_time, boot_time_initialized, get_boot_time_1, get_boot_time): Remove. (get_boot_sec): New function that simply calls Gnulib get_boot_time. (lock_file_1, current_lock_owner): Use get_boot_sec instead of get_boot_time. --- admin/merge-gnulib | 2 +- configure.ac | 47 +------ lib/boot-time-aux.h | 315 ++++++++++++++++++++++++++++++++++++++++++ lib/boot-time.c | 285 ++++++++++++++++++++++++++++++++++++++ lib/boot-time.h | 44 ++++++ lib/gnulib.mk.in | 11 ++ lib/readutmp.h | 325 ++++++++++++++++++++++++++++++++++++++++++++ m4/gnulib-comp.m4 | 7 + m4/readutmp.m4 | 117 ++++++++++++++++ src/filelock.c | 172 ++--------------------- 10 files changed, 1116 insertions(+), 209 deletions(-) create mode 100644 lib/boot-time-aux.h create mode 100644 lib/boot-time.c create mode 100644 lib/boot-time.h create mode 100644 lib/readutmp.h create mode 100644 m4/readutmp.m4 diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 2a713beb01a..fe88d1106ae 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -26,7 +26,7 @@ GNULIB_URL=https://git.savannah.gnu.org/git/gnulib.git GNULIB_MODULES=' - alignasof alloca-opt binary-io byteswap c-ctype c-strcase + alignasof alloca-opt binary-io boot-time byteswap c-ctype c-strcase canonicalize-lgpl careadlinkat close-stream copy-file-range count-leading-zeros count-one-bits count-trailing-zeros diff --git a/configure.ac b/configure.ac index 35dfed247d0..0234a82b92f 100644 --- a/configure.ac +++ b/configure.ac @@ -2539,7 +2539,7 @@ AC_DEFUN sys/sysinfo.h coff.h pty.h sys/resource.h - sys/utsname.h pwd.h utmp.h util.h + sys/utsname.h pwd.h util.h sanitizer/lsan_interface.h sanitizer/asan_interface.h sanitizer/common_interface_defs.h]) @@ -2635,51 +2635,6 @@ AC_DEFUN fi AC_SUBST([AUTO_DEPEND]) -BOOT_TIME_FILE= -AC_CACHE_CHECK([for old but post-boot file], - [emacs_cv_boot_time_file], - [AS_CASE([$opsys], - [gnu-linux], - [emacs_cv_boot_time_file=unknown - AS_IF([test $cross_compiling = no], - [# systemd puts it in /var/lib/systemd. - # initscripts puts it in /var/lib/urandom (previously /var/lib). - # Linux drivers/char/random.c before 2022-02-21 suggests /var/run. - for file in \ - /var/lib/systemd/random-seed \ - /var/lib/urandom/random-seed \ - /var/lib/random-seed \ - /var/run/random-seed - do - test -f $file && { emacs_cv_boot_time_file=$file; break; } - done])], - # This isn't perfect, as some systems might have the page file in - # another place. Also, I suspect that the time stamp of that - # file might also change when Windows enlarges the file due to - # insufficient VM. Still, this seems to be the most reliable - # way; the alternative (of using GetSystemTimes) won't work on - # laptops that hibernate, because the system clock is stopped - # then. Other possibility would be to run "net statistics - # workstation" and parse the output, but that's gross. So this - # should do; if the file is not there, the boot time will be - # returned as zero, and filelock.c already handles that. - [mingw32], [emacs_cv_boot_time_file=C:/pagefile.sys], - [*], [emacs_cv_boot_time_file=not-needed])]) - -AS_CASE([$emacs_cv_boot_time_file], - [/*|*:*], [BOOT_TIME_FILE=\"$emacs_cv_boot_time_file\"], - [not-needed], [BOOT_TIME_FILE=], - [# Guess systemd if unknown. - # If guess is wrong, Emacs falls back on something else. - BOOT_TIME_FILE=\"/var/lib/systemd/random-seed\"]) - -AS_IF([test -n "$BOOT_TIME_FILE"], - [AC_DEFINE_UNQUOTED([BOOT_TIME_FILE], [$BOOT_TIME_FILE], - [Name of file that, if it exists, postdates boot and predates - the first Emacs invocation; or a null pointer if no such file is known. - This file is used only on GNU/Linux and other systems - that lack the FreeBSD-style sysctl with KERN_BOOTTIME.])]) - #### Choose a window system. ## We leave window_system equal to none if diff --git a/lib/boot-time-aux.h b/lib/boot-time-aux.h new file mode 100644 index 00000000000..348611fc85c --- /dev/null +++ b/lib/boot-time-aux.h @@ -0,0 +1,315 @@ +/* Auxiliary functions for determining the time when the machine last booted. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible . */ + +#define SIZEOF(a) (sizeof(a)/sizeof(a[0])) + +#if defined __linux__ || defined __ANDROID__ + +/* Store the uptime counter, as managed by the Linux kernel, in *P_UPTIME. + Return 0 upon success, -1 upon failure. */ +_GL_ATTRIBUTE_MAYBE_UNUSED +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, Android, or musl libc. + In glibc < 2.17 it required linking with librt. */ +# if !defined __GLIBC__ || 2 < __GLIBC__ + (17 <= __GLIBC_MINOR__) + 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. */ + time_t s = 0; + char *p; + for (p = buf; '0' <= *p && *p <= '9'; p++) + s = 10 * s + (*p - '0'); + if (buf < p) + { + long ns = 0; + if (*p++ == '.') + for (int i = 0; i < 9; i++) + ns = 10 * ns + ('0' <= *p && *p <= '9' ? *p++ - '0' : 0); + p_uptime->tv_sec = s; + p_uptime->tv_nsec = ns; + 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 defined __linux__ && !defined __ANDROID__ + +static int +get_linux_boot_time_fallback (struct timespec *p_boot_time) +{ + /* On Alpine Linux, UTMP_FILE is not filled. It is always empty. + So, get the time stamp of a file that gets touched only during the + boot process. */ + + const char * const boot_touched_files[] = + { + "/var/lib/systemd/random-seed", /* seen on distros with systemd */ + "/var/run/utmp", /* seen on distros with OpenRC */ + "/var/lib/random-seed" /* seen on older distros */ + }; + for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++) + { + const char *filename = boot_touched_files[i]; + struct stat statbuf; + if (stat (filename, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + } + return -1; +} + +/* 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 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_linux_boot_time_final_fallback (struct timespec *p_boot_time) +{ + struct timespec uptime; + if (get_linux_uptime (&uptime) >= 0) + { + struct timespec result; +# if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__) + /* Better than: + if (0 <= clock_gettime (CLOCK_REALTIME, &result)) + because timespec_get does not need -lrt in glibc 2.16. + */ + if (! timespec_get (&result, TIME_UTC)) + return -1; +# else + /* Fall back on lower-res approach that does not need -lrt. + This is good enough; on these hosts UPTIME is even lower-res. */ + struct timeval tv; + int r = gettimeofday (&tv, NULL); + if (r < 0) + return r; + result.tv_sec = tv.tv_sec; + result.tv_nsec = tv.tv_usec * 1000; +# endif + + 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; + *p_boot_time = result; + return 0; + } + return -1; +} + +#endif + +#if defined __ANDROID__ + +static int +get_android_boot_time (struct timespec *p_boot_time) +{ + /* 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; + *p_boot_time = result; + return 0; + } + } + return -1; +} + +#endif + +#if defined __OpenBSD__ + +static int +get_openbsd_boot_time (struct timespec *p_boot_time) +{ + /* On OpenBSD, UTMP_FILE is not filled. It contains only dummy entries. + So, get the time stamp of a file that gets touched only during the + boot process. */ + const char * const boot_touched_files[] = + { + "/var/db/host.random", + "/var/run/utmp" + }; + for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++) + { + const char *filename = boot_touched_files[i]; + struct stat statbuf; + if (stat (filename, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + } + return -1; +} + +#endif + +#if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \ + && defined CTL_KERN && defined KERN_BOOTTIME \ + && !defined __minix +/* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */ +/* On Minix 3.3 this sysctl produces garbage results. Therefore avoid it. */ + +/* 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_bsd_boot_time_final_fallback (struct timespec *p_boot_time) +{ + static int request[2] = { CTL_KERN, KERN_BOOTTIME }; + struct timeval result; + size_t result_len = sizeof result; + + if (sysctl (request, 2, &result, &result_len, NULL, 0) >= 0) + { + p_boot_time->tv_sec = result.tv_sec; + p_boot_time->tv_nsec = result.tv_usec * 1000; + return 0; + } + return -1; +} + +#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 +get_windows_boot_time (struct timespec *p_boot_time) +{ + /* On Cygwin, /var/run/utmp is empty. + On native Windows, and don't exist. + Instead, on Windows, the boot time can be retrieved by looking at the + time stamp of a file that (normally) gets touched only during the boot + process, namely C:\pagefile.sys. */ + const char * const boot_touched_file = + #if defined __CYGWIN__ && !defined _WIN32 + "/cygdrive/c/pagefile.sys" + #else + "C:\\pagefile.sys" + #endif + ; + struct stat statbuf; + if (stat (boot_touched_file, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + return -1; +} + +#endif diff --git a/lib/boot-time.c b/lib/boot-time.c new file mode 100644 index 00000000000..d813bfa5825 --- /dev/null +++ b/lib/boot-time.c @@ -0,0 +1,285 @@ +/* Determine the time when the machine last booted. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible . */ + +#include + +/* Specification. */ +#include "boot-time.h" + +#include +#include +#include +#include +#include + +#if defined __linux__ || defined __ANDROID__ +# include +# include +#endif + +#if HAVE_SYS_SYSCTL_H && !defined __minix +# if HAVE_SYS_PARAM_H +# include +# endif +# include +#endif + +#if HAVE_OS_H +# 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. */ +#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) + + /* 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, Haiku */ + + 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 + +# else /* old FreeBSD, OpenBSD, native Windows */ + +# if defined __OpenBSD__ + /* Workaround for OpenBSD: */ + get_openbsd_boot_time (&found_boot_time); +# endif + +# endif + +# if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \ + && defined CTL_KERN && defined KERN_BOOTTIME \ + && !defined __minix + if (found_boot_time.tv_sec == 0) + { + get_bsd_boot_time_final_fallback (&found_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) + { + /* 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) +{ + /* 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 = cached_boot_time; + return 0; + } + else + return -1; +} + +#else + +int +get_boot_time (struct timespec *p_boot_time) +{ + return -1; +} + +#endif diff --git a/lib/boot-time.h b/lib/boot-time.h new file mode 100644 index 00000000000..401e854adbb --- /dev/null +++ b/lib/boot-time.h @@ -0,0 +1,44 @@ +/* Determine the time when the machine last booted. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible . */ + +#ifndef _BOOT_TIME_H +#define _BOOT_TIME_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Store the approximate time when the machine last booted in *P_BOOT_TIME, + and return 0. If it cannot be determined, return -1. + + This function is not multithread-safe, since on many platforms it + invokes the functions setutxent, getutxent, endutxent. These + functions are needed because they may lock FILE (so that we don't + read garbage when a concurrent process writes to FILE), but their + drawback is that they have a common global state. */ +extern int get_boot_time (struct timespec *p_boot_time); + + +#ifdef __cplusplus +} +#endif + +#endif /* _BOOT_TIME_H */ diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 785bdc70c5c..3b33f39f73b 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -76,6 +76,7 @@ # alignasof \ # alloca-opt \ # binary-io \ +# boot-time \ # byteswap \ # c-ctype \ # c-strcase \ @@ -1601,6 +1602,16 @@ libgnu_a_SOURCES += binary-io.h binary-io.c endif ## end gnulib module binary-io +## begin gnulib module boot-time +ifeq (,$(OMIT_GNULIB_MODULE_boot-time)) + +libgnu_a_SOURCES += boot-time.c + +EXTRA_DIST += boot-time-aux.h boot-time.h readutmp.h + +endif +## end gnulib module boot-time + ## begin gnulib module byteswap ifeq (,$(OMIT_GNULIB_MODULE_byteswap)) diff --git a/lib/readutmp.h b/lib/readutmp.h new file mode 100644 index 00000000000..1cf588d265b --- /dev/null +++ b/lib/readutmp.h @@ -0,0 +1,325 @@ +/* Declarations for GNU's read utmp module. + + Copyright (C) 1992-2007, 2009-2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by jla; revised by djm */ + +#ifndef __READUTMP_H__ +#define __READUTMP_H__ + +/* This file uses _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_RETURNS_NONNULL, + HAVE_UTMP_H, HAVE_UTMPX_H, HAVE_STRUCT_UTMP_*, HAVE_STRUCT_UTMPX_*, + HAVE_UTMPNAME, HAVE_UTMPXNAME. */ +#if !_GL_CONFIG_H_INCLUDED +# error "Please include config.h first." +#endif + +#include "idx.h" + +#include +#include +#include + +/* AIX 4.3.3 has both utmp.h and utmpx.h, but only struct utmp + has the ut_exit member. */ +#if (HAVE_UTMPX_H && HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_EXIT \ + && ! HAVE_STRUCT_UTMPX_UT_EXIT) +# undef HAVE_UTMPX_H +#endif + +/* HPUX 10.20 needs utmp.h, for the definition of e.g., UTMP_FILE. */ +#if HAVE_UTMP_H +# include +#endif + +/* Needed for BOOT_TIME and USER_PROCESS. */ +#if HAVE_UTMPX_H +# if defined _THREAD_SAFE && defined UTMP_DATA_INIT + /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE + defined, work around the duplicate struct utmp_data declaration. */ +# define utmp_data gl_aix_4_3_workaround_utmp_data +# endif +# include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Type of entries returned by read_utmp on all platforms. */ +struct gl_utmp +{ + /* All 'char *' here are of arbitrary length and point to storage + with lifetime equal to that of this struct. */ + char *ut_user; /* User name */ + char *ut_id; /* Session ID */ + char *ut_line; /* seat / device */ + char *ut_host; /* for remote sessions: user@host or host, + for local sessions: the X11 display :N */ + struct timespec ut_ts; /* time */ + pid_t ut_pid; /* process ID of ? */ + pid_t ut_session; /* process ID of session leader */ + short ut_type; /* BOOT_TIME, USER_PROCESS, or other */ + struct { int e_termination; int e_exit; } ut_exit; +}; + +/* The following types, macros, and constants describe the 'struct gl_utmp'. */ +#define UT_USER(UT) ((UT)->ut_user) +#define UT_TIME_MEMBER(UT) ((UT)->ut_ts.tv_sec) +#define UT_PID(UT) ((UT)->ut_pid) +#define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V)) +#define UT_TYPE_NOT_DEFINED 0 +#define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination) +#define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit) + +/* Type of entry returned by read_utmp(). */ +typedef struct gl_utmp STRUCT_UTMP; + +/* Size of the UT_USER (ut) member, or -1 if unbounded. */ +enum { UT_USER_SIZE = -1 }; + +/* Size of the ut->ut_id member, or -1 if unbounded. */ +enum { UT_ID_SIZE = -1 }; + +/* Size of the ut->ut_line member, or -1 if unbounded. */ +enum { UT_LINE_SIZE = -1 }; + +/* Size of the ut->ut_host member, or -1 if unbounded. */ +enum { UT_HOST_SIZE = -1 }; + + +/* When read_utmp accesses a file (as opposed to fetching the information + from systemd), it uses the following low-level types and macros. + Keep them here, rather than moving them into readutmp.c, for backward + compatibility. */ + +#if HAVE_UTMPX_H + +/* defines 'struct utmpx' with the following fields: + + Field Type Platforms + ---------- ------ --------- + ⎡ ut_user char[] glibc, musl, macOS, FreeBSD, AIX, HP-UX, IRIX, Solaris, Cygwin + ⎣ ut_name char[] NetBSD, Minix + ut_id char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_line char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_pid pid_t glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_type short glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ⎡ ut_tv struct glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ⎢ { tv_sec; tv_usec; } + ⎣ ut_time time_t Cygwin + ut_host char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_exit struct glibc, musl, NetBSD, Minix, HP-UX, IRIX, Solaris + { e_termination; e_exit; } + ut_session [long] int glibc, musl, NetBSD, Minix, IRIX, Solaris + ⎡ ut_addr [long] int HP-UX, Cygwin + ⎢ ut_addr_v6 [u]int[4] glibc, musl + ⎣ ut_ss struct sockaddr_storage NetBSD, Minix + */ + +# if __GLIBC__ && _TIME_BITS == 64 +/* This is a near-copy of glibc's struct utmpx, which stops working + after the year 2038. Unlike the glibc version, struct utmpx32 + describes the file format even if time_t is 64 bits. */ +struct utmpx32 +{ + short int ut_type; /* Type of login. */ + pid_t ut_pid; /* Process ID of login process. */ + char ut_line[__UT_LINESIZE]; /* Devicename. */ + char ut_id[4]; /* Inittab ID. */ + char ut_user[__UT_USERSIZE]; /* Username. */ + char ut_host[__UT_HOSTSIZE]; /* Hostname for remote login. */ + struct __exit_status ut_exit; /* Exit status of a process marked + as DEAD_PROCESS. */ + /* The fields ut_session and ut_tv must be the same size when compiled + 32- and 64-bit. This allows files and shared memory to be shared + between 32- and 64-bit applications. */ + int ut_session; /* Session ID, used for windowing. */ + struct + { + /* Seconds. Unsigned not signed, as glibc did not exist before 1970, + and if the format is still in use after 2038 its timestamps + will surely have the sign bit on. This hack stops working + at 2106-02-07 06:28:16 UTC. */ + unsigned int tv_sec; + int tv_usec; /* Microseconds. */ + } ut_tv; /* Time entry was made. */ + int ut_addr_v6[4]; /* Internet address of remote host. */ + char ut_reserved[20]; /* Reserved for future use. */ +}; +# define UTMP_STRUCT_NAME utmpx32 +# else +# define UTMP_STRUCT_NAME utmpx +# endif +# define SET_UTMP_ENT setutxent +# define GET_UTMP_ENT getutxent +# define END_UTMP_ENT endutxent +# ifdef HAVE_UTMPXNAME /* glibc, musl, macOS, NetBSD, Minix, IRIX, Solaris, Cygwin */ +# define UTMP_NAME_FUNCTION utmpxname +# elif defined UTXDB_ACTIVE /* FreeBSD */ +# define UTMP_NAME_FUNCTION(x) setutxdb (UTXDB_ACTIVE, x) +# endif + +#elif HAVE_UTMP_H + +/* defines 'struct utmp' with the following fields: + + Field Type Platforms + ---------- ------ --------- + ⎡ ut_user char[] glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ⎣ ut_name char[] macOS, old FreeBSD, NetBSD, OpenBSD, Minix + ut_id char[] glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ut_line char[] glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ut_pid pid_t glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ut_type short glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ⎡ ut_tv struct glibc, musl, Android + ⎢ { tv_sec; tv_usec; } + ⎣ ut_time time_t macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_host char[] glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, Cygwin, Android + ut_exit struct glibc, musl, AIX, HP-UX, IRIX, Solaris, Android + { e_termination; e_exit; } + ut_session [long] int glibc, musl, Android + ⎡ ut_addr [long] int HP-UX, Cygwin + ⎣ ut_addr_v6 [u]int[4] glibc, musl, Android + */ + +# define UTMP_STRUCT_NAME utmp +# define SET_UTMP_ENT setutent +# define GET_UTMP_ENT getutent +# define END_UTMP_ENT endutent +# ifdef HAVE_UTMPNAME /* glibc, musl, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android */ +# define UTMP_NAME_FUNCTION utmpname +# endif + +#endif + +/* Evaluates to 1 if gl_utmp's ut_id field may ever have a non-zero value. */ +#define HAVE_STRUCT_XTMP_UT_ID \ + (READUTMP_USE_SYSTEMD \ + || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)) + +/* Evaluates to 1 if gl_utmp's ut_pid field may ever have a non-zero value. */ +#define HAVE_STRUCT_XTMP_UT_PID \ + (READUTMP_USE_SYSTEMD \ + || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)) + +/* Evaluates to 1 if gl_utmp's ut_host field may ever be non-empty. */ +#define HAVE_STRUCT_XTMP_UT_HOST \ + (READUTMP_USE_SYSTEMD \ + || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)) + +/* Definition of UTMP_FILE. + On glibc systems, UTMP_FILE is "/var/run/utmp". */ +#if !defined UTMP_FILE && defined _PATH_UTMP +# define UTMP_FILE _PATH_UTMP +#endif +#ifdef UTMPX_FILE /* Solaris, SysVr4 */ +# undef UTMP_FILE +# define UTMP_FILE UTMPX_FILE +#endif +#ifndef UTMP_FILE +# define UTMP_FILE "/etc/utmp" +#endif + +/* Definition of WTMP_FILE. + On glibc systems, UTMP_FILE is "/var/log/wtmp". */ +#if !defined WTMP_FILE && defined _PATH_WTMP +# define WTMP_FILE _PATH_WTMP +#endif +#ifdef WTMPX_FILE /* Solaris, SysVr4 */ +# undef WTMP_FILE +# define WTMP_FILE WTMPX_FILE +#endif +#ifndef WTMP_FILE +# define WTMP_FILE "/etc/wtmp" +#endif + +/* Some platforms, such as OpenBSD, don't have an ut_type field and don't have + the BOOT_TIME and USER_PROCESS macros. But we want to support them in + 'struct gl_utmp'. */ +#if !(HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE) +# define BOOT_TIME 2 +# define USER_PROCESS 0 +#endif + +/* Macros that test (UT)->ut_type. */ +#ifdef BOOT_TIME +# define UT_TYPE_BOOT_TIME(UT) UT_TYPE_EQ (UT, BOOT_TIME) +#else +# define UT_TYPE_BOOT_TIME(UT) 0 +#endif +#ifdef USER_PROCESS +# define UT_TYPE_USER_PROCESS(UT) UT_TYPE_EQ (UT, USER_PROCESS) +#else +# define UT_TYPE_USER_PROCESS(UT) 0 +#endif + +/* Determines whether an entry *UT corresponds to a user process. */ +#define IS_USER_PROCESS(UT) \ + (UT_USER (UT)[0] \ + && (UT_TYPE_USER_PROCESS (UT) \ + || (UT_TYPE_NOT_DEFINED && UT_TIME_MEMBER (UT) != 0))) + +/* Define if read_utmp is not just a dummy. */ +#if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32 +# define READ_UTMP_SUPPORTED 1 +#endif + +/* Options for read_utmp. */ +enum + { + READ_UTMP_CHECK_PIDS = 1, + READ_UTMP_USER_PROCESS = 2, + READ_UTMP_BOOT_TIME = 4, + READ_UTMP_NO_BOOT_TIME = 8 + }; + +/* Return a copy of (UT)->ut_user, without trailing spaces, + as a freshly allocated string. */ +char *extract_trimmed_name (const STRUCT_UTMP *ut) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE + _GL_ATTRIBUTE_RETURNS_NONNULL; + +/* Read the utmp entries corresponding to file FILE into freshly- + malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to + the number of entries, and return zero. If there is any error, + return -1, setting errno, and don't modify the parameters. + A good candidate for FILE is UTMP_FILE. + If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose + process-IDs do not currently exist. + If OPTIONS & READ_UTMP_USER_PROCESS is nonzero, omit entries which + do not correspond to a user process. + If OPTIONS & READ_UTMP_BOOT_TIME is nonzero, omit all entries except + the one that contains the boot time. + If OPTIONS & READ_UTMP_NO_BOOT_TIME is nonzero, omit the boot time + entries. + + This function is not multithread-safe, since on many platforms it + invokes the functions setutxent, getutxent, endutxent. These + functions are needed because they may lock FILE (so that we don't + read garbage when a concurrent process writes to FILE), but their + drawback is that they have a common global state. */ +int read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf, + int options); + + +#ifdef __cplusplus +} +#endif + +#endif /* __READUTMP_H__ */ diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 3382e9bc241..14ff92040a4 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -51,6 +51,7 @@ AC_DEFUN # Code from module at-internal: # Code from module attribute: # Code from module binary-io: + # Code from module boot-time: # Code from module builtin-expect: # Code from module byteswap: # Code from module c-ctype: @@ -243,6 +244,7 @@ AC_DEFUN gl_ASSERT_H gl_CONDITIONAL_HEADER([assert.h]) AC_PROG_MKDIR_P + gl_PREREQ_READUTMP_H gl___BUILTIN_EXPECT gl_BYTESWAP gl_CONDITIONAL_HEADER([byteswap.h]) @@ -1252,6 +1254,9 @@ AC_DEFUN lib/attribute.h lib/binary-io.c lib/binary-io.h + lib/boot-time-aux.h + lib/boot-time.c + lib/boot-time.h lib/byteswap.in.h lib/c++defs.h lib/c-ctype.c @@ -1383,6 +1388,7 @@ AC_DEFUN lib/rawmemchr.valgrind lib/readlink.c lib/readlinkat.c + lib/readutmp.h lib/realloc.c lib/regcomp.c lib/regex.c @@ -1542,6 +1548,7 @@ AC_DEFUN m4/rawmemchr.m4 m4/readlink.m4 m4/readlinkat.m4 + m4/readutmp.m4 m4/realloc.m4 m4/regex.m4 m4/sha1.m4 diff --git a/m4/readutmp.m4 b/m4/readutmp.m4 new file mode 100644 index 00000000000..fff8d4eb7bf --- /dev/null +++ b/m4/readutmp.m4 @@ -0,0 +1,117 @@ +# readutmp.m4 serial 28 +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, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_READUTMP], +[ + AC_REQUIRE([gl_SYSTEMD_CHOICE]) + + dnl Set READUTMP_LIB to '-lsystemd' or '', depending on whether use of + dnl systemd APIs is possible and desired (only the systemd login API, here). + dnl AC_LIB_LINKFLAGS_BODY would be overkill here, since few people install + dnl libsystemd in non-system directories. + READUTMP_LIB= + if test "$SYSTEMD_CHOICE" = yes; then + AC_CHECK_HEADER([systemd/sd-login.h]) + if test $ac_cv_header_systemd_sd_login_h = yes; then + AC_CACHE_CHECK([for libsystemd version >= 254], + [gl_cv_lib_readutmp_systemd], + [gl_save_LIBS="$LIBS" + LIBS="$LIBS -lsystemd" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + uint64_t st; + sd_session_get_start_time ("1", &st); + ]]) + ], + [gl_cv_lib_readutmp_systemd=yes], + [gl_cv_lib_readutmp_systemd=no]) + LIBS="$gl_save_LIBS" + ]) + if test $gl_cv_lib_readutmp_systemd = yes; then + AC_DEFINE([READUTMP_USE_SYSTEMD], [1], + [Define if the readutmp module should use the systemd login API.]) + READUTMP_LIB='-lsystemd' + fi + fi + fi + AC_SUBST([READUTMP_LIB]) + + gl_PREREQ_READUTMP_H +]) + +# Prerequisites of readutmp.h and boot-time-aux.h. +AC_DEFUN_ONCE([gl_PREREQ_READUTMP_H], +[ + dnl Persuade utmpx.h to declare utmpxname + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_HEADERS_ONCE([utmp.h utmpx.h]) + if test $ac_cv_header_utmp_h = yes || test $ac_cv_header_utmpx_h = yes; then + dnl Prerequisites of lib/readutmp.h and lib/readutmp.c. + AC_CHECK_FUNCS_ONCE([utmpname utmpxname]) + AC_CHECK_DECLS([getutent],,,[[ +/* is a prerequisite of on FreeBSD 8.0, OpenBSD 4.6. */ +#include +#ifdef HAVE_UTMP_H +# include +#endif +]]) + utmp_includes="\ +AC_INCLUDES_DEFAULT +#ifdef HAVE_UTMPX_H +# include +#endif +#ifdef HAVE_UTMP_H +# if defined _THREAD_SAFE && defined UTMP_DATA_INIT + /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE + defined, work around the duplicate struct utmp_data declaration. */ +# define utmp_data gl_aix_4_3_workaround_utmp_data +# endif +# include +#endif +" + AC_CHECK_MEMBERS([struct utmpx.ut_user],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_user],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_name],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_name],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_type],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_type],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_pid],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_pid],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_tv],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_host],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_host],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_id],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_id],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_session],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_session],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_exit],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_exit],,,[$utmp_includes]) + + AC_CHECK_MEMBERS([struct utmpx.ut_exit.ut_exit],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_exit.e_exit],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_exit.e_exit],,,[$utmp_includes]) + + AC_CHECK_MEMBERS([struct utmpx.ut_exit.ut_termination],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_exit.e_termination],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_exit.e_termination],,,[$utmp_includes]) + fi + + AC_CHECK_HEADERS_ONCE([sys/param.h]) + dnl requires on OpenBSD 4.0. + AC_CHECK_HEADERS([sys/sysctl.h],,, + [AC_INCLUDES_DEFAULT + #if HAVE_SYS_PARAM_H + # include + #endif + ]) + AC_CHECK_FUNCS([sysctl]) + + AC_CHECK_HEADERS_ONCE([OS.h]) +]) diff --git a/src/filelock.c b/src/filelock.c index 3b1ff8ad566..d2161f1e58a 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -36,13 +36,9 @@ Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2023 Free Software #include #include #include - -#ifdef __FreeBSD__ -#include -#endif /* __FreeBSD__ */ - #include +#include #include #include "lisp.h" @@ -55,20 +51,6 @@ Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2023 Free Software #ifndef MSDOS -#ifdef HAVE_UTMP_H -#include -#endif - -/* Boot time is not available on Android. */ - -#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY -#undef BOOT_TIME -#endif - -#if !defined WTMP_FILE && !defined WINDOWSNT && defined BOOT_TIME -#define WTMP_FILE "/var/log/wtmp" -#endif - #ifdef HAVE_ANDROID #include "android.h" /* For `android_is_special_directory'. */ #endif /* HAVE_ANDROID */ @@ -127,153 +109,19 @@ #define WTMP_FILE "/var/log/wtmp" /* Return the time of the last system boot. */ -static time_t boot_time; -static bool boot_time_initialized; - -#ifdef BOOT_TIME -static void get_boot_time_1 (const char *, bool); -#endif - static time_t -get_boot_time (void) +get_boot_sec (void) { - if (boot_time_initialized) - return boot_time; - boot_time_initialized = 1; - -#if defined (CTL_KERN) && defined (KERN_BOOTTIME) - { - int mib[2]; - size_t size; - struct timeval boottime_val; - - mib[0] = CTL_KERN; - mib[1] = KERN_BOOTTIME; - size = sizeof (boottime_val); - - if (sysctl (mib, 2, &boottime_val, &size, NULL, 0) >= 0 && size != 0) - { - boot_time = boottime_val.tv_sec; - return boot_time; - } - } -#endif /* defined (CTL_KERN) && defined (KERN_BOOTTIME) */ - -#ifdef BOOT_TIME_FILE - { - struct stat st; - if (stat (BOOT_TIME_FILE, &st) == 0) - { - boot_time = st.st_mtime; - return boot_time; - } - } -#endif /* BOOT_TIME_FILE */ - -#if defined (BOOT_TIME) - /* The utmp routines maintain static state. Don't touch that state + /* get_boot_time maintains static state. Don't touch that state if we are going to dump, since it might not survive dumping. */ if (will_dump_p ()) - return boot_time; - - /* Try to get boot time from utmp before wtmp, - since utmp is typically much smaller than wtmp. - Passing a null pointer causes get_boot_time_1 - to inspect the default file, namely utmp. */ - get_boot_time_1 (0, 0); - if (boot_time) - return boot_time; - - /* Try to get boot time from the current wtmp file. */ - get_boot_time_1 (WTMP_FILE, 1); - - /* If we did not find a boot time in wtmp, look at wtmp.1, - wtmp.1.gz, wtmp.2, wtmp.2.gz, and so on. */ - for (int counter = 0; counter < 20 && ! boot_time; counter++) - { - Lisp_Object filename = Qnil; - bool delete_flag = false; - char cmd_string[sizeof WTMP_FILE ".19.gz"]; - AUTO_STRING_WITH_LEN (tempname, cmd_string, - sprintf (cmd_string, "%s.%d", WTMP_FILE, counter)); - if (! NILP (Ffile_exists_p (tempname))) - filename = tempname; - else - { - tempname = make_formatted_string (cmd_string, "%s.%d.gz", - WTMP_FILE, counter); - if (! NILP (Ffile_exists_p (tempname))) - { - /* The utmp functions on older systems accept only file - names up to 8 bytes long. Choose a 2 byte prefix, so - the 6-byte suffix does not make the name too long. */ - filename = Fmake_temp_file_internal (build_string ("wt"), Qnil, - empty_unibyte_string, Qnil); - CALLN (Fcall_process, build_string ("gzip"), Qnil, - list2 (QCfile, filename), Qnil, - build_string ("-cd"), tempname); - delete_flag = true; - } - } - - if (! NILP (filename)) - { - get_boot_time_1 (SSDATA (filename), 1); - if (delete_flag) - emacs_unlink (SSDATA (filename)); - } - } - - return boot_time; -#else - return 0; -#endif -} + return 0; -#ifdef BOOT_TIME -/* Try to get the boot time from wtmp file FILENAME. - This succeeds if that file contains a reboot record. - - If FILENAME is zero, use the same file as before; - if no FILENAME has ever been specified, this is the utmp file. - Use the newest reboot record if NEWEST, - the first reboot record otherwise. - Ignore all reboot records on or before BOOT_TIME. - Success is indicated by setting BOOT_TIME to a larger value. */ - -void -get_boot_time_1 (const char *filename, bool newest) -{ - struct utmp ut, *utp; - - if (filename) - utmpname (filename); - - setutent (); - - while (1) - { - /* Find the next reboot record. */ - ut.ut_type = BOOT_TIME; - utp = getutid (&ut); - if (! utp) - break; - /* Compare reboot times and use the newest one. */ - if (utp->ut_time > boot_time) - { - boot_time = utp->ut_time; - if (! newest) - break; - } - /* Advance on element in the file - so that getutid won't repeat the same one. */ - utp = getutent (); - if (! utp) - break; - } - endutent (); + struct timespec boot_time; + boot_time.tv_sec = 0; + get_boot_time (&boot_time); + return boot_time.tv_sec; } -#endif /* BOOT_TIME */ /* An arbitrary limit on lock contents length. 8 K should be plenty big enough in practice. */ @@ -418,7 +266,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) static int lock_file_1 (Lisp_Object lfname, bool force) { - intmax_t boot = get_boot_time (); + intmax_t boot = get_boot_sec (); Lisp_Object luser_name = Fuser_login_name (Qnil); Lisp_Object lhost_name = Fsystem_name (); @@ -604,7 +452,7 @@ current_lock_owner (lock_info_type *owner, Lisp_Object lfname) && (kill (pid, 0) >= 0 || errno == EPERM) && (boot_time == 0 || (boot_time <= TYPE_MAXIMUM (time_t) - && within_one_second (boot_time, get_boot_time ())))) + && within_one_second (boot_time, get_boot_sec ())))) return ANOTHER_OWNS_IT; /* The owner process is dead or has a strange pid, so try to zap the lockfile. */ -- 2.39.2