1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-05 22:20:24 -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:
Paul Eggert 2023-08-12 19:39:11 -07:00
parent b35431b218
commit 5e736ca6cc
10 changed files with 1116 additions and 209 deletions

View file

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

View file

@ -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
View 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
View 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
View 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 */

View file

@ -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
View 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__ */

View file

@ -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
View 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])
])

View file

@ -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;
return 0;
/* 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. */