1 /* GNU's read utmp module.
3 Copyright (C) 1992-2001, 2003-2006, 2009-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
18 /* Written by jla; revised by djm */
27 #include <sys/types.h>
34 #if defined __linux__ || defined __ANDROID__
35 # include <sys/sysinfo.h>
38 #if READUTMP_USE_SYSTEMD
40 # include <systemd/sd-login.h>
43 #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__) && !defined __minix
45 # include <sys/param.h>
47 # include <sys/sysctl.h>
54 #include "stat-time.h"
57 /* Each of the FILE streams in this file is only used in a single thread. */
58 #include "unlocked-io.h"
60 /* Some helper functions. */
61 #include "boot-time-aux.h"
63 /* The following macros describe the 'struct UTMP_STRUCT_NAME',
64 *not* 'struct gl_utmp'. */
69 #undef UT_TYPE_NOT_DEFINED
70 #undef UT_EXIT_E_TERMINATION
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)
78 # define UT_USER(UT) ((UT)->ut_user)
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)
85 # define UT_TIME_MEMBER(UT) ((UT)->ut_time)
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)
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
100 # define UT_TYPE_EQ(UT, V) 0
101 # define UT_TYPE_NOT_DEFINED 1
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)
110 # define UT_EXIT_E_TERMINATION(UT) 0
113 # if HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION
114 # define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
116 # define UT_EXIT_E_TERMINATION(UT) 0
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)
126 # define UT_EXIT_E_EXIT(UT) 0
129 # if HAVE_STRUCT_UTMP_UT_EXIT_E_EXIT
130 # define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)
132 # define UT_EXIT_E_EXIT(UT) 0
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)
146 # pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
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. */
153 extract_trimmed_name (const STRUCT_UTMP
*ut
)
155 char const *name
= ut
->ut_user
;
156 idx_t len
= strlen (name
);
158 for (p
= name
+ len
; name
< p
&& p
[-1] == ' '; p
--)
160 return ximemdup0 (name
, p
- name
);
163 #if READ_UTMP_SUPPORTED
165 /* Is the utmp entry UT desired by the user who asked for OPTIONS? */
168 desirable_utmp_entry (STRUCT_UTMP
const *ut
, int options
)
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')
177 bool boot_time
= UT_TYPE_BOOT_TIME (ut
);
178 if ((options
& READ_UTMP_BOOT_TIME
) && !boot_time
)
180 if ((options
& READ_UTMP_NO_BOOT_TIME
) && boot_time
)
183 bool user_proc
= IS_USER_PROCESS (ut
);
184 if ((options
& READ_UTMP_USER_PROCESS
) && !user_proc
)
186 # if !(defined __CYGWIN__ || defined _WIN32)
187 if ((options
& READ_UTMP_CHECK_PIDS
)
190 && (kill (UT_PID (ut
), 0) < 0 && errno
== ESRCH
))
197 /* A memory allocation for an in-progress read_utmp. */
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
;
207 /* The number of utmp entries. */
210 /* The string byte sequence length. Strings are null-terminated. */
213 /* The total number of bytes allocated. This equals
214 FILLED * sizeof *UTMP + [size of free area] + STRING_BYTES. */
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. */
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
)
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
;
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
;
244 memmove (q
+ new_string_offset
, q
+ old_string_offset
, a
.string_bytes
);
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
);
259 ut
->ut_session
= session
;
261 ut
->ut_exit
.e_termination
= termination
;
262 ut
->ut_exit
.e_exit
= exit
;
263 if (desirable_utmp_entry (ut
, options
))
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
);
274 a
.string_bytes
+= needed_string_bytes
;
279 /* Relocate the string pointers in A back to their natural position. */
280 static struct utmp_alloc
281 finish_utmp (struct utmp_alloc a
)
283 char *stringlim
= (char *) a
.utmp
+ a
.alloc_bytes
;
285 for (idx_t i
= 0; i
< a
.filled
; i
++)
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
;
296 /* Determine whether A already contains an entry of type BOOT_TIME. */
297 _GL_ATTRIBUTE_MAYBE_UNUSED
299 have_boot_time (struct utmp_alloc a
)
301 for (idx_t i
= 0; i
< a
.filled
; i
++)
303 struct gl_utmp
*ut
= &a
.utmp
[i
];
304 if (UT_TYPE_BOOT_TIME (ut
))
310 #if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION
311 # if !HAVE_DECL_ENDUTENT /* Android */
312 void endutent (void);
317 read_utmp_from_file (char const *file
, idx_t
*n_entries
, STRUCT_UTMP
**utmp_buf
,
320 if ((options
& READ_UTMP_BOOT_TIME
) != 0
321 && (options
& (READ_UTMP_USER_PROCESS
| READ_UTMP_NO_BOOT_TIME
)) != 0)
323 /* No entries can match the given options. */
329 struct utmp_alloc a
= {0};
331 # if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H
333 # if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */
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
);
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};
351 while ((entry
= GET_UTMP_ENT ()) != NULL
)
353 struct UTMP_STRUCT_NAME
const *ut
= (struct UTMP_STRUCT_NAME
const *) entry
;
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 };
359 { .tv_sec
= ut
->ut_time
, .tv_nsec
= 0 };
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
),
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
),
375 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
380 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
386 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
391 UT_EXIT_E_TERMINATION (ut
), UT_EXIT_E_EXIT (ut
)
393 # if defined __linux__ && !defined __ANDROID__
395 && memcmp (UT_USER (ut
), "runlevel", strlen ("runlevel") + 1) == 0
396 && memcmp (ut
->ut_line
, "~", strlen ("~") + 1) == 0)
401 && UT_USER (ut
)[0] == '\0'
402 && memcmp (ut
->ut_line
, "run-level ", strlen ("run-level ")) == 0)
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.
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"
423 if ((options
& (READ_UTMP_USER_PROCESS
| READ_UTMP_NO_BOOT_TIME
)) == 0
426 for (idx_t i
= 0; i
< a
.filled
; i
++)
428 struct gl_utmp
*ut
= &a
.utmp
[i
];
429 if (UT_TYPE_BOOT_TIME (ut
))
431 /* Workaround for Raspbian: */
432 if (ut
->ut_ts
.tv_sec
<= 60 && runlevel_ts
.tv_sec
!= 0)
433 ut
->ut_ts
= runlevel_ts
;
437 if (!have_boot_time (a
))
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"),
447 0, BOOT_TIME
, boot_time
, 0, 0, 0);
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
457 if ((options
& (READ_UTMP_USER_PROCESS
| READ_UTMP_NO_BOOT_TIME
)) == 0
458 && strcmp (file
, UTMP_FILE
) == 0
459 && !have_boot_time (a
))
461 struct timespec boot_time
;
462 if (get_android_boot_time (&boot_time
) >= 0)
463 a
= add_utmp (a
, options
,
464 "reboot", strlen ("reboot"),
468 0, BOOT_TIME
, boot_time
, 0, 0, 0);
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
483 for (idx_t i
= 0; i
< a
.filled
; i
++)
485 struct gl_utmp
*ut
= &a
.utmp
[i
];
486 if (UT_TYPE_BOOT_TIME (ut
))
488 if (ut
->ut_ts
.tv_sec
<= 60 && runlevel_ts
.tv_sec
!= 0)
489 ut
->ut_ts
= runlevel_ts
;
496 # else /* old FreeBSD, OpenBSD, HP-UX, Haiku */
498 FILE *f
= fopen (file
, "re");
504 struct UTMP_STRUCT_NAME ut
;
506 if (fread (&ut
, sizeof ut
, 1, f
) == 0)
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
),
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
),
521 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
526 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
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 },
534 (struct timespec
) { .tv_sec
= ut
.ut_time
, .tv_nsec
= 0 },
536 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
541 UT_EXIT_E_TERMINATION (&ut
), UT_EXIT_E_EXIT (&ut
)
545 int saved_errno
= ferror (f
) ? errno
: 0;
548 if (saved_errno
!= 0)
557 if (strcmp (file
, UTMP_FILE
) != 0)
559 int saved_errno
= errno
;
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
))
571 struct timespec boot_time
;
572 if (get_openbsd_boot_time (&boot_time
) >= 0)
573 a
= add_utmp (a
, options
,
574 "reboot", strlen ("reboot"),
578 0, BOOT_TIME
, boot_time
, 0, 0, 0);
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
))
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"),
596 0, BOOT_TIME
, boot_time
, 0, 0, 0);
601 # if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
602 && defined CTL_KERN && defined KERN_BOOTTIME \
604 if ((options
& (READ_UTMP_USER_PROCESS
| READ_UTMP_NO_BOOT_TIME
)) == 0
605 && strcmp (file
, UTMP_FILE
) == 0
606 && !have_boot_time (a
))
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"),
615 0, BOOT_TIME
, boot_time
, 0, 0, 0);
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
))
624 struct timespec boot_time
;
625 if (get_haiku_boot_time (&boot_time
) >= 0)
626 a
= add_utmp (a
, options
,
627 "reboot", strlen ("reboot"),
631 0, BOOT_TIME
, boot_time
, 0, 0, 0);
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
))
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"),
647 0, BOOT_TIME
, boot_time
, 0, 0, 0);
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
))
658 struct timespec boot_time
;
659 if (get_windows_boot_time (&boot_time
) >= 0)
660 a
= add_utmp (a
, options
,
661 "reboot", strlen ("reboot"),
665 0, BOOT_TIME
, boot_time
, 0, 0, 0);
671 *n_entries
= a
.filled
;
677 # if READUTMP_USE_SYSTEMD
678 /* Use systemd and Linux /proc and kernel APIs. */
680 static struct timespec
681 get_boot_time_uncached (void)
683 /* Try to find the boot time in the /var/run/utmp file. */
686 STRUCT_UTMP
*utmp
= NULL
;
687 read_utmp_from_file (UTMP_FILE
, &n_entries
, &utmp
, READ_UTMP_BOOT_TIME
);
690 struct timespec result
= utmp
[0].ut_ts
;
697 /* We shouldn't get here. */
698 return (struct timespec
) {0};
701 static struct timespec
704 static bool volatile cached
;
705 static struct timespec
volatile boot_time
;
709 boot_time
= get_boot_time_uncached ();
715 /* Guess the pty name that was opened for the given user right after
716 the given time AT. */
718 guess_pty_name (uid_t uid
, const struct timespec at
)
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");
725 /* Buffer containing /dev/pts/N. */
726 char name_buf
[9 + 10 + 1];
727 memcpy (name_buf
, "/dev/pts/", 9);
729 char best_name
[9 + 10 + 1];
730 struct timespec best_time
= { .tv_sec
= 0, .tv_nsec
= 0 };
734 struct dirent
*dp
= readdir (dirp
);
737 if (dp
->d_name
[0] != '.' && strlen (dp
->d_name
) <= 10)
739 /* Compose the absolute file name /dev/pts/N. */
740 strcpy (name_buf
+ 9, dp
->d_name
);
742 /* Find its owner and ctime. */
744 if (stat (name_buf
, &st
) >= 0
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
)))
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
)))
757 strcpy (best_name
, name_buf
);
758 best_time
= st
.st_ctim
;
766 /* Did we find an entry owned by ID, and is it at most 5 seconds
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);
779 read_utmp_from_systemd (idx_t
*n_entries
, STRUCT_UTMP
**utmp_buf
, int options
)
781 /* Fill entries, simulating what a utmp file would contain. */
782 struct utmp_alloc a
= {0};
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"),
791 0, BOOT_TIME
, get_boot_time (), 0, 0, 0);
793 /* Synthesize USER_PROCESS entries. */
794 if (!(options
& READ_UTMP_BOOT_TIME
))
797 int num_sessions
= sd_get_sessions (&sessions
);
798 if (num_sessions
>= 0 && sessions
!= NULL
)
801 for (session_ptr
= sessions
; *session_ptr
!= NULL
; session_ptr
++)
803 char *session
= *session_ptr
;
806 if (sd_session_get_start_time (session
, &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;
813 if (sd_session_get_seat (session
, &seat
) < 0)
820 if (sd_session_get_tty (session
, &tty
) < 0)
823 /* Try harder to get a sensible value for the tty. */
824 if (sd_session_get_type (session
, &type
) < 0)
826 if (strcmp (type
, "tty") == 0)
829 if (sd_session_get_service (session
, &service
) < 0)
833 char *pty
= (sd_session_get_uid (session
, &uid
) < 0 ? NULL
834 : guess_pty_name (uid
, start_ts
));
836 if (service
!= NULL
&& pty
!= NULL
)
838 tty
= xmalloc (strlen (service
) + 1 + strlen (pty
) + 1);
839 stpcpy (stpcpy (stpcpy (tty
, service
), " "), pty
);
843 else if (service
!= NULL
)
845 else if (pty
!= NULL
)
850 /* Create up to two USER_PROCESS entries: one for the seat,
852 if (seat
!= NULL
|| tty
!= NULL
)
855 if (sd_session_get_username (session
, &user
) < 0)
859 if (sd_session_get_leader (session
, &leader_pid
) < 0)
864 if (sd_session_get_remote_host (session
, &remote_host
) < 0)
867 /* For backward compatibility, put the X11 display into the
869 if (!type
&& sd_session_get_type (session
, &type
) < 0)
871 if (strcmp (type
, "x11") == 0)
874 if (sd_session_get_display (session
, &display
) < 0)
876 /* Workaround: gdm "forgets" to pass the display to
877 systemd, thus display may be NULL here. */
885 if (sd_session_get_remote_user (session
, &remote_user
) < 0)
889 host
= xmalloc (strlen (remote_user
) + 1
890 + strlen (remote_host
) + 1);
891 stpcpy (stpcpy (stpcpy (host
, remote_user
), "@"),
899 a
= add_utmp (a
, options
,
901 session
, strlen (session
),
904 leader_pid
/* the best we have */,
905 USER_PROCESS
, start_ts
, leader_pid
, 0, 0);
907 a
= add_utmp (a
, options
,
909 session
, strlen (session
),
912 leader_pid
/* the best we have */,
913 USER_PROCESS
, start_ts
, leader_pid
, 0, 0);
933 *n_entries
= a
.filled
;
942 read_utmp (char const *file
, idx_t
*n_entries
, STRUCT_UTMP
**utmp_buf
,
945 # if READUTMP_USE_SYSTEMD
946 if (strcmp (file
, UTMP_FILE
) == 0)
947 /* Imitate reading UTMP_FILE, using systemd and Linux APIs. */
948 return read_utmp_from_systemd (n_entries
, utmp_buf
, options
);
951 return read_utmp_from_file (file
, n_entries
, utmp_buf
, options
);
954 #else /* dummy fallback */
957 read_utmp (char const *file
, idx_t
*n_entries
, STRUCT_UTMP
**utmp_buf
,