1 /* 2 * Copyright (c) 1980 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1980 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)finger.c 5.11 (Berkeley) 09/20/88"; 26 #endif /* not lint */ 27 28 /* 29 * This is a finger program. It prints out useful information about users 30 * by digging it up from various system files. It is not very portable 31 * because the most useful parts of the information (the full user name, 32 * office, and phone numbers) are all stored in the VAX-unused gecos field 33 * of /etc/passwd, which, unfortunately, other UNIXes use for other things. 34 * 35 * There are three output formats, all of which give login name, teletype 36 * line number, and login time. The short output format is reminiscent 37 * of finger on ITS, and gives one line of information per user containing 38 * in addition to the minimum basic requirements (MBR), the full name of 39 * the user, his idle time and office location and phone number. The 40 * quick style output is UNIX who-like, giving only name, teletype and 41 * login time. Finally, the long style output give the same information 42 * as the short (in more legible format), the home directory and shell 43 * of the user, and, if it exits, a copy of the file .plan in the users 44 * home directory. Finger may be called with or without a list of people 45 * to finger -- if no list is given, all the people currently logged in 46 * are fingered. 47 * 48 * The program is validly called by one of the following: 49 * 50 * finger {short form list of users} 51 * finger -l {long form list of users} 52 * finger -b {briefer long form list of users} 53 * finger -q {quick list of users} 54 * finger -i {quick list of users with idle times} 55 * finger namelist {long format list of specified users} 56 * finger -s namelist {short format list of specified users} 57 * finger -w namelist {narrow short format list of specified users} 58 * 59 * where 'namelist' is a list of users login names. 60 * The other options can all be given after one '-', or each can have its 61 * own '-'. The -f option disables the printing of headers for short and 62 * quick outputs. The -b option briefens long format outputs. The -p 63 * option turns off plans for long format outputs. 64 */ 65 66 #include <sys/types.h> 67 #include <sys/stat.h> 68 #include <utmp.h> 69 #include <sys/signal.h> 70 #include <pwd.h> 71 #include <stdio.h> 72 #include <lastlog.h> 73 #include <ctype.h> 74 #include <sys/time.h> 75 #include <sys/socket.h> 76 #include <netinet/in.h> 77 #include <netdb.h> 78 79 #define ASTERISK '*' /* ignore this in real name */ 80 #define COMMA ',' /* separator in pw_gecos field */ 81 #define COMMAND '-' /* command line flag char */ 82 #define CORY 'C' /* cory hall office */ 83 #define EVANS 'E' /* evans hall office */ 84 #define SAMENAME '&' /* repeat login name in real name */ 85 #define TALKABLE 0220 /* tty is writable if 220 mode */ 86 87 struct utmp user; 88 #define NMAX sizeof(user.ut_name) 89 #define LMAX sizeof(user.ut_line) 90 #define HMAX sizeof(user.ut_host) 91 92 struct person { /* one for each person fingered */ 93 char *name; /* name */ 94 char tty[LMAX+1]; /* null terminated tty line */ 95 char host[HMAX+1]; /* null terminated remote host name */ 96 long loginat; /* time of (last) login */ 97 long idletime; /* how long idle (if logged in) */ 98 char *realname; /* pointer to full name */ 99 char *office; /* pointer to office name */ 100 char *officephone; /* pointer to office phone no. */ 101 char *homephone; /* pointer to home phone no. */ 102 char *random; /* for any random stuff in pw_gecos */ 103 struct passwd *pwd; /* structure of /etc/passwd stuff */ 104 char loggedin; /* person is logged in */ 105 char writable; /* tty is writable */ 106 char original; /* this is not a duplicate entry */ 107 struct person *link; /* link to next person */ 108 }; 109 110 char LASTLOG[] = "/usr/adm/lastlog"; /* last login info */ 111 char USERLOG[] = "/etc/utmp"; /* who is logged in */ 112 char PLAN[] = "/.plan"; /* what plan file is */ 113 char PROJ[] = "/.project"; /* what project file */ 114 115 int unbrief = 1; /* -b option default */ 116 int header = 1; /* -f option default */ 117 int hack = 1; /* -h option default */ 118 int idle = 0; /* -i option default */ 119 int large = 0; /* -l option default */ 120 int match = 1; /* -m option default */ 121 int plan = 1; /* -p option default */ 122 int unquick = 1; /* -q option default */ 123 int small = 0; /* -s option default */ 124 int wide = 1; /* -w option default */ 125 126 int unshort; 127 int lf; /* LASTLOG file descriptor */ 128 struct person *person1; /* list of people */ 129 long tloc; /* current time */ 130 131 struct passwd *pwdcopy(); 132 char *strcpy(); 133 char *malloc(); 134 char *ctime(); 135 136 main(argc, argv) 137 int argc; 138 register char **argv; 139 { 140 FILE *fp; 141 register char *s; 142 143 /* parse command line for (optional) arguments */ 144 while (*++argv && **argv == COMMAND) 145 for (s = *argv + 1; *s; s++) 146 switch (*s) { 147 case 'b': 148 unbrief = 0; 149 break; 150 case 'f': 151 header = 0; 152 break; 153 case 'h': 154 hack = 0; 155 break; 156 case 'i': 157 idle = 1; 158 unquick = 0; 159 break; 160 case 'l': 161 large = 1; 162 break; 163 case 'm': 164 match = 0; 165 break; 166 case 'p': 167 plan = 0; 168 break; 169 case 'q': 170 unquick = 0; 171 break; 172 case 's': 173 small = 1; 174 break; 175 case 'w': 176 wide = 0; 177 break; 178 default: 179 fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n"); 180 exit(1); 181 } 182 if (unquick || idle) 183 time(&tloc); 184 /* 185 * *argv == 0 means no names given 186 */ 187 if (*argv == 0) 188 doall(); 189 else 190 donames(argv); 191 if (person1) 192 print(); 193 exit(0); 194 } 195 196 doall() 197 { 198 register struct person *p; 199 register struct passwd *pw; 200 int uf; 201 char name[NMAX + 1]; 202 203 unshort = large; 204 if ((uf = open(USERLOG, 0)) < 0) { 205 fprintf(stderr, "finger: error opening %s\n", USERLOG); 206 exit(2); 207 } 208 if (unquick) { 209 extern _pw_stayopen; 210 211 setpwent(); 212 _pw_stayopen = 1; 213 fwopen(); 214 } 215 while (read(uf, (char *)&user, sizeof user) == sizeof user) { 216 if (user.ut_name[0] == 0) 217 continue; 218 if (person1 == 0) 219 p = person1 = (struct person *) malloc(sizeof *p); 220 else { 221 p->link = (struct person *) malloc(sizeof *p); 222 p = p->link; 223 } 224 bcopy(user.ut_name, name, NMAX); 225 name[NMAX] = 0; 226 bcopy(user.ut_line, p->tty, LMAX); 227 p->tty[LMAX] = 0; 228 bcopy(user.ut_host, p->host, HMAX); 229 p->host[HMAX] = 0; 230 p->loginat = user.ut_time; 231 p->pwd = 0; 232 p->loggedin = 1; 233 if (unquick && (pw = getpwnam(name))) { 234 p->pwd = pwdcopy(pw); 235 decode(p); 236 p->name = p->pwd->pw_name; 237 } else 238 p->name = strcpy(malloc(strlen(name) + 1), name); 239 } 240 if (unquick) { 241 fwclose(); 242 endpwent(); 243 } 244 close(uf); 245 if (person1 == 0) { 246 printf("No one logged on\n"); 247 return; 248 } 249 p->link = 0; 250 } 251 252 donames(argv) 253 char **argv; 254 { 255 register struct person *p; 256 register struct passwd *pw; 257 int uf; 258 259 /* 260 * get names from command line and check to see if they're 261 * logged in 262 */ 263 unshort = !small; 264 for (; *argv != 0; argv++) { 265 if (netfinger(*argv)) 266 continue; 267 if (person1 == 0) 268 p = person1 = (struct person *) malloc(sizeof *p); 269 else { 270 p->link = (struct person *) malloc(sizeof *p); 271 p = p->link; 272 } 273 p->name = *argv; 274 p->loggedin = 0; 275 p->original = 1; 276 p->pwd = 0; 277 } 278 if (person1 == 0) 279 return; 280 p->link = 0; 281 /* 282 * if we are doing it, read /etc/passwd for the useful info 283 */ 284 if (unquick) { 285 setpwent(); 286 if (!match) { 287 extern _pw_stayopen; 288 289 _pw_stayopen = 1; 290 for (p = person1; p != 0; p = p->link) 291 if (pw = getpwnam(p->name)) 292 p->pwd = pwdcopy(pw); 293 } else while ((pw = getpwent()) != 0) { 294 for (p = person1; p != 0; p = p->link) { 295 if (!p->original) 296 continue; 297 if (strcmp(p->name, pw->pw_name) != 0 && 298 !matchcmp(pw->pw_gecos, pw->pw_name, p->name)) 299 continue; 300 if (p->pwd == 0) 301 p->pwd = pwdcopy(pw); 302 else { 303 struct person *new; 304 /* 305 * handle multiple login names, insert 306 * new "duplicate" entry behind 307 */ 308 new = (struct person *) 309 malloc(sizeof *new); 310 new->pwd = pwdcopy(pw); 311 new->name = p->name; 312 new->original = 1; 313 new->loggedin = 0; 314 new->link = p->link; 315 p->original = 0; 316 p->link = new; 317 p = new; 318 } 319 } 320 } 321 endpwent(); 322 } 323 /* Now get login information */ 324 if ((uf = open(USERLOG, 0)) < 0) { 325 fprintf(stderr, "finger: error opening %s\n", USERLOG); 326 exit(2); 327 } 328 while (read(uf, (char *)&user, sizeof user) == sizeof user) { 329 if (*user.ut_name == 0) 330 continue; 331 for (p = person1; p != 0; p = p->link) { 332 if (p->loggedin == 2) 333 continue; 334 if (strncmp(p->pwd ? p->pwd->pw_name : p->name, 335 user.ut_name, NMAX) != 0) 336 continue; 337 if (p->loggedin == 0) { 338 bcopy(user.ut_line, p->tty, LMAX); 339 p->tty[LMAX] = 0; 340 bcopy(user.ut_host, p->host, HMAX); 341 p->host[HMAX] = 0; 342 p->loginat = user.ut_time; 343 p->loggedin = 1; 344 } else { /* p->loggedin == 1 */ 345 struct person *new; 346 new = (struct person *) malloc(sizeof *new); 347 new->name = p->name; 348 bcopy(user.ut_line, new->tty, LMAX); 349 new->tty[LMAX] = 0; 350 bcopy(user.ut_host, new->host, HMAX); 351 new->host[HMAX] = 0; 352 new->loginat = user.ut_time; 353 new->pwd = p->pwd; 354 new->loggedin = 1; 355 new->original = 0; 356 new->link = p->link; 357 p->loggedin = 2; 358 p->link = new; 359 p = new; 360 } 361 } 362 } 363 close(uf); 364 if (unquick) { 365 fwopen(); 366 for (p = person1; p != 0; p = p->link) 367 decode(p); 368 fwclose(); 369 } 370 } 371 372 print() 373 { 374 register FILE *fp; 375 register struct person *p; 376 register char *s; 377 register c; 378 379 /* 380 * print out what we got 381 */ 382 if (header) { 383 if (unquick) { 384 if (!unshort) 385 if (wide) 386 printf("Login Name TTY Idle When Office\n"); 387 else 388 printf("Login TTY Idle When Office\n"); 389 } else { 390 printf("Login TTY When"); 391 if (idle) 392 printf(" Idle"); 393 putchar('\n'); 394 } 395 } 396 for (p = person1; p != 0; p = p->link) { 397 if (!unquick) { 398 quickprint(p); 399 continue; 400 } 401 if (!unshort) { 402 shortprint(p); 403 continue; 404 } 405 personprint(p); 406 if (p->pwd != 0) { 407 if (hack) { 408 s = malloc(strlen(p->pwd->pw_dir) + 409 sizeof PROJ); 410 strcpy(s, p->pwd->pw_dir); 411 strcat(s, PROJ); 412 if ((fp = fopen(s, "r")) != 0) { 413 printf("Project: "); 414 while ((c = getc(fp)) != EOF) { 415 if (c == '\n') 416 break; 417 if (isprint(c) || isspace(c)) 418 putchar(c); 419 else 420 putchar(c ^ 100); 421 } 422 fclose(fp); 423 putchar('\n'); 424 } 425 free(s); 426 } 427 if (plan) { 428 s = malloc(strlen(p->pwd->pw_dir) + 429 sizeof PLAN); 430 strcpy(s, p->pwd->pw_dir); 431 strcat(s, PLAN); 432 if ((fp = fopen(s, "r")) == 0) 433 printf("No Plan.\n"); 434 else { 435 printf("Plan:\n"); 436 while ((c = getc(fp)) != EOF) 437 if (isprint(c) || isspace(c)) 438 putchar(c); 439 else 440 putchar(c ^ 100); 441 fclose(fp); 442 } 443 free(s); 444 } 445 } 446 if (p->link != 0) 447 putchar('\n'); 448 } 449 } 450 451 /* 452 * Duplicate a pwd entry. 453 * Note: Only the useful things (what the program currently uses) are copied. 454 */ 455 struct passwd * 456 pwdcopy(pfrom) 457 register struct passwd *pfrom; 458 { 459 register struct passwd *pto; 460 461 pto = (struct passwd *) malloc(sizeof *pto); 462 #define savestr(s) strcpy(malloc(strlen(s) + 1), s) 463 pto->pw_name = savestr(pfrom->pw_name); 464 pto->pw_uid = pfrom->pw_uid; 465 pto->pw_gecos = savestr(pfrom->pw_gecos); 466 pto->pw_dir = savestr(pfrom->pw_dir); 467 pto->pw_shell = savestr(pfrom->pw_shell); 468 #undef savestr 469 return pto; 470 } 471 472 /* 473 * print out information on quick format giving just name, tty, login time 474 * and idle time if idle is set. 475 */ 476 quickprint(pers) 477 register struct person *pers; 478 { 479 printf("%-*.*s ", NMAX, NMAX, pers->name); 480 if (pers->loggedin) { 481 if (idle) { 482 findidle(pers); 483 printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*', 484 LMAX, pers->tty, ctime(&pers->loginat)); 485 ltimeprint(" ", &pers->idletime, ""); 486 } else 487 printf(" %-*s %-16.16s", LMAX, 488 pers->tty, ctime(&pers->loginat)); 489 putchar('\n'); 490 } else 491 printf(" Not Logged In\n"); 492 } 493 494 /* 495 * print out information in short format, giving login name, full name, 496 * tty, idle time, login time, office location and phone. 497 */ 498 shortprint(pers) 499 register struct person *pers; 500 { 501 char *p; 502 char dialup; 503 504 if (pers->pwd == 0) { 505 printf("%-15s ???\n", pers->name); 506 return; 507 } 508 printf("%-*s", NMAX, pers->pwd->pw_name); 509 dialup = 0; 510 if (wide) { 511 if (pers->realname) 512 printf(" %-20.20s", pers->realname); 513 else 514 printf(" ??? "); 515 } 516 putchar(' '); 517 if (pers->loggedin && !pers->writable) 518 putchar('*'); 519 else 520 putchar(' '); 521 if (*pers->tty) { 522 if (pers->tty[0] == 't' && pers->tty[1] == 't' && 523 pers->tty[2] == 'y') { 524 if (pers->tty[3] == 'd' && pers->loggedin) 525 dialup = 1; 526 printf("%-2.2s ", pers->tty + 3); 527 } else 528 printf("%-2.2s ", pers->tty); 529 } else 530 printf(" "); 531 p = ctime(&pers->loginat); 532 if (pers->loggedin) { 533 stimeprint(&pers->idletime); 534 printf(" %3.3s %-5.5s ", p, p + 11); 535 } else if (pers->loginat == 0) 536 printf(" < . . . . >"); 537 else if (tloc - pers->loginat >= 180 * 24 * 60 * 60) 538 printf(" <%-6.6s, %-4.4s>", p + 4, p + 20); 539 else 540 printf(" <%-12.12s>", p + 4); 541 if (dialup && pers->homephone) 542 printf(" %20s", pers->homephone); 543 else { 544 if (pers->office) 545 printf(" %-11.11s", pers->office); 546 else if (pers->officephone || pers->homephone) 547 printf(" "); 548 if (pers->officephone) 549 printf(" %s", pers->officephone); 550 else if (pers->homephone) 551 printf(" %s", pers->homephone); 552 } 553 putchar('\n'); 554 } 555 556 /* 557 * print out a person in long format giving all possible information. 558 * directory and shell are inhibited if unbrief is clear. 559 */ 560 personprint(pers) 561 register struct person *pers; 562 { 563 if (pers->pwd == 0) { 564 printf("Login name: %-10s\t\t\tIn real life: ???\n", 565 pers->name); 566 return; 567 } 568 printf("Login name: %-10s", pers->pwd->pw_name); 569 if (pers->loggedin && !pers->writable) 570 printf(" (messages off) "); 571 else 572 printf(" "); 573 if (pers->realname) 574 printf("In real life: %s", pers->realname); 575 if (pers->office) { 576 printf("\nOffice: %-.11s", pers->office); 577 if (pers->officephone) { 578 printf(", %s", pers->officephone); 579 if (pers->homephone) 580 printf("\t\tHome phone: %s", pers->homephone); 581 else if (pers->random) 582 printf("\t\t%s", pers->random); 583 } else 584 if (pers->homephone) 585 printf("\t\t\tHome phone: %s", pers->homephone); 586 else if (pers->random) 587 printf("\t\t\t%s", pers->random); 588 } else if (pers->officephone) { 589 printf("\nPhone: %s", pers->officephone); 590 if (pers->homephone) 591 printf(", %s", pers->homephone); 592 if (pers->random) 593 printf(", %s", pers->random); 594 } else if (pers->homephone) { 595 printf("\nPhone: %s", pers->homephone); 596 if (pers->random) 597 printf(", %s", pers->random); 598 } else if (pers->random) 599 printf("\n%s", pers->random); 600 if (unbrief) { 601 printf("\nDirectory: %-25s", pers->pwd->pw_dir); 602 if (*pers->pwd->pw_shell) 603 printf("\tShell: %-s", pers->pwd->pw_shell); 604 } 605 if (pers->loggedin) { 606 register char *ep = ctime(&pers->loginat); 607 if (*pers->host) { 608 printf("\nOn since %15.15s on %s from %s", 609 &ep[4], pers->tty, pers->host); 610 ltimeprint("\n", &pers->idletime, " Idle Time"); 611 } else { 612 printf("\nOn since %15.15s on %-*s", 613 &ep[4], LMAX, pers->tty); 614 ltimeprint("\t", &pers->idletime, " Idle Time"); 615 } 616 } else if (pers->loginat == 0) 617 printf("\nNever logged in."); 618 else if (tloc - pers->loginat > 180 * 24 * 60 * 60) { 619 register char *ep = ctime(&pers->loginat); 620 printf("\nLast login %10.10s, %4.4s on %s", 621 ep, ep+20, pers->tty); 622 if (*pers->host) 623 printf(" from %s", pers->host); 624 } else { 625 register char *ep = ctime(&pers->loginat); 626 printf("\nLast login %16.16s on %s", ep, pers->tty); 627 if (*pers->host) 628 printf(" from %s", pers->host); 629 } 630 putchar('\n'); 631 } 632 633 /* 634 * very hacky section of code to format phone numbers. filled with 635 * magic constants like 4, 7 and 10. 636 */ 637 char * 638 phone(s, len, alldigits) 639 register char *s; 640 int len; 641 char alldigits; 642 { 643 char fonebuf[15]; 644 register char *p = fonebuf; 645 register i; 646 647 if (!alldigits) 648 return (strcpy(malloc(len + 1), s)); 649 switch (len) { 650 case 4: 651 *p++ = ' '; 652 *p++ = 'x'; 653 *p++ = '2'; 654 *p++ = '-'; 655 for (i = 0; i < 4; i++) 656 *p++ = *s++; 657 break; 658 case 5: 659 *p++ = ' '; 660 *p++ = 'x'; 661 *p++ = *s++; 662 *p++ = '-'; 663 for (i = 0; i < 4; i++) 664 *p++ = *s++; 665 break; 666 case 7: 667 for (i = 0; i < 3; i++) 668 *p++ = *s++; 669 *p++ = '-'; 670 for (i = 0; i < 4; i++) 671 *p++ = *s++; 672 break; 673 case 10: 674 for (i = 0; i < 3; i++) 675 *p++ = *s++; 676 *p++ = '-'; 677 for (i = 0; i < 3; i++) 678 *p++ = *s++; 679 *p++ = '-'; 680 for (i = 0; i < 4; i++) 681 *p++ = *s++; 682 break; 683 case 0: 684 return 0; 685 default: 686 return (strcpy(malloc(len + 1), s)); 687 } 688 *p++ = 0; 689 return (strcpy(malloc(p - fonebuf), fonebuf)); 690 } 691 692 /* 693 * decode the information in the gecos field of /etc/passwd 694 */ 695 decode(pers) 696 register struct person *pers; 697 { 698 char buffer[256]; 699 register char *bp, *gp, *lp; 700 int alldigits; 701 int hasspace; 702 int len; 703 704 pers->realname = 0; 705 pers->office = 0; 706 pers->officephone = 0; 707 pers->homephone = 0; 708 pers->random = 0; 709 if (pers->pwd == 0) 710 return; 711 gp = pers->pwd->pw_gecos; 712 bp = buffer; 713 if (*gp == ASTERISK) 714 gp++; 715 while (*gp && *gp != COMMA) /* name */ 716 if (*gp == SAMENAME) { 717 lp = pers->pwd->pw_name; 718 if (islower(*lp)) 719 *bp++ = toupper(*lp++); 720 while (*bp++ = *lp++) 721 ; 722 bp--; 723 gp++; 724 } else 725 *bp++ = *gp++; 726 *bp++ = 0; 727 if ((len = bp - buffer) > 1) 728 pers->realname = strcpy(malloc(len), buffer); 729 if (*gp == COMMA) { /* office */ 730 gp++; 731 hasspace = 0; 732 bp = buffer; 733 while (*gp && *gp != COMMA) { 734 *bp = *gp++; 735 if (*bp == ' ') 736 hasspace = 1; 737 /* leave 5 for Cory and Evans expansion */ 738 if (bp < buffer + sizeof buffer - 6) 739 bp++; 740 } 741 *bp = 0; 742 len = bp - buffer; 743 bp--; /* point to last character */ 744 if (hasspace || len == 0) 745 len++; 746 else if (*bp == CORY) { 747 strcpy(bp, " Cory"); 748 len += 5; 749 } else if (*bp == EVANS) { 750 strcpy(bp, " Evans"); 751 len += 6; 752 } else 753 len++; 754 if (len > 1) 755 pers->office = strcpy(malloc(len), buffer); 756 } 757 if (*gp == COMMA) { /* office phone */ 758 gp++; 759 bp = buffer; 760 alldigits = 1; 761 while (*gp && *gp != COMMA) { 762 *bp = *gp++; 763 if (!isdigit(*bp)) 764 alldigits = 0; 765 if (bp < buffer + sizeof buffer - 1) 766 bp++; 767 } 768 *bp = 0; 769 pers->officephone = phone(buffer, bp - buffer, alldigits); 770 } 771 if (*gp == COMMA) { /* home phone */ 772 gp++; 773 bp = buffer; 774 alldigits = 1; 775 while (*gp && *gp != COMMA) { 776 *bp = *gp++; 777 if (!isdigit(*bp)) 778 alldigits = 0; 779 if (bp < buffer + sizeof buffer - 1) 780 bp++; 781 } 782 *bp = 0; 783 pers->homephone = phone(buffer, bp - buffer, alldigits); 784 } 785 if (pers->loggedin) 786 findidle(pers); 787 else 788 findwhen(pers); 789 } 790 791 /* 792 * find the last log in of a user by checking the LASTLOG file. 793 * the entry is indexed by the uid, so this can only be done if 794 * the uid is known (which it isn't in quick mode) 795 */ 796 797 fwopen() 798 { 799 if ((lf = open(LASTLOG, 0)) < 0) 800 fprintf(stderr, "finger: %s open error\n", LASTLOG); 801 } 802 803 findwhen(pers) 804 register struct person *pers; 805 { 806 struct lastlog ll; 807 int i; 808 809 if (lf >= 0) { 810 lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0); 811 if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) { 812 bcopy(ll.ll_line, pers->tty, LMAX); 813 pers->tty[LMAX] = 0; 814 bcopy(ll.ll_host, pers->host, HMAX); 815 pers->host[HMAX] = 0; 816 pers->loginat = ll.ll_time; 817 } else { 818 if (i != 0) 819 fprintf(stderr, "finger: %s read error\n", 820 LASTLOG); 821 pers->tty[0] = 0; 822 pers->host[0] = 0; 823 pers->loginat = 0L; 824 } 825 } else { 826 pers->tty[0] = 0; 827 pers->host[0] = 0; 828 pers->loginat = 0L; 829 } 830 } 831 832 fwclose() 833 { 834 if (lf >= 0) 835 close(lf); 836 } 837 838 /* 839 * find the idle time of a user by doing a stat on /dev/tty??, 840 * where tty?? has been gotten from USERLOG, supposedly. 841 */ 842 findidle(pers) 843 register struct person *pers; 844 { 845 struct stat ttystatus; 846 static char buffer[20] = "/dev/"; 847 long t; 848 #define TTYLEN 5 849 850 strcpy(buffer + TTYLEN, pers->tty); 851 buffer[TTYLEN+LMAX] = 0; 852 if (stat(buffer, &ttystatus) < 0) { 853 fprintf(stderr, "finger: Can't stat %s\n", buffer); 854 exit(4); 855 } 856 time(&t); 857 if (t < ttystatus.st_atime) 858 pers->idletime = 0L; 859 else 860 pers->idletime = t - ttystatus.st_atime; 861 pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE; 862 } 863 864 /* 865 * print idle time in short format; this program always prints 4 characters; 866 * if the idle time is zero, it prints 4 blanks. 867 */ 868 stimeprint(dt) 869 long *dt; 870 { 871 register struct tm *delta; 872 873 delta = gmtime(dt); 874 if (delta->tm_yday == 0) 875 if (delta->tm_hour == 0) 876 if (delta->tm_min == 0) 877 printf(" "); 878 else 879 printf(" %2d", delta->tm_min); 880 else 881 if (delta->tm_hour >= 10) 882 printf("%3d:", delta->tm_hour); 883 else 884 printf("%1d:%02d", 885 delta->tm_hour, delta->tm_min); 886 else 887 printf("%3dd", delta->tm_yday); 888 } 889 890 /* 891 * print idle time in long format with care being taken not to pluralize 892 * 1 minutes or 1 hours or 1 days. 893 * print "prefix" first. 894 */ 895 ltimeprint(before, dt, after) 896 long *dt; 897 char *before, *after; 898 { 899 register struct tm *delta; 900 901 delta = gmtime(dt); 902 if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 && 903 delta->tm_sec <= 10) 904 return (0); 905 printf("%s", before); 906 if (delta->tm_yday >= 10) 907 printf("%d days", delta->tm_yday); 908 else if (delta->tm_yday > 0) 909 printf("%d day%s %d hour%s", 910 delta->tm_yday, delta->tm_yday == 1 ? "" : "s", 911 delta->tm_hour, delta->tm_hour == 1 ? "" : "s"); 912 else 913 if (delta->tm_hour >= 10) 914 printf("%d hours", delta->tm_hour); 915 else if (delta->tm_hour > 0) 916 printf("%d hour%s %d minute%s", 917 delta->tm_hour, delta->tm_hour == 1 ? "" : "s", 918 delta->tm_min, delta->tm_min == 1 ? "" : "s"); 919 else 920 if (delta->tm_min >= 10) 921 printf("%2d minutes", delta->tm_min); 922 else if (delta->tm_min == 0) 923 printf("%2d seconds", delta->tm_sec); 924 else 925 printf("%d minute%s %d second%s", 926 delta->tm_min, 927 delta->tm_min == 1 ? "" : "s", 928 delta->tm_sec, 929 delta->tm_sec == 1 ? "" : "s"); 930 printf("%s", after); 931 } 932 933 matchcmp(gname, login, given) 934 register char *gname; 935 char *login; 936 char *given; 937 { 938 char buffer[100]; 939 register char *bp, *lp; 940 register c; 941 942 if (*gname == ASTERISK) 943 gname++; 944 lp = 0; 945 bp = buffer; 946 for (;;) 947 switch (c = *gname++) { 948 case SAMENAME: 949 for (lp = login; bp < buffer + sizeof buffer 950 && (*bp++ = *lp++);) 951 ; 952 bp--; 953 break; 954 case ' ': 955 case COMMA: 956 case '\0': 957 *bp = 0; 958 if (namecmp(buffer, given)) 959 return (1); 960 if (c == COMMA || c == 0) 961 return (0); 962 bp = buffer; 963 break; 964 default: 965 if (bp < buffer + sizeof buffer) 966 *bp++ = c; 967 } 968 /*NOTREACHED*/ 969 } 970 971 namecmp(name1, name2) 972 register char *name1, *name2; 973 { 974 register c1, c2; 975 976 for (;;) { 977 c1 = *name1++; 978 if (islower(c1)) 979 c1 = toupper(c1); 980 c2 = *name2++; 981 if (islower(c2)) 982 c2 = toupper(c2); 983 if (c1 != c2) 984 break; 985 if (c1 == 0) 986 return (1); 987 } 988 if (!c1) { 989 for (name2--; isdigit(*name2); name2++) 990 ; 991 if (*name2 == 0) 992 return (1); 993 } else if (!c2) { 994 for (name1--; isdigit(*name1); name1++) 995 ; 996 if (*name2 == 0) 997 return (1); 998 } 999 return (0); 1000 } 1001 1002 netfinger(name) 1003 char *name; 1004 { 1005 char *host; 1006 char fname[100]; 1007 struct hostent *hp; 1008 struct servent *sp; 1009 struct sockaddr_in sin; 1010 int s; 1011 char *rindex(); 1012 register FILE *f; 1013 register int c; 1014 register int lastc; 1015 1016 if (name == NULL) 1017 return (0); 1018 host = rindex(name, '@'); 1019 if (host == NULL) 1020 return (0); 1021 *host++ = 0; 1022 hp = gethostbyname(host); 1023 if (hp == NULL) { 1024 static struct hostent def; 1025 static struct in_addr defaddr; 1026 static char *alist[1]; 1027 static char namebuf[128]; 1028 int inet_addr(); 1029 1030 defaddr.s_addr = inet_addr(host); 1031 if (defaddr.s_addr == -1) { 1032 printf("unknown host: %s\n", host); 1033 return (1); 1034 } 1035 strcpy(namebuf, host); 1036 def.h_name = namebuf; 1037 def.h_addr_list = alist, def.h_addr = (char *)&defaddr; 1038 def.h_length = sizeof (struct in_addr); 1039 def.h_addrtype = AF_INET; 1040 def.h_aliases = 0; 1041 hp = &def; 1042 } 1043 sp = getservbyname("finger", "tcp"); 1044 if (sp == 0) { 1045 printf("tcp/finger: unknown service\n"); 1046 return (1); 1047 } 1048 sin.sin_family = hp->h_addrtype; 1049 bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); 1050 sin.sin_port = sp->s_port; 1051 s = socket(hp->h_addrtype, SOCK_STREAM, 0); 1052 if (s < 0) { 1053 perror("socket"); 1054 return (1); 1055 } 1056 printf("[%s]\n", hp->h_name); 1057 fflush(stdout); 1058 if (connect(s, (char *)&sin, sizeof (sin)) < 0) { 1059 perror("connect"); 1060 close(s); 1061 return (1); 1062 } 1063 if (large) write(s, "/W ", 3); 1064 write(s, name, strlen(name)); 1065 write(s, "\r\n", 2); 1066 f = fdopen(s, "r"); 1067 while ((c = getc(f)) != EOF) { 1068 switch(c) { 1069 case 0210: 1070 case 0211: 1071 case 0212: 1072 case 0214: 1073 c -= 0200; 1074 break; 1075 case 0215: 1076 c = '\n'; 1077 break; 1078 } 1079 lastc = c; 1080 if (isprint(c) || isspace(c)) 1081 putchar(c); 1082 else 1083 putchar(c ^ 100); 1084 } 1085 if (lastc != '\n') 1086 putchar('\n'); 1087 (void)fclose(f); 1088 return (1); 1089 } 1090