nstrftime, c-nstrftime tests: Avoid test failures on native Windows.
[gnulib.git] / tests / test-readutmp.c
blobdf0fe4ac774223d5adcbaba9020ccc9db1b6680b
1 /* Test of readutmp module.
2 Copyright (C) 2023-2024 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. */
19 #include <config.h>
21 #include "readutmp.h"
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
29 #include "idx.h"
30 #include "xalloc.h"
32 #define ELEMENT STRUCT_UTMP
33 #define COMPARE(entry1, entry2) \
34 _GL_CMP (UT_TIME_MEMBER (entry1), UT_TIME_MEMBER (entry2))
35 #define STATIC static
36 #include "array-mergesort.h"
38 #include "macros.h"
40 int
41 main (int argc, char *argv[])
43 STRUCT_UTMP *entries;
44 idx_t num_entries;
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);
50 #else
51 fprintf (stderr, "Skipping test: neither <utmpx.h> nor <utmp.h> is available\n");
52 #endif
53 return 77;
56 printf ("Here are the read_utmp results.\n");
57 printf ("Flags: B = Boot, U = User Process\n");
58 printf ("\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
66 we do it here.
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.
71 Seen on glibc
72 - The value "runlevel" denotes a runlevel change.
73 Seen on glibc
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.
77 Seen on NetBSD
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.
82 Seen on glibc
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
86 seen on Solaris.
87 - Common devices 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.
97 if (num_entries > 0)
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;
105 idx_t i;
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);
119 char timbuf[100];
120 if (gmt == NULL
121 || strftime (timbuf, sizeof (timbuf), "%Y-%m-%d %H:%M:%S", gmt)
122 == 0)
123 strcpy (timbuf, "---");
125 printf ("%-19s %-18s %-11s %10ld %4d %3d %c %c %s\n",
126 timbuf,
127 user,
128 device,
129 pid,
130 termination,
131 exit,
132 UT_TYPE_BOOT_TIME (entry) ? 'X' : ' ',
133 UT_TYPE_USER_PROCESS (entry) ? 'X' : ' ',
134 host);
136 if (UT_TYPE_BOOT_TIME (entry))
137 boot_time_count++;
139 fflush (stdout);
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);
157 free (entries);
159 return test_exit_status;