diff options
| author | Paul Eggert | 2023-08-12 19:39:11 -0700 |
|---|---|---|
| committer | Paul Eggert | 2023-08-12 19:46:12 -0700 |
| commit | 5e736ca6ccfa131736ab0b3a298de2cb319e7dfb (patch) | |
| tree | 65b06f7a73dc2a04c60af430bc95102a347979df /lib | |
| parent | b35431b218ada2d84eb251d18e5543388b598d80 (diff) | |
| download | emacs-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.h | 315 | ||||
| -rw-r--r-- | lib/boot-time.c | 285 | ||||
| -rw-r--r-- | lib/boot-time.h | 44 | ||||
| -rw-r--r-- | lib/gnulib.mk.in | 11 | ||||
| -rw-r--r-- | lib/readutmp.h | 325 |
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 | ||
| 26 | static int | ||
| 27 | get_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 | |||
| 84 | static int | ||
| 85 | get_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. */ | ||
| 117 | static int | ||
| 118 | get_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 | |||
| 159 | static int | ||
| 160 | get_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 | |||
| 190 | static int | ||
| 191 | get_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. */ | ||
| 226 | static int | ||
| 227 | get_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 | |||
| 246 | static int | ||
| 247 | get_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. */ | ||
| 276 | static int | ||
| 277 | get_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 | |||
| 291 | static int | ||
| 292 | get_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 | ||
| 69 | struct utmp *getutent (void); | ||
| 70 | #endif | ||
| 71 | |||
| 72 | #if defined __linux__ || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32 | ||
| 73 | |||
| 74 | static int | ||
| 75 | get_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 | |||
| 253 | int | ||
| 254 | get_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 | |||
| 279 | int | ||
| 280 | get_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 | ||
| 25 | extern "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. */ | ||
| 37 | extern 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 | |||
| 1601 | endif | 1602 | endif |
| 1602 | ## end gnulib module binary-io | 1603 | ## end gnulib module binary-io |
| 1603 | 1604 | ||
| 1605 | ## begin gnulib module boot-time | ||
| 1606 | ifeq (,$(OMIT_GNULIB_MODULE_boot-time)) | ||
| 1607 | |||
| 1608 | libgnu_a_SOURCES += boot-time.c | ||
| 1609 | |||
| 1610 | EXTRA_DIST += boot-time-aux.h boot-time.h readutmp.h | ||
| 1611 | |||
| 1612 | endif | ||
| 1613 | ## end gnulib module boot-time | ||
| 1614 | |||
| 1604 | ## begin gnulib module byteswap | 1615 | ## begin gnulib module byteswap |
| 1605 | ifeq (,$(OMIT_GNULIB_MODULE_byteswap)) | 1616 | ifeq (,$(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 | ||
| 60 | extern "C" { | ||
| 61 | #endif | ||
| 62 | |||
| 63 | |||
| 64 | /* Type of entries returned by read_utmp on all platforms. */ | ||
| 65 | struct 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(). */ | ||
| 91 | typedef struct gl_utmp STRUCT_UTMP; | ||
| 92 | |||
| 93 | /* Size of the UT_USER (ut) member, or -1 if unbounded. */ | ||
| 94 | enum { UT_USER_SIZE = -1 }; | ||
| 95 | |||
| 96 | /* Size of the ut->ut_id member, or -1 if unbounded. */ | ||
| 97 | enum { UT_ID_SIZE = -1 }; | ||
| 98 | |||
| 99 | /* Size of the ut->ut_line member, or -1 if unbounded. */ | ||
| 100 | enum { UT_LINE_SIZE = -1 }; | ||
| 101 | |||
| 102 | /* Size of the ut->ut_host member, or -1 if unbounded. */ | ||
| 103 | enum { 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. */ | ||
| 139 | struct 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. */ | ||
| 284 | enum | ||
| 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. */ | ||
| 294 | char *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. */ | ||
| 317 | int 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__ */ | ||