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