aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorPaul Eggert2023-08-12 19:39:11 -0700
committerPaul Eggert2023-08-12 19:46:12 -0700
commit5e736ca6ccfa131736ab0b3a298de2cb319e7dfb (patch)
tree65b06f7a73dc2a04c60af430bc95102a347979df /lib
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.
Diffstat (limited to 'lib')
-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
5 files changed, 980 insertions, 0 deletions
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__ */