/* Declarations for GNU's read utmp module.

   Copyright (C) 1992-2007, 2009-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 jla; revised by djm */

#ifndef __READUTMP_H__
#define __READUTMP_H__

/* This file uses _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_RETURNS_NONNULL,
   HAVE_UTMP_H, HAVE_UTMPX_H, HAVE_STRUCT_UTMP_*, HAVE_STRUCT_UTMPX_*,
   HAVE_UTMPNAME, HAVE_UTMPXNAME.  */
#if !_GL_CONFIG_H_INCLUDED
# error "Please include config.h first."
#endif

#include "idx.h"

#include <stdlib.h>
#include <sys/types.h>
#include <time.h>

/* AIX 4.3.3 has both utmp.h and utmpx.h, but only struct utmp
   has the ut_exit member.  */
#if (HAVE_UTMPX_H && HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_EXIT \
     && ! HAVE_STRUCT_UTMPX_UT_EXIT)
# undef HAVE_UTMPX_H
#endif

/* HPUX 10.20 needs utmp.h, for the definition of e.g., UTMP_FILE.  */
#if HAVE_UTMP_H
# include <utmp.h>
#endif

/* Needed for BOOT_TIME and USER_PROCESS.  */
#if HAVE_UTMPX_H
# if defined _THREAD_SAFE && defined UTMP_DATA_INIT
    /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE
       defined, work around the duplicate struct utmp_data declaration.  */
#  define utmp_data gl_aix_4_3_workaround_utmp_data
# endif
# include <utmpx.h>
#endif


#ifdef __cplusplus
extern "C" {
#endif


/* Type of entries returned by read_utmp on all platforms.  */
struct gl_utmp
{
  /* All 'char *' here are of arbitrary length and point to storage
     with lifetime equal to that of this struct.  */
  char *ut_user;                /* User name */
  char *ut_id;                  /* Session ID */
  char *ut_line;                /* seat / device */
  char *ut_host;                /* for remote sessions: user@host or host,
                                   for local sessions: the X11 display :N */
  struct timespec ut_ts;        /* time */
  pid_t ut_pid;                 /* process ID of ? */
  pid_t ut_session;             /* process ID of session leader */
  short ut_type;                /* BOOT_TIME, USER_PROCESS, or other */
  struct { int e_termination; int e_exit; } ut_exit;
};

/* The following types, macros, and constants describe the 'struct gl_utmp'.  */
#define UT_USER(UT) ((UT)->ut_user)
#define UT_TIME_MEMBER(UT) ((UT)->ut_ts.tv_sec)
#define UT_PID(UT) ((UT)->ut_pid)
#define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V))
#define UT_TYPE_NOT_DEFINED 0
#define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination)
#define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit)

/* Type of entry returned by read_utmp().  */
typedef struct gl_utmp STRUCT_UTMP;

/* Size of the UT_USER (ut) member, or -1 if unbounded.  */
enum { UT_USER_SIZE = -1 };

/* Size of the ut->ut_id member, or -1 if unbounded.  */
enum { UT_ID_SIZE = -1 };

/* Size of the ut->ut_line member, or -1 if unbounded.  */
enum { UT_LINE_SIZE = -1 };

/* Size of the ut->ut_host member, or -1 if unbounded.  */
enum { UT_HOST_SIZE = -1 };


/* When read_utmp accesses a file (as opposed to fetching the information
   from systemd), it uses the following low-level types and macros.
   Keep them here, rather than moving them into readutmp.c, for backward
   compatibility.  */

#if HAVE_UTMPX_H

/* <utmpx.h> defines 'struct utmpx' with the following fields:

     Field        Type                       Platforms
     ----------   ------                     ---------
   ⎡ ut_user      char[]                     glibc, musl, macOS, FreeBSD, AIX, HP-UX, IRIX, Solaris, Cygwin
   ⎣ ut_name      char[]                     NetBSD, Minix
     ut_id        char[]                     glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
     ut_line      char[]                     glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
     ut_pid       pid_t                      glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
     ut_type      short                      glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
   ⎡ ut_tv        struct                     glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
   ⎢              { tv_sec; tv_usec; }
   ⎣ ut_time      time_t                     Cygwin
     ut_host      char[]                     glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
     ut_exit      struct                     glibc, musl, NetBSD, Minix, HP-UX, IRIX, Solaris
                  { e_termination; e_exit; }
     ut_session   [long] int                 glibc, musl, NetBSD, Minix, IRIX, Solaris
   ⎡ ut_addr      [long] int                 HP-UX, Cygwin
   ⎢ ut_addr_v6   [u]int[4]                  glibc, musl
   ⎣ ut_ss        struct sockaddr_storage    NetBSD, Minix
 */

# if __GLIBC__ && _TIME_BITS == 64
/* This is a near-copy of glibc's struct utmpx, which stops working
   after the year 2038.  Unlike the glibc version, struct utmpx32
   describes the file format even if time_t is 64 bits.  */
#define _GL_UT_USER_SIZE  sizeof (((struct utmpx *) 0)->ut_user)
#define _GL_UT_ID_SIZE    sizeof (((struct utmpx *) 0)->ut_id)
#define _GL_UT_LINE_SIZE  sizeof (((struct utmpx *) 0)->ut_line)
#define _GL_UT_HOST_SIZE  sizeof (((struct utmpx *) 0)->ut_host)
struct utmpx32
{
  short int ut_type;               /* Type of login.  */
  pid_t ut_pid;                    /* Process ID of login process.  */
  char ut_line[_GL_UT_LINE_SIZE];  /* Devicename.  */
  char ut_id[_GL_UT_ID_SIZE];      /* Inittab ID.  */
  char ut_user[_GL_UT_USER_SIZE];  /* Username.  */
  char ut_host[_GL_UT_HOST_SIZE];  /* Hostname for remote login. */
  struct __exit_status ut_exit;    /* Exit status of a process marked
                                      as DEAD_PROCESS.  */
  /* The fields ut_session and ut_tv must be the same size when compiled
     32- and 64-bit.  This allows files and shared memory to be shared
     between 32- and 64-bit applications.  */
  int ut_session;                  /* Session ID, used for windowing.  */
  struct
  {
    /* Seconds.  Unsigned not signed, as glibc did not exist before 1970,
       and if the format is still in use after 2038 its timestamps
       will surely have the sign bit on.  This hack stops working
       at 2106-02-07 06:28:16 UTC.  */
    unsigned int tv_sec;
    int tv_usec;                   /* Microseconds.  */
  } ut_tv;                         /* Time entry was made.  */
  int ut_addr_v6[4];               /* Internet address of remote host.  */
  char ut_reserved[20];            /* Reserved for future use.  */
};
#  define UTMP_STRUCT_NAME utmpx32
# else
#  define UTMP_STRUCT_NAME utmpx
# endif
# define SET_UTMP_ENT setutxent
# define GET_UTMP_ENT getutxent
# define END_UTMP_ENT endutxent
# ifdef HAVE_UTMPXNAME /* glibc, musl, macOS, NetBSD, Minix, IRIX, Solaris, Cygwin */
#  define UTMP_NAME_FUNCTION utmpxname
# elif defined UTXDB_ACTIVE /* FreeBSD */
#  define UTMP_NAME_FUNCTION(x) setutxdb (UTXDB_ACTIVE, x)
# endif

#elif HAVE_UTMP_H

/* <utmp.h> defines 'struct utmp' with the following fields:

     Field        Type                       Platforms
     ----------   ------                     ---------
   ⎡ ut_user      char[]                     glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
   ⎣ ut_name      char[]                     macOS, old FreeBSD, NetBSD, OpenBSD, Minix
     ut_id        char[]                     glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
     ut_line      char[]                     glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
     ut_pid       pid_t                      glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
     ut_type      short                      glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android
   ⎡ ut_tv        struct                     glibc, musl, Android
   ⎢              { tv_sec; tv_usec; }
   ⎣ ut_time      time_t                     macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin
     ut_host      char[]                     glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, Cygwin, Android
     ut_exit      struct                     glibc, musl, AIX, HP-UX, IRIX, Solaris, Android
                  { e_termination; e_exit; }
     ut_session   [long] int                 glibc, musl, Android
   ⎡ ut_addr      [long] int                 HP-UX, Cygwin
   ⎣ ut_addr_v6   [u]int[4]                  glibc, musl, Android
 */

# define UTMP_STRUCT_NAME utmp
# define SET_UTMP_ENT setutent
# define GET_UTMP_ENT getutent
# define END_UTMP_ENT endutent
# ifdef HAVE_UTMPNAME /* glibc, musl, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android */
#  define UTMP_NAME_FUNCTION utmpname
# endif

#endif

/* Evaluates to 1 if gl_utmp's ut_id field may ever have a non-zero value.  */
#define HAVE_STRUCT_XTMP_UT_ID \
  (READUTMP_USE_SYSTEMD \
   || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID))

/* Evaluates to 1 if gl_utmp's ut_pid field may ever have a non-zero value.  */
#define HAVE_STRUCT_XTMP_UT_PID \
  (READUTMP_USE_SYSTEMD \
   || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID))

/* Evaluates to 1 if gl_utmp's ut_host field may ever be non-empty.  */
#define HAVE_STRUCT_XTMP_UT_HOST \
  (READUTMP_USE_SYSTEMD \
   || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST))

/* Definition of UTMP_FILE.
   On glibc systems, UTMP_FILE is "/var/run/utmp".  */
#if !defined UTMP_FILE && defined _PATH_UTMP
# define UTMP_FILE _PATH_UTMP
#endif
#ifdef UTMPX_FILE /* Solaris, SysVr4 */
# undef UTMP_FILE
# define UTMP_FILE UTMPX_FILE
#endif
#ifndef UTMP_FILE
# define UTMP_FILE "/etc/utmp"
#endif

/* Definition of WTMP_FILE.
   On glibc systems, UTMP_FILE is "/var/log/wtmp".  */
#if !defined WTMP_FILE && defined _PATH_WTMP
# define WTMP_FILE _PATH_WTMP
#endif
#ifdef WTMPX_FILE /* Solaris, SysVr4 */
# undef WTMP_FILE
# define WTMP_FILE WTMPX_FILE
#endif
#ifndef WTMP_FILE
# define WTMP_FILE "/etc/wtmp"
#endif

/* In early versions of Android, <utmp.h> did not define BOOT_TIME, only
   USER_PROCESS.  We need to use the value that is defined in newer versions
   of Android.  */
#if defined __ANDROID__ && !defined BOOT_TIME
# define BOOT_TIME 2
#endif

/* Some platforms, such as OpenBSD, don't have an ut_type field and don't have
   the BOOT_TIME and USER_PROCESS macros.  But we want to support them in
   'struct gl_utmp'.  */
#if !(HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
# define BOOT_TIME 2
# define USER_PROCESS 0
#endif

/* Macros that test (UT)->ut_type.  */
#ifdef BOOT_TIME
# define UT_TYPE_BOOT_TIME(UT) ((UT)->ut_type == BOOT_TIME)
#else
# define UT_TYPE_BOOT_TIME(UT) 0
#endif
#ifdef USER_PROCESS
# define UT_TYPE_USER_PROCESS(UT) ((UT)->ut_type == USER_PROCESS)
#else
# define UT_TYPE_USER_PROCESS(UT) 0
#endif

/* Determines whether an entry *UT corresponds to a user process.  */
#define IS_USER_PROCESS(UT)                                    \
  ((UT)->ut_user[0] && UT_TYPE_USER_PROCESS (UT))

/* Define if read_utmp is not just a dummy.  */
#if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32
# define READ_UTMP_SUPPORTED 1
#endif

/* Options for read_utmp.  */
enum
  {
    READ_UTMP_CHECK_PIDS   = 1,
    READ_UTMP_USER_PROCESS = 2,
    READ_UTMP_BOOT_TIME    = 4,
    READ_UTMP_NO_BOOT_TIME = 8
  };

/* Return a copy of (UT)->ut_user, without trailing spaces,
   as a freshly allocated string.  */
char *extract_trimmed_name (const STRUCT_UTMP *ut)
  _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE
  _GL_ATTRIBUTE_RETURNS_NONNULL;

/* Read the utmp entries corresponding to file FILE into freshly-
   malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to
   the number of entries, and return zero.  If there is any error,
   return -1, setting errno, and don't modify the parameters.
   A good candidate for FILE is UTMP_FILE.
   If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose
   process-IDs do not currently exist.
   If OPTIONS & READ_UTMP_USER_PROCESS is nonzero, omit entries which
   do not correspond to a user process.
   If OPTIONS & READ_UTMP_BOOT_TIME is nonzero, omit all entries except
   the one that contains the boot time.
   If OPTIONS & READ_UTMP_NO_BOOT_TIME is nonzero, omit the boot time
   entries.

   This function is not multithread-safe, since on many platforms it
   invokes the functions setutxent, getutxent, endutxent.  These
   functions are needed because they may lock FILE (so that we don't
   read garbage when a concurrent process writes to FILE), but their
   drawback is that they have a common global state.  */
int read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf,
               int options);


#ifdef __cplusplus
}
#endif

#endif /* __READUTMP_H__ */
