1 /* Test of readutmp module.
2 Copyright (C) 2023-2025 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program 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>, 2023. */
32 #define ELEMENT STRUCT_UTMP
33 #define COMPARE(entry1, entry2) \
34 _GL_CMP (UT_TIME_MEMBER (entry1), UT_TIME_MEMBER (entry2))
36 #include "array-mergesort.h"
41 main (int argc
, char *argv
[])
46 if (read_utmp (UTMP_FILE
, &num_entries
, &entries
, 0) < 0)
48 #if READ_UTMP_SUPPORTED
49 fprintf (stderr
, "Skipping test: cannot open %s\n", UTMP_FILE
);
51 fprintf (stderr
, "Skipping test: neither <utmpx.h> nor <utmp.h> is available\n");
56 printf ("Here are the read_utmp results.\n");
57 printf ("Flags: B = Boot, U = User Process\n");
59 printf (" Termiā Flags\n");
60 printf (" Time (GMT) User Device PID nation Exit B U Host\n");
61 printf ("------------------- ------------------ ----------- ---------- ------ ---- - - ----\n");
63 /* What do the results look like?
64 * On Alpine Linux, Cygwin, Android, the output is empty.
65 * The entries are usually not sorted according to the Time column, so
67 * In the User column, special values exist:
68 - The empty string denotes a system event.
69 Seen on glibc, macOS, FreeBSD, NetBSD, OpenBSD, AIX
70 - The value "reboot" denotes a reboot.
72 - The value "runlevel" denotes a runlevel change.
74 - The value "LOGIN" denotes the start of a virtual console.
75 Seen on glibc, Solaris
76 - The value "/usr/libexec/getty" denotes the start of a virtual console.
78 * In the Device column:
79 - The empty string denotes a system event.
80 Seen on macOS, FreeBSD, AIX
81 - The value "~" denotes an event with no associated device.
83 - The values "system boot", "system down", "run-level N" are
84 seen on NetBSD, AIX, Solaris.
85 - The values "old time", "new time" are
88 - On glibc: "ttyN" (console) and "pts/N" (pseudo-terminals).
89 - On macOS: "ttysNNN" (pseudo-terminals).
90 - On FreeBSD: "ttyvN" (console).
91 - On NetBSD: "ttyEN", "constty" (console).
92 - On OpenBSD: "ttyCN", "console" (console) and "ttypN" (pseudo-terminals).
93 - on AIX: "vtyN" (console) and "pts/N" (pseudo-terminals).
94 - On Solaris: "vt/N", "console" (console) and "pts/N" (pseudo-terminals).
95 * The PID column is zero on platforms without a 'ut_pid' field: OpenBSD.
99 /* Sort the entries according to increasing UT_TIME_MEMBER (entry).
100 Use a stable sort algorithm. */
101 merge_sort_inplace (entries
, num_entries
,
102 XNMALLOC (num_entries
, STRUCT_UTMP
));
104 idx_t boot_time_count
= 0;
106 for (i
= 0; i
< num_entries
; i
++)
108 const STRUCT_UTMP
*entry
= &entries
[i
];
110 char *user
= extract_trimmed_name (entry
);
111 const char *device
= entry
->ut_line
;
112 long pid
= UT_PID (entry
);
113 int termination
= UT_EXIT_E_TERMINATION (entry
);
114 int exit
= UT_EXIT_E_EXIT (entry
);
115 const char *host
= entry
->ut_host
;
117 time_t tim
= UT_TIME_MEMBER (entry
);
118 struct tm
*gmt
= gmtime (&tim
);
121 || strftime (timbuf
, sizeof (timbuf
), "%Y-%m-%d %H:%M:%S", gmt
)
123 strcpy (timbuf
, "---");
125 printf ("%-19s %-18s %-11s %10ld %4d %3d %c %c %s\n",
132 UT_TYPE_BOOT_TIME (entry
) ? 'X' : ' ',
133 UT_TYPE_USER_PROCESS (entry
) ? 'X' : ' ',
136 if (UT_TYPE_BOOT_TIME (entry
))
141 /* If the first time is more than 5 years in the past or the last time
142 is more than a week in the future, the time_t members are wrong. */
143 time_t first
= UT_TIME_MEMBER (&entries
[0]);
144 time_t last
= UT_TIME_MEMBER (&entries
[num_entries
- 1]);
145 time_t now
= time (NULL
);
146 ASSERT (first
>= now
- 157680000);
147 ASSERT (last
<= now
+ 604800);
149 /* read_utmp should not produce multiple BOOT_TIME entries. */
150 ASSERT (boot_time_count
<= 1);
152 /* read_utmp should fake a BOOT_TIME entry if needed.
153 Platform specific hacks go into lib/boot-time-aux.h. */
154 ASSERT (boot_time_count
>= 1);
159 return test_exit_status
;