diff options
Diffstat (limited to 'gnulib-tests/test-readutmp.c')
-rw-r--r-- | gnulib-tests/test-readutmp.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/gnulib-tests/test-readutmp.c b/gnulib-tests/test-readutmp.c new file mode 100644 index 0000000..6b2e579 --- /dev/null +++ b/gnulib-tests/test-readutmp.c @@ -0,0 +1,160 @@ +/* Test of readutmp module. + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2023. */ + +#include <config.h> + +#include "readutmp.h" + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "idx.h" +#include "xalloc.h" + +#define ELEMENT STRUCT_UTMP +#define COMPARE(entry1, entry2) \ + _GL_CMP (UT_TIME_MEMBER (entry1), UT_TIME_MEMBER (entry2)) +#define STATIC static +#include "array-mergesort.h" + +#include "macros.h" + +int +main (int argc, char *argv[]) +{ + STRUCT_UTMP *entries; + idx_t num_entries; + + if (read_utmp (UTMP_FILE, &num_entries, &entries, 0) < 0) + { + #if READ_UTMP_SUPPORTED + fprintf (stderr, "Skipping test: cannot open %s\n", UTMP_FILE); + #else + fprintf (stderr, "Skipping test: neither <utmpx.h> nor <utmp.h> is available\n"); + #endif + return 77; + } + + printf ("Here are the read_utmp results.\n"); + printf ("Flags: B = Boot, U = User Process\n"); + printf ("\n"); + printf (" Termiā Flags\n"); + printf (" Time (GMT) User Device PID nation Exit B U Host\n"); + printf ("------------------- ------------------ ----------- ---------- ------ ---- - - ----\n"); + + /* What do the results look like? + * On Alpine Linux, Cygwin, Android, the output is empty. + * The entries are usually not sorted according to the Time column, so + we do it here. + * In the User column, special values exist: + - The empty string denotes a system event. + Seen on glibc, macOS, FreeBSD, NetBSD, OpenBSD, AIX + - The value "reboot" denotes a reboot. + Seen on glibc + - The value "runlevel" denotes a runlevel change. + Seen on glibc + - The value "LOGIN" denotes the start of a virtual console. + Seen on glibc, Solaris + - The value "/usr/libexec/getty" denotes the start of a virtual console. + Seen on NetBSD + * In the Device column: + - The empty string denotes a system event. + Seen on macOS, FreeBSD, AIX + - The value "~" denotes an event with no associated device. + Seen on glibc + - The values "system boot", "system down", "run-level N" are + seen on NetBSD, AIX, Solaris. + - The values "old time", "new time" are + seen on Solaris. + - Common devices are: + - On glibc: "ttyN" (console) and "pts/N" (pseudo-terminals). + - On macOS: "ttysNNN" (pseudo-terminals). + - On FreeBSD: "ttyvN" (console). + - On NetBSD: "ttyEN", "constty" (console). + - On OpenBSD: "ttyCN", "console" (console) and "ttypN" (pseudo-terminals). + - on AIX: "vtyN" (console) and "pts/N" (pseudo-terminals). + - On Solaris: "vt/N", "console" (console) and "pts/N" (pseudo-terminals). + * The PID column is zero on platforms without a 'ut_pid' field: OpenBSD. + */ + if (num_entries > 0) + { + /* Sort the entries according to increasing UT_TIME_MEMBER (entry). + Use a stable sort algorithm. */ + merge_sort_inplace (entries, num_entries, + XNMALLOC (num_entries, STRUCT_UTMP)); + + idx_t boot_time_count = 0; + idx_t i; + for (i = 0; i < num_entries; i++) + { + const STRUCT_UTMP *entry = &entries[i]; + + char *user = extract_trimmed_name (entry); + const char *device = entry->ut_line; + long pid = UT_PID (entry); + int termination = UT_EXIT_E_TERMINATION (entry); + int exit = UT_EXIT_E_EXIT (entry); + const char *host = entry->ut_host; + + time_t tim = UT_TIME_MEMBER (entry); + struct tm *gmt = gmtime (&tim); + char timbuf[100]; + if (gmt == NULL + || strftime (timbuf, sizeof (timbuf), "%Y-%m-%d %H:%M:%S", gmt) + == 0) + strcpy (timbuf, "---"); + + printf ("%-19s %-18s %-11s %10ld %4d %3d %c %c %s\n", + timbuf, + user, + device, + pid, + termination, + exit, + UT_TYPE_BOOT_TIME (entry) ? 'X' : ' ', + UT_TYPE_USER_PROCESS (entry) ? 'X' : ' ', + host); + + if (UT_TYPE_BOOT_TIME (entry)) + boot_time_count++; + } + fflush (stdout); + + /* If the first time is more than 5 years in the past or the last time + is more than a week in the future, the time_t members are wrong. */ + time_t first = UT_TIME_MEMBER (&entries[0]); + time_t last = UT_TIME_MEMBER (&entries[num_entries - 1]); + time_t now = time (NULL); + ASSERT (first >= now - 157680000); + ASSERT (last <= now + 604800); + + /* read_utmp should not produce multiple BOOT_TIME entries. */ + ASSERT (boot_time_count <= 1); + + /* read_utmp should fake a BOOT_TIME entry if needed. + Platform specific hacks go into lib/boot-time-aux.h. */ + ASSERT (boot_time_count >= 1); + } + + free (entries); + + return 0; +} |