mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-06 06:20:55 -08:00
Improve boot-time gathering
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 <sys/sysctl.h>. [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.
This commit is contained in:
parent
b35431b218
commit
5e736ca6cc
10 changed files with 1116 additions and 209 deletions
|
|
@ -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
|
||||
|
|
|
|||
47
configure.ac
47
configure.ac
|
|
@ -2539,7 +2539,7 @@ AC_CHECK_HEADERS_ONCE(
|
|||
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 @@ if test "$GCC" = yes && test "$ac_enable_autodepend" = yes; then
|
|||
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
|
||||
|
|
|
|||
315
lib/boot-time-aux.h
Normal file
315
lib/boot-time-aux.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Bruno Haible <bruno@clisp.org>. */
|
||||
|
||||
#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, <utmpx.h> and <utmp.h> 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
|
||||
285
lib/boot-time.c
Normal file
285
lib/boot-time.c
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Bruno Haible <bruno@clisp.org>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "boot-time.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined __linux__ || defined __ANDROID__
|
||||
# include <sys/sysinfo.h>
|
||||
# include <time.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_SYSCTL_H && !defined __minix
|
||||
# if HAVE_SYS_PARAM_H
|
||||
# include <sys/param.h>
|
||||
# endif
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_OS_H
|
||||
# include <OS.h>
|
||||
#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
|
||||
44
lib/boot-time.h
Normal file
44
lib/boot-time.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Bruno Haible <bruno@clisp.org>. */
|
||||
|
||||
#ifndef _BOOT_TIME_H
|
||||
#define _BOOT_TIME_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#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 */
|
||||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
325
lib/readutmp.h
Normal file
325
lib/readutmp.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* 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 <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
/* 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 <utmp.h>
|
||||
#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 <utmpx.h>
|
||||
#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
|
||||
|
||||
/* <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
|
||||
|
||||
/* <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__ */
|
||||
|
|
@ -51,6 +51,7 @@ AC_DEFUN([gl_EARLY],
|
|||
# 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_INIT],
|
|||
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([gl_FILE_LIST], [
|
|||
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([gl_FILE_LIST], [
|
|||
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([gl_FILE_LIST], [
|
|||
m4/rawmemchr.m4
|
||||
m4/readlink.m4
|
||||
m4/readlinkat.m4
|
||||
m4/readutmp.m4
|
||||
m4/realloc.m4
|
||||
m4/regex.m4
|
||||
m4/sha1.m4
|
||||
|
|
|
|||
117
m4/readutmp.m4
Normal file
117
m4/readutmp.m4
Normal file
|
|
@ -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 <stdint.h>
|
||||
#include <systemd/sd-login.h>
|
||||
]], [[
|
||||
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],,,[[
|
||||
/* <sys/types.h> is a prerequisite of <utmp.h> on FreeBSD 8.0, OpenBSD 4.6. */
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_UTMP_H
|
||||
# include <utmp.h>
|
||||
#endif
|
||||
]])
|
||||
utmp_includes="\
|
||||
AC_INCLUDES_DEFAULT
|
||||
#ifdef HAVE_UTMPX_H
|
||||
# include <utmpx.h>
|
||||
#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 <utmp.h>
|
||||
#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 <sys/sysctl.h> requires <sys/param.h> on OpenBSD 4.0.
|
||||
AC_CHECK_HEADERS([sys/sysctl.h],,,
|
||||
[AC_INCLUDES_DEFAULT
|
||||
#if HAVE_SYS_PARAM_H
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
])
|
||||
AC_CHECK_FUNCS([sysctl])
|
||||
|
||||
AC_CHECK_HEADERS_ONCE([OS.h])
|
||||
])
|
||||
172
src/filelock.c
172
src/filelock.c
|
|
@ -36,13 +36,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/sysctl.h>
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <boot-time.h>
|
||||
#include <c-ctype.h>
|
||||
|
||||
#include "lisp.h"
|
||||
|
|
@ -55,20 +51,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
|
||||
#ifndef MSDOS
|
||||
|
||||
#ifdef HAVE_UTMP_H
|
||||
#include <utmp.h>
|
||||
#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 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
|
||||
/* 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
|
||||
|
||||
struct timespec boot_time;
|
||||
boot_time.tv_sec = 0;
|
||||
get_boot_time (&boot_time);
|
||||
return boot_time.tv_sec;
|
||||
}
|
||||
|
||||
#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 ();
|
||||
}
|
||||
#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. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue