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