summaryrefslogtreecommitdiffstats
path: root/gnulib-tests/test-readutmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib-tests/test-readutmp.c')
-rw-r--r--gnulib-tests/test-readutmp.c160
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;
+}