/* 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 . */ /* Written by Bruno Haible , 2023. */ #include #include "readutmp.h" #include #include #include #include #include #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 nor 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; }