(root)/
coreutils-9.4/
lib/
readutmp.c
       1  /* GNU's read utmp module.
       2  
       3     Copyright (C) 1992-2001, 2003-2006, 2009-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by jla; revised by djm */
      19  
      20  #include <config.h>
      21  
      22  #include "readutmp.h"
      23  
      24  #include <errno.h>
      25  #include <stdio.h>
      26  
      27  #include <sys/types.h>
      28  #include <sys/stat.h>
      29  #include <signal.h>
      30  #include <string.h>
      31  #include <stdlib.h>
      32  #include <stdint.h>
      33  
      34  #if defined __linux__ || defined __ANDROID__
      35  # include <sys/sysinfo.h>
      36  # include <time.h>
      37  #endif
      38  #if READUTMP_USE_SYSTEMD
      39  # include <dirent.h>
      40  # include <systemd/sd-login.h>
      41  #endif
      42  
      43  #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__) && !defined __minix
      44  # if HAVE_SYS_PARAM_H
      45  #  include <sys/param.h>
      46  # endif
      47  # include <sys/sysctl.h>
      48  #endif
      49  
      50  #if HAVE_OS_H
      51  # include <OS.h>
      52  #endif
      53  
      54  #include "stat-time.h"
      55  #include "xalloc.h"
      56  
      57  /* Each of the FILE streams in this file is only used in a single thread.  */
      58  #include "unlocked-io.h"
      59  
      60  /* Some helper functions.  */
      61  #include "boot-time-aux.h"
      62  
      63  /* The following macros describe the 'struct UTMP_STRUCT_NAME',
      64     *not* 'struct gl_utmp'.  */
      65  #undef UT_USER
      66  #undef UT_TIME_MEMBER
      67  #undef UT_PID
      68  #undef UT_TYPE_EQ
      69  #undef UT_TYPE_NOT_DEFINED
      70  #undef UT_EXIT_E_TERMINATION
      71  #undef UT_EXIT_E_EXIT
      72  
      73  /* Accessor macro for the member named ut_user or ut_name.  */
      74  #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_NAME \
      75       : HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_NAME)
      76  # define UT_USER(UT) ((UT)->ut_name)
      77  #else
      78  # define UT_USER(UT) ((UT)->ut_user)
      79  #endif
      80  
      81  /* Accessor macro for the member of type time_t (or 'unsigned int').  */
      82  #if HAVE_UTMPX_H || (HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_TV)
      83  # define UT_TIME_MEMBER(UT) ((UT)->ut_tv.tv_sec)
      84  #else
      85  # define UT_TIME_MEMBER(UT) ((UT)->ut_time)
      86  #endif
      87  
      88  /* Accessor macro for the member named ut_pid.  */
      89  #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
      90  # define UT_PID(UT) ((UT)->ut_pid)
      91  #else
      92  # define UT_PID(UT) 0
      93  #endif
      94  
      95  /* Accessor macros for the member named ut_type.  */
      96  #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
      97  # define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V))
      98  # define UT_TYPE_NOT_DEFINED 0
      99  #else
     100  # define UT_TYPE_EQ(UT, V) 0
     101  # define UT_TYPE_NOT_DEFINED 1
     102  #endif
     103  
     104  #if HAVE_UTMPX_H
     105  # if HAVE_STRUCT_UTMPX_UT_EXIT_E_TERMINATION
     106  #  define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
     107  # elif HAVE_STRUCT_UTMPX_UT_EXIT_UT_TERMINATION /* OSF/1 */
     108  #  define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.ut_termination)
     109  # else
     110  #  define UT_EXIT_E_TERMINATION(UT) 0
     111  # endif
     112  #elif HAVE_UTMP_H
     113  # if HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION
     114  #  define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
     115  # else
     116  #  define UT_EXIT_E_TERMINATION(UT) 0
     117  # endif
     118  #endif
     119  
     120  #if HAVE_UTMPX_H
     121  # if HAVE_STRUCT_UTMPX_UT_EXIT_E_EXIT
     122  #  define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)
     123  # elif HAVE_STRUCT_UTMPX_UT_EXIT_UT_EXIT /* OSF/1 */
     124  #  define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.ut_exit)
     125  # else
     126  #  define UT_EXIT_E_EXIT(UT) 0
     127  # endif
     128  #elif HAVE_UTMP_H
     129  # if HAVE_STRUCT_UTMP_UT_EXIT_E_EXIT
     130  #  define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)
     131  # else
     132  #  define UT_EXIT_E_EXIT(UT) 0
     133  # endif
     134  #endif
     135  
     136  /* Size of the UT_USER (ut) member.  */
     137  #define UT_USER_SIZE  sizeof UT_USER ((struct UTMP_STRUCT_NAME *) 0)
     138  /* Size of the ut->ut_id member.  */
     139  #define UT_ID_SIZE    sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_id)
     140  /* Size of the ut->ut_line member.  */
     141  #define UT_LINE_SIZE  sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_line)
     142  /* Size of the ut->ut_host member.  */
     143  #define UT_HOST_SIZE  sizeof (((struct UTMP_STRUCT_NAME *) 0)->ut_host)
     144  
     145  #if 8 <= __GNUC__
     146  # pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
     147  #endif
     148  
     149  /* Copy UT->ut_user into storage obtained from malloc.  Then remove any
     150     trailing spaces from the copy, NUL terminate it, and return the copy.  */
     151  
     152  char *
     153  extract_trimmed_name (const STRUCT_UTMP *ut)
     154  {
     155    char const *name = ut->ut_user;
     156    idx_t len = strlen (name);
     157    char const *p;
     158    for (p = name + len; name < p && p[-1] == ' '; p--)
     159      continue;
     160    return ximemdup0 (name, p - name);
     161  }
     162  
     163  #if READ_UTMP_SUPPORTED
     164  
     165  /* Is the utmp entry UT desired by the user who asked for OPTIONS?  */
     166  
     167  static bool
     168  desirable_utmp_entry (STRUCT_UTMP const *ut, int options)
     169  {
     170  # if defined __OpenBSD__ && !HAVE_UTMPX_H
     171    /* Eliminate entirely empty entries.  */
     172    if (ut->ut_ts.tv_sec == 0 && ut->ut_user[0] == '\0'
     173        && ut->ut_line[0] == '\0' && ut->ut_host[0] == '\0')
     174      return false;
     175  # endif
     176  
     177    bool boot_time = UT_TYPE_BOOT_TIME (ut);
     178    if ((options & READ_UTMP_BOOT_TIME) && !boot_time)
     179      return false;
     180    if ((options & READ_UTMP_NO_BOOT_TIME) && boot_time)
     181      return false;
     182  
     183    bool user_proc = IS_USER_PROCESS (ut);
     184    if ((options & READ_UTMP_USER_PROCESS) && !user_proc)
     185      return false;
     186  # if !(defined __CYGWIN__ || defined _WIN32)
     187    if ((options & READ_UTMP_CHECK_PIDS)
     188        && user_proc
     189        && 0 < UT_PID (ut)
     190        && (kill (UT_PID (ut), 0) < 0 && errno == ESRCH))
     191      return false;
     192  # endif
     193  
     194    return true;
     195  }
     196  
     197  /* A memory allocation for an in-progress read_utmp.  */
     198  
     199  struct utmp_alloc
     200  {
     201    /* A pointer to a possibly-empty array of utmp entries,
     202       followed by a possibly-empty sequence of unused bytes,
     203       followed by a possibly-empty sequence of string bytes.
     204       UTMP is either null or allocated by malloc.  */
     205    struct gl_utmp *utmp;
     206  
     207    /* The number of utmp entries.  */
     208    idx_t filled;
     209  
     210    /* The string byte sequence length.  Strings are null-terminated.  */
     211    idx_t string_bytes;
     212  
     213    /* The total number of bytes allocated.  This equals
     214       FILLED * sizeof *UTMP + [size of free area] + STRING_BYTES.  */
     215    idx_t alloc_bytes;
     216  };
     217  
     218  /* Use the memory allocation A, and if the read_utmp options OPTIONS
     219     permit it, add a new entry with the given USER, etc.  Grow A as
     220     needed, reporting an error and exit on memory allocation failure.
     221     Return the resulting memory allocation.  */
     222  
     223  static struct utmp_alloc
     224  add_utmp (struct utmp_alloc a, int options,
     225            char const *user, idx_t user_len,
     226            char const *id, idx_t id_len,
     227            char const *line, idx_t line_len,
     228            char const *host, idx_t host_len,
     229            pid_t pid, short type, struct timespec ts, long session,
     230            int termination, int exit)
     231  {
     232    int entry_bytes = sizeof (struct gl_utmp);
     233    idx_t avail = a.alloc_bytes - (entry_bytes * a.filled + a.string_bytes);
     234    idx_t needed_string_bytes =
     235      (user_len + 1) + (id_len + 1) + (line_len + 1) + (host_len + 1);
     236    idx_t needed = entry_bytes + needed_string_bytes;
     237    if (avail < needed)
     238      {
     239        idx_t old_string_offset = a.alloc_bytes - a.string_bytes;
     240        void *new = xpalloc (a.utmp, &a.alloc_bytes, needed - avail, -1, 1);
     241        idx_t new_string_offset = a.alloc_bytes - a.string_bytes;
     242        a.utmp = new;
     243        char *q = new;
     244        memmove (q + new_string_offset, q + old_string_offset, a.string_bytes);
     245      }
     246    struct gl_utmp *ut = &a.utmp[a.filled];
     247    char *stringlim = (char *) a.utmp + a.alloc_bytes;
     248    char *p = stringlim - a.string_bytes;
     249    *--p = '\0'; /* NUL-terminate ut->ut_user */
     250    ut->ut_user = p = memcpy (p - user_len, user, user_len);
     251    *--p = '\0'; /* NUL-terminate ut->ut_id */
     252    ut->ut_id   = p = memcpy (p -   id_len,   id,   id_len);
     253    *--p = '\0'; /* NUL-terminate ut->ut_line */
     254    ut->ut_line = p = memcpy (p - line_len, line, line_len);
     255    *--p = '\0'; /* NUL-terminate ut->ut_host */
     256    ut->ut_host =     memcpy (p - host_len, host, host_len);
     257    ut->ut_ts = ts;
     258    ut->ut_pid = pid;
     259    ut->ut_session = session;
     260    ut->ut_type = type;
     261    ut->ut_exit.e_termination = termination;
     262    ut->ut_exit.e_exit = exit;
     263    if (desirable_utmp_entry (ut, options))
     264      {
     265        /* Now that UT has been checked, relocate its string slots to be
     266           relative to the end of the allocated storage, so that these
     267           slots survive realloc.  The slots will be relocated back just
     268           before read_utmp returns.  */
     269        ut->ut_user = (char *) (intptr_t) (ut->ut_user - stringlim);
     270        ut->ut_id   = (char *) (intptr_t) (ut->ut_id   - stringlim);
     271        ut->ut_line = (char *) (intptr_t) (ut->ut_line - stringlim);
     272        ut->ut_host = (char *) (intptr_t) (ut->ut_host - stringlim);
     273        a.filled++;
     274        a.string_bytes += needed_string_bytes;
     275      }
     276    return a;
     277  }
     278  
     279  /* Relocate the string pointers in A back to their natural position.  */
     280  static struct utmp_alloc
     281  finish_utmp (struct utmp_alloc a)
     282  {
     283    char *stringlim = (char *) a.utmp + a.alloc_bytes;
     284  
     285    for (idx_t i = 0; i < a.filled; i++)
     286      {
     287        a.utmp[i].ut_user = (intptr_t) a.utmp[i].ut_user + stringlim;
     288        a.utmp[i].ut_id   = (intptr_t) a.utmp[i].ut_id   + stringlim;
     289        a.utmp[i].ut_line = (intptr_t) a.utmp[i].ut_line + stringlim;
     290        a.utmp[i].ut_host = (intptr_t) a.utmp[i].ut_host + stringlim;
     291      }
     292  
     293    return a;
     294  }
     295  
     296  /* Determine whether A already contains an entry of type BOOT_TIME.  */
     297  _GL_ATTRIBUTE_MAYBE_UNUSED
     298  static bool
     299  have_boot_time (struct utmp_alloc a)
     300  {
     301    for (idx_t i = 0; i < a.filled; i++)
     302      {
     303        struct gl_utmp *ut = &a.utmp[i];
     304        if (UT_TYPE_BOOT_TIME (ut))
     305          return true;
     306      }
     307    return false;
     308  }
     309  
     310  #if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION
     311  # if !HAVE_DECL_ENDUTENT /* Android */
     312  void endutent (void);
     313  # endif
     314  #endif
     315  
     316  static int
     317  read_utmp_from_file (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     318                       int options)
     319  {
     320    if ((options & READ_UTMP_BOOT_TIME) != 0
     321        && (options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) != 0)
     322      {
     323        /* No entries can match the given options.  */
     324        *n_entries = 0;
     325        *utmp_buf = NULL;
     326        return 0;
     327      }
     328  
     329    struct utmp_alloc a = {0};
     330  
     331  # if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H
     332  
     333  #  if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */
     334  
     335    /* Ignore the return value for now.
     336       Solaris' utmpname returns 1 upon success -- which is contrary
     337       to what the GNU libc version does.  In addition, older GNU libc
     338       versions are actually void.   */
     339    UTMP_NAME_FUNCTION ((char *) file);
     340  
     341    SET_UTMP_ENT ();
     342  
     343  #   if (defined __linux__ && !defined __ANDROID__) || defined __minix
     344    bool file_is_utmp = (strcmp (file, UTMP_FILE) == 0);
     345    /* Timestamp of the "runlevel" entry, if any.  */
     346    struct timespec runlevel_ts = {0};
     347  #   endif
     348  
     349    void const *entry;
     350  
     351    while ((entry = GET_UTMP_ENT ()) != NULL)
     352      {
     353        struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) entry;
     354  
     355        struct timespec ts =
     356          #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
     357          { .tv_sec = ut->ut_tv.tv_sec, .tv_nsec = ut->ut_tv.tv_usec * 1000 };
     358          #else
     359          { .tv_sec = ut->ut_time, .tv_nsec = 0 };
     360          #endif
     361  
     362        a = add_utmp (a, options,
     363                      UT_USER (ut), strnlen (UT_USER (ut), UT_USER_SIZE),
     364                      #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)
     365                      ut->ut_id, strnlen (ut->ut_id, UT_ID_SIZE),
     366                      #else
     367                      "", 0,
     368                      #endif
     369                      ut->ut_line, strnlen (ut->ut_line, UT_LINE_SIZE),
     370                      #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)
     371                      ut->ut_host, strnlen (ut->ut_host, UT_HOST_SIZE),
     372                      #else
     373                      "", 0,
     374                      #endif
     375                      #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
     376                      ut->ut_pid,
     377                      #else
     378                      0,
     379                      #endif
     380                      #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
     381                      ut->ut_type,
     382                      #else
     383                      0,
     384                      #endif
     385                      ts,
     386                      #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
     387                      ut->ut_session,
     388                      #else
     389                      0,
     390                      #endif
     391                      UT_EXIT_E_TERMINATION (ut), UT_EXIT_E_EXIT (ut)
     392                     );
     393  #   if defined __linux__ && !defined __ANDROID__
     394        if (file_is_utmp
     395            && memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0
     396            && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0)
     397          runlevel_ts = ts;
     398  #   endif
     399  #   if defined __minix
     400        if (file_is_utmp
     401            && UT_USER (ut)[0] == '\0'
     402            && memcmp (ut->ut_line, "run-level ", strlen ("run-level ")) == 0)
     403          runlevel_ts = ts;
     404  #   endif
     405      }
     406  
     407    END_UTMP_ENT ();
     408  
     409  #   if defined __linux__ && !defined __ANDROID__
     410    /* On Alpine Linux, UTMP_FILE is not filled.  It is always empty.
     411       So, fake a BOOT_TIME entry, by getting the time stamp of a file that
     412       gets touched only during the boot process.
     413  
     414       On Raspbian, which runs on hardware without a real-time clock, during boot,
     415         1. the clock gets set to 1970-01-01 00:00:00,
     416         2. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
     417            ut_user = "reboot", ut_line = "~", time = 1970-01-01 00:00:05 or so,
     418         3. the clock gets set to a correct value through NTP,
     419         4. an entry gets written into /var/run/utmp, with
     420            ut_user = "runlevel", ut_line = "~", time = correct value.
     421       In this case, copy the time from the "runlevel" entry to the "reboot"
     422       entry.  */
     423    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     424        && file_is_utmp)
     425      {
     426        for (idx_t i = 0; i < a.filled; i++)
     427          {
     428            struct gl_utmp *ut = &a.utmp[i];
     429            if (UT_TYPE_BOOT_TIME (ut))
     430              {
     431                /* Workaround for Raspbian:  */
     432                if (ut->ut_ts.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
     433                  ut->ut_ts = runlevel_ts;
     434                break;
     435              }
     436          }
     437        if (!have_boot_time (a))
     438          {
     439            /* Workaround for Alpine Linux:  */
     440            struct timespec boot_time;
     441            if (get_linux_boot_time_fallback (&boot_time) >= 0)
     442              a = add_utmp (a, options,
     443                            "reboot", strlen ("reboot"),
     444                            "", 0,
     445                            "~", strlen ("~"),
     446                            "", 0,
     447                            0, BOOT_TIME, boot_time, 0, 0, 0);
     448          }
     449      }
     450  #   endif
     451  
     452  #   if defined __ANDROID__
     453    /* On Android, there is no /var, and normal processes don't have access
     454       to system files.  Therefore use the kernel's uptime counter, although
     455       it produces wrong values after the date has been bumped in the running
     456       system.  */
     457    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     458        && strcmp (file, UTMP_FILE) == 0
     459        && !have_boot_time (a))
     460      {
     461        struct timespec boot_time;
     462        if (get_android_boot_time (&boot_time) >= 0)
     463          a = add_utmp (a, options,
     464                        "reboot", strlen ("reboot"),
     465                        "", 0,
     466                        "", 0,
     467                        "", 0,
     468                        0, BOOT_TIME, boot_time, 0, 0, 0);
     469      }
     470  #   endif
     471  
     472  #   if defined __minix
     473    /* On Minix, during boot,
     474         1. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
     475            ut_user = "", ut_line = "system boot", time = 1970-01-01 00:00:00,
     476         2. an entry gets written into /var/run/utmp, with
     477            ut_user = "", ut_line = "run-level m", time = correct value.
     478       In this case, copy the time from the "run-level m" entry to the
     479       "system boot" entry.  */
     480    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     481        && file_is_utmp)
     482      {
     483        for (idx_t i = 0; i < a.filled; i++)
     484          {
     485            struct gl_utmp *ut = &a.utmp[i];
     486            if (UT_TYPE_BOOT_TIME (ut))
     487              {
     488                if (ut->ut_ts.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
     489                  ut->ut_ts = runlevel_ts;
     490                break;
     491              }
     492          }
     493      }
     494  #   endif
     495  
     496  #  else /* old FreeBSD, OpenBSD, HP-UX, Haiku */
     497  
     498    FILE *f = fopen (file, "re");
     499  
     500    if (f != NULL)
     501      {
     502        for (;;)
     503          {
     504            struct UTMP_STRUCT_NAME ut;
     505  
     506            if (fread (&ut, sizeof ut, 1, f) == 0)
     507              break;
     508            a = add_utmp (a, options,
     509                          UT_USER (&ut), strnlen (UT_USER (&ut), UT_USER_SIZE),
     510                          #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)
     511                          ut.ut_id, strnlen (ut.ut_id, UT_ID_SIZE),
     512                          #else
     513                          "", 0,
     514                          #endif
     515                          ut.ut_line, strnlen (ut.ut_line, UT_LINE_SIZE),
     516                          #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)
     517                          ut.ut_host, strnlen (ut.ut_host, UT_HOST_SIZE),
     518                          #else
     519                          "", 0,
     520                          #endif
     521                          #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)
     522                          ut.ut_pid,
     523                          #else
     524                          0,
     525                          #endif
     526                          #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
     527                          ut.ut_type,
     528                          #else
     529                          0,
     530                          #endif
     531                          #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
     532                          (struct timespec) { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 },
     533                          #else
     534                          (struct timespec) { .tv_sec = ut.ut_time, .tv_nsec = 0 },
     535                          #endif
     536                          #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_SESSION : HAVE_STRUCT_UTMP_UT_SESSION)
     537                          ut.ut_session,
     538                          #else
     539                          0,
     540                          #endif
     541                          UT_EXIT_E_TERMINATION (&ut), UT_EXIT_E_EXIT (&ut)
     542                         );
     543          }
     544  
     545        int saved_errno = ferror (f) ? errno : 0;
     546        if (fclose (f) != 0)
     547          saved_errno = errno;
     548        if (saved_errno != 0)
     549          {
     550            free (a.utmp);
     551            errno = saved_errno;
     552            return -1;
     553          }
     554      }
     555    else
     556      {
     557        if (strcmp (file, UTMP_FILE) != 0)
     558          {
     559            int saved_errno = errno;
     560            free (a.utmp);
     561            errno = saved_errno;
     562            return -1;
     563          }
     564      }
     565  
     566  #   if defined __OpenBSD__
     567    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     568        && strcmp (file, UTMP_FILE) == 0
     569        && !have_boot_time (a))
     570      {
     571        struct timespec boot_time;
     572        if (get_openbsd_boot_time (&boot_time) >= 0)
     573          a = add_utmp (a, options,
     574                        "reboot", strlen ("reboot"),
     575                        "", 0,
     576                        "", 0,
     577                        "", 0,
     578                        0, BOOT_TIME, boot_time, 0, 0, 0);
     579      }
     580  #   endif
     581  
     582  #  endif
     583  
     584  #  if defined __linux__ && !defined __ANDROID__
     585    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     586        && strcmp (file, UTMP_FILE) == 0
     587        && !have_boot_time (a))
     588      {
     589        struct timespec boot_time;
     590        if (get_linux_boot_time_final_fallback (&boot_time) >= 0)
     591          a = add_utmp (a, options,
     592                        "reboot", strlen ("reboot"),
     593                        "", 0,
     594                        "~", strlen ("~"),
     595                        "", 0,
     596                        0, BOOT_TIME, boot_time, 0, 0, 0);
     597      }
     598  
     599  #  endif
     600  
     601  #  if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
     602        && defined CTL_KERN && defined KERN_BOOTTIME \
     603        && !defined __minix
     604    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     605        && strcmp (file, UTMP_FILE) == 0
     606        && !have_boot_time (a))
     607      {
     608        struct timespec boot_time;
     609        if (get_bsd_boot_time_final_fallback (&boot_time) >= 0)
     610          a = add_utmp (a, options,
     611                        "reboot", strlen ("reboot"),
     612                        "", 0,
     613                        "", 0,
     614                        "", 0,
     615                        0, BOOT_TIME, boot_time, 0, 0, 0);
     616      }
     617  #  endif
     618  
     619  #  if defined __HAIKU__
     620    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     621        && strcmp (file, UTMP_FILE) == 0
     622        && !have_boot_time (a))
     623      {
     624        struct timespec boot_time;
     625        if (get_haiku_boot_time (&boot_time) >= 0)
     626          a = add_utmp (a, options,
     627                        "reboot", strlen ("reboot"),
     628                        "", 0,
     629                        "", 0,
     630                        "", 0,
     631                        0, BOOT_TIME, boot_time, 0, 0, 0);
     632      }
     633  #  endif
     634  
     635  #  if HAVE_OS_H /* BeOS, Haiku */
     636    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     637        && strcmp (file, UTMP_FILE) == 0
     638        && !have_boot_time (a))
     639      {
     640        struct timespec boot_time;
     641        if (get_haiku_boot_time_final_fallback (&boot_time) >= 0)
     642          a = add_utmp (a, options,
     643                        "reboot", strlen ("reboot"),
     644                        "", 0,
     645                        "", 0,
     646                        "", 0,
     647                        0, BOOT_TIME, boot_time, 0, 0, 0);
     648      }
     649  #  endif
     650  
     651  # endif
     652  
     653  # if defined __CYGWIN__ || defined _WIN32
     654    if ((options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)) == 0
     655        && strcmp (file, UTMP_FILE) == 0
     656        && !have_boot_time (a))
     657      {
     658        struct timespec boot_time;
     659        if (get_windows_boot_time (&boot_time) >= 0)
     660          a = add_utmp (a, options,
     661                        "reboot", strlen ("reboot"),
     662                        "", 0,
     663                        "", 0,
     664                        "", 0,
     665                        0, BOOT_TIME, boot_time, 0, 0, 0);
     666      }
     667  # endif
     668  
     669    a = finish_utmp (a);
     670  
     671    *n_entries = a.filled;
     672    *utmp_buf = a.utmp;
     673  
     674    return 0;
     675  }
     676  
     677  # if READUTMP_USE_SYSTEMD
     678  /* Use systemd and Linux /proc and kernel APIs.  */
     679  
     680  static struct timespec
     681  get_boot_time_uncached (void)
     682  {
     683    /* Try to find the boot time in the /var/run/utmp file.  */
     684    {
     685      idx_t n_entries = 0;
     686      STRUCT_UTMP *utmp = NULL;
     687      read_utmp_from_file (UTMP_FILE, &n_entries, &utmp, READ_UTMP_BOOT_TIME);
     688      if (n_entries > 0)
     689        {
     690          struct timespec result = utmp[0].ut_ts;
     691          free (utmp);
     692          return result;
     693        }
     694      free (utmp);
     695    }
     696  
     697    /* We shouldn't get here.  */
     698    return (struct timespec) {0};
     699  }
     700  
     701  static struct timespec
     702  get_boot_time (void)
     703  {
     704    static bool volatile cached;
     705    static struct timespec volatile boot_time;
     706  
     707    if (!cached)
     708      {
     709        boot_time = get_boot_time_uncached ();
     710        cached = true;
     711      }
     712    return boot_time;
     713  }
     714  
     715  /* Guess the pty name that was opened for the given user right after
     716     the given time AT.  */
     717  static char *
     718  guess_pty_name (uid_t uid, const struct timespec at)
     719  {
     720    /* Traverse the entries of the /dev/pts/ directory, looking for devices
     721       which are owned by UID and whose ctime is shortly after AT.  */
     722    DIR *dirp = opendir ("/dev/pts");
     723    if (dirp != NULL)
     724      {
     725        /* Buffer containing /dev/pts/N.  */
     726        char name_buf[9 + 10 + 1];
     727        memcpy (name_buf, "/dev/pts/", 9);
     728  
     729        char best_name[9 + 10 + 1];
     730        struct timespec best_time = { .tv_sec = 0, .tv_nsec = 0 };
     731  
     732        for (;;)
     733          {
     734            struct dirent *dp = readdir (dirp);
     735            if (dp == NULL)
     736              break;
     737            if (dp->d_name[0] != '.' && strlen (dp->d_name) <= 10)
     738              {
     739                /* Compose the absolute file name /dev/pts/N.  */
     740                strcpy (name_buf + 9, dp->d_name);
     741  
     742                /* Find its owner and ctime.  */
     743                struct stat st;
     744                if (stat (name_buf, &st) >= 0
     745                    && st.st_uid == uid
     746                    && (st.st_ctim.tv_sec > at.tv_sec
     747                        || (st.st_ctim.tv_sec == at.tv_sec
     748                            && st.st_ctim.tv_nsec >= at.tv_nsec)))
     749                  {
     750                    /* This entry has the owner UID and a ctime >= AT.  */
     751                    /* Is this entry the best one so far?  */
     752                    if ((best_time.tv_sec == 0 && best_time.tv_nsec == 0)
     753                        || (st.st_ctim.tv_sec < best_time.tv_sec
     754                            || (st.st_ctim.tv_sec == best_time.tv_sec
     755                                && st.st_ctim.tv_nsec < best_time.tv_nsec)))
     756                      {
     757                        strcpy (best_name, name_buf);
     758                        best_time = st.st_ctim;
     759                      }
     760                  }
     761              }
     762          }
     763  
     764        closedir (dirp);
     765  
     766        /* Did we find an entry owned by ID, and is it at most 5 seconds
     767           after AT?  */
     768        if (!(best_time.tv_sec == 0 && best_time.tv_nsec == 0)
     769            && (best_time.tv_sec < at.tv_sec + 5
     770                || (best_time.tv_sec == at.tv_sec + 5
     771                    && best_time.tv_nsec <= at.tv_nsec)))
     772          return xstrdup (best_name + 5);
     773      }
     774  
     775    return NULL;
     776  }
     777  
     778  static int
     779  read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options)
     780  {
     781    /* Fill entries, simulating what a utmp file would contain.  */
     782    struct utmp_alloc a = {0};
     783  
     784    /* Synthesize a BOOT_TIME entry.  */
     785    if (!(options & (READ_UTMP_USER_PROCESS | READ_UTMP_NO_BOOT_TIME)))
     786      a = add_utmp (a, options,
     787                    "reboot", strlen ("reboot"),
     788                    "", 0,
     789                    "~", strlen ("~"),
     790                    "", 0,
     791                    0, BOOT_TIME, get_boot_time (), 0, 0, 0);
     792  
     793    /* Synthesize USER_PROCESS entries.  */
     794    if (!(options & READ_UTMP_BOOT_TIME))
     795      {
     796        char **sessions;
     797        int num_sessions = sd_get_sessions (&sessions);
     798        if (num_sessions >= 0)
     799          {
     800            char **session_ptr;
     801            for (session_ptr = sessions; *session_ptr != NULL; session_ptr++)
     802              {
     803                char *session = *session_ptr;
     804  
     805                uint64_t start_usec;
     806                if (sd_session_get_start_time (session, &start_usec) < 0)
     807                  start_usec = 0;
     808                struct timespec start_ts;
     809                start_ts.tv_sec = start_usec / 1000000;
     810                start_ts.tv_nsec = start_usec % 1000000 * 1000;
     811  
     812                char *seat;
     813                if (sd_session_get_seat (session, &seat) < 0)
     814                  seat = NULL;
     815  
     816                char missing[] = "";
     817  
     818                char *type = NULL;
     819                char *tty;
     820                if (sd_session_get_tty (session, &tty) < 0)
     821                  {
     822                    tty = NULL;
     823                    /* Try harder to get a sensible value for the tty.  */
     824                    if (sd_session_get_type (session, &type) < 0)
     825                      type = missing;
     826                    if (strcmp (type, "tty") == 0)
     827                      {
     828                        char *service;
     829                        if (sd_session_get_service (session, &service) < 0)
     830                          service = NULL;
     831  
     832                        uid_t uid;
     833                        char *pty = (sd_session_get_uid (session, &uid) < 0 ? NULL
     834                                     : guess_pty_name (uid, start_ts));
     835  
     836                        if (service != NULL && pty != NULL)
     837                          {
     838                            tty = xmalloc (strlen (service) + 1 + strlen (pty) + 1);
     839                            stpcpy (stpcpy (stpcpy (tty, service), " "), pty);
     840                            free (pty);
     841                            free (service);
     842                          }
     843                        else if (service != NULL)
     844                          tty = service;
     845                        else if (pty != NULL)
     846                          tty = pty;
     847                      }
     848                  }
     849  
     850                /* Create up to two USER_PROCESS entries: one for the seat,
     851                   one for the tty.  */
     852                if (seat != NULL || tty != NULL)
     853                  {
     854                    char *user;
     855                    if (sd_session_get_username (session, &user) < 0)
     856                      user = missing;
     857  
     858                    pid_t leader_pid;
     859                    if (sd_session_get_leader (session, &leader_pid) < 0)
     860                      leader_pid = 0;
     861  
     862                    char *host;
     863                    char *remote_host;
     864                    if (sd_session_get_remote_host (session, &remote_host) < 0)
     865                      {
     866                        host = missing;
     867                        /* For backward compatibility, put the X11 display into the
     868                           host field.  */
     869                        if (!type && sd_session_get_type (session, &type) < 0)
     870                          type = missing;
     871                        if (strcmp (type, "x11") == 0)
     872                          {
     873                            char *display;
     874                            if (sd_session_get_display (session, &display) < 0)
     875                              display = NULL;
     876                            host = display;
     877                          }
     878                      }
     879                    else
     880                      {
     881                        char *remote_user;
     882                        if (sd_session_get_remote_user (session, &remote_user) < 0)
     883                          host = remote_host;
     884                        else
     885                          {
     886                            host = xmalloc (strlen (remote_user) + 1
     887                                            + strlen (remote_host) + 1);
     888                            stpcpy (stpcpy (stpcpy (host, remote_user), "@"),
     889                                    remote_host);
     890                            free (remote_user);
     891                            free (remote_host);
     892                          }
     893                      }
     894  
     895                    if (seat != NULL)
     896                      a = add_utmp (a, options,
     897                                    user, strlen (user),
     898                                    session, strlen (session),
     899                                    seat, strlen (seat),
     900                                    host, strlen (host),
     901                                    leader_pid /* the best we have */,
     902                                    USER_PROCESS, start_ts, leader_pid, 0, 0);
     903                    if (tty != NULL)
     904                      a = add_utmp (a, options,
     905                                    user, strlen (user),
     906                                    session, strlen (session),
     907                                    tty, strlen (tty),
     908                                    host, strlen (host),
     909                                    leader_pid /* the best we have */,
     910                                    USER_PROCESS, start_ts, leader_pid, 0, 0);
     911  
     912                    if (host != missing)
     913                      free (host);
     914                    if (user != missing)
     915                      free (user);
     916                  }
     917  
     918                if (type != missing)
     919                  free (type);
     920                free (tty);
     921                free (seat);
     922                free (session);
     923              }
     924            free (sessions);
     925          }
     926      }
     927  
     928    a = finish_utmp (a);
     929  
     930    *n_entries = a.filled;
     931    *utmp_buf = a.utmp;
     932  
     933    return 0;
     934  }
     935  
     936  # endif
     937  
     938  int
     939  read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     940             int options)
     941  {
     942  # if READUTMP_USE_SYSTEMD
     943    if (strcmp (file, UTMP_FILE) == 0)
     944      /* Imitate reading UTMP_FILE, using systemd and Linux APIs.  */
     945      return read_utmp_from_systemd (n_entries, utmp_buf, options);
     946  # endif
     947  
     948    return read_utmp_from_file (file, n_entries, utmp_buf, options);
     949  }
     950  
     951  #else /* dummy fallback */
     952  
     953  int
     954  read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
     955             int options)
     956  {
     957    errno = ENOSYS;
     958    return -1;
     959  }
     960  
     961  #endif