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 } 874