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 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 "@(#)who.c 1.33 06/04/03 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 83 #include 84 #include 85 #include 86 #include 87 #include 88 #include 89 #include 90 #include 91 #include 92 #include 93 #include 94 #include 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 } 868 (载自) |
|