root/usr/src/cmd/who/who.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


/*
 * Copyright (c) 2013 Gary Mills
 *
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 */

/*
 *      This program analyzes information found in /var/adm/utmpx
 *
 *      Additionally information is gathered from /etc/inittab
 *      if requested.
 *
 *
 *      Syntax:
 *
 *              who am i        Displays info on yourself
 *
 *              who -a          Displays information about All
 *                              entries in /var/adm/utmpx
 *
 *              who -b          Displays info on last boot
 *
 *              who -d          Displays info on DEAD PROCESSES
 *
 *              who -H          Displays HEADERS for output
 *
 *              who -l          Displays info on LOGIN entries
 *
 *              who -m          Same as who am i
 *
 *              who -p          Displays info on PROCESSES spawned by init
 *
 *              who -q          Displays short information on
 *                              current users who LOGGED ON
 *
 *              who -r          Displays info of current run-level
 *
 *              who -s          Displays requested info in SHORT form
 *
 *              who -t          Displays info on TIME changes
 *
 *              who -T          Displays writeability of each user
 *                              (+ writeable, - non-writeable, ? hung)
 *
 *              who -u          Displays LONG info on users
 *                              who have LOGGED ON
 */

#define         DATE_FMT        "%b %e %H:%M"

/*
 *  %b  Abbreviated month name
 *  %e  Day of month
 *  %H  hour (24-hour clock)
 *  %M  minute
 */
#include        <errno.h>
#include        <fcntl.h>
#include        <stdio.h>
#include        <string.h>
#include        <sys/types.h>
#include        <unistd.h>
#include        <stdlib.h>
#include        <sys/stat.h>
#include        <time.h>
#include        <utmpx.h>
#include        <locale.h>
#include        <pwd.h>
#include        <limits.h>

static void process(void);
static void ck_file(char *);
static void dump(void);

static struct   utmpx *utmpp;   /* pointer for getutxent()      */

/*
 * Use the full lengths from utmpx for user and line.
 */
#define NMAX    (sizeof (utmpp->ut_user))
#define LMAX    (sizeof (utmpp->ut_line))

/* Print minimum field widths. */
#define LOGIN_WIDTH     8
#define LINE_WIDTH      12

static char     comment[BUFSIZ]; /* holds inittab comment       */
static char     errmsg[BUFSIZ]; /* used in snprintf for errors  */
static int      fildes;         /* file descriptor for inittab  */
static int      Hopt = 0;       /* 1 = who -H                   */
static char     *inittab;       /* ptr to inittab contents      */
static char     *iinit;         /* index into inittab           */
static int      justme = 0;     /* 1 = who am i                 */
static struct   tm *lptr;       /* holds user login time        */
static char     *myname;        /* pointer to invoker's name    */
static char     *mytty;         /* holds device user is on      */
static char     nameval[sizeof (utmpp->ut_user) + 1]; /*  invoker's name */
static int      number = 8;     /* number of users per -q line  */
static int      optcnt = 0;     /* keeps count of options       */
static char     outbuf[BUFSIZ]; /* buffer for output            */
static char     *program;       /* holds name of this program   */
#ifdef  XPG4
static int      aopt = 0;       /* 1 = who -a                   */
static int      dopt = 0;       /* 1 = who -d                   */
#endif  /* XPG4 */
static int      qopt = 0;       /* 1 = who -q                   */
static int      sopt = 0;       /* 1 = who -s                   */
static struct   stat stbuf;     /* area for stat buffer         */
static struct   stat *stbufp;   /* ptr to structure             */
static int      terse = 1;      /* 1 = print terse msgs         */
static int      Topt = 0;       /* 1 = who -T                   */
static time_t   timnow;         /* holds current time           */
static int      totlusrs = 0;   /* cntr for users on system     */
static int      uopt = 0;       /* 1 = who -u                   */
static char     user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
static int      validtype[UTMAXTYPE+1]; /* holds valid types    */
static int      wrap;           /* flag to indicate wrap        */
static char     time_buf[128];  /* holds date and time string   */
static char     *end;           /* used in strtol for end pointer */

int
main(int argc, char **argv)
{
        int     goerr = 0;      /* non-zero indicates cmd error */
        int     i;
        int     optsw;          /* switch for while of getopt() */

        (void) setlocale(LC_ALL, "");

#if     !defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
#endif
        (void) textdomain(TEXT_DOMAIN);

        validtype[USER_PROCESS] = 1;
        validtype[EMPTY] = 0;
        stbufp = &stbuf;

        /*
         *      Strip off path name of this command
         */
        for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i)
                ;
        if (i >= 0)
                argv[0] += i+1;
        program = argv[0];

        /*
         *      Buffer stdout for speed
         */
        setbuf(stdout, outbuf);

        /*
         *      Retrieve options specified on command line
         *      XCU4 - add -m option
         */
        while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
                optcnt++;
                switch (optsw) {

                case 'a':
                        optcnt += 7;
                        validtype[BOOT_TIME] = 1;
                        validtype[DEAD_PROCESS] = 1;
                        validtype[LOGIN_PROCESS] = 1;
                        validtype[INIT_PROCESS] = 1;
                        validtype[RUN_LVL] = 1;
                        validtype[OLD_TIME] = 1;
                        validtype[NEW_TIME] = 1;
                        validtype[USER_PROCESS] = 1;
#ifdef  XPG4
                        aopt = 1;
#endif  /* XPG4 */
                        uopt = 1;
                        Topt = 1;
                        if (!sopt) terse = 0;
                        break;

                case 'b':
                        validtype[BOOT_TIME] = 1;
                        if (!uopt) validtype[USER_PROCESS] = 0;
                        break;

                case 'd':
                        validtype[DEAD_PROCESS] = 1;
                        if (!uopt) validtype[USER_PROCESS] = 0;
#ifdef  XPG4
                        dopt = 1;
#endif  /* XPG4 */
                        break;

                case 'H':
                        optcnt--; /* Don't count Header */
                        Hopt = 1;
                        break;

                case 'l':
                        validtype[LOGIN_PROCESS] = 1;
                        if (!uopt) validtype[USER_PROCESS] = 0;
                        terse = 0;
                        break;
                case 'm':               /* New XCU4 option */
                        justme = 1;
                        break;

                case 'n':
                        errno = 0;
                        number = strtol(optarg, &end, 10);
                        if (errno != 0 || *end != '\0') {
                                (void) fprintf(stderr, gettext(
                                    "%s: Invalid numeric argument\n"),
                                    program);
                                exit(1);
                        }
                        if (number < 1) {
                                (void) fprintf(stderr, gettext(
                                    "%s: Number of users per line must "
                                    "be at least 1\n"), program);
                                exit(1);
                        }
                        break;

                case 'p':
                        validtype[INIT_PROCESS] = 1;
                        if (!uopt) validtype[USER_PROCESS] = 0;
                        break;

                case 'q':
                        qopt = 1;
                        break;

                case 'r':
                        validtype[RUN_LVL] = 1;
                        terse = 0;
                        if (!uopt) validtype[USER_PROCESS] = 0;
                        break;

                case 's':
                        sopt = 1;
                        terse = 1;
                        break;

                case 't':
                        validtype[OLD_TIME] = 1;
                        validtype[NEW_TIME] = 1;
                        if (!uopt) validtype[USER_PROCESS] = 0;
                        break;

                case 'T':
                        Topt = 1;
#ifdef  XPG4
                        terse = 1;      /* XPG4 requires -T */
#else   /* XPG4 */
                        terse = 0;
#endif  /* XPG4 */
                        break;

                case 'u':
                        uopt = 1;
                        validtype[USER_PROCESS] = 1;
                        if (!sopt) terse = 0;
                        break;

                case '?':
                        goerr++;
                        break;
                default:
                        break;
                }
        }
#ifdef  XPG4
        /*
         * XCU4 changes - check for illegal sopt, Topt & aopt combination
         */
        if (sopt == 1) {
                terse = 1;
                if (Topt == 1 || aopt == 1)
                        goerr++;
        }
#endif  /* XPG4 */

        if (goerr > 0) {
#ifdef  XPG4
                /*
                 * XCU4 - slightly different usage with -s -a & -T
                 */
                (void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
                (void) fprintf(stderr,
                    gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));

                (void) fprintf(stderr, gettext(
                    "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
#else   /* XPG4 */
                (void) fprintf(stderr, gettext(
                    "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
                    program);
#endif  /* XPG4 */
                (void) fprintf(stderr,
                    gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
                (void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
                /*
                 * XCU4 changes - be explicit with "am i" options
                 */
                (void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
                (void) fprintf(stderr, gettext(
                    "a\tall (bdlprtu options)\n"));
                (void) fprintf(stderr, gettext("b\tboot time\n"));
                (void) fprintf(stderr, gettext("d\tdead processes\n"));
                (void) fprintf(stderr, gettext("H\tprint header\n"));
                (void) fprintf(stderr, gettext("l\tlogin processes\n"));
                (void) fprintf(stderr, gettext(
                    "n #\tspecify number of users per line for -q\n"));
                (void) fprintf(stderr,
                    gettext("p\tprocesses other than getty or users\n"));
                (void) fprintf(stderr, gettext("q\tquick %s\n"), program);
                (void) fprintf(stderr, gettext("r\trun level\n"));
                (void) fprintf(stderr, gettext(
                "s\tshort form of %s (no time since last output or pid)\n"),
                    program);
                (void) fprintf(stderr, gettext("t\ttime changes\n"));
                (void) fprintf(stderr, gettext(
                    "T\tstatus of tty (+ writable, - not writable, "
                    "? hung)\n"));
                (void) fprintf(stderr, gettext("u\tuseful information\n"));
                (void) fprintf(stderr,
                    gettext("m\tinformation only about current terminal\n"));
                (void) fprintf(stderr, gettext(
                    "am i\tinformation about current terminal "
                    "(same as -m)\n"));
                (void) fprintf(stderr, gettext(
                    "am I\tinformation about current terminal "
                    "(same as -m)\n"));
                exit(1);
        }

        /*
         * XCU4: If -q option ignore all other options
         */
        if (qopt == 1) {
                Hopt = 0;
                sopt = 0;
                Topt = 0;
                uopt = 0;
                justme = 0;
                validtype[ACCOUNTING] = 0;
                validtype[BOOT_TIME] = 0;
                validtype[DEAD_PROCESS] = 0;
                validtype[LOGIN_PROCESS] = 0;
                validtype[INIT_PROCESS] = 0;
                validtype[RUN_LVL] = 0;
                validtype[OLD_TIME] = 0;
                validtype[NEW_TIME] = 0;
                validtype[USER_PROCESS] = 1;
        }

        if (argc == optind + 1) {
                optcnt++;
                ck_file(argv[optind]);
                (void) utmpxname(argv[optind]);
        }

        /*
         *      Test for 'who am i' or 'who am I'
         *      XCU4 - check if justme was already set by -m option
         */
        if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
            ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
            argv[2][1] == '\0'))) {
                justme = 1;
                myname = nameval;
                (void) cuserid(myname);
                if ((mytty = ttyname(fileno(stdin))) == NULL &&
                    (mytty = ttyname(fileno(stdout))) == NULL &&
                    (mytty = ttyname(fileno(stderr))) == NULL) {
                        (void) fprintf(stderr, gettext(
                        "Must be attached to terminal for 'am I' option\n"));
                        (void) fflush(stderr);
                        exit(1);
                } else
                        mytty += 5; /* bump past "/dev/" */
        }

        if (!terse) {
                if (Hopt)
                        (void) printf(gettext(
        "NAME       LINE         TIME          IDLE    PID  COMMENTS\n"));

                timnow = time(0);

                if ((fildes = open("/etc/inittab",
                    O_NONBLOCK|O_RDONLY)) == -1) {
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("%s: Cannot open /etc/inittab"), program);
                        perror(errmsg);
                        exit(errno);
                }

                if (fstat(fildes, stbufp) == -1) {
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("%s: Cannot stat /etc/inittab"), program);
                        perror(errmsg);
                        exit(errno);
                }

                if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("%s: Cannot allocate %ld bytes"),
                            program, stbufp->st_size);
                        perror(errmsg);
                        exit(errno);
                }

                if (read(fildes, inittab, stbufp->st_size)
                    != stbufp->st_size) {
                        (void) snprintf(errmsg, sizeof (errmsg),
                            gettext("%s: Error reading /etc/inittab"),
                            program);
                        perror(errmsg);
                        exit(errno);
                }

                inittab[stbufp->st_size] = '\0';
                iinit = inittab;
        } else {
                if (Hopt) {
#ifdef  XPG4
                        if (dopt) {
                                (void) printf(gettext(
                        "NAME       LINE         TIME           COMMENTS\n"));
                        } else {
                                (void) printf(
                                    gettext("NAME       LINE         TIME\n"));
                        }
#else   /* XPG4 */
                        (void) printf(
                            gettext("NAME       LINE         TIME\n"));
#endif  /* XPG4 */
                }
        }
        process();

        /*
         *      'who -q' requires EOL upon exit,
         *      followed by total line
         */
        if (qopt)
                (void) printf(gettext("\n# users=%d\n"), totlusrs);
        return (0);
}

static void
dump()
{
        char    device[sizeof (utmpp->ut_line) + 1];
        time_t hr;
        time_t  idle;
        time_t min;
        char    path[sizeof (utmpp->ut_line) + 6];
        int     pexit;
        int     pterm;
        int     rc;
        char    w;      /* writeability indicator */

        /*
         * Get and check user name
         */
        if (utmpp->ut_user[0] == '\0')
                (void) strcpy(user, "   .");
        else {
                (void) strncpy(user, utmpp->ut_user, sizeof (user));
                user[sizeof (user) - 1] = '\0';
        }
        totlusrs++;

        /*
         * Do print in 'who -q' format
         */
        if (qopt) {
                /*
                 * XCU4 - Use non user macro for correct user count
                 */
                if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
                        (void) printf("\n");
                (void) printf("%-*.*s ", LOGIN_WIDTH, NMAX, user);
                return;
        }


        pexit = (int)' ';
        pterm = (int)' ';

        /*
         *      Get exit info if applicable
         */
        if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
                pterm = utmpp->ut_exit.e_termination;
                pexit = utmpp->ut_exit.e_exit;
        }

        /*
         *      Massage ut_xtime field
         */
        lptr = localtime(&utmpp->ut_xtime);
        (void) strftime(time_buf, sizeof (time_buf),
            dcgettext(NULL, DATE_FMT, LC_TIME), lptr);

        /*
         *      Get and massage device
         */
        if (utmpp->ut_line[0] == '\0')
                (void) strcpy(device, "     .");
        else {
                (void) strncpy(device, utmpp->ut_line,
                    sizeof (utmpp->ut_line));
                device[sizeof (utmpp->ut_line)] = '\0';
        }

        /*
         *      Get writeability if requested
         *      XCU4 - only print + or - for user processes
         */
        if (Topt && (utmpp->ut_type == USER_PROCESS)) {
                w = '-';
                (void) strcpy(path, "/dev/");
                (void) strncpy(path + 5, utmpp->ut_line,
                    sizeof (utmpp->ut_line));
                path[5 + sizeof (utmpp->ut_line)] = '\0';

                if ((rc = stat(path, stbufp)) == -1) w = '?';
                else if ((stbufp->st_mode & S_IWOTH) ||
                    (stbufp->st_mode & S_IWGRP))  /* Check group & other */
                        w = '+';

        } else
                w = ' ';

        /*
         *      Print the TERSE portion of the output
         */
        (void) printf("%-*.*s %c %-12s %s", LOGIN_WIDTH, NMAX, user,
            w, device, time_buf);

        if (!terse) {
                /*
                 *      Stat device for idle time
                 *      (Don't complain if you can't)
                 */
                rc = -1;
                if (utmpp->ut_type == USER_PROCESS) {
                        (void) strcpy(path, "/dev/");
                        (void) strncpy(path + 5, utmpp->ut_line,
                            sizeof (utmpp->ut_line));
                        path[5 + sizeof (utmpp->ut_line)] = '\0';
                        rc = stat(path, stbufp);
                }
                if (rc != -1) {
                        idle = timnow - stbufp->st_mtime;
                        hr = idle/3600;
                        min = (unsigned)(idle/60)%60;
                        if (hr == 0 && min == 0)
                                (void) printf(gettext("   .  "));
                        else {
                                if (hr < 24)
                                        (void) printf(" %2d:%2.2d", (int)hr,
                                            (int)min);
                                else
                                        (void) printf(gettext("  old "));
                        }
                }

                /*
                 *      Add PID for verbose output
                 */
                if (utmpp->ut_type != BOOT_TIME &&
                    utmpp->ut_type != RUN_LVL &&
                    utmpp->ut_type != ACCOUNTING)
                        (void) printf("  %5ld", utmpp->ut_pid);

                /*
                 *      Handle /etc/inittab comment
                 */
                if (utmpp->ut_type == DEAD_PROCESS) {
                        (void) printf(gettext("  id=%4.4s "),
                            utmpp->ut_id);
                        (void) printf(gettext("term=%-3d "), pterm);
                        (void) printf(gettext("exit=%d  "), pexit);
                } else if (utmpp->ut_type != INIT_PROCESS) {
                        /*
                         *      Search for each entry in inittab
                         *      string. Keep our place from
                         *      search to search to try and
                         *      minimize the work. Wrap once if needed
                         *      for each entry.
                         */
                        wrap = 0;
                        /*
                         *      Look for a line beginning with
                         *      utmpp->ut_id
                         */
                        while ((rc = strncmp(utmpp->ut_id, iinit,
                            strcspn(iinit, ":"))) != 0) {
                                for (; *iinit != '\n'; iinit++)
                                        ;
                                iinit++;

                                /*
                                 *      Wrap once if necessary to
                                 *      find entry in inittab
                                 */
                                if (*iinit == '\0') {
                                        if (!wrap) {
                                                iinit = inittab;
                                                wrap = 1;
                                        }
                                }
                        }

                        if (*iinit != '\0') {
                                /*
                                 *      We found our entry
                                 */
                                for (iinit++; *iinit != '#' &&
                                    *iinit != '\n'; iinit++)
                                        ;
                                if (*iinit == '#') {
                                        for (iinit++; *iinit == ' ' ||
                                            *iinit == '\t'; iinit++)
                                                ;
                                        for (rc = 0; *iinit != '\n'; iinit++)
                                                comment[rc++] = *iinit;
                                        comment[rc] = '\0';
                                } else
                                        (void) strcpy(comment, " ");

                                (void) printf("  %s", comment);
                        } else
                                iinit = inittab;        /* Reset pointer */
                }
                if (utmpp->ut_type == INIT_PROCESS)
                        (void) printf(gettext("  id=%4.4s"), utmpp->ut_id);
        }
#ifdef  XPG4
        else
                if (dopt && utmpp->ut_type == DEAD_PROCESS) {
                        (void) printf(gettext("\tterm=%-3d "), pterm);
                        (void) printf(gettext("exit=%d  "), pexit);
                }
#endif  /* XPG4 */


        /*
         *      Handle RUN_LVL process - If no alt. file - Only one!
         */
        if (utmpp->ut_type == RUN_LVL) {
                (void) printf("     %c  %5ld  %c", pterm, utmpp->ut_pid,
                    pexit);
                if (optcnt == 1 && !validtype[USER_PROCESS]) {
                        (void) printf("\n");
                        exit(0);
                }
        }

        /*
         *      Handle BOOT_TIME process -  If no alt. file - Only one!
         */
        if (utmpp->ut_type == BOOT_TIME) {
                if (optcnt == 1 && !validtype[USER_PROCESS]) {
                        (void) printf("\n");
                        exit(0);
                }
        }

        /*
         *      Get remote host from utmpx structure
         */
        if (utmpp->ut_host[0])
                (void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
                    utmpp->ut_host);

        /*
         *      Now, put on the trailing EOL
         */
        (void) printf("\n");
}

static void
process()
{
        struct passwd *pwp;
        int i = 0;
        char *ttname;

        /*
         *      Loop over each entry in /var/adm/utmpx
         */

        setutxent();
        while ((utmpp = getutxent()) != NULL) {
#ifdef DEBUG
        (void) printf(
            "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
            utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
#endif
                if (utmpp->ut_type <= UTMAXTYPE) {
                        /*
                         *      Handle "am i"
                         */
                        if (justme) {
                                if (strncmp(myname, utmpp->ut_user,
                                    sizeof (utmpp->ut_user)) == 0 &&
                                    strncmp(mytty, utmpp->ut_line,
                                    sizeof (utmpp->ut_line)) == 0 &&
                                    utmpp->ut_type == USER_PROCESS) {
                                        /*
                                         * we have have found ourselves
                                         * in the utmp file and the entry
                                         * is a user process, this is not
                                         * meaningful otherwise
                                         *
                                         */

                                        dump();
                                        exit(0);
                                }
                                continue;
                        }

                        /*
                         *      Print the line if we want it
                         */
                        if (validtype[utmpp->ut_type]) {
#ifdef  XPG4
                                if (utmpp->ut_type == LOGIN_PROCESS) {
                                        if ((utmpp->ut_line[0] == '\0') ||
                                            (strcmp(utmpp->ut_user,
                                            "LOGIN") != 0))
                                                continue;
                                }
#endif  /* XPG4 */
                                dump();
                        }
                } else {
                        (void) fprintf(stderr,
                            gettext("%s: Error --- entry has ut_type "
                            "of %d\n"), program, utmpp->ut_type);
                        (void) fprintf(stderr,
                            gettext(" when maximum is %d\n"), UTMAXTYPE);
                }
        }

        /*
         * If justme is set at this point than the utmp entry
         * was not found.
         */
        if (justme) {
                static struct utmpx utmpt;

                pwp = getpwuid(geteuid());
                if (pwp != NULL)
                        while (i < (int)sizeof (utmpt.ut_user) &&
                            *pwp->pw_name != 0)
                                utmpt.ut_user[i++] = *pwp->pw_name++;

                ttname = ttyname(1);

                i = 0;
                if (ttname != NULL)
                        while (i < (int)sizeof (utmpt.ut_line) &&
                            *ttname != 0)
                                utmpt.ut_line[i++] = *ttname++;

                utmpt.ut_id[0] = 0;
                utmpt.ut_pid = getpid();
                utmpt.ut_type = USER_PROCESS;
                (void) time(&utmpt.ut_xtime);
                utmpp = &utmpt;
                dump();
                exit(0);
        }
}

/*
 *      This routine checks the following:
 *
 *      1.      File exists
 *
 *      2.      We have read permissions
 *
 *      3.      It is a multiple of utmp entries in size
 *
 *      Failing any of these conditions causes who(1) to
 *      abort processing.
 *
 *      4.      If file is empty we exit right away as there
 *              is no info to report on.
 *
 *      This routine does not check utmpx files.
 */
static void
ck_file(char *name)
{
        struct  stat sbuf;
        int     rc;

        /*
         *      Does file exist? Do stat to check, and save structure
         *      so that we can check on the file's size later on.
         */
        if ((rc = stat(name, &sbuf)) == -1) {
                (void) snprintf(errmsg, sizeof (errmsg),
                    gettext("%s: Cannot stat file '%s'"), program, name);
                perror(errmsg);
                exit(1);
        }

        /*
         *      The only real way we can be sure we can access the
         *      file is to try. If we succeed then we close it.
         */
        if (access(name, R_OK) < 0) {
                (void) snprintf(errmsg, sizeof (errmsg),
                    gettext("%s: Cannot open file '%s'"), program, name);
                perror(errmsg);
                exit(1);
        }

        /*
         *      If the file is empty, we are all done.
         */
        if (!sbuf.st_size)
                exit(0);

        /*
         *      Make sure the file is a utmp file.
         *      We can only check for size being a multiple of
         *      utmp structures in length.
         */
        rc = sbuf.st_size % (int)sizeof (struct utmpx);
        if (rc) {
                (void) fprintf(stderr, gettext("%s: File '%s' is not "
                    "a utmpx file\n"), program, name);
                exit(1);
        }
}