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 *p_boot_time
= get_stat_mtime (&statbuf
);
118 /* The following approach is only usable as a fallback, because it is of
120 boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
121 and therefore produces wrong values after the date has been bumped in the
122 running system, which happens frequently if the system is running in a
123 virtual machine and this VM has been put into "saved" or "sleep" state
126 get_linux_boot_time_final_fallback (struct timespec
*p_boot_time
)
128 struct timespec uptime
;
129 if (get_linux_uptime (&uptime
) >= 0)
131 struct timespec result
;
132 # if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__)
134 if (0 <= clock_gettime (CLOCK_REALTIME, &result))
135 because timespec_get does not need -lrt in glibc 2.16.
137 if (! timespec_get (&result
, TIME_UTC
))
140 /* Fall back on lower-res approach that does not need -lrt.
141 This is good enough; on these hosts UPTIME is even lower-res. */
143 int r
= gettimeofday (&tv
, NULL
);
146 result
.tv_sec
= tv
.tv_sec
;
147 result
.tv_nsec
= tv
.tv_usec
* 1000;
150 if (result
.tv_nsec
< uptime
.tv_nsec
)
152 result
.tv_nsec
+= 1000000000;
155 result
.tv_sec
-= uptime
.tv_sec
;
156 result
.tv_nsec
-= uptime
.tv_nsec
;
157 *p_boot_time
= result
;
165 #if defined __ANDROID__
168 get_android_boot_time (struct timespec
*p_boot_time
)
170 /* On Android, there is no /var, and normal processes don't have access
171 to system files. Therefore use the kernel's uptime counter, although
172 it produces wrong values after the date has been bumped in the running
174 struct timespec uptime
;
175 if (get_linux_uptime (&uptime
) >= 0)
177 struct timespec result
;
178 if (clock_gettime (CLOCK_REALTIME
, &result
) >= 0)
180 if (result
.tv_nsec
< uptime
.tv_nsec
)
182 result
.tv_nsec
+= 1000000000;
185 result
.tv_sec
-= uptime
.tv_sec
;
186 result
.tv_nsec
-= uptime
.tv_nsec
;
187 *p_boot_time
= result
;
196 #if defined __OpenBSD__
199 get_openbsd_boot_time (struct timespec
*p_boot_time
)
201 /* On OpenBSD, UTMP_FILE is not filled. It contains only dummy entries.
202 So, get the time stamp of a file that gets touched only during the
204 const char * const boot_touched_files
[] =
206 "/var/db/host.random",
209 for (idx_t i
= 0; i
< SIZEOF (boot_touched_files
); i
++)
211 const char *filename
= boot_touched_files
[i
];
213 if (stat (filename
, &statbuf
) >= 0)
215 *p_boot_time
= get_stat_mtime (&statbuf
);
224 #if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
225 && defined CTL_KERN && defined KERN_BOOTTIME \
227 /* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */
228 /* On Minix 3.3 this sysctl produces garbage results. Therefore avoid it. */
230 /* The following approach is only usable as a fallback, because it produces
231 wrong values after the date has been bumped in the running system, which
232 happens frequently if the system is running in a virtual machine and this
233 VM has been put into "saved" or "sleep" state and then resumed. */
235 get_bsd_boot_time_final_fallback (struct timespec
*p_boot_time
)
237 static int request
[2] = { CTL_KERN
, KERN_BOOTTIME
};
238 struct timeval result
;
239 size_t result_len
= sizeof result
;
241 if (sysctl (request
, 2, &result
, &result_len
, NULL
, 0) >= 0)
243 p_boot_time
->tv_sec
= result
.tv_sec
;
244 p_boot_time
->tv_nsec
= result
.tv_usec
* 1000;
252 #if defined __HAIKU__
255 get_haiku_boot_time (struct timespec
*p_boot_time
)
257 /* On Haiku, /etc/utmp does not exist. During boot,
258 1. the current time is restored, but possibly with a wrong time zone,
259 that is, with an offset of a few hours,
260 2. some symlinks and files get created,
261 3. the various devices are brought up, in particular the network device,
262 4. the correct date and time is set,
263 5. some more device nodes get created.
264 The boot time can be retrieved by looking at a directory created during
265 phase 5, such as /dev/input. */
266 const char * const boot_touched_file
= "/dev/input";
268 if (stat (boot_touched_file
, &statbuf
) >= 0)
270 *p_boot_time
= get_stat_mtime (&statbuf
);
278 #if HAVE_OS_H /* BeOS, Haiku */
280 /* The following approach is only usable as a fallback, because it produces
281 wrong values after the date has been bumped in the running system, which
282 happens frequently if the system is running in a virtual machine and this
283 VM has been put into "saved" or "sleep" state and then resumed. */
285 get_haiku_boot_time_final_fallback (struct timespec
*p_boot_time
)
289 get_system_info (&si
);
290 p_boot_time
->tv_sec
= si
.boot_time
/ 1000000;
291 p_boot_time
->tv_nsec
= (si
.boot_time
% 1000000) * 1000;
297 #if defined __CYGWIN__ || defined _WIN32
300 get_windows_boot_time (struct timespec
*p_boot_time
)
302 /* On Cygwin, /var/run/utmp is empty.
303 On native Windows, <utmpx.h> and <utmp.h> don't exist.
304 Instead, on Windows, the boot time can be retrieved by looking at the
305 time stamp of a file that (normally) gets touched only during the boot
306 process, namely C:\pagefile.sys. */
307 const char * const boot_touched_file
=
308 #if defined __CYGWIN__ && !defined _WIN32
309 "/cygdrive/c/pagefile.sys"
315 if (stat (boot_touched_file
, &statbuf
) >= 0)
317 *p_boot_time
= get_stat_mtime (&statbuf
);