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