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