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