1 /* GNU's read utmp module.
2
3 Copyright (C) 1992-2001, 2003-2006, 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 #include <config.h>
21
22 #include "readutmp.h"
23
24 #include <errno.h>
25 #include <stdio.h>
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33
34 #if defined __linux__ || defined __ANDROID__
35 # include <sys/sysinfo.h>
36 # include <time.h>
37 #endif
38 #if READUTMP_USE_SYSTEMD
39 # include <dirent.h>
40 # include <systemd/sd-login.h>
41 #endif
42
43 #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__) && !defined __minix
44 # if HAVE_SYS_PARAM_H
45 # include <sys/param.h>
46 # endif
47 # include <sys/sysctl.h>
48 #endif
49
50 #if HAVE_OS_H
51 # include <OS.h>
52 #endif
53
54 #include "stat-time.h"
55 #include "xalloc.h"
56
57 /* Each of the FILE streams in this file is only used in a single thread. */
58 #include "unlocked-io.h"
59
60 /* Some helper functions. */
61 #include "boot-time-aux.h"
62
63 /* The following macros describe the 'struct UTMP_STRUCT_NAME',
64 *not* 'struct gl_utmp'. */
65 #undef UT_USER
66 #undef UT_TIME_MEMBER
67 #undef UT_PID
68 #undef UT_TYPE_EQ
69 #undef UT_TYPE_NOT_DEFINED
70 #undef UT_EXIT_E_TERMINATION
71 #undef UT_EXIT_E_EXIT
72
73 /* Accessor macro for the member named ut_user or ut_name. */
74 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_NAME \
75 : HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_NAME)
76 # define UT_USER(UT) ((UT)->ut_name)
77 #else
78 # define UT_USER(UT) ((UT)->ut_user)
79 #endif
80
81 /* Accessor macro for the member of type time_t (or 'unsigned int'). */
82 #if HAVE_UTMPX_H || (HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_TV)
83 # define UT_TIME_MEMBER(UT) ((UT)->ut_tv.tv_sec)
84 #else
85 # define UT_TIME_MEMBER(UT) ((UT)->ut_time)
86 #endif
87
88 /* Accessor macro for the member named ut_pid. */
89 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
90 # define UT_PID(UT) ((UT)->ut_pid)
91 #else
92 # define UT_PID(UT) 0
93 #endif
94
95 /* Accessor macros for the member named ut_type. */
96 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
97 # define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V))
98 # define UT_TYPE_NOT_DEFINED 0
99 #else
100 # define UT_TYPE_EQ(UT, V) 0
101 # define UT_TYPE_NOT_DEFINED 1
102 #endif
103
104 #if HAVE_UTMPX_H
105 # if HAVE_STRUCT_UTMPX_UT_EXIT_E_TERMINATION
106 # define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
107 # elif HAVE_STRUCT_UTMPX_UT_EXIT_UT_TERMINATION /* OSF/1 */
108 # define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.ut_termination)
109 # else
110 # define UT_EXIT_E_TERMINATION(UT) 0
111 # endif
112 #elif HAVE_UTMP_H
113 # if HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION
114 # define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
115 # else
116 # define UT_EXIT_E_TERMINATION(UT) 0
117 # endif
118 #endif
119
120 #if HAVE_UTMPX_H
121 # if HAVE_STRUCT_UTMPX_UT_EXIT_E_EXIT
122 # define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)
123 # elif HAVE_STRUCT_UTMPX_UT_EXIT_UT_EXIT /* OSF/1 */
124 # define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.ut_exit)
125 # else
126 # define UT_EXIT_E_EXIT(UT) 0
127 # endif
128 #elif HAVE_UTMP_H
129 # if HAVE_STRUCT_UTMP_UT_EXIT_E_EXIT
130 # define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)
131 # else
132 # define UT_EXIT_E_EXIT(UT) 0
133 # endif
134 #endif
135
136 /* Size of the UT_USER (ut) member. */
137 #define UT_USER_SIZE sizeof UT_USER ((struct UTMP_STRUCT_NAME *) 0)
138 /* Size of the ut->ut_id member. */
139 #define UT_ID_SIZE sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_id)
140 /* Size of the ut->ut_line member. */
141 #define UT_LINE_SIZE sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_line)
142 /* Size of the ut->ut_host member. */
143 #define UT_HOST_SIZE sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_host)
144
145 #if 8 <= __GNUC__
146 # pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
147 #endif
148
149 /* Copy UT->ut_user into storage obtained from malloc. Then remove any
150 trailing spaces from the copy, NUL terminate it, and return the copy. */
151
152 char *
153 extract_trimmed_name (const STRUCT_UTMP *ut)
154 {
155 char const *name = ut->ut_user;
156 idx_t len = strlen (name);
157 char const *p;
158 for (p = name + len; name < p && p[-1] == ' '; p--)
159 continue;
160 return ximemdup0 (name, p - name);
161 }
162
163 #if READ_UTMP_SUPPORTED
164
165 /* Is the utmp entry UT desired by the user who asked for OPTIONS? */
166
167 static bool
168 desirable_utmp_entry (STRUCT_UTMP const *ut, int options)
169 {
170 # if defined __OpenBSD__ && !HAVE_UTMPX_H
171 /* Eliminate entirely empty entries. */
172 if (ut->ut_ts.tv_sec == 0 && ut->ut_user[0] == '\0'
173 && ut->ut_line[0] == '\0' && ut->ut_host[0] == '\0')
174 return false;
175 # endif
176
177 bool boot_time = UT_TYPE_BOOT_TIME (ut);
178 if ((options & READ_UTMP_BOOT_TIME) && !boot_time)
179 return false;
180 if ((options & READ_UTMP_NO_BOOT_TIME) && boot_time)
181 return false;
182
183 bool user_proc = IS_USER_PROCESS (ut);
184 if ((options & READ_UTMP_USER_PROCESS) && !user_proc)
185 return false;
186 # if !(defined __CYGWIN__ || defined _WIN32)
187 if ((options & READ_UTMP_CHECK_PIDS)
188 && user_proc
189 && 0 < UT_PID (ut)
190 && (kill (UT_PID (ut), 0) < 0 && errno == ESRCH))
191 return false;
192 # endif
193
194 return true;
195 }
196
197 /* A memory allocation for an in-progress read_utmp. */
198
199 struct utmp_alloc
200 {
201 /* A pointer to a possibly-empty array of utmp entries,
202 followed by a possibly-empty sequence of unused bytes,
203 followed by a possibly-empty sequence of string bytes.
204 UTMP is either null or allocated by malloc. */
205 struct gl_utmp *utmp;
206
207 /* The number of utmp entries. */
208 idx_t filled;
209
210 /* The string byte sequence length. Strings are null-terminated. */
211 idx_t string_bytes;
212
213 /* The total number of bytes allocated. This equals
214 FILLED * sizeof *UTMP + [size of free area] + STRING_BYTES. */
215 idx_t alloc_bytes;
216 };
217
218 /* Use the memory allocation A, and if the read_utmp options OPTIONS
219 permit it, add a new entry with the given USER, etc. Grow A as
220 needed, reporting an error and exit on memory allocation failure.
221 Return the resulting memory allocation. */
222
223 static struct utmp_alloc
224 add_utmp (struct utmp_alloc a, int options,
225 char const *user, idx_t user_len,
226 char const *id, idx_t id_len,
227 char const *line, idx_t line_len,
228 char const *host, idx_t host_len,
229 pid_t pid, short type, struct timespec ts, long session,
230 int termination, int exit)
231 {
232 int entry_bytes = sizeof (struct gl_utmp);
233 idx_t avail = a.alloc_bytes - (entry_bytes * a.filled + a.string_bytes);
234 idx_t needed_string_bytes =
235 (user_len + 1) + (id_len + 1) + (line_len + 1) + (host_len + 1);
236 idx_t needed = entry_bytes + needed_string_bytes;
237 if (avail < needed)
238 {
239 idx_t old_string_offset = a.alloc_bytes - a.string_bytes;
240 void *new = xpalloc (a.utmp, &a.alloc_bytes, needed - avail, -1, 1);
241 idx_t new_string_offset = a.alloc_bytes - a.string_bytes;
242 a.utmp = new;
243 char *q = new;
244 memmove (q + new_string_offset, q + old_string_offset, a.string_bytes);
245 }
246 struct gl_utmp *ut = &a.utmp[a.filled];
247 char *stringlim = (char *) a.utmp + a.alloc_bytes;
248 char *p = stringlim - a.string_bytes;
249 *--p = '\0'; /* NUL-terminate ut->ut_user */
250 ut->ut_user = p = memcpy (p - user_len, user, user_len);
251 *--p = '\0'; /* NUL-terminate ut->ut_id */
252 ut->ut_id = p = memcpy (p - id_len, id, id_len);
253 *--p = '\0'; /* NUL-terminate ut->ut_line */
254 ut->ut_line = p = memcpy (p - line_len, line, line_len);
255 *--p = '\0'; /* NUL-terminate ut->ut_host */
256 ut->ut_host = memcpy (p - host_len, host, host_len);
257 ut->ut_ts = ts;
258 ut->ut_pid = pid;
259 ut->ut_session = session;
260 ut->ut_type = type;
261 ut->ut_exit.e_termination = termination;
262 ut->ut_exit.e_exit = exit;
263 if (desirable_utmp_entry (ut, options))
264 {
265 /* Now that UT has been checked, relocate its string slots to be
266 relative to the end of the allocated storage, so that these
267 slots survive realloc. The slots will be relocated back just
268 before read_utmp returns. */
269 ut->ut_user = (char *) (intptr_t) (ut->ut_user - stringlim);
270 ut->ut_id = (char *) (intptr_t) (ut->ut_id - stringlim);
271 ut->ut_line = (char *) (intptr_t) (ut->ut_line - stringlim);
272 ut->ut_host = (char *) (intptr_t) (ut->ut_host - stringlim);
273 a.filled++;
274 a.string_bytes += needed_string_bytes;
275 }
276 return a;
277 }
278
279 /* Relocate the string pointers in A back to their natural position. */
280 static struct utmp_alloc
281 finish_utmp (struct utmp_alloc a)
282 {
283 char *stringlim = (char *) a.utmp + a.alloc_bytes;
284
285 for (idx_t i = 0; i < a.filled; i++)
286 {
287 a.utmp[i].ut_user = (intptr_t) a.utmp[i].ut_user + stringlim;
288 a.utmp[i].ut_id = (intptr_t) a.utmp[i].ut_id + stringlim;
289 a.utmp[i].ut_line = (intptr_t) a.utmp[i].ut_line + stringlim;
290 a.utmp[i].ut_host = (intptr_t) a.utmp[i].ut_host + stringlim;
291 }
292
293 return a;
294 }
295
296 /* Determine whether A already contains an entry of type BOOT_TIME. */
297 _GL_ATTRIBUTE_MAYBE_UNUSED
298 static bool
299 have_boot_time (struct utmp_alloc a)
300 {
301 for (idx_t i = 0; i < a.filled; i++)
302 {
303 struct gl_utmp *ut = &a.utmp[i];
304 if (UT_TYPE_BOOT_TIME (ut))
305 return true;
306 }
307 return false;
308 }
309
310 #if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION
311 # if !HAVE_DECL_ENDUTENT /* Android */
312 void endutent (void);
313 # endif
314 #endif
315
316 static int
317 read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
318 int options)
319 {
320 if ((options & READ_UTMP_BOOT_TIME) != 0
321 && (options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) != 0)
322 {
323 /* No entries can match the given options. */
324 *n_entries = 0;
325 *utmp_buf = NULL;
326 return 0;
327 }
328
329 struct utmp_alloc a = {0};
330
331 # if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H
332
333 # if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */
334
335 /* Ignore the return value for now.
336 Solaris' utmpname returns 1 upon success -- which is contrary
337 to what the GNU libc version does. In addition, older GNU libc
338 versions are actually void. */
339 UTMP_NAME_FUNCTION ((char *) file);
340
341 SET_UTMP_ENT ();
342
343 # if (defined __linux__ && !defined __ANDROID__) || defined __minix
344 bool file_is_utmp = (strcmp (file, UTMP_FILE) == 0);
345 /* Timestamp of the "runlevel" entry, if any. */
346 struct timespec runlevel_ts = {0};
347 # endif
348
349 void const *entry;
350
351 while ((entry = GET_UTMP_ENT ()) != NULL)
352 {
353 struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) entry;
354
355 struct timespec ts =
356 #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
357 { .tv_sec = ut->ut_tv.tv_sec, .tv_nsec = ut->ut_tv.tv_usec * 1000 };
358 #else
359 { .tv_sec = ut->ut_time, .tv_nsec = 0 };
360 #endif
361
362 a = add_utmp (a, options,
363 UT_USER (ut), strnlen (UT_USER (ut), UT_USER_SIZE),
364 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)
365 ut->ut_id, strnlen (ut->ut_id, UT_ID_SIZE),
366 #else
367 "", 0,
368 #endif
369 ut->ut_line, strnlen (ut->ut_line, UT_LINE_SIZE),
370 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)
371 ut->ut_host, strnlen (ut->ut_host, UT_HOST_SIZE),
372 #else
373 "", 0,
374 #endif
375 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
376 ut->ut_pid,
377 #else
378 0,
379 #endif
380 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
381 ut->ut_type,
382 #else
383 0,
384 #endif
385 ts,
386 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
387 ut->ut_session,
388 #else
389 0,
390 #endif
391 UT_EXIT_E_TERMINATION (ut), UT_EXIT_E_EXIT (ut)
392 );
393 # if defined __linux__ && !defined __ANDROID__
394 if (file_is_utmp
395 && memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0
396 && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0)
397 runlevel_ts = ts;
398 # endif
399 # if defined __minix
400 if (file_is_utmp
401 && UT_USER (ut)[0] == '\0'
402 && memcmp (ut->ut_line, "run-level ", strlen ("run-level ")) == 0)
403 runlevel_ts = ts;
404 # endif
405 }
406
407 END_UTMP_ENT ();
408
409 # if defined __linux__ && !defined __ANDROID__
410 /* On Alpine Linux, UTMP_FILE is not filled. It is always empty.
411 So, fake a BOOT_TIME entry, by getting the time stamp of a file that
412 gets touched only during the boot process.
413
414 On Raspbian, which runs on hardware without a real-time clock, during boot,
415 1. the clock gets set to 1970-01-01 00:00:00,
416 2. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
417 ut_user = "reboot", ut_line = "~", time = 1970-01-01 00:00:05 or so,
418 3. the clock gets set to a correct value through NTP,
419 4. an entry gets written into /var/run/utmp, with
420 ut_user = "runlevel", ut_line = "~", time = correct value.
421 In this case, copy the time from the "runlevel" entry to the "reboot"
422 entry. */
423 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
424 && file_is_utmp)
425 {
426 for (idx_t i = 0; i < a.filled; i++)
427 {
428 struct gl_utmp *ut = &a.utmp[i];
429 if (UT_TYPE_BOOT_TIME (ut))
430 {
431 /* Workaround for Raspbian: */
432 if (ut->ut_ts.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
433 ut->ut_ts = runlevel_ts;
434 break;
435 }
436 }
437 if (!have_boot_time (a))
438 {
439 /* Workaround for Alpine Linux: */
440 struct timespec boot_time;
441 if (get_linux_boot_time_fallback (&boot_time) >= 0)
442 a = add_utmp (a, options,
443 "reboot", strlen ("reboot"),
444 "", 0,
445 "~", strlen ("~"),
446 "", 0,
447 0, BOOT_TIME, boot_time, 0, 0, 0);
448 }
449 }
450 # endif
451
452 # if defined __ANDROID__
453 /* On Android, there is no /var, and normal processes don't have access
454 to system files. Therefore use the kernel's uptime counter, although
455 it produces wrong values after the date has been bumped in the running
456 system. */
457 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
458 && strcmp (file, UTMP_FILE) == 0
459 && !have_boot_time (a))
460 {
461 struct timespec boot_time;
462 if (get_android_boot_time (&boot_time) >= 0)
463 a = add_utmp (a, options,
464 "reboot", strlen ("reboot"),
465 "", 0,
466 "", 0,
467 "", 0,
468 0, BOOT_TIME, boot_time, 0, 0, 0);
469 }
470 # endif
471
472 # if defined __minix
473 /* On Minix, during boot,
474 1. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
475 ut_user = "", ut_line = "system boot", time = 1970-01-01 00:00:00,
476 2. an entry gets written into /var/run/utmp, with
477 ut_user = "", ut_line = "run-level m", time = correct value.
478 In this case, copy the time from the "run-level m" entry to the
479 "system boot" entry. */
480 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
481 && file_is_utmp)
482 {
483 for (idx_t i = 0; i < a.filled; i++)
484 {
485 struct gl_utmp *ut = &a.utmp[i];
486 if (UT_TYPE_BOOT_TIME (ut))
487 {
488 if (ut->ut_ts.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
489 ut->ut_ts = runlevel_ts;
490 break;
491 }
492 }
493 }
494 # endif
495
496 # else /* old FreeBSD, OpenBSD, HP-UX, Haiku */
497
498 FILE *f = fopen (file, "re");
499
500 if (f != NULL)
501 {
502 for (;;)
503 {
504 struct UTMP_STRUCT_NAME ut;
505
506 if (fread (&ut, sizeof ut, 1, f) == 0)
507 break;
508 a = add_utmp (a, options,
509 UT_USER (&ut), strnlen (UT_USER (&ut), UT_USER_SIZE),
510 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)
511 ut.ut_id, strnlen (ut.ut_id, UT_ID_SIZE),
512 #else
513 "", 0,
514 #endif
515 ut.ut_line, strnlen (ut.ut_line, UT_LINE_SIZE),
516 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)
517 ut.ut_host, strnlen (ut.ut_host, UT_HOST_SIZE),
518 #else
519 "", 0,
520 #endif
521 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
522 ut.ut_pid,
523 #else
524 0,
525 #endif
526 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
527 ut.ut_type,
528 #else
529 0,
530 #endif
531 #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
532 (struct timespec) { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 },
533 #else
534 (struct timespec) { .tv_sec = ut.ut_time, .tv_nsec = 0 },
535 #endif
536 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
537 ut.ut_session,
538 #else
539 0,
540 #endif
541 UT_EXIT_E_TERMINATION (&ut), UT_EXIT_E_EXIT (&ut)
542 );
543 }
544
545 int saved_errno = ferror (f) ? errno : 0;
546 if (fclose (f) != 0)
547 saved_errno = errno;
548 if (saved_errno != 0)
549 {
550 free (a.utmp);
551 errno = saved_errno;
552 return -1;
553 }
554 }
555 else
556 {
557 if (strcmp (file, UTMP_FILE) != 0)
558 {
559 int saved_errno = errno;
560 free (a.utmp);
561 errno = saved_errno;
562 return -1;
563 }
564 }
565
566 # if defined __OpenBSD__
567 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
568 && strcmp (file, UTMP_FILE) == 0
569 && !have_boot_time (a))
570 {
571 struct timespec boot_time;
572 if (get_openbsd_boot_time (&boot_time) >= 0)
573 a = add_utmp (a, options,
574 "reboot", strlen ("reboot"),
575 "", 0,
576 "", 0,
577 "", 0,
578 0, BOOT_TIME, boot_time, 0, 0, 0);
579 }
580 # endif
581
582 # endif
583
584 # if defined __linux__ && !defined __ANDROID__
585 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
586 && strcmp (file, UTMP_FILE) == 0
587 && !have_boot_time (a))
588 {
589 struct timespec boot_time;
590 if (get_linux_boot_time_final_fallback (&boot_time) >= 0)
591 a = add_utmp (a, options,
592 "reboot", strlen ("reboot"),
593 "", 0,
594 "~", strlen ("~"),
595 "", 0,
596 0, BOOT_TIME, boot_time, 0, 0, 0);
597 }
598
599 # endif
600
601 # if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
602 && defined CTL_KERN && defined KERN_BOOTTIME \
603 && !defined __minix
604 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
605 && strcmp (file, UTMP_FILE) == 0
606 && !have_boot_time (a))
607 {
608 struct timespec boot_time;
609 if (get_bsd_boot_time_final_fallback (&boot_time) >= 0)
610 a = add_utmp (a, options,
611 "reboot", strlen ("reboot"),
612 "", 0,
613 "", 0,
614 "", 0,
615 0, BOOT_TIME, boot_time, 0, 0, 0);
616 }
617 # endif
618
619 # if defined __HAIKU__
620 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
621 && strcmp (file, UTMP_FILE) == 0
622 && !have_boot_time (a))
623 {
624 struct timespec boot_time;
625 if (get_haiku_boot_time (&boot_time) >= 0)
626 a = add_utmp (a, options,
627 "reboot", strlen ("reboot"),
628 "", 0,
629 "", 0,
630 "", 0,
631 0, BOOT_TIME, boot_time, 0, 0, 0);
632 }
633 # endif
634
635 # if HAVE_OS_H /* BeOS, Haiku */
636 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
637 && strcmp (file, UTMP_FILE) == 0
638 && !have_boot_time (a))
639 {
640 struct timespec boot_time;
641 if (get_haiku_boot_time_final_fallback (&boot_time) >= 0)
642 a = add_utmp (a, options,
643 "reboot", strlen ("reboot"),
644 "", 0,
645 "", 0,
646 "", 0,
647 0, BOOT_TIME, boot_time, 0, 0, 0);
648 }
649 # endif
650
651 # endif
652
653 # if defined __CYGWIN__ || defined _WIN32
654 if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
655 && strcmp (file, UTMP_FILE) == 0
656 && !have_boot_time (a))
657 {
658 struct timespec boot_time;
659 if (get_windows_boot_time (&boot_time) >= 0)
660 a = add_utmp (a, options,
661 "reboot", strlen ("reboot"),
662 "", 0,
663 "", 0,
664 "", 0,
665 0, BOOT_TIME, boot_time, 0, 0, 0);
666 }
667 # endif
668
669 a = finish_utmp (a);
670
671 *n_entries = a.filled;
672 *utmp_buf = a.utmp;
673
674 return 0;
675 }
676
677 # if READUTMP_USE_SYSTEMD
678 /* Use systemd and Linux /proc and kernel APIs. */
679
680 static struct timespec
681 get_boot_time_uncached (void)
682 {
683 /* Try to find the boot time in the /var/run/utmp file. */
684 {
685 idx_t n_entries = 0;
686 STRUCT_UTMP *utmp = NULL;
687 read_utmp_from_file (UTMP_FILE, &n_entries, &utmp, READ_UTMP_BOOT_TIME);
688 if (n_entries > 0)
689 {
690 struct timespec result = utmp[0].ut_ts;
691 free (utmp);
692 return result;
693 }
694 free (utmp);
695 }
696
697 /* We shouldn't get here. */
698 return (struct timespec) {0};
699 }
700
701 static struct timespec
702 get_boot_time (void)
703 {
704 static bool volatile cached;
705 static struct timespec volatile boot_time;
706
707 if (!cached)
708 {
709 boot_time = get_boot_time_uncached ();
710 cached = true;
711 }
712 return boot_time;
713 }
714
715 /* Guess the pty name that was opened for the given user right after
716 the given time AT. */
717 static char *
718 guess_pty_name (uid_t uid, const struct timespec at)
719 {
720 /* Traverse the entries of the /dev/pts/ directory, looking for devices
721 which are owned by UID and whose ctime is shortly after AT. */
722 DIR *dirp = opendir ("/dev/pts");
723 if (dirp != NULL)
724 {
725 /* Buffer containing /dev/pts/N. */
726 char name_buf[9 + 10 + 1];
727 memcpy (name_buf, "/dev/pts/", 9);
728
729 char best_name[9 + 10 + 1];
730 struct timespec best_time = { .tv_sec = 0, .tv_nsec = 0 };
731
732 for (;;)
733 {
734 struct dirent *dp = readdir (dirp);
735 if (dp == NULL)
736 break;
737 if (dp->d_name[0] != '.' && strlen (dp->d_name) <= 10)
738 {
739 /* Compose the absolute file name /dev/pts/N. */
740 strcpy (name_buf + 9, dp->d_name);
741
742 /* Find its owner and ctime. */
743 struct stat st;
744 if (stat (name_buf, &st) >= 0
745 && st.st_uid == uid
746 && (st.st_ctim.tv_sec > at.tv_sec
747 || (st.st_ctim.tv_sec == at.tv_sec
748 && st.st_ctim.tv_nsec >= at.tv_nsec)))
749 {
750 /* This entry has the owner UID and a ctime >= AT. */
751 /* Is this entry the best one so far? */
752 if ((best_time.tv_sec == 0 && best_time.tv_nsec == 0)
753 || (st.st_ctim.tv_sec < best_time.tv_sec
754 || (st.st_ctim.tv_sec == best_time.tv_sec
755 && st.st_ctim.tv_nsec < best_time.tv_nsec)))
756 {
757 strcpy (best_name, name_buf);
758 best_time = st.st_ctim;
759 }
760 }
761 }
762 }
763
764 closedir (dirp);
765
766 /* Did we find an entry owned by ID, and is it at most 5 seconds
767 after AT? */
768 if (!(best_time.tv_sec == 0 && best_time.tv_nsec == 0)
769 && (best_time.tv_sec < at.tv_sec + 5
770 || (best_time.tv_sec == at.tv_sec + 5
771 && best_time.tv_nsec <= at.tv_nsec)))
772 return xstrdup (best_name + 5);
773 }
774
775 return NULL;
776 }
777
778 static int
779 read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
780 {
781 /* Fill entries, simulating what a utmp file would contain. */
782 struct utmp_alloc a = {0};
783
784 /* Synthesize a BOOT_TIME entry. */
785 if (!(options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)))
786 a = add_utmp (a, options,
787 "reboot", strlen ("reboot"),
788 "", 0,
789 "~", strlen ("~"),
790 "", 0,
791 0, BOOT_TIME, get_boot_time (), 0, 0, 0);
792
793 /* Synthesize USER_PROCESS entries. */
794 if (!(options & READ_UTMP_BOOT_TIME))
795 {
796 char **sessions;
797 int num_sessions = sd_get_sessions (&sessions);
798 if (num_sessions >= 0)
799 {
800 char **session_ptr;
801 for (session_ptr = sessions; *session_ptr != NULL; session_ptr++)
802 {
803 char *session = *session_ptr;
804
805 uint64_t start_usec;
806 if (sd_session_get_start_time (session, &start_usec) < 0)
807 start_usec = 0;
808 struct timespec start_ts;
809 start_ts.tv_sec = start_usec / 1000000;
810 start_ts.tv_nsec = start_usec % 1000000 * 1000;
811
812 char *seat;
813 if (sd_session_get_seat (session, &seat) < 0)
814 seat = NULL;
815
816 char missing[] = "";
817
818 char *type = NULL;
819 char *tty;
820 if (sd_session_get_tty (session, &tty) < 0)
821 {
822 tty = NULL;
823 /* Try harder to get a sensible value for the tty. */
824 if (sd_session_get_type (session, &type) < 0)
825 type = missing;
826 if (strcmp (type, "tty") == 0)
827 {
828 char *service;
829 if (sd_session_get_service (session, &service) < 0)
830 service = NULL;
831
832 uid_t uid;
833 char *pty = (sd_session_get_uid (session, &uid) < 0 ? NULL
834 : guess_pty_name (uid, start_ts));
835
836 if (service != NULL && pty != NULL)
837 {
838 tty = xmalloc (strlen (service) + 1 + strlen (pty) + 1);
839 stpcpy (stpcpy (stpcpy (tty, service), " "), pty);
840 free (pty);
841 free (service);
842 }
843 else if (service != NULL)
844 tty = service;
845 else if (pty != NULL)
846 tty = pty;
847 }
848 }
849
850 /* Create up to two USER_PROCESS entries: one for the seat,
851 one for the tty. */
852 if (seat != NULL || tty != NULL)
853 {
854 char *user;
855 if (sd_session_get_username (session, &user) < 0)
856 user = missing;
857
858 pid_t leader_pid;
859 if (sd_session_get_leader (session, &leader_pid) < 0)
860 leader_pid = 0;
861
862 char *host;
863 char *remote_host;
864 if (sd_session_get_remote_host (session, &remote_host) < 0)
865 {
866 host = missing;
867 /* For backward compatibility, put the X11 display into the
868 host field. */
869 if (!type && sd_session_get_type (session, &type) < 0)
870 type = missing;
871 if (strcmp (type, "x11") == 0)
872 {
873 char *display;
874 if (sd_session_get_display (session, &display) < 0)
875 display = NULL;
876 host = display;
877 }
878 }
879 else
880 {
881 char *remote_user;
882 if (sd_session_get_remote_user (session, &remote_user) < 0)
883 host = remote_host;
884 else
885 {
886 host = xmalloc (strlen (remote_user) + 1
887 + strlen (remote_host) + 1);
888 stpcpy (stpcpy (stpcpy (host, remote_user), "@"),
889 remote_host);
890 free (remote_user);
891 free (remote_host);
892 }
893 }
894
895 if (seat != NULL)
896 a = add_utmp (a, options,
897 user, strlen (user),
898 session, strlen (session),
899 seat, strlen (seat),
900 host, strlen (host),
901 leader_pid /* the best we have */,
902 USER_PROCESS, start_ts, leader_pid, 0, 0);
903 if (tty != NULL)
904 a = add_utmp (a, options,
905 user, strlen (user),
906 session, strlen (session),
907 tty, strlen (tty),
908 host, strlen (host),
909 leader_pid /* the best we have */,
910 USER_PROCESS, start_ts, leader_pid, 0, 0);
911
912 if (host != missing)
913 free (host);
914 if (user != missing)
915 free (user);
916 }
917
918 if (type != missing)
919 free (type);
920 free (tty);
921 free (seat);
922 free (session);
923 }
924 free (sessions);
925 }
926 }
927
928 a = finish_utmp (a);
929
930 *n_entries = a.filled;
931 *utmp_buf = a.utmp;
932
933 return 0;
934 }
935
936 # endif
937
938 int
939 read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
940 int options)
941 {
942 # if READUTMP_USE_SYSTEMD
943 if (strcmp (file, UTMP_FILE) == 0)
944 /* Imitate reading UTMP_FILE, using systemd and Linux APIs. */
945 return read_utmp_from_systemd (n_entries, utmp_buf, options);
946 # endif
947
948 return read_utmp_from_file (file, n_entries, utmp_buf, options);
949 }
950
951 #else /* dummy fallback */
952
953 int
954 read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
955 int options)
956 {
957 errno = ENOSYS;
958 return -1;
959 }
960
961 #endif