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 # if HAVE_DECL_SYSINFO /* not available in Android API < 9 */
69 /* The sysinfo call returns the uptime with a resolution of 1 sec only. */
70 struct sysinfo info;
71 if (sysinfo (&info) >= 0)
72 {
73 p_uptime->tv_sec = info.uptime;
74 p_uptime->tv_nsec = 0;
75 return 0;
76 }
77 # endif
78
79 return -1;
80 }
81
82 #endif
83
84 #if defined __linux__ && !defined __ANDROID__
85
86 static int
87 get_linux_boot_time_fallback (struct timespec *p_boot_time)
88 {
89 /* On Alpine Linux, UTMP_FILE is not filled. It is always empty.
90 So, get the time stamp of a file that gets touched only during the
91 boot process. */
92
93 const char * const boot_touched_files[] =
94 {
95 "/var/lib/systemd/random-seed", /* seen on distros with systemd */
96 "/var/run/utmp", /* seen on distros with OpenRC */
97 "/var/lib/random-seed" /* seen on older distros */
98 };
99 for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
100 {
101 const char *filename = boot_touched_files[i];
102 struct stat statbuf;
103 if (stat (filename, &statbuf) >= 0)
104 {
105 *p_boot_time = get_stat_mtime (&statbuf);
106 return 0;
107 }
108 }
109 return -1;
110 }
111
112 /* The following approach is only usable as a fallback, because it is of
113 the form
114 boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
115 and therefore produces wrong values after the date has been bumped in the
116 running system, which happens frequently if the system is running in a
117 virtual machine and this VM has been put into "saved" or "sleep" state
118 and then resumed. */
119 static int
120 get_linux_boot_time_final_fallback (struct timespec *p_boot_time)
121 {
122 struct timespec uptime;
123 if (get_linux_uptime (&uptime) >= 0)
124 {
125 struct timespec result;
126 # if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__)
127 /* Better than:
128 if (0 <= clock_gettime (CLOCK_REALTIME, &result))
129 because timespec_get does not need -lrt in glibc 2.16.
130 */
131 if (! timespec_get (&result, TIME_UTC))
132 return -1;
133 # else
134 /* Fall back on lower-res approach that does not need -lrt.
135 This is good enough; on these hosts UPTIME is even lower-res. */
136 struct timeval tv;
137 int r = gettimeofday (&tv, NULL);
138 if (r < 0)
139 return r;
140 result.tv_sec = tv.tv_sec;
141 result.tv_nsec = tv.tv_usec * 1000;
142 # endif
143
144 if (result.tv_nsec < uptime.tv_nsec)
145 {
146 result.tv_nsec += 1000000000;
147 result.tv_sec -= 1;
148 }
149 result.tv_sec -= uptime.tv_sec;
150 result.tv_nsec -= uptime.tv_nsec;
151 *p_boot_time = result;
152 return 0;
153 }
154 return -1;
155 }
156
157 #endif
158
159 #if defined __ANDROID__
160
161 static int
162 get_android_boot_time (struct timespec *p_boot_time)
163 {
164 /* On Android, there is no /var, and normal processes don't have access
165 to system files. Therefore use the kernel's uptime counter, although
166 it produces wrong values after the date has been bumped in the running
167 system. */
168 struct timespec uptime;
169 if (get_linux_uptime (&uptime) >= 0)
170 {
171 struct timespec result;
172 if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
173 {
174 if (result.tv_nsec < uptime.tv_nsec)
175 {
176 result.tv_nsec += 1000000000;
177 result.tv_sec -= 1;
178 }
179 result.tv_sec -= uptime.tv_sec;
180 result.tv_nsec -= uptime.tv_nsec;
181 *p_boot_time = result;
182 return 0;
183 }
184 }
185 return -1;
186 }
187
188 #endif
189
190 #if defined __OpenBSD__
191
192 static int
193 get_openbsd_boot_time (struct timespec *p_boot_time)
194 {
195 /* On OpenBSD, UTMP_FILE is not filled. It contains only dummy entries.
196 So, get the time stamp of a file that gets touched only during the
197 boot process. */
198 const char * const boot_touched_files[] =
199 {
200 "/var/db/host.random",
201 "/var/run/utmp"
202 };
203 for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
204 {
205 const char *filename = boot_touched_files[i];
206 struct stat statbuf;
207 if (stat (filename, &statbuf) >= 0)
208 {
209 *p_boot_time = get_stat_mtime (&statbuf);
210 return 0;
211 }
212 }
213 return -1;
214 }
215
216 #endif
217
218 #if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
219 && defined CTL_KERN && defined KERN_BOOTTIME \
220 && !defined __minix
221 /* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */
222 /* On Minix 3.3 this sysctl produces garbage results. Therefore avoid it. */
223
224 /* The following approach is only usable as a fallback, because it produces
225 wrong values after the date has been bumped in the running system, which
226 happens frequently if the system is running in a virtual machine and this
227 VM has been put into "saved" or "sleep" state and then resumed. */
228 static int
229 get_bsd_boot_time_final_fallback (struct timespec *p_boot_time)
230 {
231 static int request[2] = { CTL_KERN, KERN_BOOTTIME };
232 struct timeval result;
233 size_t result_len = sizeof result;
234
235 if (sysctl (request, 2, &result, &result_len, NULL, 0) >= 0)
236 {
237 p_boot_time->tv_sec = result.tv_sec;
238 p_boot_time->tv_nsec = result.tv_usec * 1000;
239 return 0;
240 }
241 return -1;
242 }
243
244 #endif
245
246 #if defined __HAIKU__
247
248 static int
249 get_haiku_boot_time (struct timespec *p_boot_time)
250 {
251 /* On Haiku, /etc/utmp does not exist. During boot,
252 1. the current time is restored, but possibly with a wrong time zone,
253 that is, with an offset of a few hours,
254 2. some symlinks and files get created,
255 3. the various devices are brought up, in particular the network device,
256 4. the correct date and time is set,
257 5. some more device nodes get created.
258 The boot time can be retrieved by looking at a directory created during
259 phase 5, such as /dev/input. */
260 const char * const boot_touched_file = "/dev/input";
261 struct stat statbuf;
262 if (stat (boot_touched_file, &statbuf) >= 0)
263 {
264 *p_boot_time = get_stat_mtime (&statbuf);
265 return 0;
266 }
267 return -1;
268 }
269
270 #endif
271
272 #if HAVE_OS_H /* BeOS, Haiku */
273
274 /* The following approach is only usable as a fallback, because it produces
275 wrong values after the date has been bumped in the running system, which
276 happens frequently if the system is running in a virtual machine and this
277 VM has been put into "saved" or "sleep" state and then resumed. */
278 static int
279 get_haiku_boot_time_final_fallback (struct timespec *p_boot_time)
280 {
281 system_info si;
282
283 get_system_info (&si);
284 p_boot_time->tv_sec = si.boot_time / 1000000;
285 p_boot_time->tv_nsec = (si.boot_time % 1000000) * 1000;
286 return 0;
287 }
288
289 #endif
290
291 #if defined __CYGWIN__ || defined _WIN32
292
293 static int
294 get_windows_boot_time (struct timespec *p_boot_time)
295 {
296 /* On Cygwin, /var/run/utmp is empty.
297 On native Windows, <utmpx.h> and <utmp.h> don't exist.
298 Instead, on Windows, the boot time can be retrieved by looking at the
299 time stamp of a file that (normally) gets touched only during the boot
300 process, namely C:\pagefile.sys. */
301 const char * const boot_touched_file =
302 #if defined __CYGWIN__ && !defined _WIN32
303 "/cygdrive/c/pagefile.sys"
304 #else
305 "C:\\pagefile.sys"
306 #endif
307 ;
308 struct stat statbuf;
309 if (stat (boot_touched_file, &statbuf) >= 0)
310 {
311 *p_boot_time = get_stat_mtime (&statbuf);
312 return 0;
313 }
314 return -1;
315 }
316
317 #endif