1 /* Auxiliary functions for determining the time when the machine last booted.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>. */
19 #define SIZEOF(a) (sizeof(a)/sizeof(a[0]))
21 #if defined __linux__ || defined __ANDROID__
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
27 get_linux_uptime (struct timespec
*p_uptime
)
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)
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");
44 size_t n
= fread (buf
, 1, sizeof (buf
) - 1, fp
);
49 /* buf now contains two values: the uptime and the idle time. */
52 for (p
= buf
; '0' <= *p
&& *p
<= '9'; p
++)
53 s
= 10 * s
+ (*p
- '0');
58 for (int i
= 0; i
< 9; i
++)
59 ns
= 10 * ns
+ ('0' <= *p
&& *p
<= '9' ? *p
++ - '0' : 0);
61 p_uptime
->tv_nsec
= ns
;
68 # if HAVE_DECL_SYSINFO /* not available in Android API < 9 */
69 /* The sysinfo call returns the uptime with a resolution of 1 sec only. */
71 if (sysinfo (&info
) >= 0)
73 p_uptime
->tv_sec
= info
.uptime
;
74 p_uptime
->tv_nsec
= 0;
84 #if defined __linux__ && !defined __ANDROID__
87 get_linux_boot_time_fallback (struct timespec
*p_boot_time
)
89 /* On Devuan with the 'runit' init system and on Artix with the 's6' init
90 system, UTMP_FILE contains USER_PROCESS and other entries, but no
92 On Alpine Linux, UTMP_FILE is not filled. It is always empty.
93 So, in both cases, get the time stamp of a file that gets touched only
94 during the boot process. */
96 const char * const boot_touched_files
[] =
98 "/var/lib/systemd/random-seed", /* seen on distros with systemd */
99 "/var/lib/urandom/random-seed", /* seen on Devuan with runit */
100 "/var/lib/random-seed", /* seen on Artix with s6 */
101 /* This must come last, since on several distros /var/run/utmp is
102 modified when a user logs in, i.e. long after boot. */
103 "/var/run/utmp" /* seen on Alpine Linux with OpenRC */
105 for (idx_t i
= 0; i
< SIZEOF (boot_touched_files
); i
++)
107 const char *filename
= boot_touched_files
[i
];
109 if (stat (filename
, &statbuf
) >= 0)
111 struct timespec boot_time
= get_stat_mtime (&statbuf
);
112 /* On Alpine 3.20.0_rc2 /var/run/utmp was observed with bogus
113 timestamps of ~10 s. Reject timestamps before
114 2005-07-25 23:34:15 UTC (1122334455), as neither Alpine
115 nor Devuan existed then. */
116 if (boot_time
.tv_sec
>= 1122334455)
118 *p_boot_time
= boot_time
;
126 /* The following approach is only usable as a fallback, because it is of
128 boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
129 and therefore produces wrong values after the date has been bumped in the
130 running system, which happens frequently if the system is running in a
131 virtual machine and this VM has been put into "saved" or "sleep" state
134 get_linux_boot_time_final_fallback (struct timespec
*p_boot_time
)
136 struct timespec uptime
;
137 if (get_linux_uptime (&uptime
) >= 0)
139 struct timespec result
;
140 # if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__)
142 if (0 <= clock_gettime (CLOCK_REALTIME, &result))
143 because timespec_get does not need -lrt in glibc 2.16.
145 if (! timespec_get (&result
, TIME_UTC
))
148 /* Fall back on lower-res approach that does not need -lrt.
149 This is good enough; on these hosts UPTIME is even lower-res. */
151 int r
= gettimeofday (&tv
, NULL
);
154 result
.tv_sec
= tv
.tv_sec
;
155 result
.tv_nsec
= tv
.tv_usec
* 1000;
158 if (result
.tv_nsec
< uptime
.tv_nsec
)
160 result
.tv_nsec
+= 1000000000;
163 result
.tv_sec
-= uptime
.tv_sec
;
164 result
.tv_nsec
-= uptime
.tv_nsec
;
165 *p_boot_time
= result
;
173 #if defined __ANDROID__
176 get_android_boot_time (struct timespec
*p_boot_time
)
178 /* On Android, there is no /var, and normal processes don't have access
179 to system files. Therefore use the kernel's uptime counter, although
180 it produces wrong values after the date has been bumped in the running
182 struct timespec uptime
;
183 if (get_linux_uptime (&uptime
) >= 0)
185 struct timespec result
;
186 if (clock_gettime (CLOCK_REALTIME
, &result
) >= 0)
188 if (result
.tv_nsec
< uptime
.tv_nsec
)
190 result
.tv_nsec
+= 1000000000;
193 result
.tv_sec
-= uptime
.tv_sec
;
194 result
.tv_nsec
-= uptime
.tv_nsec
;
195 *p_boot_time
= result
;
204 #if defined __OpenBSD__
207 get_openbsd_boot_time (struct timespec
*p_boot_time
)
209 /* On OpenBSD, UTMP_FILE is not filled. It contains only dummy entries.
210 So, get the time stamp of a file that gets touched only during the
212 const char * const boot_touched_files
[] =
214 "/var/db/host.random",
217 for (idx_t i
= 0; i
< SIZEOF (boot_touched_files
); i
++)
219 const char *filename
= boot_touched_files
[i
];
221 if (stat (filename
, &statbuf
) >= 0)
223 *p_boot_time
= get_stat_mtime (&statbuf
);
232 #if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
233 && defined CTL_KERN && defined KERN_BOOTTIME \
235 /* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */
236 /* On Minix 3.3 this sysctl produces garbage results. Therefore avoid it. */
238 /* The following approach is only usable as a fallback, because it produces
239 wrong values after the date has been bumped in the running system, which
240 happens frequently if the system is running in a virtual machine and this
241 VM has been put into "saved" or "sleep" state and then resumed. */
243 get_bsd_boot_time_final_fallback (struct timespec
*p_boot_time
)
245 static int request
[2] = { CTL_KERN
, KERN_BOOTTIME
};
246 struct timeval result
;
247 size_t result_len
= sizeof result
;
249 if (sysctl (request
, 2, &result
, &result_len
, NULL
, 0) >= 0)
251 p_boot_time
->tv_sec
= result
.tv_sec
;
252 p_boot_time
->tv_nsec
= result
.tv_usec
* 1000;
260 #if defined __HAIKU__
263 get_haiku_boot_time (struct timespec
*p_boot_time
)
265 /* On Haiku, /etc/utmp does not exist. During boot,
266 1. the current time is restored, but possibly with a wrong time zone,
267 that is, with an offset of a few hours,
268 2. some symlinks and files get created,
269 3. the various devices are brought up, in particular the network device,
270 4. the correct date and time is set,
271 5. some more device nodes get created.
272 The boot time can be retrieved by looking at a directory created during
273 phase 5, such as /dev/input. */
274 const char * const boot_touched_file
= "/dev/input";
276 if (stat (boot_touched_file
, &statbuf
) >= 0)
278 *p_boot_time
= get_stat_mtime (&statbuf
);
286 #if HAVE_OS_H /* BeOS, Haiku */
288 /* The following approach is only usable as a fallback, because it produces
289 wrong values after the date has been bumped in the running system, which
290 happens frequently if the system is running in a virtual machine and this
291 VM has been put into "saved" or "sleep" state and then resumed. */
293 get_haiku_boot_time_final_fallback (struct timespec
*p_boot_time
)
297 get_system_info (&si
);
298 p_boot_time
->tv_sec
= si
.boot_time
/ 1000000;
299 p_boot_time
->tv_nsec
= (si
.boot_time
% 1000000) * 1000;
305 #if defined __CYGWIN__ || defined _WIN32
308 get_windows_boot_time (struct timespec
*p_boot_time
)
310 /* On Cygwin, /var/run/utmp is empty.
311 On native Windows, <utmpx.h> and <utmp.h> don't exist.
312 Instead, on Windows, the boot time can be retrieved by looking at the
313 time stamp of a file that (normally) gets touched only during the boot
314 process, namely C:\pagefile.sys. */
315 const char * const boot_touched_files
[] =
317 #if defined __CYGWIN__ && !defined _WIN32
318 /* It is more portable to use /proc/cygdrive/c than /cygdrive/c. */
319 "/proc/cygdrive/c/pagefile.sys",
320 /* A fallback, working around a Cygwin 3.5.3 bug. It has a modification
321 time about 1.5 minutes after the last boot; but that's better than
323 "/proc/cygdrive/c/ProgramData/Microsoft/Windows/DeviceMetadataCache/dmrc.idx"
328 for (idx_t i
= 0; i
< SIZEOF (boot_touched_files
); i
++)
330 const char *filename
= boot_touched_files
[i
];
332 if (stat (filename
, &statbuf
) >= 0)
334 # if defined __CYGWIN__ && !defined _WIN32
335 /* Work around a Cygwin 3.5.3 bug.
336 <https://cygwin.com/pipermail/cygwin/2024-May/255931.html> */
337 if (!S_ISDIR (statbuf
.st_mode
))
340 *p_boot_time
= get_stat_mtime (&statbuf
);
349 # if !(_WIN32_WINNT >= _WIN32_WINNT_VISTA)
351 /* Don't assume that UNICODE is not defined. */
353 # define LoadLibrary LoadLibraryA
355 /* Avoid warnings from gcc -Wcast-function-type. */
356 # define GetProcAddress \
357 (void *) GetProcAddress
359 /* GetTickCount64 is only available on Windows Vista and later. */
360 typedef ULONGLONG (WINAPI
* GetTickCount64FuncType
) (void);
362 static GetTickCount64FuncType GetTickCount64Func
= NULL
;
363 static BOOL initialized
= FALSE
;
368 HMODULE kernel32
= LoadLibrary ("kernel32.dll");
369 if (kernel32
!= NULL
)
372 (GetTickCount64FuncType
) GetProcAddress (kernel32
, "GetTickCount64");
379 # define GetTickCount64Func GetTickCount64
383 /* Fallback for Windows in the form:
384 boot time = current time - uptime
385 This uses the GetTickCount64 function which is only available on Windows
386 Vista and later. See:
387 <https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64>. */
389 get_windows_boot_time_fallback (struct timespec
*p_boot_time
)
391 # if !(_WIN32_WINNT >= _WIN32_WINNT_VISTA)
395 if (GetTickCount64Func
!= NULL
)
397 ULONGLONG uptime_ms
= GetTickCount64Func ();
398 struct timespec uptime
;
399 struct timespec result
;
401 if (gettimeofday (&tv
, NULL
) >= 0)
403 uptime
.tv_sec
= uptime_ms
/ 1000;
404 uptime
.tv_nsec
= (uptime_ms
% 1000) * 1000000;
405 result
.tv_sec
= tv
.tv_sec
;
406 result
.tv_nsec
= tv
.tv_usec
* 1000;
407 if (result
.tv_nsec
< uptime
.tv_nsec
)
409 result
.tv_nsec
+= 1000000000;
412 result
.tv_sec
-= uptime
.tv_sec
;
413 result
.tv_nsec
-= uptime
.tv_nsec
;
414 *p_boot_time
= result
;