aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2023-08-12 19:39:11 -0700
committerPaul Eggert2023-08-12 19:46:12 -0700
commit5e736ca6ccfa131736ab0b3a298de2cb319e7dfb (patch)
tree65b06f7a73dc2a04c60af430bc95102a347979df
parentb35431b218ada2d84eb251d18e5543388b598d80 (diff)
downloademacs-5e736ca6ccfa131736ab0b3a298de2cb319e7dfb.tar.gz
emacs-5e736ca6ccfa131736ab0b3a298de2cb319e7dfb.zip
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.
-rwxr-xr-xadmin/merge-gnulib2
-rw-r--r--configure.ac47
-rw-r--r--lib/boot-time-aux.h315
-rw-r--r--lib/boot-time.c285
-rw-r--r--lib/boot-time.h44
-rw-r--r--lib/gnulib.mk.in11
-rw-r--r--lib/readutmp.h325
-rw-r--r--m4/gnulib-comp.m47
-rw-r--r--m4/readutmp.m4117
-rw-r--r--src/filelock.c172
10 files changed, 1116 insertions, 209 deletions
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index 2a713beb01a..fe88d1106ae 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -26,7 +26,7 @@
26GNULIB_URL=https://git.savannah.gnu.org/git/gnulib.git 26GNULIB_URL=https://git.savannah.gnu.org/git/gnulib.git
27 27
28GNULIB_MODULES=' 28GNULIB_MODULES='
29 alignasof alloca-opt binary-io byteswap c-ctype c-strcase 29 alignasof alloca-opt binary-io boot-time byteswap c-ctype c-strcase
30 canonicalize-lgpl 30 canonicalize-lgpl
31 careadlinkat close-stream copy-file-range 31 careadlinkat close-stream copy-file-range
32 count-leading-zeros count-one-bits count-trailing-zeros 32 count-leading-zeros count-one-bits count-trailing-zeros
diff --git a/configure.ac b/configure.ac
index 35dfed247d0..0234a82b92f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2539,7 +2539,7 @@ AC_CHECK_HEADERS_ONCE(
2539 sys/sysinfo.h 2539 sys/sysinfo.h
2540 coff.h pty.h 2540 coff.h pty.h
2541 sys/resource.h 2541 sys/resource.h
2542 sys/utsname.h pwd.h utmp.h util.h 2542 sys/utsname.h pwd.h util.h
2543 sanitizer/lsan_interface.h 2543 sanitizer/lsan_interface.h
2544 sanitizer/asan_interface.h 2544 sanitizer/asan_interface.h
2545 sanitizer/common_interface_defs.h]) 2545 sanitizer/common_interface_defs.h])
@@ -2635,51 +2635,6 @@ if test "$GCC" = yes && test "$ac_enable_autodepend" = yes; then
2635fi 2635fi
2636AC_SUBST([AUTO_DEPEND]) 2636AC_SUBST([AUTO_DEPEND])
2637 2637
2638BOOT_TIME_FILE=
2639AC_CACHE_CHECK([for old but post-boot file],
2640 [emacs_cv_boot_time_file],
2641 [AS_CASE([$opsys],
2642 [gnu-linux],
2643 [emacs_cv_boot_time_file=unknown
2644 AS_IF([test $cross_compiling = no],
2645 [# systemd puts it in /var/lib/systemd.
2646 # initscripts puts it in /var/lib/urandom (previously /var/lib).
2647 # Linux drivers/char/random.c before 2022-02-21 suggests /var/run.
2648 for file in \
2649 /var/lib/systemd/random-seed \
2650 /var/lib/urandom/random-seed \
2651 /var/lib/random-seed \
2652 /var/run/random-seed
2653 do
2654 test -f $file && { emacs_cv_boot_time_file=$file; break; }
2655 done])],
2656 # This isn't perfect, as some systems might have the page file in
2657 # another place. Also, I suspect that the time stamp of that
2658 # file might also change when Windows enlarges the file due to
2659 # insufficient VM. Still, this seems to be the most reliable
2660 # way; the alternative (of using GetSystemTimes) won't work on
2661 # laptops that hibernate, because the system clock is stopped
2662 # then. Other possibility would be to run "net statistics
2663 # workstation" and parse the output, but that's gross. So this
2664 # should do; if the file is not there, the boot time will be
2665 # returned as zero, and filelock.c already handles that.
2666 [mingw32], [emacs_cv_boot_time_file=C:/pagefile.sys],
2667 [*], [emacs_cv_boot_time_file=not-needed])])
2668
2669AS_CASE([$emacs_cv_boot_time_file],
2670 [/*|*:*], [BOOT_TIME_FILE=\"$emacs_cv_boot_time_file\"],
2671 [not-needed], [BOOT_TIME_FILE=],
2672 [# Guess systemd if unknown.
2673 # If guess is wrong, Emacs falls back on something else.
2674 BOOT_TIME_FILE=\"/var/lib/systemd/random-seed\"])
2675
2676AS_IF([test -n "$BOOT_TIME_FILE"],
2677 [AC_DEFINE_UNQUOTED([BOOT_TIME_FILE], [$BOOT_TIME_FILE],
2678 [Name of file that, if it exists, postdates boot and predates
2679 the first Emacs invocation; or a null pointer if no such file is known.
2680 This file is used only on GNU/Linux and other systems
2681 that lack the FreeBSD-style sysctl with KERN_BOOTTIME.])])
2682
2683#### Choose a window system. 2638#### Choose a window system.
2684 2639
2685## We leave window_system equal to none if 2640## We leave window_system equal to none if
diff --git a/lib/boot-time-aux.h b/lib/boot-time-aux.h
new file mode 100644
index 00000000000..348611fc85c
--- /dev/null
+++ b/lib/boot-time-aux.h
@@ -0,0 +1,315 @@
1/* Auxiliary functions for determining the time when the machine last booted.
2 Copyright (C) 2023 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation, either version 3 of the License,
7 or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17/* Written by Bruno Haible <bruno@clisp.org>. */
18
19#define SIZEOF(a) (sizeof(a)/sizeof(a[0]))
20
21#if defined __linux__ || defined __ANDROID__
22
23/* Store the uptime counter, as managed by the Linux kernel, in *P_UPTIME.
24 Return 0 upon success, -1 upon failure. */
25_GL_ATTRIBUTE_MAYBE_UNUSED
26static int
27get_linux_uptime (struct timespec *p_uptime)
28{
29 /* The clock_gettime facility returns the uptime with a resolution of 1 µsec.
30 It is available with glibc >= 2.14, Android, or musl libc.
31 In glibc < 2.17 it required linking with librt. */
32# if !defined __GLIBC__ || 2 < __GLIBC__ + (17 <= __GLIBC_MINOR__)
33 if (clock_gettime (CLOCK_BOOTTIME, p_uptime) >= 0)
34 return 0;
35# endif
36
37 /* /proc/uptime contains the uptime with a resolution of 0.01 sec.
38 But it does not have read permissions on Android. */
39# if !defined __ANDROID__
40 FILE *fp = fopen ("/proc/uptime", "re");
41 if (fp != NULL)
42 {
43 char buf[32 + 1];
44 size_t n = fread (buf, 1, sizeof (buf) - 1, fp);
45 fclose (fp);
46 if (n > 0)
47 {
48 buf[n] = '\0';
49 /* buf now contains two values: the uptime and the idle time. */
50 time_t s = 0;
51 char *p;
52 for (p = buf; '0' <= *p && *p <= '9'; p++)
53 s = 10 * s + (*p - '0');
54 if (buf < p)
55 {
56 long ns = 0;
57 if (*p++ == '.')
58 for (int i = 0; i < 9; i++)
59 ns = 10 * ns + ('0' <= *p && *p <= '9' ? *p++ - '0' : 0);
60 p_uptime->tv_sec = s;
61 p_uptime->tv_nsec = ns;
62 return 0;
63 }
64 }
65 }
66# endif
67
68 /* The sysinfo call returns the uptime with a resolution of 1 sec only. */
69 struct sysinfo info;
70 if (sysinfo (&info) >= 0)
71 {
72 p_uptime->tv_sec = info.uptime;
73 p_uptime->tv_nsec = 0;
74 return 0;
75 }
76
77 return -1;
78}
79
80#endif
81
82#if defined __linux__ && !defined __ANDROID__
83
84static int
85get_linux_boot_time_fallback (struct timespec *p_boot_time)
86{
87 /* On Alpine Linux, UTMP_FILE is not filled. It is always empty.
88 So, get the time stamp of a file that gets touched only during the
89 boot process. */
90
91 const char * const boot_touched_files[] =
92 {
93 "/var/lib/systemd/random-seed", /* seen on distros with systemd */
94 "/var/run/utmp", /* seen on distros with OpenRC */
95 "/var/lib/random-seed" /* seen on older distros */
96 };
97 for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
98 {
99 const char *filename = boot_touched_files[i];
100 struct stat statbuf;
101 if (stat (filename, &statbuf) >= 0)
102 {
103 *p_boot_time = get_stat_mtime (&statbuf);
104 return 0;
105 }
106 }
107 return -1;
108}
109
110/* The following approach is only usable as a fallback, because it is of
111 the form
112 boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
113 and therefore produces wrong values after the date has been bumped in the
114 running system, which happens frequently if the system is running in a
115 virtual machine and this VM has been put into "saved" or "sleep" state
116 and then resumed. */
117static int
118get_linux_boot_time_final_fallback (struct timespec *p_boot_time)
119{
120 struct timespec uptime;
121 if (get_linux_uptime (&uptime) >= 0)
122 {
123 struct timespec result;
124# if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__)
125 /* Better than:
126 if (0 <= clock_gettime (CLOCK_REALTIME, &result))
127 because timespec_get does not need -lrt in glibc 2.16.
128 */
129 if (! timespec_get (&result, TIME_UTC))
130 return -1;
131# else
132 /* Fall back on lower-res approach that does not need -lrt.
133 This is good enough; on these hosts UPTIME is even lower-res. */
134 struct timeval tv;
135 int r = gettimeofday (&tv, NULL);
136 if (r < 0)
137 return r;
138 result.tv_sec = tv.tv_sec;
139 result.tv_nsec = tv.tv_usec * 1000;
140# endif
141
142 if (result.tv_nsec < uptime.tv_nsec)
143 {
144 result.tv_nsec += 1000000000;
145 result.tv_sec -= 1;
146 }
147 result.tv_sec -= uptime.tv_sec;
148 result.tv_nsec -= uptime.tv_nsec;
149 *p_boot_time = result;
150 return 0;
151 }
152 return -1;
153}
154
155#endif
156
157#if defined __ANDROID__
158
159static int
160get_android_boot_time (struct timespec *p_boot_time)
161{
162 /* On Android, there is no /var, and normal processes don't have access
163 to system files. Therefore use the kernel's uptime counter, although
164 it produces wrong values after the date has been bumped in the running
165 system. */
166 struct timespec uptime;
167 if (get_linux_uptime (&uptime) >= 0)
168 {
169 struct timespec result;
170 if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
171 {
172 if (result.tv_nsec < uptime.tv_nsec)
173 {
174 result.tv_nsec += 1000000000;
175 result.tv_sec -= 1;
176 }
177 result.tv_sec -= uptime.tv_sec;
178 result.tv_nsec -= uptime.tv_nsec;
179 *p_boot_time = result;
180 return 0;
181 }
182 }
183 return -1;
184}
185
186#endif
187
188#if defined __OpenBSD__
189
190static int
191get_openbsd_boot_time (struct timespec *p_boot_time)
192{
193 /* On OpenBSD, UTMP_FILE is not filled. It contains only dummy entries.
194 So, get the time stamp of a file that gets touched only during the
195 boot process. */
196 const char * const boot_touched_files[] =
197 {
198 "/var/db/host.random",
199 "/var/run/utmp"
200 };
201 for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
202 {
203 const char *filename = boot_touched_files[i];
204 struct stat statbuf;
205 if (stat (filename, &statbuf) >= 0)
206 {
207 *p_boot_time = get_stat_mtime (&statbuf);
208 return 0;
209 }
210 }
211 return -1;
212}
213
214#endif
215
216#if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
217 && defined CTL_KERN && defined KERN_BOOTTIME \
218 && !defined __minix
219/* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */
220/* On Minix 3.3 this sysctl produces garbage results. Therefore avoid it. */
221
222/* The following approach is only usable as a fallback, because it produces
223 wrong values after the date has been bumped in the running system, which
224 happens frequently if the system is running in a virtual machine and this
225 VM has been put into "saved" or "sleep" state and then resumed. */
226static int
227get_bsd_boot_time_final_fallback (struct timespec *p_boot_time)
228{
229 static int request[2] = { CTL_KERN, KERN_BOOTTIME };
230 struct timeval result;
231 size_t result_len = sizeof result;
232
233 if (sysctl (request, 2, &result, &result_len, NULL, 0) >= 0)
234 {
235 p_boot_time->tv_sec = result.tv_sec;
236 p_boot_time->tv_nsec = result.tv_usec * 1000;
237 return 0;
238 }
239 return -1;
240}
241
242#endif
243
244#if defined __HAIKU__
245
246static int
247get_haiku_boot_time (struct timespec *p_boot_time)
248{
249 /* On Haiku, /etc/utmp does not exist. During boot,
250 1. the current time is restored, but possibly with a wrong time zone,
251 that is, with an offset of a few hours,
252 2. some symlinks and files get created,
253 3. the various devices are brought up, in particular the network device,
254 4. the correct date and time is set,
255 5. some more device nodes get created.
256 The boot time can be retrieved by looking at a directory created during
257 phase 5, such as /dev/input. */
258 const char * const boot_touched_file = "/dev/input";
259 struct stat statbuf;
260 if (stat (boot_touched_file, &statbuf) >= 0)
261 {
262 *p_boot_time = get_stat_mtime (&statbuf);
263 return 0;
264 }
265 return -1;
266}
267
268#endif
269
270#if HAVE_OS_H /* BeOS, Haiku */
271
272/* The following approach is only usable as a fallback, because it produces
273 wrong values after the date has been bumped in the running system, which
274 happens frequently if the system is running in a virtual machine and this
275 VM has been put into "saved" or "sleep" state and then resumed. */
276static int
277get_haiku_boot_time_final_fallback (struct timespec *p_boot_time)
278{
279 system_info si;
280
281 get_system_info (&si);
282 p_boot_time->tv_sec = si.boot_time / 1000000;
283 p_boot_time->tv_nsec = (si.boot_time % 1000000) * 1000;
284 return 0;
285}
286
287#endif
288
289#if defined __CYGWIN__ || defined _WIN32
290
291static int
292get_windows_boot_time (struct timespec *p_boot_time)
293{
294 /* On Cygwin, /var/run/utmp is empty.
295 On native Windows, <utmpx.h> and <utmp.h> don't exist.
296 Instead, on Windows, the boot time can be retrieved by looking at the
297 time stamp of a file that (normally) gets touched only during the boot
298 process, namely C:\pagefile.sys. */
299 const char * const boot_touched_file =
300 #if defined __CYGWIN__ && !defined _WIN32
301 "/cygdrive/c/pagefile.sys"
302 #else
303 "C:\\pagefile.sys"
304 #endif
305 ;
306 struct stat statbuf;
307 if (stat (boot_touched_file, &statbuf) >= 0)
308 {
309 *p_boot_time = get_stat_mtime (&statbuf);
310 return 0;
311 }
312 return -1;
313}
314
315#endif
diff --git a/lib/boot-time.c b/lib/boot-time.c
new file mode 100644
index 00000000000..d813bfa5825
--- /dev/null
+++ b/lib/boot-time.c
@@ -0,0 +1,285 @@
1/* Determine the time when the machine last booted.
2 Copyright (C) 2023 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation, either version 3 of the License,
7 or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17/* Written by Bruno Haible <bruno@clisp.org>. */
18
19#include <config.h>
20
21/* Specification. */
22#include "boot-time.h"
23
24#include <stddef.h>
25#include <stdio.h>
26#include <string.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29
30#if defined __linux__ || defined __ANDROID__
31# include <sys/sysinfo.h>
32# include <time.h>
33#endif
34
35#if HAVE_SYS_SYSCTL_H && !defined __minix
36# if HAVE_SYS_PARAM_H
37# include <sys/param.h>
38# endif
39# include <sys/sysctl.h>
40#endif
41
42#if HAVE_OS_H
43# include <OS.h>
44#endif
45
46#include "idx.h"
47#include "readutmp.h"
48#include "stat-time.h"
49
50/* Each of the FILE streams in this file is only used in a single thread. */
51#include "unlocked-io.h"
52
53/* Some helper functions. */
54#include "boot-time-aux.h"
55
56/* The following macros describe the 'struct UTMP_STRUCT_NAME',
57 *not* 'struct gl_utmp'. */
58#undef UT_USER
59
60/* Accessor macro for the member named ut_user or ut_name. */
61#if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_NAME \
62 : HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_NAME)
63# define UT_USER(UT) ((UT)->ut_name)
64#else
65# define UT_USER(UT) ((UT)->ut_user)
66#endif
67
68#if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION && !HAVE_DECL_GETUTENT
69struct utmp *getutent (void);
70#endif
71
72#if defined __linux__ || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32
73
74static int
75get_boot_time_uncached (struct timespec *p_boot_time)
76{
77 struct timespec found_boot_time = {0};
78
79# if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
80
81 /* Try to find the boot time in the /var/run/utmp file. */
82
83# if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */
84
85 /* Ignore the return value for now.
86 Solaris' utmpname returns 1 upon success -- which is contrary
87 to what the GNU libc version does. In addition, older GNU libc
88 versions are actually void. */
89 UTMP_NAME_FUNCTION ((char *) UTMP_FILE);
90
91 SET_UTMP_ENT ();
92
93# if (defined __linux__ && !defined __ANDROID__) || defined __minix
94 /* Timestamp of the "runlevel" entry, if any. */
95 struct timespec runlevel_ts = {0};
96# endif
97
98 void const *entry;
99
100 while ((entry = GET_UTMP_ENT ()) != NULL)
101 {
102 struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) entry;
103
104 struct timespec ts =
105 #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
106 { .tv_sec = ut->ut_tv.tv_sec, .tv_nsec = ut->ut_tv.tv_usec * 1000 };
107 #else
108 { .tv_sec = ut->ut_time, .tv_nsec = 0 };
109 #endif
110
111 if (ut->ut_type == BOOT_TIME)
112 found_boot_time = ts;
113
114# if defined __linux__ && !defined __ANDROID__
115 if (memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0
116 && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0)
117 runlevel_ts = ts;
118# endif
119# if defined __minix
120 if (UT_USER (ut)[0] == '\0'
121 && memcmp (ut->ut_line, "run-level ", strlen ("run-level ")) == 0)
122 runlevel_ts = ts;
123# endif
124 }
125
126 END_UTMP_ENT ();
127
128# if defined __linux__ && !defined __ANDROID__
129 /* On Raspbian, which runs on hardware without a real-time clock, during boot,
130 1. the clock gets set to 1970-01-01 00:00:00,
131 2. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
132 ut_user = "reboot", ut_line = "~", time = 1970-01-01 00:00:05 or so,
133 3. the clock gets set to a correct value through NTP,
134 4. an entry gets written into /var/run/utmp, with
135 ut_user = "runlevel", ut_line = "~", time = correct value.
136 In this case, get the time from the "runlevel" entry. */
137
138 /* Workaround for Raspbian: */
139 if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
140 found_boot_time = runlevel_ts;
141 if (found_boot_time.tv_sec == 0)
142 {
143 /* Workaround for Alpine Linux: */
144 get_linux_boot_time_fallback (&found_boot_time);
145 }
146# endif
147
148# if defined __ANDROID__
149 if (found_boot_time.tv_sec == 0)
150 {
151 /* Workaround for Android: */
152 get_android_boot_time (&found_boot_time);
153 }
154# endif
155
156# if defined __minix
157 /* On Minix, during boot,
158 1. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
159 ut_user = "", ut_line = "system boot", time = 1970-01-01 00:00:00,
160 2. an entry gets written into /var/run/utmp, with
161 ut_user = "", ut_line = "run-level m", time = correct value.
162 In this case, copy the time from the "run-level m" entry to the
163 "system boot" entry. */
164 if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
165 found_boot_time = runlevel_ts;
166# endif
167
168# else /* HP-UX, Haiku */
169
170 FILE *f = fopen (UTMP_FILE, "re");
171
172 if (f != NULL)
173 {
174 for (;;)
175 {
176 struct UTMP_STRUCT_NAME ut;
177
178 if (fread (&ut, sizeof ut, 1, f) == 0)
179 break;
180
181 struct timespec ts =
182 #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
183 { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 };
184 #else
185 { .tv_sec = ut.ut_time, .tv_nsec = 0 };
186 #endif
187
188 if (ut.ut_type == BOOT_TIME)
189 found_boot_time = ts;
190 }
191
192 fclose (f);
193 }
194
195# endif
196
197# if defined __linux__ && !defined __ANDROID__
198 if (found_boot_time.tv_sec == 0)
199 {
200 get_linux_boot_time_final_fallback (&found_boot_time);
201 }
202# endif
203
204# else /* old FreeBSD, OpenBSD, native Windows */
205
206# if defined __OpenBSD__
207 /* Workaround for OpenBSD: */
208 get_openbsd_boot_time (&found_boot_time);
209# endif
210
211# endif
212
213# if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
214 && defined CTL_KERN && defined KERN_BOOTTIME \
215 && !defined __minix
216 if (found_boot_time.tv_sec == 0)
217 {
218 get_bsd_boot_time_final_fallback (&found_boot_time);
219 }
220# endif
221
222# if defined __HAIKU__
223 if (found_boot_time.tv_sec == 0)
224 {
225 get_haiku_boot_time (&found_boot_time);
226 }
227# endif
228
229# if HAVE_OS_H
230 if (found_boot_time.tv_sec == 0)
231 {
232 get_haiku_boot_time_final_fallback (&found_boot_time);
233 }
234# endif
235
236# if defined __CYGWIN__ || defined _WIN32
237 if (found_boot_time.tv_sec == 0)
238 {
239 /* Workaround for Windows: */
240 get_windows_boot_time (&found_boot_time);
241 }
242# endif
243
244 if (found_boot_time.tv_sec != 0)
245 {
246 *p_boot_time = found_boot_time;
247 return 0;
248 }
249 else
250 return -1;
251}
252
253int
254get_boot_time (struct timespec *p_boot_time)
255{
256 /* Cache the result from get_boot_time_uncached. */
257 static int volatile cached_result = -1;
258 static struct timespec volatile cached_boot_time;
259
260 if (cached_result < 0)
261 {
262 struct timespec boot_time;
263 int result = get_boot_time_uncached (&boot_time);
264 cached_boot_time = boot_time;
265 cached_result = result;
266 }
267
268 if (cached_result == 0)
269 {
270 *p_boot_time = cached_boot_time;
271 return 0;
272 }
273 else
274 return -1;
275}
276
277#else
278
279int
280get_boot_time (struct timespec *p_boot_time)
281{
282 return -1;
283}
284
285#endif
diff --git a/lib/boot-time.h b/lib/boot-time.h
new file mode 100644
index 00000000000..401e854adbb
--- /dev/null
+++ b/lib/boot-time.h
@@ -0,0 +1,44 @@
1/* Determine the time when the machine last booted.
2 Copyright (C) 2023 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation, either version 3 of the License,
7 or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17/* Written by Bruno Haible <bruno@clisp.org>. */
18
19#ifndef _BOOT_TIME_H
20#define _BOOT_TIME_H
21
22#include <time.h>
23
24#ifdef __cplusplus
25extern "C" {
26#endif
27
28
29/* Store the approximate time when the machine last booted in *P_BOOT_TIME,
30 and return 0. If it cannot be determined, return -1.
31
32 This function is not multithread-safe, since on many platforms it
33 invokes the functions setutxent, getutxent, endutxent. These
34 functions are needed because they may lock FILE (so that we don't
35 read garbage when a concurrent process writes to FILE), but their
36 drawback is that they have a common global state. */
37extern int get_boot_time (struct timespec *p_boot_time);
38
39
40#ifdef __cplusplus
41}
42#endif
43
44#endif /* _BOOT_TIME_H */
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 785bdc70c5c..3b33f39f73b 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -76,6 +76,7 @@
76# alignasof \ 76# alignasof \
77# alloca-opt \ 77# alloca-opt \
78# binary-io \ 78# binary-io \
79# boot-time \
79# byteswap \ 80# byteswap \
80# c-ctype \ 81# c-ctype \
81# c-strcase \ 82# c-strcase \
@@ -1601,6 +1602,16 @@ libgnu_a_SOURCES += binary-io.h binary-io.c
1601endif 1602endif
1602## end gnulib module binary-io 1603## end gnulib module binary-io
1603 1604
1605## begin gnulib module boot-time
1606ifeq (,$(OMIT_GNULIB_MODULE_boot-time))
1607
1608libgnu_a_SOURCES += boot-time.c
1609
1610EXTRA_DIST += boot-time-aux.h boot-time.h readutmp.h
1611
1612endif
1613## end gnulib module boot-time
1614
1604## begin gnulib module byteswap 1615## begin gnulib module byteswap
1605ifeq (,$(OMIT_GNULIB_MODULE_byteswap)) 1616ifeq (,$(OMIT_GNULIB_MODULE_byteswap))
1606 1617
diff --git a/lib/readutmp.h b/lib/readutmp.h
new file mode 100644
index 00000000000..1cf588d265b
--- /dev/null
+++ b/lib/readutmp.h
@@ -0,0 +1,325 @@
1/* Declarations for GNU's read utmp module.
2
3 Copyright (C) 1992-2007, 2009-2023 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18/* Written by jla; revised by djm */
19
20#ifndef __READUTMP_H__
21#define __READUTMP_H__
22
23/* This file uses _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_RETURNS_NONNULL,
24 HAVE_UTMP_H, HAVE_UTMPX_H, HAVE_STRUCT_UTMP_*, HAVE_STRUCT_UTMPX_*,
25 HAVE_UTMPNAME, HAVE_UTMPXNAME. */
26#if !_GL_CONFIG_H_INCLUDED
27# error "Please include config.h first."
28#endif
29
30#include "idx.h"
31
32#include <stdlib.h>
33#include <sys/types.h>
34#include <time.h>
35
36/* AIX 4.3.3 has both utmp.h and utmpx.h, but only struct utmp
37 has the ut_exit member. */
38#if (HAVE_UTMPX_H && HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_EXIT \
39 && ! HAVE_STRUCT_UTMPX_UT_EXIT)
40# undef HAVE_UTMPX_H
41#endif
42
43/* HPUX 10.20 needs utmp.h, for the definition of e.g., UTMP_FILE. */
44#if HAVE_UTMP_H
45# include <utmp.h>
46#endif
47
48/* Needed for BOOT_TIME and USER_PROCESS. */
49#if HAVE_UTMPX_H
50# if defined _THREAD_SAFE && defined UTMP_DATA_INIT
51 /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE
52 defined, work around the duplicate struct utmp_data declaration. */
53# define utmp_data gl_aix_4_3_workaround_utmp_data
54# endif
55# include <utmpx.h>
56#endif
57
58
59#ifdef __cplusplus
60extern "C" {
61#endif
62
63
64/* Type of entries returned by read_utmp on all platforms. */
65struct gl_utmp
66{
67 /* All 'char *' here are of arbitrary length and point to storage
68 with lifetime equal to that of this struct. */
69 char *ut_user; /* User name */
70 char *ut_id; /* Session ID */
71 char *ut_line; /* seat / device */
72 char *ut_host; /* for remote sessions: user@host or host,
73 for local sessions: the X11 display :N */
74 struct timespec ut_ts; /* time */
75 pid_t ut_pid; /* process ID of ? */
76 pid_t ut_session; /* process ID of session leader */
77 short ut_type; /* BOOT_TIME, USER_PROCESS, or other */
78 struct { int e_termination; int e_exit; } ut_exit;
79};
80
81/* The following types, macros, and constants describe the 'struct gl_utmp'. */
82#define UT_USER(UT) ((UT)->ut_user)
83#define UT_TIME_MEMBER(UT) ((UT)->ut_ts.tv_sec)
84#define UT_PID(UT) ((UT)->ut_pid)
85#define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V))
86#define UT_TYPE_NOT_DEFINED 0
87#define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
88#define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)
89
90/* Type of entry returned by read_utmp(). */
91typedef struct gl_utmp STRUCT_UTMP;
92
93/* Size of the UT_USER (ut) member, or -1 if unbounded. */
94enum { UT_USER_SIZE = -1 };
95
96/* Size of the ut->ut_id member, or -1 if unbounded. */
97enum { UT_ID_SIZE = -1 };
98
99/* Size of the ut->ut_line member, or -1 if unbounded. */
100enum { UT_LINE_SIZE = -1 };
101
102/* Size of the ut->ut_host member, or -1 if unbounded. */
103enum { UT_HOST_SIZE = -1 };
104
105
106/* When read_utmp accesses a file (as opposed to fetching the information
107 from systemd), it uses the following low-level types and macros.
108 Keep them here, rather than moving them into readutmp.c, for backward
109 compatibility. */
110
111#if HAVE_UTMPX_H
112
113/* <utmpx.h> defines 'struct utmpx' with the following fields:
114
115 Field Type Platforms
116 ---------- ------ ---------
117 ⎡ ut_user char[] glibc, musl, macOS, FreeBSD, AIX, HP-UX, IRIX, Solaris, Cygwin
118 ⎣ ut_name char[] NetBSD, Minix
119 ut_id char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
120 ut_line char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
121 ut_pid pid_t glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
122 ut_type short glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
123 ⎡ ut_tv struct glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
124 ⎢ { tv_sec; tv_usec; }
125 ⎣ ut_time time_t Cygwin
126 ut_host char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
127 ut_exit struct glibc, musl, NetBSD, Minix, HP-UX, IRIX, Solaris
128 { e_termination; e_exit; }
129 ut_session [long] int glibc, musl, NetBSD, Minix, IRIX, Solaris
130 ⎡ ut_addr [long] int HP-UX, Cygwin
131 ⎢ ut_addr_v6 [u]int[4] glibc, musl
132 ⎣ ut_ss struct sockaddr_storage NetBSD, Minix
133 */
134
135# if __GLIBC__ && _TIME_BITS == 64
136/* This is a near-copy of glibc's struct utmpx, which stops working
137 after the year 2038. Unlike the glibc version, struct utmpx32
138 describes the file format even if time_t is 64 bits. */
139struct utmpx32
140{
141 short int ut_type; /* Type of login. */
142 pid_t ut_pid; /* Process ID of login process. */
143 char ut_line[__UT_LINESIZE]; /* Devicename. */
144 char ut_id[4]; /* Inittab ID. */
145 char ut_user[__UT_USERSIZE]; /* Username. */
146 char ut_host[__UT_HOSTSIZE]; /* Hostname for remote login. */
147 struct __exit_status ut_exit; /* Exit status of a process marked
148 as DEAD_PROCESS. */
149 /* The fields ut_session and ut_tv must be the same size when compiled
150 32- and 64-bit. This allows files and shared memory to be shared
151 between 32- and 64-bit applications. */
152 int ut_session; /* Session ID, used for windowing. */
153 struct
154 {
155 /* Seconds. Unsigned not signed, as glibc did not exist before 1970,
156 and if the format is still in use after 2038 its timestamps
157 will surely have the sign bit on. This hack stops working
158 at 2106-02-07 06:28:16 UTC. */
159 unsigned int tv_sec;
160 int tv_usec; /* Microseconds. */
161 } ut_tv; /* Time entry was made. */
162 int ut_addr_v6[4]; /* Internet address of remote host. */
163 char ut_reserved[20]; /* Reserved for future use. */
164};
165# define UTMP_STRUCT_NAME utmpx32
166# else
167# define UTMP_STRUCT_NAME utmpx
168# endif
169# define SET_UTMP_ENT setutxent
170# define GET_UTMP_ENT getutxent
171# define END_UTMP_ENT endutxent
172# ifdef HAVE_UTMPXNAME /* glibc, musl, macOS, NetBSD, Minix, IRIX, Solaris, Cygwin */
173# define UTMP_NAME_FUNCTION utmpxname
174# elif defined UTXDB_ACTIVE /* FreeBSD */
175# define UTMP_NAME_FUNCTION(x) setutxdb (UTXDB_ACTIVE, x)
176# endif
177
178#elif HAVE_UTMP_H
179
180/* <utmp.h> defines 'struct utmp' with the following fields:
181
182 Field Type Platforms
183 ---------- ------ ---------
184 ⎡ ut_user char[] glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
185 ⎣ ut_name char[] macOS, old FreeBSD, NetBSD, OpenBSD, Minix
186 ut_id char[] glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
187 ut_line char[] glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
188 ut_pid pid_t glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
189 ut_type short glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
190 ⎡ ut_tv struct glibc, musl, Android
191 ⎢ { tv_sec; tv_usec; }
192 ⎣ ut_time time_t macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
193 ut_host char[] glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, Cygwin, Android
194 ut_exit struct glibc, musl, AIX, HP-UX, IRIX, Solaris, Android
195 { e_termination; e_exit; }
196 ut_session [long] int glibc, musl, Android
197 ⎡ ut_addr [long] int HP-UX, Cygwin
198 ⎣ ut_addr_v6 [u]int[4] glibc, musl, Android
199 */
200
201# define UTMP_STRUCT_NAME utmp
202# define SET_UTMP_ENT setutent
203# define GET_UTMP_ENT getutent
204# define END_UTMP_ENT endutent
205# ifdef HAVE_UTMPNAME /* glibc, musl, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android */
206# define UTMP_NAME_FUNCTION utmpname
207# endif
208
209#endif
210
211/* Evaluates to 1 if gl_utmp's ut_id field may ever have a non-zero value. */
212#define HAVE_STRUCT_XTMP_UT_ID \
213 (READUTMP_USE_SYSTEMD \
214 || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID))
215
216/* Evaluates to 1 if gl_utmp's ut_pid field may ever have a non-zero value. */
217#define HAVE_STRUCT_XTMP_UT_PID \
218 (READUTMP_USE_SYSTEMD \
219 || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID))
220
221/* Evaluates to 1 if gl_utmp's ut_host field may ever be non-empty. */
222#define HAVE_STRUCT_XTMP_UT_HOST \
223 (READUTMP_USE_SYSTEMD \
224 || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST))
225
226/* Definition of UTMP_FILE.
227 On glibc systems, UTMP_FILE is "/var/run/utmp". */
228#if !defined UTMP_FILE && defined _PATH_UTMP
229# define UTMP_FILE _PATH_UTMP
230#endif
231#ifdef UTMPX_FILE /* Solaris, SysVr4 */
232# undef UTMP_FILE
233# define UTMP_FILE UTMPX_FILE
234#endif
235#ifndef UTMP_FILE
236# define UTMP_FILE "/etc/utmp"
237#endif
238
239/* Definition of WTMP_FILE.
240 On glibc systems, UTMP_FILE is "/var/log/wtmp". */
241#if !defined WTMP_FILE && defined _PATH_WTMP
242# define WTMP_FILE _PATH_WTMP
243#endif
244#ifdef WTMPX_FILE /* Solaris, SysVr4 */
245# undef WTMP_FILE
246# define WTMP_FILE WTMPX_FILE
247#endif
248#ifndef WTMP_FILE
249# define WTMP_FILE "/etc/wtmp"
250#endif
251
252/* Some platforms, such as OpenBSD, don't have an ut_type field and don't have
253 the BOOT_TIME and USER_PROCESS macros. But we want to support them in
254 'struct gl_utmp'. */
255#if !(HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
256# define BOOT_TIME 2
257# define USER_PROCESS 0
258#endif
259
260/* Macros that test (UT)->ut_type. */
261#ifdef BOOT_TIME
262# define UT_TYPE_BOOT_TIME(UT) UT_TYPE_EQ (UT, BOOT_TIME)
263#else
264# define UT_TYPE_BOOT_TIME(UT) 0
265#endif
266#ifdef USER_PROCESS
267# define UT_TYPE_USER_PROCESS(UT) UT_TYPE_EQ (UT, USER_PROCESS)
268#else
269# define UT_TYPE_USER_PROCESS(UT) 0
270#endif
271
272/* Determines whether an entry *UT corresponds to a user process. */
273#define IS_USER_PROCESS(UT) \
274 (UT_USER (UT)[0] \
275 && (UT_TYPE_USER_PROCESS (UT) \
276 || (UT_TYPE_NOT_DEFINED && UT_TIME_MEMBER (UT) != 0)))
277
278/* Define if read_utmp is not just a dummy. */
279#if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32
280# define READ_UTMP_SUPPORTED 1
281#endif
282
283/* Options for read_utmp. */
284enum
285 {
286 READ_UTMP_CHECK_PIDS = 1,
287 READ_UTMP_USER_PROCESS = 2,
288 READ_UTMP_BOOT_TIME = 4,
289 READ_UTMP_NO_BOOT_TIME = 8
290 };
291
292/* Return a copy of (UT)->ut_user, without trailing spaces,
293 as a freshly allocated string. */
294char *extract_trimmed_name (const STRUCT_UTMP *ut)
295 _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE
296 _GL_ATTRIBUTE_RETURNS_NONNULL;
297
298/* Read the utmp entries corresponding to file FILE into freshly-
299 malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to
300 the number of entries, and return zero. If there is any error,
301 return -1, setting errno, and don't modify the parameters.
302 A good candidate for FILE is UTMP_FILE.
303 If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose
304 process-IDs do not currently exist.
305 If OPTIONS & READ_UTMP_USER_PROCESS is nonzero, omit entries which
306 do not correspond to a user process.
307 If OPTIONS & READ_UTMP_BOOT_TIME is nonzero, omit all entries except
308 the one that contains the boot time.
309 If OPTIONS & READ_UTMP_NO_BOOT_TIME is nonzero, omit the boot time
310 entries.
311
312 This function is not multithread-safe, since on many platforms it
313 invokes the functions setutxent, getutxent, endutxent. These
314 functions are needed because they may lock FILE (so that we don't
315 read garbage when a concurrent process writes to FILE), but their
316 drawback is that they have a common global state. */
317int read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
318 int options);
319
320
321#ifdef __cplusplus
322}
323#endif
324
325#endif /* __READUTMP_H__ */
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 3382e9bc241..14ff92040a4 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -51,6 +51,7 @@ AC_DEFUN([gl_EARLY],
51 # Code from module at-internal: 51 # Code from module at-internal:
52 # Code from module attribute: 52 # Code from module attribute:
53 # Code from module binary-io: 53 # Code from module binary-io:
54 # Code from module boot-time:
54 # Code from module builtin-expect: 55 # Code from module builtin-expect:
55 # Code from module byteswap: 56 # Code from module byteswap:
56 # Code from module c-ctype: 57 # Code from module c-ctype:
@@ -243,6 +244,7 @@ AC_DEFUN([gl_INIT],
243 gl_ASSERT_H 244 gl_ASSERT_H
244 gl_CONDITIONAL_HEADER([assert.h]) 245 gl_CONDITIONAL_HEADER([assert.h])
245 AC_PROG_MKDIR_P 246 AC_PROG_MKDIR_P
247 gl_PREREQ_READUTMP_H
246 gl___BUILTIN_EXPECT 248 gl___BUILTIN_EXPECT
247 gl_BYTESWAP 249 gl_BYTESWAP
248 gl_CONDITIONAL_HEADER([byteswap.h]) 250 gl_CONDITIONAL_HEADER([byteswap.h])
@@ -1252,6 +1254,9 @@ AC_DEFUN([gl_FILE_LIST], [
1252 lib/attribute.h 1254 lib/attribute.h
1253 lib/binary-io.c 1255 lib/binary-io.c
1254 lib/binary-io.h 1256 lib/binary-io.h
1257 lib/boot-time-aux.h
1258 lib/boot-time.c
1259 lib/boot-time.h
1255 lib/byteswap.in.h 1260 lib/byteswap.in.h
1256 lib/c++defs.h 1261 lib/c++defs.h
1257 lib/c-ctype.c 1262 lib/c-ctype.c
@@ -1383,6 +1388,7 @@ AC_DEFUN([gl_FILE_LIST], [
1383 lib/rawmemchr.valgrind 1388 lib/rawmemchr.valgrind
1384 lib/readlink.c 1389 lib/readlink.c
1385 lib/readlinkat.c 1390 lib/readlinkat.c
1391 lib/readutmp.h
1386 lib/realloc.c 1392 lib/realloc.c
1387 lib/regcomp.c 1393 lib/regcomp.c
1388 lib/regex.c 1394 lib/regex.c
@@ -1542,6 +1548,7 @@ AC_DEFUN([gl_FILE_LIST], [
1542 m4/rawmemchr.m4 1548 m4/rawmemchr.m4
1543 m4/readlink.m4 1549 m4/readlink.m4
1544 m4/readlinkat.m4 1550 m4/readlinkat.m4
1551 m4/readutmp.m4
1545 m4/realloc.m4 1552 m4/realloc.m4
1546 m4/regex.m4 1553 m4/regex.m4
1547 m4/sha1.m4 1554 m4/sha1.m4
diff --git a/m4/readutmp.m4 b/m4/readutmp.m4
new file mode 100644
index 00000000000..fff8d4eb7bf
--- /dev/null
+++ b/m4/readutmp.m4
@@ -0,0 +1,117 @@
1# readutmp.m4 serial 28
2dnl Copyright (C) 2002-2023 Free Software Foundation, Inc.
3dnl This file is free software; the Free Software Foundation
4dnl gives unlimited permission to copy and/or distribute it,
5dnl with or without modifications, as long as this notice is preserved.
6
7AC_DEFUN([gl_READUTMP],
8[
9 AC_REQUIRE([gl_SYSTEMD_CHOICE])
10
11 dnl Set READUTMP_LIB to '-lsystemd' or '', depending on whether use of
12 dnl systemd APIs is possible and desired (only the systemd login API, here).
13 dnl AC_LIB_LINKFLAGS_BODY would be overkill here, since few people install
14 dnl libsystemd in non-system directories.
15 READUTMP_LIB=
16 if test "$SYSTEMD_CHOICE" = yes; then
17 AC_CHECK_HEADER([systemd/sd-login.h])
18 if test $ac_cv_header_systemd_sd_login_h = yes; then
19 AC_CACHE_CHECK([for libsystemd version >= 254],
20 [gl_cv_lib_readutmp_systemd],
21 [gl_save_LIBS="$LIBS"
22 LIBS="$LIBS -lsystemd"
23 AC_LINK_IFELSE(
24 [AC_LANG_PROGRAM([[
25 #include <stdint.h>
26 #include <systemd/sd-login.h>
27 ]], [[
28 uint64_t st;
29 sd_session_get_start_time ("1", &st);
30 ]])
31 ],
32 [gl_cv_lib_readutmp_systemd=yes],
33 [gl_cv_lib_readutmp_systemd=no])
34 LIBS="$gl_save_LIBS"
35 ])
36 if test $gl_cv_lib_readutmp_systemd = yes; then
37 AC_DEFINE([READUTMP_USE_SYSTEMD], [1],
38 [Define if the readutmp module should use the systemd login API.])
39 READUTMP_LIB='-lsystemd'
40 fi
41 fi
42 fi
43 AC_SUBST([READUTMP_LIB])
44
45 gl_PREREQ_READUTMP_H
46])
47
48# Prerequisites of readutmp.h and boot-time-aux.h.
49AC_DEFUN_ONCE([gl_PREREQ_READUTMP_H],
50[
51 dnl Persuade utmpx.h to declare utmpxname
52 AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
53
54 AC_CHECK_HEADERS_ONCE([utmp.h utmpx.h])
55 if test $ac_cv_header_utmp_h = yes || test $ac_cv_header_utmpx_h = yes; then
56 dnl Prerequisites of lib/readutmp.h and lib/readutmp.c.
57 AC_CHECK_FUNCS_ONCE([utmpname utmpxname])
58 AC_CHECK_DECLS([getutent],,,[[
59/* <sys/types.h> is a prerequisite of <utmp.h> on FreeBSD 8.0, OpenBSD 4.6. */
60#include <sys/types.h>
61#ifdef HAVE_UTMP_H
62# include <utmp.h>
63#endif
64]])
65 utmp_includes="\
66AC_INCLUDES_DEFAULT
67#ifdef HAVE_UTMPX_H
68# include <utmpx.h>
69#endif
70#ifdef HAVE_UTMP_H
71# if defined _THREAD_SAFE && defined UTMP_DATA_INIT
72 /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE
73 defined, work around the duplicate struct utmp_data declaration. */
74# define utmp_data gl_aix_4_3_workaround_utmp_data
75# endif
76# include <utmp.h>
77#endif
78"
79 AC_CHECK_MEMBERS([struct utmpx.ut_user],,,[$utmp_includes])
80 AC_CHECK_MEMBERS([struct utmp.ut_user],,,[$utmp_includes])
81 AC_CHECK_MEMBERS([struct utmpx.ut_name],,,[$utmp_includes])
82 AC_CHECK_MEMBERS([struct utmp.ut_name],,,[$utmp_includes])
83 AC_CHECK_MEMBERS([struct utmpx.ut_type],,,[$utmp_includes])
84 AC_CHECK_MEMBERS([struct utmp.ut_type],,,[$utmp_includes])
85 AC_CHECK_MEMBERS([struct utmpx.ut_pid],,,[$utmp_includes])
86 AC_CHECK_MEMBERS([struct utmp.ut_pid],,,[$utmp_includes])
87 AC_CHECK_MEMBERS([struct utmp.ut_tv],,,[$utmp_includes])
88 AC_CHECK_MEMBERS([struct utmpx.ut_host],,,[$utmp_includes])
89 AC_CHECK_MEMBERS([struct utmp.ut_host],,,[$utmp_includes])
90 AC_CHECK_MEMBERS([struct utmpx.ut_id],,,[$utmp_includes])
91 AC_CHECK_MEMBERS([struct utmp.ut_id],,,[$utmp_includes])
92 AC_CHECK_MEMBERS([struct utmpx.ut_session],,,[$utmp_includes])
93 AC_CHECK_MEMBERS([struct utmp.ut_session],,,[$utmp_includes])
94 AC_CHECK_MEMBERS([struct utmpx.ut_exit],,,[$utmp_includes])
95 AC_CHECK_MEMBERS([struct utmp.ut_exit],,,[$utmp_includes])
96
97 AC_CHECK_MEMBERS([struct utmpx.ut_exit.ut_exit],,,[$utmp_includes])
98 AC_CHECK_MEMBERS([struct utmpx.ut_exit.e_exit],,,[$utmp_includes])
99 AC_CHECK_MEMBERS([struct utmp.ut_exit.e_exit],,,[$utmp_includes])
100
101 AC_CHECK_MEMBERS([struct utmpx.ut_exit.ut_termination],,,[$utmp_includes])
102 AC_CHECK_MEMBERS([struct utmpx.ut_exit.e_termination],,,[$utmp_includes])
103 AC_CHECK_MEMBERS([struct utmp.ut_exit.e_termination],,,[$utmp_includes])
104 fi
105
106 AC_CHECK_HEADERS_ONCE([sys/param.h])
107 dnl <sys/sysctl.h> requires <sys/param.h> on OpenBSD 4.0.
108 AC_CHECK_HEADERS([sys/sysctl.h],,,
109 [AC_INCLUDES_DEFAULT
110 #if HAVE_SYS_PARAM_H
111 # include <sys/param.h>
112 #endif
113 ])
114 AC_CHECK_FUNCS([sysctl])
115
116 AC_CHECK_HEADERS_ONCE([OS.h])
117])
diff --git a/src/filelock.c b/src/filelock.c
index 3b1ff8ad566..d2161f1e58a 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -36,13 +36,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
36#include <sys/file.h> 36#include <sys/file.h>
37#include <fcntl.h> 37#include <fcntl.h>
38#include <unistd.h> 38#include <unistd.h>
39
40#ifdef __FreeBSD__
41#include <sys/sysctl.h>
42#endif /* __FreeBSD__ */
43
44#include <errno.h> 39#include <errno.h>
45 40
41#include <boot-time.h>
46#include <c-ctype.h> 42#include <c-ctype.h>
47 43
48#include "lisp.h" 44#include "lisp.h"
@@ -55,20 +51,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
55 51
56#ifndef MSDOS 52#ifndef MSDOS
57 53
58#ifdef HAVE_UTMP_H
59#include <utmp.h>
60#endif
61
62/* Boot time is not available on Android. */
63
64#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
65#undef BOOT_TIME
66#endif
67
68#if !defined WTMP_FILE && !defined WINDOWSNT && defined BOOT_TIME
69#define WTMP_FILE "/var/log/wtmp"
70#endif
71
72#ifdef HAVE_ANDROID 54#ifdef HAVE_ANDROID
73#include "android.h" /* For `android_is_special_directory'. */ 55#include "android.h" /* For `android_is_special_directory'. */
74#endif /* HAVE_ANDROID */ 56#endif /* HAVE_ANDROID */
@@ -127,153 +109,19 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
127 109
128/* Return the time of the last system boot. */ 110/* Return the time of the last system boot. */
129 111
130static time_t boot_time;
131static bool boot_time_initialized;
132
133#ifdef BOOT_TIME
134static void get_boot_time_1 (const char *, bool);
135#endif
136
137static time_t 112static time_t
138get_boot_time (void) 113get_boot_sec (void)
139{ 114{
140 if (boot_time_initialized) 115 /* get_boot_time maintains static state. Don't touch that state
141 return boot_time;
142 boot_time_initialized = 1;
143
144#if defined (CTL_KERN) && defined (KERN_BOOTTIME)
145 {
146 int mib[2];
147 size_t size;
148 struct timeval boottime_val;
149
150 mib[0] = CTL_KERN;
151 mib[1] = KERN_BOOTTIME;
152 size = sizeof (boottime_val);
153
154 if (sysctl (mib, 2, &boottime_val, &size, NULL, 0) >= 0 && size != 0)
155 {
156 boot_time = boottime_val.tv_sec;
157 return boot_time;
158 }
159 }
160#endif /* defined (CTL_KERN) && defined (KERN_BOOTTIME) */
161
162#ifdef BOOT_TIME_FILE
163 {
164 struct stat st;
165 if (stat (BOOT_TIME_FILE, &st) == 0)
166 {
167 boot_time = st.st_mtime;
168 return boot_time;
169 }
170 }
171#endif /* BOOT_TIME_FILE */
172
173#if defined (BOOT_TIME)
174 /* The utmp routines maintain static state. Don't touch that state
175 if we are going to dump, since it might not survive dumping. */ 116 if we are going to dump, since it might not survive dumping. */
176 if (will_dump_p ()) 117 if (will_dump_p ())
177 return boot_time; 118 return 0;
178
179 /* Try to get boot time from utmp before wtmp,
180 since utmp is typically much smaller than wtmp.
181 Passing a null pointer causes get_boot_time_1
182 to inspect the default file, namely utmp. */
183 get_boot_time_1 (0, 0);
184 if (boot_time)
185 return boot_time;
186
187 /* Try to get boot time from the current wtmp file. */
188 get_boot_time_1 (WTMP_FILE, 1);
189
190 /* If we did not find a boot time in wtmp, look at wtmp.1,
191 wtmp.1.gz, wtmp.2, wtmp.2.gz, and so on. */
192 for (int counter = 0; counter < 20 && ! boot_time; counter++)
193 {
194 Lisp_Object filename = Qnil;
195 bool delete_flag = false;
196 char cmd_string[sizeof WTMP_FILE ".19.gz"];
197 AUTO_STRING_WITH_LEN (tempname, cmd_string,
198 sprintf (cmd_string, "%s.%d", WTMP_FILE, counter));
199 if (! NILP (Ffile_exists_p (tempname)))
200 filename = tempname;
201 else
202 {
203 tempname = make_formatted_string (cmd_string, "%s.%d.gz",
204 WTMP_FILE, counter);
205 if (! NILP (Ffile_exists_p (tempname)))
206 {
207 /* The utmp functions on older systems accept only file
208 names up to 8 bytes long. Choose a 2 byte prefix, so
209 the 6-byte suffix does not make the name too long. */
210 filename = Fmake_temp_file_internal (build_string ("wt"), Qnil,
211 empty_unibyte_string, Qnil);
212 CALLN (Fcall_process, build_string ("gzip"), Qnil,
213 list2 (QCfile, filename), Qnil,
214 build_string ("-cd"), tempname);
215 delete_flag = true;
216 }
217 }
218
219 if (! NILP (filename))
220 {
221 get_boot_time_1 (SSDATA (filename), 1);
222 if (delete_flag)
223 emacs_unlink (SSDATA (filename));
224 }
225 }
226
227 return boot_time;
228#else
229 return 0;
230#endif
231}
232 119
233#ifdef BOOT_TIME 120 struct timespec boot_time;
234/* Try to get the boot time from wtmp file FILENAME. 121 boot_time.tv_sec = 0;
235 This succeeds if that file contains a reboot record. 122 get_boot_time (&boot_time);
236 123 return boot_time.tv_sec;
237 If FILENAME is zero, use the same file as before;
238 if no FILENAME has ever been specified, this is the utmp file.
239 Use the newest reboot record if NEWEST,
240 the first reboot record otherwise.
241 Ignore all reboot records on or before BOOT_TIME.
242 Success is indicated by setting BOOT_TIME to a larger value. */
243
244void
245get_boot_time_1 (const char *filename, bool newest)
246{
247 struct utmp ut, *utp;
248
249 if (filename)
250 utmpname (filename);
251
252 setutent ();
253
254 while (1)
255 {
256 /* Find the next reboot record. */
257 ut.ut_type = BOOT_TIME;
258 utp = getutid (&ut);
259 if (! utp)
260 break;
261 /* Compare reboot times and use the newest one. */
262 if (utp->ut_time > boot_time)
263 {
264 boot_time = utp->ut_time;
265 if (! newest)
266 break;
267 }
268 /* Advance on element in the file
269 so that getutid won't repeat the same one. */
270 utp = getutent ();
271 if (! utp)
272 break;
273 }
274 endutent ();
275} 124}
276#endif /* BOOT_TIME */
277 125
278/* An arbitrary limit on lock contents length. 8 K should be plenty 126/* An arbitrary limit on lock contents length. 8 K should be plenty
279 big enough in practice. */ 127 big enough in practice. */
@@ -418,7 +266,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force)
418static int 266static int
419lock_file_1 (Lisp_Object lfname, bool force) 267lock_file_1 (Lisp_Object lfname, bool force)
420{ 268{
421 intmax_t boot = get_boot_time (); 269 intmax_t boot = get_boot_sec ();
422 Lisp_Object luser_name = Fuser_login_name (Qnil); 270 Lisp_Object luser_name = Fuser_login_name (Qnil);
423 Lisp_Object lhost_name = Fsystem_name (); 271 Lisp_Object lhost_name = Fsystem_name ();
424 272
@@ -604,7 +452,7 @@ current_lock_owner (lock_info_type *owner, Lisp_Object lfname)
604 && (kill (pid, 0) >= 0 || errno == EPERM) 452 && (kill (pid, 0) >= 0 || errno == EPERM)
605 && (boot_time == 0 453 && (boot_time == 0
606 || (boot_time <= TYPE_MAXIMUM (time_t) 454 || (boot_time <= TYPE_MAXIMUM (time_t)
607 && within_one_second (boot_time, get_boot_time ())))) 455 && within_one_second (boot_time, get_boot_sec ()))))
608 return ANOTHER_OWNS_IT; 456 return ANOTHER_OWNS_IT;
609 /* The owner process is dead or has a strange pid, so try to 457 /* The owner process is dead or has a strange pid, so try to
610 zap the lockfile. */ 458 zap the lockfile. */