1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  22 /*        All Rights Reserved   */
  23 
  24 
  25 /*
  26  * Copyright (c) 2013 Gary Mills
  27  *
  28  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  29  * Use is subject to license terms.
  30  */
  31 
  32 /*
  33  * Copyright (c) 2018, Joyent, Inc.
  34  */
  35 
  36 /*
  37  *      This program analyzes information found in /var/adm/utmpx
  38  *
  39  *      Additionally information is gathered from /etc/inittab
  40  *      if requested.
  41  *
  42  *
  43  *      Syntax:
  44  *
  45  *              who am i        Displays info on yourself
  46  *
  47  *              who -a          Displays information about All
  48  *                              entries in /var/adm/utmpx
  49  *
  50  *              who -b          Displays info on last boot
  51  *
  52  *              who -d          Displays info on DEAD PROCESSES
  53  *
  54  *              who -H          Displays HEADERS for output
  55  *
  56  *              who -l          Displays info on LOGIN entries
  57  *
  58  *              who -m          Same as who am i
  59  *
  60  *              who -p          Displays info on PROCESSES spawned by init
  61  *
  62  *              who -q          Displays short information on
  63  *                              current users who LOGGED ON
  64  *
  65  *              who -r          Displays info of current run-level
  66  *
  67  *              who -s          Displays requested info in SHORT form
  68  *
  69  *              who -t          Displays info on TIME changes
  70  *
  71  *              who -T          Displays writeability of each user
  72  *                              (+ writeable, - non-writeable, ? hung)
  73  *
  74  *              who -u          Displays LONG info on users
  75  *                              who have LOGGED ON
  76  */
  77 
  78 #define         DATE_FMT        "%b %e %H:%M"
  79 
  80 /*
  81  *  %b  Abbreviated month name
  82  *  %e  Day of month
  83  *  %H  hour (24-hour clock)
  84  *  %M  minute
  85  */
  86 #include        <errno.h>
  87 #include        <fcntl.h>
  88 #include        <stdio.h>
  89 #include        <string.h>
  90 #include        <sys/types.h>
  91 #include        <unistd.h>
  92 #include        <stdlib.h>
  93 #include        <sys/stat.h>
  94 #include        <time.h>
  95 #include        <utmpx.h>
  96 #include        <locale.h>
  97 #include        <pwd.h>
  98 #include        <limits.h>
  99 
 100 static void process(void);
 101 static void ck_file(char *);
 102 static void dump(void);
 103 
 104 static struct   utmpx *utmpp;   /* pointer for getutxent()      */
 105 
 106 /*
 107  * Use the full lengths from utmpx for user and line.
 108  */
 109 #define NMAX    (sizeof (utmpp->ut_user))
 110 #define LMAX    (sizeof (utmpp->ut_line))
 111 
 112 /* Print minimum field widths. */
 113 #define LOGIN_WIDTH     8
 114 #define LINE_WIDTH      12
 115 
 116 static char     comment[BUFSIZ]; /* holds inittab comment       */
 117 static char     errmsg[BUFSIZ]; /* used in snprintf for errors  */
 118 static int      fildes;         /* file descriptor for inittab  */
 119 static int      Hopt = 0;       /* 1 = who -H                   */
 120 static char     *inittab;       /* ptr to inittab contents      */
 121 static char     *iinit;         /* index into inittab           */
 122 static int      justme = 0;     /* 1 = who am i                 */
 123 static struct   tm *lptr;       /* holds user login time        */
 124 static char     *myname;        /* pointer to invoker's name    */
 125 static char     *mytty;         /* holds device user is on      */
 126 static char     nameval[sizeof (utmpp->ut_user) + 1]; /*  invoker's name */
 127 static int      number = 8;     /* number of users per -q line  */
 128 static int      optcnt = 0;     /* keeps count of options       */
 129 static char     outbuf[BUFSIZ]; /* buffer for output            */
 130 static char     *program;       /* holds name of this program   */
 131 #ifdef  XPG4
 132 static int      aopt = 0;       /* 1 = who -a                   */
 133 static int      dopt = 0;       /* 1 = who -d                   */
 134 #endif  /* XPG4 */
 135 static int      qopt = 0;       /* 1 = who -q                   */
 136 static int      sopt = 0;       /* 1 = who -s                   */
 137 static struct   stat stbuf;     /* area for stat buffer         */
 138 static struct   stat *stbufp;   /* ptr to structure             */
 139 static int      terse = 1;      /* 1 = print terse msgs         */
 140 static int      Topt = 0;       /* 1 = who -T                   */
 141 static time_t   timnow;         /* holds current time           */
 142 static int      totlusrs = 0;   /* cntr for users on system     */
 143 static int      uopt = 0;       /* 1 = who -u                   */
 144 static char     user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
 145 static int      validtype[UTMAXTYPE+1]; /* holds valid types    */
 146 static int      wrap;           /* flag to indicate wrap        */
 147 static char     time_buf[128];  /* holds date and time string   */
 148 static char     *end;           /* used in strtol for end pointer */
 149 
 150 int
 151 main(int argc, char **argv)
 152 {
 153         int     goerr = 0;      /* non-zero indicates cmd error */
 154         int     i;
 155         int     optsw;          /* switch for while of getopt() */
 156 
 157         (void) setlocale(LC_ALL, "");
 158 
 159 #if     !defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
 160 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 161 #endif
 162         (void) textdomain(TEXT_DOMAIN);
 163 
 164         validtype[USER_PROCESS] = 1;
 165         validtype[EMPTY] = 0;
 166         stbufp = &stbuf;
 167 
 168         /*
 169          *      Strip off path name of this command
 170          */
 171         for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i)
 172                 ;
 173         if (i >= 0)
 174                 argv[0] += i+1;
 175         program = argv[0];
 176 
 177         /*
 178          *      Buffer stdout for speed
 179          */
 180         setbuf(stdout, outbuf);
 181 
 182         /*
 183          *      Retrieve options specified on command line
 184          *      XCU4 - add -m option
 185          */
 186         while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
 187                 optcnt++;
 188                 switch (optsw) {
 189 
 190                 case 'a':
 191                         optcnt += 7;
 192                         validtype[BOOT_TIME] = 1;
 193                         validtype[DEAD_PROCESS] = 1;
 194                         validtype[LOGIN_PROCESS] = 1;
 195                         validtype[INIT_PROCESS] = 1;
 196                         validtype[RUN_LVL] = 1;
 197                         validtype[OLD_TIME] = 1;
 198                         validtype[NEW_TIME] = 1;
 199                         validtype[USER_PROCESS] = 1;
 200 #ifdef  XPG4
 201                         aopt = 1;
 202 #endif  /* XPG4 */
 203                         uopt = 1;
 204                         Topt = 1;
 205                         if (!sopt) terse = 0;
 206                         break;
 207 
 208                 case 'b':
 209                         validtype[BOOT_TIME] = 1;
 210                         if (!uopt) validtype[USER_PROCESS] = 0;
 211                         break;
 212 
 213                 case 'd':
 214                         validtype[DEAD_PROCESS] = 1;
 215                         if (!uopt) validtype[USER_PROCESS] = 0;
 216 #ifdef  XPG4
 217                         dopt = 1;
 218 #endif  /* XPG4 */
 219                         break;
 220 
 221                 case 'H':
 222                         optcnt--; /* Don't count Header */
 223                         Hopt = 1;
 224                         break;
 225 
 226                 case 'l':
 227                         validtype[LOGIN_PROCESS] = 1;
 228                         if (!uopt) validtype[USER_PROCESS] = 0;
 229                         terse = 0;
 230                         break;
 231                 case 'm':               /* New XCU4 option */
 232                         justme = 1;
 233                         break;
 234 
 235                 case 'n':
 236                         errno = 0;
 237                         number = strtol(optarg, &end, 10);
 238                         if (errno != 0 || *end != '\0') {
 239                                 (void) fprintf(stderr, gettext(
 240                                     "%s: Invalid numeric argument\n"),
 241                                     program);
 242                                 exit(1);
 243                         }
 244                         if (number < 1) {
 245                                 (void) fprintf(stderr, gettext(
 246                                     "%s: Number of users per line must "
 247                                     "be at least 1\n"), program);
 248                                 exit(1);
 249                         }
 250                         break;
 251 
 252                 case 'p':
 253                         validtype[INIT_PROCESS] = 1;
 254                         if (!uopt) validtype[USER_PROCESS] = 0;
 255                         break;
 256 
 257                 case 'q':
 258                         qopt = 1;
 259                         break;
 260 
 261                 case 'r':
 262                         validtype[RUN_LVL] = 1;
 263                         terse = 0;
 264                         if (!uopt) validtype[USER_PROCESS] = 0;
 265                         break;
 266 
 267                 case 's':
 268                         sopt = 1;
 269                         terse = 1;
 270                         break;
 271 
 272                 case 't':
 273                         validtype[OLD_TIME] = 1;
 274                         validtype[NEW_TIME] = 1;
 275                         if (!uopt) validtype[USER_PROCESS] = 0;
 276                         break;
 277 
 278                 case 'T':
 279                         Topt = 1;
 280 #ifdef  XPG4
 281                         terse = 1;      /* XPG4 requires -T */
 282 #else   /* XPG4 */
 283                         terse = 0;
 284 #endif  /* XPG4 */
 285                         break;
 286 
 287                 case 'u':
 288                         uopt = 1;
 289                         validtype[USER_PROCESS] = 1;
 290                         if (!sopt) terse = 0;
 291                         break;
 292 
 293                 case '?':
 294                         goerr++;
 295                         break;
 296                 default:
 297                         break;
 298                 }
 299         }
 300 #ifdef  XPG4
 301         /*
 302          * XCU4 changes - check for illegal sopt, Topt & aopt combination
 303          */
 304         if (sopt == 1) {
 305                 terse = 1;
 306                 if (Topt == 1 || aopt == 1)
 307                         goerr++;
 308         }
 309 #endif  /* XPG4 */
 310 
 311         if (goerr > 0) {
 312 #ifdef  XPG4
 313                 /*
 314                  * XCU4 - slightly different usage with -s -a & -T
 315                  */
 316                 (void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
 317                 (void) fprintf(stderr,
 318                     gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));
 319 
 320                 (void) fprintf(stderr, gettext(
 321                     "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
 322 #else   /* XPG4 */
 323                 (void) fprintf(stderr, gettext(
 324                     "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
 325                     program);
 326 #endif  /* XPG4 */
 327                 (void) fprintf(stderr,
 328                     gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
 329                 (void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
 330                 /*
 331                  * XCU4 changes - be explicit with "am i" options
 332                  */
 333                 (void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
 334                 (void) fprintf(stderr, gettext(
 335                     "a\tall (bdlprtu options)\n"));
 336                 (void) fprintf(stderr, gettext("b\tboot time\n"));
 337                 (void) fprintf(stderr, gettext("d\tdead processes\n"));
 338                 (void) fprintf(stderr, gettext("H\tprint header\n"));
 339                 (void) fprintf(stderr, gettext("l\tlogin processes\n"));
 340                 (void) fprintf(stderr, gettext(
 341                     "n #\tspecify number of users per line for -q\n"));
 342                 (void) fprintf(stderr,
 343                     gettext("p\tprocesses other than getty or users\n"));
 344                 (void) fprintf(stderr, gettext("q\tquick %s\n"), program);
 345                 (void) fprintf(stderr, gettext("r\trun level\n"));
 346                 (void) fprintf(stderr, gettext(
 347                 "s\tshort form of %s (no time since last output or pid)\n"),
 348                     program);
 349                 (void) fprintf(stderr, gettext("t\ttime changes\n"));
 350                 (void) fprintf(stderr, gettext(
 351                     "T\tstatus of tty (+ writable, - not writable, "
 352                     "? hung)\n"));
 353                 (void) fprintf(stderr, gettext("u\tuseful information\n"));
 354                 (void) fprintf(stderr,
 355                     gettext("m\tinformation only about current terminal\n"));
 356                 (void) fprintf(stderr, gettext(
 357                     "am i\tinformation about current terminal "
 358                     "(same as -m)\n"));
 359                 (void) fprintf(stderr, gettext(
 360                     "am I\tinformation about current terminal "
 361                     "(same as -m)\n"));
 362                 exit(1);
 363         }
 364 
 365         /*
 366          * XCU4: If -q option ignore all other options
 367          */
 368         if (qopt == 1) {
 369                 Hopt = 0;
 370                 sopt = 0;
 371                 Topt = 0;
 372                 uopt = 0;
 373                 justme = 0;
 374                 validtype[ACCOUNTING] = 0;
 375                 validtype[BOOT_TIME] = 0;
 376                 validtype[DEAD_PROCESS] = 0;
 377                 validtype[LOGIN_PROCESS] = 0;
 378                 validtype[INIT_PROCESS] = 0;
 379                 validtype[RUN_LVL] = 0;
 380                 validtype[OLD_TIME] = 0;
 381                 validtype[NEW_TIME] = 0;
 382                 validtype[USER_PROCESS] = 1;
 383         }
 384 
 385         if (argc == optind + 1) {
 386                 optcnt++;
 387                 ck_file(argv[optind]);
 388                 (void) utmpxname(argv[optind]);
 389         }
 390 
 391         /*
 392          *      Test for 'who am i' or 'who am I'
 393          *      XCU4 - check if justme was already set by -m option
 394          */
 395         if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
 396             ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
 397             argv[2][1] == '\0'))) {
 398                 justme = 1;
 399                 myname = nameval;
 400                 (void) cuserid(myname);
 401                 if ((mytty = ttyname(fileno(stdin))) == NULL &&
 402                     (mytty = ttyname(fileno(stdout))) == NULL &&
 403                     (mytty = ttyname(fileno(stderr))) == NULL) {
 404                         (void) fprintf(stderr, gettext(
 405                         "Must be attached to terminal for 'am I' option\n"));
 406                         (void) fflush(stderr);
 407                         exit(1);
 408                 } else
 409                         mytty += 5; /* bump past "/dev/" */
 410         }
 411 
 412         if (!terse) {
 413                 if (Hopt)
 414                         (void) printf(gettext(
 415         "NAME       LINE         TIME          IDLE    PID  COMMENTS\n"));
 416 
 417                 timnow = time(0);
 418 
 419                 if ((fildes = open("/etc/inittab",
 420                     O_NONBLOCK|O_RDONLY)) == -1) {
 421                         (void) snprintf(errmsg, sizeof (errmsg),
 422                             gettext("%s: Cannot open /etc/inittab"), program);
 423                         perror(errmsg);
 424                         exit(errno);
 425                 }
 426 
 427                 if (fstat(fildes, stbufp) == -1) {
 428                         (void) snprintf(errmsg, sizeof (errmsg),
 429                             gettext("%s: Cannot stat /etc/inittab"), program);
 430                         perror(errmsg);
 431                         exit(errno);
 432                 }
 433 
 434                 if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
 435                         (void) snprintf(errmsg, sizeof (errmsg),
 436                             gettext("%s: Cannot allocate %ld bytes"),
 437                             program, stbufp->st_size);
 438                         perror(errmsg);
 439                         exit(errno);
 440                 }
 441 
 442                 if (read(fildes, inittab, stbufp->st_size)
 443                     != stbufp->st_size) {
 444                         (void) snprintf(errmsg, sizeof (errmsg),
 445                             gettext("%s: Error reading /etc/inittab"),
 446                             program);
 447                         perror(errmsg);
 448                         exit(errno);
 449                 }
 450 
 451                 inittab[stbufp->st_size] = '\0';
 452                 iinit = inittab;
 453         } else {
 454                 if (Hopt) {
 455 #ifdef  XPG4
 456                         if (dopt) {
 457                                 (void) printf(gettext(
 458                         "NAME       LINE         TIME           COMMENTS\n"));
 459                         } else {
 460                                 (void) printf(
 461                                     gettext("NAME       LINE         TIME\n"));
 462                         }
 463 #else   /* XPG4 */
 464                         (void) printf(
 465                             gettext("NAME       LINE         TIME\n"));
 466 #endif  /* XPG4 */
 467                 }
 468         }
 469         process();
 470 
 471         /*
 472          *      'who -q' requires EOL upon exit,
 473          *      followed by total line
 474          */
 475         if (qopt)
 476                 (void) printf(gettext("\n# users=%d\n"), totlusrs);
 477         return (0);
 478 }
 479 
 480 static void
 481 dump()
 482 {
 483         char    device[sizeof (utmpp->ut_line) + 1];
 484         time_t hr;
 485         time_t  idle;
 486         time_t min;
 487         char    path[sizeof (utmpp->ut_line) + 6];
 488         int     pexit;
 489         int     pterm;
 490         int     rc;
 491         char    w;      /* writeability indicator */
 492 
 493         /*
 494          * Get and check user name
 495          */
 496         if (utmpp->ut_user[0] == '\0')
 497                 (void) strcpy(user, "   .");
 498         else {
 499                 (void) strncpy(user, utmpp->ut_user, sizeof (user));
 500                 user[sizeof (user) - 1] = '\0';
 501         }
 502         totlusrs++;
 503 
 504         /*
 505          * Do print in 'who -q' format
 506          */
 507         if (qopt) {
 508                 /*
 509                  * XCU4 - Use non user macro for correct user count
 510                  */
 511                 if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
 512                         (void) printf("\n");
 513                 (void) printf("%-*.*s ", LOGIN_WIDTH, NMAX, user);
 514                 return;
 515         }
 516 
 517 
 518         pexit = (int)' ';
 519         pterm = (int)' ';
 520 
 521         /*
 522          *      Get exit info if applicable
 523          */
 524         if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
 525                 pterm = utmpp->ut_exit.e_termination;
 526                 pexit = utmpp->ut_exit.e_exit;
 527         }
 528 
 529         /*
 530          *      Massage ut_xtime field
 531          */
 532         lptr = localtime(&utmpp->ut_xtime);
 533         (void) strftime(time_buf, sizeof (time_buf),
 534             dcgettext(NULL, DATE_FMT, LC_TIME), lptr);
 535 
 536         /*
 537          *      Get and massage device
 538          */
 539         if (utmpp->ut_line[0] == '\0')
 540                 (void) strcpy(device, "     .");
 541         else {
 542                 (void) strncpy(device, utmpp->ut_line,
 543                     sizeof (utmpp->ut_line));
 544                 device[sizeof (utmpp->ut_line)] = '\0';
 545         }
 546 
 547         /*
 548          *      Get writeability if requested
 549          *      XCU4 - only print + or - for user processes
 550          */
 551         if (Topt && (utmpp->ut_type == USER_PROCESS)) {
 552                 w = '-';
 553                 (void) strcpy(path, "/dev/");
 554                 (void) strncpy(path + 5, utmpp->ut_line,
 555                     sizeof (utmpp->ut_line));
 556                 path[5 + sizeof (utmpp->ut_line)] = '\0';
 557 
 558                 if ((rc = stat(path, stbufp)) == -1) w = '?';
 559                 else if ((stbufp->st_mode & S_IWOTH) ||
 560                     (stbufp->st_mode & S_IWGRP))  /* Check group & other */
 561                         w = '+';
 562 
 563         } else
 564                 w = ' ';
 565 
 566         /*
 567          *      Print the TERSE portion of the output
 568          */
 569         (void) printf("%-*.*s %c %-12s %s", LOGIN_WIDTH, NMAX, user,
 570             w, device, time_buf);
 571 
 572         if (!terse) {
 573                 /*
 574                  *      Stat device for idle time
 575                  *      (Don't complain if you can't)
 576                  */
 577                 rc = -1;
 578                 if (utmpp->ut_type == USER_PROCESS) {
 579                         (void) strcpy(path, "/dev/");
 580                         (void) strncpy(path + 5, utmpp->ut_line,
 581                             sizeof (utmpp->ut_line));
 582                         path[5 + sizeof (utmpp->ut_line)] = '\0';
 583                         rc = stat(path, stbufp);
 584                 }
 585                 if (rc != -1) {
 586                         idle = timnow - stbufp->st_mtime;
 587                         hr = idle/3600;
 588                         min = (unsigned)(idle/60)%60;
 589                         if (hr == 0 && min == 0)
 590                                 (void) printf(gettext("   .  "));
 591                         else {
 592                                 if (hr < 24)
 593                                         (void) printf(" %2d:%2.2d", (int)hr,
 594                                             (int)min);
 595                                 else
 596                                         (void) printf(gettext("  old "));
 597                         }
 598                 }
 599 
 600                 /*
 601                  *      Add PID for verbose output
 602                  */
 603                 if (utmpp->ut_type != BOOT_TIME &&
 604                     utmpp->ut_type != RUN_LVL &&
 605                     utmpp->ut_type != ACCOUNTING)
 606                         (void) printf("  %5ld", utmpp->ut_pid);
 607 
 608                 /*
 609                  *      Handle /etc/inittab comment
 610                  */
 611                 if (utmpp->ut_type == DEAD_PROCESS) {
 612                         (void) printf(gettext("  id=%4.4s "),
 613                             utmpp->ut_id);
 614                         (void) printf(gettext("term=%-3d "), pterm);
 615                         (void) printf(gettext("exit=%d  "), pexit);
 616                 } else if (utmpp->ut_type != INIT_PROCESS) {
 617                         /*
 618                          *      Search for each entry in inittab
 619                          *      string. Keep our place from
 620                          *      search to search to try and
 621                          *      minimize the work. Wrap once if needed
 622                          *      for each entry.
 623                          */
 624                         wrap = 0;
 625                         /*
 626                          *      Look for a line beginning with
 627                          *      utmpp->ut_id
 628                          */
 629                         while ((rc = strncmp(utmpp->ut_id, iinit,
 630                             strcspn(iinit, ":"))) != 0) {
 631                                 for (; *iinit != '\n'; iinit++)
 632                                         ;
 633                                 iinit++;
 634 
 635                                 /*
 636                                  *      Wrap once if necessary to
 637                                  *      find entry in inittab
 638                                  */
 639                                 if (*iinit == '\0') {
 640                                         if (!wrap) {
 641                                                 iinit = inittab;
 642                                                 wrap = 1;
 643                                         }
 644                                 }
 645                         }
 646 
 647                         if (*iinit != '\0') {
 648                                 /*
 649                                  *      We found our entry
 650                                  */
 651                                 for (iinit++; *iinit != '#' &&
 652                                     *iinit != '\n'; iinit++)
 653                                         ;
 654                                 if (*iinit == '#') {
 655                                         for (iinit++; *iinit == ' ' ||
 656                                             *iinit == '\t'; iinit++)
 657                                                 ;
 658                                         for (rc = 0; *iinit != '\n'; iinit++)
 659                                                 comment[rc++] = *iinit;
 660                                         comment[rc] = '\0';
 661                                 } else
 662                                         (void) strcpy(comment, " ");
 663 
 664                                 (void) printf("  %s", comment);
 665                         } else
 666                                 iinit = inittab;        /* Reset pointer */
 667                 }
 668                 if (utmpp->ut_type == INIT_PROCESS)
 669                         (void) printf(gettext("  id=%4.4s"), utmpp->ut_id);
 670         }
 671 #ifdef  XPG4
 672         else
 673                 if (dopt && utmpp->ut_type == DEAD_PROCESS) {
 674                         (void) printf(gettext("\tterm=%-3d "), pterm);
 675                         (void) printf(gettext("exit=%d  "), pexit);
 676                 }
 677 #endif  /* XPG4 */
 678 
 679 
 680         /*
 681          *      Handle RUN_LVL process - If no alt. file - Only one!
 682          */
 683         if (utmpp->ut_type == RUN_LVL) {
 684                 (void) printf("     %c  %5ld  %c", pterm, utmpp->ut_pid,
 685                     pexit);
 686                 if (optcnt == 1 && !validtype[USER_PROCESS]) {
 687                         (void) printf("\n");
 688                         exit(0);
 689                 }
 690         }
 691 
 692         /*
 693          *      Handle BOOT_TIME process -  If no alt. file - Only one!
 694          */
 695         if (utmpp->ut_type == BOOT_TIME) {
 696                 if (optcnt == 1 && !validtype[USER_PROCESS]) {
 697                         (void) printf("\n");
 698                         exit(0);
 699                 }
 700         }
 701 
 702         /*
 703          *      Get remote host from utmpx structure
 704          */
 705         if (utmpp->ut_host[0])
 706                 (void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
 707                     utmpp->ut_host);
 708 
 709         /*
 710          *      Now, put on the trailing EOL
 711          */
 712         (void) printf("\n");
 713 }
 714 
 715 static void
 716 process()
 717 {
 718         struct passwd *pwp;
 719         int i = 0;
 720         char *ttname;
 721 
 722         /*
 723          *      Loop over each entry in /var/adm/utmpx
 724          */
 725 
 726         setutxent();
 727         while ((utmpp = getutxent()) != NULL) {
 728 #ifdef DEBUG
 729         (void) printf(
 730             "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
 731             utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
 732 #endif
 733                 if (utmpp->ut_type <= UTMAXTYPE) {
 734                         /*
 735                          *      Handle "am i"
 736                          */
 737                         if (justme) {
 738                                 if (strncmp(myname, utmpp->ut_user,
 739                                     sizeof (utmpp->ut_user)) == 0 &&
 740                                     strncmp(mytty, utmpp->ut_line,
 741                                     sizeof (utmpp->ut_line)) == 0 &&
 742                                     utmpp->ut_type == USER_PROCESS) {
 743                                         /*
 744                                          * we have have found ourselves
 745                                          * in the utmp file and the entry
 746                                          * is a user process, this is not
 747                                          * meaningful otherwise
 748                                          *
 749                                          */
 750 
 751                                         dump();
 752                                         exit(0);
 753                                 }
 754                                 continue;
 755                         }
 756 
 757                         /*
 758                          *      Print the line if we want it
 759                          */
 760                         if (validtype[utmpp->ut_type]) {
 761 #ifdef  XPG4
 762                                 if (utmpp->ut_type == LOGIN_PROCESS) {
 763                                         if ((utmpp->ut_line[0] == '\0') ||
 764                                             (strcmp(utmpp->ut_user,
 765                                             "LOGIN") != 0))
 766                                                 continue;
 767                                 }
 768 #endif  /* XPG4 */
 769                                 dump();
 770                         }
 771                 } else {
 772                         (void) fprintf(stderr,
 773                             gettext("%s: Error --- entry has ut_type "
 774                             "of %d\n"), program, utmpp->ut_type);
 775                         (void) fprintf(stderr,
 776                             gettext(" when maximum is %d\n"), UTMAXTYPE);
 777                 }
 778         }
 779 
 780         /*
 781          * If justme is set at this point than the utmp entry
 782          * was not found.
 783          */
 784         if (justme) {
 785                 static struct utmpx utmpt;
 786 
 787                 pwp = getpwuid(geteuid());
 788                 if (pwp != NULL)
 789                         while (i < (int)sizeof (utmpt.ut_user) &&
 790                             *pwp->pw_name != 0)
 791                                 utmpt.ut_user[i++] = *pwp->pw_name++;
 792 
 793                 ttname = ttyname(1);
 794 
 795                 i = 0;
 796                 if (ttname != NULL)
 797                         while (i < (int)sizeof (utmpt.ut_line) &&
 798                             *ttname != 0)
 799                                 utmpt.ut_line[i++] = *ttname++;
 800 
 801                 utmpt.ut_id[0] = 0;
 802                 utmpt.ut_pid = getpid();
 803                 utmpt.ut_type = USER_PROCESS;
 804                 (void) time(&utmpt.ut_xtime);
 805                 utmpp = &utmpt;
 806                 dump();
 807                 exit(0);
 808         }
 809 }
 810 
 811 /*
 812  *      This routine checks the following:
 813  *
 814  *      1.      File exists
 815  *
 816  *      2.      We have read permissions
 817  *
 818  *      3.      It is a multiple of utmp entries in size
 819  *
 820  *      Failing any of these conditions causes who(1) to
 821  *      abort processing.
 822  *
 823  *      4.      If file is empty we exit right away as there
 824  *              is no info to report on.
 825  *
 826  *      This routine does not check utmpx files.
 827  */
 828 static void
 829 ck_file(char *name)
 830 {
 831         struct  stat sbuf;
 832         int     rc;
 833 
 834         /*
 835          *      Does file exist? Do stat to check, and save structure
 836          *      so that we can check on the file's size later on.
 837          */
 838         if ((rc = stat(name, &sbuf)) == -1) {
 839                 (void) snprintf(errmsg, sizeof (errmsg),
 840                     gettext("%s: Cannot stat file '%s'"), program, name);
 841                 perror(errmsg);
 842                 exit(1);
 843         }
 844 
 845         /*
 846          *      The only real way we can be sure we can access the
 847          *      file is to try. If we succeed then we close it.
 848          */
 849         if (access(name, R_OK) < 0) {
 850                 (void) snprintf(errmsg, sizeof (errmsg),
 851                     gettext("%s: Cannot open file '%s'"), program, name);
 852                 perror(errmsg);
 853                 exit(1);
 854         }
 855 
 856         /*
 857          *      If the file is empty, we are all done.
 858          */
 859         if (!sbuf.st_size)
 860                 exit(0);
 861 
 862         /*
 863          *      Make sure the file is a utmp file.
 864          *      We can only check for size being a multiple of
 865          *      utmp structures in length.
 866          */
 867         rc = sbuf.st_size % (int)sizeof (struct utmpx);
 868         if (rc) {
 869                 (void) fprintf(stderr, gettext("%s: File '%s' is not "
 870                     "a utmpx file\n"), program, name);
 871                 exit(1);
 872         }
 873 }