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 static struct utmpx *utmpp; /* pointer for getutxent() */ 102 103 /* 104 * utmpx defines wider fields for user and line. For compatibility of output, 105 * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN 106 * to use the full lengths. 107 */ 108 #ifndef UTMPX_NAMELEN 109 /* XXX - utmp - fix name length */ 110 #define NMAX (_POSIX_LOGIN_NAME_MAX - 1) 111 #define LMAX 12 112 #else /* UTMPX_NAMELEN */ 113 #define NMAX (sizeof (utmpp->ut_user)) 114 #define LMAX (sizeof (utmpp->ut_line)) 115 #endif 116 117 static char comment[BUFSIZ]; /* holds inittab comment */ 118 static char errmsg[BUFSIZ]; /* used in snprintf for errors */ 119 static int fildes; /* file descriptor for inittab */ 120 static int Hopt = 0; /* 1 = who -H */ 121 static char *inittab; /* ptr to inittab contents */ 122 static char *iinit; /* index into inittab */ 123 static int justme = 0; /* 1 = who am i */ 124 static struct tm *lptr; /* holds user login time */ 125 static char *myname; /* pointer to invoker's name */ 126 static char *mytty; /* holds device user is on */ 127 static char nameval[sizeof (utmpp->ut_user) + 1]; /* invoker's name */ 128 static int number = 8; /* number of users per -q line */ 129 static int optcnt = 0; /* keeps count of options */ 130 static char outbuf[BUFSIZ]; /* buffer for output */ 131 static char *program; /* holds name of this program */ 132 #ifdef XPG4 133 static int aopt = 0; /* 1 = who -a */ 134 static int dopt = 0; /* 1 = who -d */ 135 #endif /* XPG4 */ 136 static int qopt = 0; /* 1 = who -q */ 137 static int sopt = 0; /* 1 = who -s */ 138 static struct stat stbuf; /* area for stat buffer */ 139 static struct stat *stbufp; /* ptr to structure */ 140 static int terse = 1; /* 1 = print terse msgs */ 141 static int Topt = 0; /* 1 = who -T */ 142 static time_t timnow; /* holds current time */ 143 static int totlusrs = 0; /* cntr for users on system */ 144 static int uopt = 0; /* 1 = who -u */ 145 static char user[sizeof (utmpp->ut_user) + 1]; /* holds user name */ 146 static int validtype[UTMAXTYPE+1]; /* holds valid types */ 147 static int wrap; /* flag to indicate wrap */ 148 static char time_buf[128]; /* holds date and time string */ 149 static char *end; /* used in strtol for end pointer */ 150 151 int 152 main(int argc, char **argv) 153 { 154 int goerr = 0; /* non-zero indicates cmd error */ 155 int i; 156 int optsw; /* switch for while of getopt() */ 157 158 (void) setlocale(LC_ALL, ""); 159 160 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 161 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 162 #endif 163 (void) textdomain(TEXT_DOMAIN); 164 165 validtype[USER_PROCESS] = 1; 166 validtype[EMPTY] = 0; 167 stbufp = &stbuf; 168 169 /* 170 * Strip off path name of this command 171 */ 172 for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i); 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 ", 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", NMAX, user, w, device, time_buf); 570 571 if (!terse) { 572 (void) strcpy(path, "/dev/"); 573 (void) strncpy(path + 5, utmpp->ut_line, 574 sizeof (utmpp->ut_line)); 575 path[5 + sizeof (utmpp->ut_line)] = '\0'; 576 577 /* 578 * Stat device for idle time 579 * (Don't complain if you can't) 580 */ 581 if ((rc = stat(path, stbufp)) != -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 iinit++; 629 630 /* 631 * Wrap once if necessary to 632 * find entry in inittab 633 */ 634 if (*iinit == '\0') { 635 if (!wrap) { 636 iinit = inittab; 637 wrap = 1; 638 } 639 } 640 } 641 642 if (*iinit != '\0') { 643 /* 644 * We found our entry 645 */ 646 for (iinit++; *iinit != '#' && 647 *iinit != '\n'; iinit++); 648 if (*iinit == '#') { 649 for (iinit++; *iinit == ' ' || 650 *iinit == '\t'; iinit++); 651 for (rc = 0; *iinit != '\n'; iinit++) 652 comment[rc++] = *iinit; 653 comment[rc] = '\0'; 654 } else 655 (void) strcpy(comment, " "); 656 657 (void) printf(" %s", comment); 658 } else 659 iinit = inittab; /* Reset pointer */ 660 } 661 if (utmpp->ut_type == INIT_PROCESS) 662 (void) printf(gettext(" id=%4.4s"), utmpp->ut_id); 663 } 664 #ifdef XPG4 665 else 666 if (dopt && utmpp->ut_type == DEAD_PROCESS) { 667 (void) printf(gettext("\tterm=%-3d "), pterm); 668 (void) printf(gettext("exit=%d "), pexit); 669 } 670 #endif /* XPG4 */ 671 672 673 /* 674 * Handle RUN_LVL process - If no alt. file - Only one! 675 */ 676 if (utmpp->ut_type == RUN_LVL) { 677 (void) printf(" %c %5ld %c", pterm, utmpp->ut_pid, 678 pexit); 679 if (optcnt == 1 && !validtype[USER_PROCESS]) { 680 (void) printf("\n"); 681 exit(0); 682 } 683 } 684 685 /* 686 * Handle BOOT_TIME process - If no alt. file - Only one! 687 */ 688 if (utmpp->ut_type == BOOT_TIME) { 689 if (optcnt == 1 && !validtype[USER_PROCESS]) { 690 (void) printf("\n"); 691 exit(0); 692 } 693 } 694 695 /* 696 * Get remote host from utmpx structure 697 */ 698 if (utmpp && utmpp->ut_host[0]) 699 (void) printf("\t(%.*s)", sizeof (utmpp->ut_host), 700 utmpp->ut_host); 701 702 /* 703 * Now, put on the trailing EOL 704 */ 705 (void) printf("\n"); 706 } 707 708 static void 709 process() 710 { 711 struct passwd *pwp; 712 int i = 0; 713 char *ttname; 714 715 /* 716 * Loop over each entry in /var/adm/utmpx 717 */ 718 719 setutxent(); 720 while ((utmpp = getutxent()) != NULL) { 721 #ifdef DEBUG 722 (void) printf( 723 "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n", 724 utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type); 725 #endif 726 if (utmpp->ut_type <= UTMAXTYPE) { 727 /* 728 * Handle "am i" 729 */ 730 if (justme) { 731 if (strncmp(myname, utmpp->ut_user, 732 sizeof (utmpp->ut_user)) == 0 && 733 strncmp(mytty, utmpp->ut_line, 734 sizeof (utmpp->ut_line)) == 0 && 735 utmpp->ut_type == USER_PROCESS) { 736 /* 737 * we have have found ourselves 738 * in the utmp file and the entry 739 * is a user process, this is not 740 * meaningful otherwise 741 * 742 */ 743 744 dump(); 745 exit(0); 746 } 747 continue; 748 } 749 750 /* 751 * Print the line if we want it 752 */ 753 if (validtype[utmpp->ut_type]) { 754 #ifdef XPG4 755 if (utmpp->ut_type == LOGIN_PROCESS) { 756 if ((utmpp->ut_line[0] == '\0') || 757 (strcmp(utmpp->ut_user, "LOGIN") != 0)) 758 continue; 759 } 760 #endif /* XPG4 */ 761 dump(); 762 } 763 } else { 764 (void) fprintf(stderr, 765 gettext("%s: Error --- entry has ut_type " 766 "of %d\n"), program, utmpp->ut_type); 767 (void) fprintf(stderr, 768 gettext(" when maximum is %d\n"), UTMAXTYPE); 769 } 770 } 771 772 /* 773 * If justme is set at this point than the utmp entry 774 * was not found. 775 */ 776 if (justme) { 777 static struct utmpx utmpt; 778 779 pwp = getpwuid(geteuid()); 780 if (pwp != NULL) 781 while (i < (int)sizeof (utmpt.ut_user) && 782 *pwp->pw_name != 0) 783 utmpt.ut_user[i++] = *pwp->pw_name++; 784 785 ttname = ttyname(1); 786 787 i = 0; 788 if (ttname != NULL) 789 while (i < (int)sizeof (utmpt.ut_line) && 790 *ttname != 0) 791 utmpt.ut_line[i++] = *ttname++; 792 793 utmpt.ut_id[0] = 0; 794 utmpt.ut_pid = getpid(); 795 utmpt.ut_type = USER_PROCESS; 796 (void) time(&utmpt.ut_xtime); 797 utmpp = &utmpt; 798 dump(); 799 exit(0); 800 } 801 } 802 803 /* 804 * This routine checks the following: 805 * 806 * 1. File exists 807 * 808 * 2. We have read permissions 809 * 810 * 3. It is a multiple of utmp entries in size 811 * 812 * Failing any of these conditions causes who(1) to 813 * abort processing. 814 * 815 * 4. If file is empty we exit right away as there 816 * is no info to report on. 817 * 818 * This routine does not check utmpx files. 819 */ 820 static void 821 ck_file(char *name) 822 { 823 struct stat sbuf; 824 int rc; 825 826 /* 827 * Does file exist? Do stat to check, and save structure 828 * so that we can check on the file's size later on. 829 */ 830 if ((rc = stat(name, &sbuf)) == -1) { 831 (void) snprintf(errmsg, sizeof (errmsg), 832 gettext("%s: Cannot stat file '%s'"), program, name); 833 perror(errmsg); 834 exit(1); 835 } 836 837 /* 838 * The only real way we can be sure we can access the 839 * file is to try. If we succeed then we close it. 840 */ 841 if (access(name, R_OK) < 0) { 842 (void) snprintf(errmsg, sizeof (errmsg), 843 gettext("%s: Cannot open file '%s'"), program, name); 844 perror(errmsg); 845 exit(1); 846 } 847 848 /* 849 * If the file is empty, we are all done. 850 */ 851 if (!sbuf.st_size) 852 exit(0); 853 854 /* 855 * Make sure the file is a utmp file. 856 * We can only check for size being a multiple of 857 * utmp structures in length. 858 */ 859 rc = sbuf.st_size % (int)sizeof (struct utmpx); 860 if (rc) { 861 (void) fprintf(stderr, gettext("%s: File '%s' is not " 862 "a utmpx file\n"), program, name); 863 exit(1); 864 } 865 } 866