(root)/
coreutils-9.4/
gnulib-tests/
test-readutmp.c
       1  /* Test of readutmp module.
       2     Copyright (C) 2023 Free Software Foundation, Inc.
       3  
       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.
       8  
       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.
      13  
      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/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
      18  
      19  #include <config.h>
      20  
      21  #include "readutmp.h"
      22  
      23  #include <stddef.h>
      24  #include <stdio.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <time.h>
      28  
      29  #include "idx.h"
      30  #include "xalloc.h"
      31  
      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"
      37  
      38  #include "macros.h"
      39  
      40  int
      41  main (int argc, char *argv[])
      42  {
      43    STRUCT_UTMP *entries;
      44    idx_t num_entries;
      45  
      46    if (read_utmp (UTMP_FILE, &num_entries, &entries, 0) < 0)
      47      {
      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;
      54      }
      55  
      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");
      62  
      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.
      96     */
      97    if (num_entries > 0)
      98      {
      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));
     103  
     104        idx_t boot_time_count = 0;
     105        idx_t i;
     106        for (i = 0; i < num_entries; i++)
     107          {
     108            const STRUCT_UTMP *entry = &entries[i];
     109  
     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;
     116  
     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, "---");
     124  
     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);
     135  
     136            if (UT_TYPE_BOOT_TIME (entry))
     137              boot_time_count++;
     138          }
     139        fflush (stdout);
     140  
     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);
     148  
     149        /* read_utmp should not produce multiple BOOT_TIME entries.  */
     150        ASSERT (boot_time_count <= 1);
     151  
     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);
     155      }
     156  
     157    free (entries);
     158  
     159    return 0;
     160  }