1 /* $Header: /src/pub/tcsh/tc.who.c,v 3.29 2000/06/09 18:33:25 kim Exp $ */ 2 /* 3 * tc.who.c: Watch logins and logouts... 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 #include "sh.h" 38 39 RCSID("$Id: tc.who.c,v 3.29 2000/06/09 18:33:25 kim Exp $") 40 41 #include "tc.h" 42 43 #ifndef HAVENOUTMP 44 /* 45 * kfk 26 Jan 1984 - for login watch functions. 46 */ 47 #include <ctype.h> 48 49 #ifdef HAVEUTMPX 50 # include <utmpx.h> 51 /* I just redefine a few words here. Changing every occurrence below 52 * seems like too much of work. All UTMP functions have equivalent 53 * UTMPX counterparts, so they can be added all here when needed. 54 * Kimmo Suominen, Oct 14 1991 55 */ 56 # ifndef _PATH_UTMP 57 # define _PATH_UTMP UTMPX_FILE 58 # endif /* _PATH_UTMP */ 59 # define utmp utmpx 60 # define ut_time ut_xtime 61 #else /* !HAVEUTMPX */ 62 # ifndef WINNT 63 # include <utmp.h> 64 # endif /* WINNT */ 65 #endif /* HAVEUTMPX */ 66 67 #ifndef BROKEN_CC 68 # define UTNAMLEN sizeof(((struct utmp *) 0)->ut_name) 69 # define UTLINLEN sizeof(((struct utmp *) 0)->ut_line) 70 # ifdef UTHOST 71 # ifdef _SEQUENT_ 72 # define UTHOSTLEN 100 73 # else 74 # define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host) 75 # endif 76 # endif /* UTHOST */ 77 #else 78 /* give poor cc a little help if it needs it */ 79 struct utmp __ut; 80 81 # define UTNAMLEN sizeof(__ut.ut_name) 82 # define UTLINLEN sizeof(__ut.ut_line) 83 # ifdef UTHOST 84 # ifdef _SEQUENT_ 85 # define UTHOSTLEN 100 86 # else 87 # define UTHOSTLEN sizeof(__ut.ut_host) 88 # endif 89 # endif /* UTHOST */ 90 #endif /* BROKEN_CC */ 91 92 #ifndef _PATH_UTMP 93 # ifdef UTMP_FILE 94 # define _PATH_UTMP UTMP_FILE 95 # else 96 # define _PATH_UTMP "/etc/utmp" 97 # endif /* UTMP_FILE */ 98 #endif /* _PATH_UTMP */ 99 100 101 struct who { 102 struct who *who_next; 103 struct who *who_prev; 104 char who_name[UTNAMLEN + 1]; 105 char who_new[UTNAMLEN + 1]; 106 char who_tty[UTLINLEN + 1]; 107 #ifdef UTHOST 108 char who_host[UTHOSTLEN + 1]; 109 #endif /* UTHOST */ 110 time_t who_time; 111 int who_status; 112 }; 113 114 static struct who whohead, whotail; 115 static time_t watch_period = 0; 116 static time_t stlast = 0; 117 #ifdef WHODEBUG 118 static void debugwholist __P((struct who *, struct who *)); 119 #endif 120 static void print_who __P((struct who *)); 121 122 123 #define ONLINE 01 124 #define OFFLINE 02 125 #define CHANGED 04 126 #define STMASK 07 127 #define ANNOUNCE 010 128 129 /* 130 * Karl Kleinpaste, 26 Jan 1984. 131 * Initialize the dummy tty list for login watch. 132 * This dummy list eliminates boundary conditions 133 * when doing pointer-chase searches. 134 */ 135 void 136 initwatch() 137 { 138 whohead.who_next = &whotail; 139 whotail.who_prev = &whohead; 140 stlast = 1; 141 #ifdef WHODEBUG 142 debugwholist(NULL, NULL); 143 #endif /* WHODEBUG */ 144 } 145 146 void 147 resetwatch() 148 { 149 watch_period = 0; 150 stlast = 0; 151 } 152 153 /* 154 * Karl Kleinpaste, 26 Jan 1984. 155 * Watch /etc/utmp for login/logout changes. 156 */ 157 void 158 watch_login(force) 159 int force; 160 { 161 int utmpfd, comp = -1, alldone; 162 int firsttime = stlast == 1; 163 #ifdef BSDSIGS 164 sigmask_t omask; 165 #endif /* BSDSIGS */ 166 struct utmp utmp; 167 struct who *wp, *wpnew; 168 struct varent *v; 169 Char **vp = NULL; 170 time_t t, interval = MAILINTVL; 171 struct stat sta; 172 #if defined(UTHOST) && defined(_SEQUENT_) 173 char *host, *ut_find_host(); 174 #endif 175 #ifdef WINNT 176 static int ncbs_posted = 0; 177 USE(utmp); 178 USE(utmpfd); 179 USE(sta); 180 USE(wpnew); 181 #endif /* WINNT */ 182 183 /* stop SIGINT, lest our login list get trashed. */ 184 #ifdef BSDSIGS 185 omask = sigblock(sigmask(SIGINT)); 186 #else 187 (void) sighold(SIGINT); 188 #endif 189 190 v = adrof(STRwatch); 191 if (v == NULL && !force) { 192 #ifdef BSDSIGS 193 (void) sigsetmask(omask); 194 #else 195 (void) sigrelse(SIGINT); 196 #endif 197 return; /* no names to watch */ 198 } 199 if (!force) { 200 trim(vp = v->vec); 201 if (blklen(vp) % 2) /* odd # args: 1st == # minutes. */ 202 interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL; 203 } 204 else 205 interval = 0; 206 207 (void) time(&t); 208 #ifdef WINNT 209 /* 210 * Since NCB_ASTATs take time, start em async at least 90 secs 211 * before we are due -amol 6/5/97 212 */ 213 if (!ncbs_posted) { 214 unsigned long tdiff = t - watch_period; 215 if (!watch_period || ((tdiff > 0) && (tdiff > (interval - 90)))) { 216 start_ncbs(vp); 217 ncbs_posted = 1; 218 } 219 } 220 #endif /* WINNT */ 221 if (t - watch_period < interval) { 222 #ifdef BSDSIGS 223 (void) sigsetmask(omask); 224 #else 225 (void) sigrelse(SIGINT); 226 #endif 227 return; /* not long enough yet... */ 228 } 229 watch_period = t; 230 #ifdef WINNT 231 ncbs_posted = 0; 232 #else /* !WINNT */ 233 234 /* 235 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de> 236 * Don't open utmp all the time, stat it first... 237 */ 238 if (stat(_PATH_UTMP, &sta)) { 239 if (!force) 240 xprintf(CGETS(26, 1, 241 "cannot stat %s. Please \"unset watch\".\n"), 242 _PATH_UTMP); 243 # ifdef BSDSIGS 244 (void) sigsetmask(omask); 245 # else 246 (void) sigrelse(SIGINT); 247 # endif 248 return; 249 } 250 if (stlast == sta.st_mtime) { 251 # ifdef BSDSIGS 252 (void) sigsetmask(omask); 253 # else 254 (void) sigrelse(SIGINT); 255 # endif 256 return; 257 } 258 stlast = sta.st_mtime; 259 if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) { 260 if (!force) 261 xprintf(CGETS(26, 2, 262 "%s cannot be opened. Please \"unset watch\".\n"), 263 _PATH_UTMP); 264 # ifdef BSDSIGS 265 (void) sigsetmask(omask); 266 # else 267 (void) sigrelse(SIGINT); 268 # endif 269 return; 270 } 271 272 /* 273 * xterm clears the entire utmp entry - mark everyone on the status list 274 * OFFLINE or we won't notice X "logouts" 275 */ 276 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 277 wp->who_status = OFFLINE; 278 wp->who_time = 0; 279 } 280 281 /* 282 * Read in the utmp file, sort the entries, and update existing entries or 283 * add new entries to the status list. 284 */ 285 while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) { 286 287 # ifdef DEAD_PROCESS 288 # ifndef IRIS4D 289 if (utmp.ut_type != USER_PROCESS) 290 continue; 291 # else 292 /* Why is that? Cause the utmp file is always corrupted??? */ 293 if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS) 294 continue; 295 # endif /* IRIS4D */ 296 # endif /* DEAD_PROCESS */ 297 298 if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0') 299 continue; /* completely void entry */ 300 # ifdef DEAD_PROCESS 301 if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0') 302 continue; 303 # endif /* DEAD_PROCESS */ 304 wp = whohead.who_next; 305 while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0) 306 wp = wp->who_next;/* find that tty! */ 307 308 if (wp->who_next && comp == 0) { /* found the tty... */ 309 # ifdef DEAD_PROCESS 310 if (utmp.ut_type == DEAD_PROCESS) { 311 wp->who_time = utmp.ut_time; 312 wp->who_status = OFFLINE; 313 } 314 else 315 # endif /* DEAD_PROCESS */ 316 if (utmp.ut_name[0] == '\0') { 317 wp->who_time = utmp.ut_time; 318 wp->who_status = OFFLINE; 319 } 320 else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) { 321 /* someone is logged in */ 322 wp->who_time = utmp.ut_time; 323 wp->who_status = 0; /* same guy */ 324 } 325 else { 326 (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN); 327 # ifdef UTHOST 328 # ifdef _SEQUENT_ 329 host = ut_find_host(wp->who_tty); 330 if (host) 331 (void) strncpy(wp->who_host, host, UTHOSTLEN); 332 else 333 wp->who_host[0] = 0; 334 # else 335 (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN); 336 # endif 337 # endif /* UTHOST */ 338 wp->who_time = utmp.ut_time; 339 if (wp->who_name[0] == '\0') 340 wp->who_status = ONLINE; 341 else 342 wp->who_status = CHANGED; 343 } 344 } 345 else { /* new tty in utmp */ 346 wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 347 (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN); 348 # ifdef UTHOST 349 # ifdef _SEQUENT_ 350 host = ut_find_host(wpnew->who_tty); 351 if (host) 352 (void) strncpy(wpnew->who_host, host, UTHOSTLEN); 353 else 354 wpnew->who_host[0] = 0; 355 # else 356 (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN); 357 # endif 358 # endif /* UTHOST */ 359 wpnew->who_time = utmp.ut_time; 360 # ifdef DEAD_PROCESS 361 if (utmp.ut_type == DEAD_PROCESS) 362 wpnew->who_status = OFFLINE; 363 else 364 # endif /* DEAD_PROCESS */ 365 if (utmp.ut_name[0] == '\0') 366 wpnew->who_status = OFFLINE; 367 else { 368 (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN); 369 wpnew->who_status = ONLINE; 370 } 371 # ifdef WHODEBUG 372 debugwholist(wpnew, wp); 373 # endif /* WHODEBUG */ 374 375 wpnew->who_next = wp; /* link in a new 'who' */ 376 wpnew->who_prev = wp->who_prev; 377 wpnew->who_prev->who_next = wpnew; 378 wp->who_prev = wpnew; /* linked in now */ 379 } 380 } 381 (void) close(utmpfd); 382 # if defined(UTHOST) && defined(_SEQUENT_) 383 endutent(); 384 # endif 385 #endif /* !WINNT */ 386 387 if (force || vp == NULL) 388 return; 389 390 /* 391 * The state of all logins is now known, so we can search the user's list 392 * of watchables to print the interesting ones. 393 */ 394 for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' && 395 *(vp + 1) != NULL && **(vp + 1) != '\0'; 396 vp += 2) { /* args used in pairs... */ 397 398 if (eq(*vp, STRany) && eq(*(vp + 1), STRany)) 399 alldone = 1; 400 401 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 402 if (wp->who_status & ANNOUNCE || 403 (!eq(STRany, vp[0]) && 404 !Gmatch(str2short(wp->who_name), vp[0]) && 405 !Gmatch(str2short(wp->who_new), vp[0])) || 406 (!Gmatch(str2short(wp->who_tty), vp[1]) && 407 !eq(STRany, vp[1]))) 408 continue; /* entry doesn't qualify */ 409 /* already printed or not right one to print */ 410 411 412 if (wp->who_time == 0)/* utmp entry was cleared */ 413 wp->who_time = watch_period; 414 415 if ((wp->who_status & OFFLINE) && 416 (wp->who_name[0] != '\0')) { 417 if (!firsttime) 418 print_who(wp); 419 wp->who_name[0] = '\0'; 420 wp->who_status |= ANNOUNCE; 421 continue; 422 } 423 if (wp->who_status & ONLINE) { 424 if (!firsttime) 425 print_who(wp); 426 (void) strcpy(wp->who_name, wp->who_new); 427 wp->who_status |= ANNOUNCE; 428 continue; 429 } 430 if (wp->who_status & CHANGED) { 431 if (!firsttime) 432 print_who(wp); 433 (void) strcpy(wp->who_name, wp->who_new); 434 wp->who_status |= ANNOUNCE; 435 continue; 436 } 437 } 438 } 439 #ifdef BSDSIGS 440 (void) sigsetmask(omask); 441 #else 442 (void) sigrelse(SIGINT); 443 #endif 444 } 445 446 #ifdef WHODEBUG 447 static void 448 debugwholist(new, wp) 449 register struct who *new, *wp; 450 { 451 register struct who *a; 452 453 a = whohead.who_next; 454 while (a->who_next != NULL) { 455 xprintf("%s/%s -> ", a->who_name, a->who_tty); 456 a = a->who_next; 457 } 458 xprintf("TAIL\n"); 459 if (a != &whotail) { 460 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n")); 461 abort(); 462 } 463 a = whotail.who_prev; 464 xprintf(CGETS(26, 4, "backward: ")); 465 while (a->who_prev != NULL) { 466 xprintf("%s/%s -> ", a->who_name, a->who_tty); 467 a = a->who_prev; 468 } 469 xprintf("HEAD\n"); 470 if (a != &whohead) { 471 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n")); 472 abort(); 473 } 474 if (new) 475 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty); 476 if (wp) 477 xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty); 478 } 479 #endif /* WHODEBUG */ 480 481 482 static void 483 print_who(wp) 484 struct who *wp; 485 { 486 #ifdef UTHOST 487 Char *cp = str2short(CGETS(26, 7, "%n has %a %l from %m.")); 488 #else 489 Char *cp = str2short(CGETS(26, 8, "%n has %a %l.")); 490 #endif /* UTHOST */ 491 struct varent *vp = adrof(STRwho); 492 Char buf[BUFSIZE]; 493 494 if (vp && vp->vec[0]) 495 cp = vp->vec[0]; 496 497 tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp); 498 for (cp = buf; *cp;) 499 xputchar(*cp++); 500 xputchar('\n'); 501 } /* end print_who */ 502 503 504 const char * 505 who_info(ptr, c, wbuf, wbufsiz) 506 ptr_t ptr; 507 int c; 508 char *wbuf; 509 size_t wbufsiz; 510 { 511 struct who *wp = (struct who *) ptr; 512 #ifdef UTHOST 513 char *wb = wbuf; 514 int flg; 515 char *pb; 516 #endif /* UTHOST */ 517 518 switch (c) { 519 case 'n': /* user name */ 520 switch (wp->who_status & STMASK) { 521 case ONLINE: 522 case CHANGED: 523 return wp->who_new; 524 case OFFLINE: 525 return wp->who_name; 526 default: 527 break; 528 } 529 break; 530 531 case 'a': 532 switch (wp->who_status & STMASK) { 533 case ONLINE: 534 return CGETS(26, 9, "logged on"); 535 case OFFLINE: 536 return CGETS(26, 10, "logged off"); 537 case CHANGED: 538 xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"), 539 wp->who_name); 540 return wbuf; 541 default: 542 break; 543 } 544 break; 545 546 #ifdef UTHOST 547 case 'm': 548 if (wp->who_host[0] == '\0') 549 return CGETS(26, 12, "local"); 550 else { 551 /* the ':' stuff is for <host>:<display>.<screen> */ 552 for (pb = wp->who_host, flg = Isdigit(*pb) ? '\0' : '.'; 553 *pb != '\0' && 554 (*pb != flg || ((pb = strchr(pb, ':')) != 0)); 555 pb++) { 556 if (*pb == ':') 557 flg = '\0'; 558 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb; 559 } 560 *wb = '\0'; 561 return wbuf; 562 } 563 564 case 'M': 565 if (wp->who_host[0] == '\0') 566 return CGETS(26, 12, "local"); 567 else { 568 for (pb = wp->who_host; *pb != '\0'; pb++) 569 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb; 570 *wb = '\0'; 571 return wbuf; 572 } 573 #endif /* UTHOST */ 574 575 case 'l': 576 return wp->who_tty; 577 578 default: 579 wbuf[0] = '%'; 580 wbuf[1] = (char) c; 581 wbuf[2] = '\0'; 582 return wbuf; 583 } 584 return NULL; 585 } 586 587 void 588 /*ARGSUSED*/ 589 dolog(v, c) 590 Char **v; 591 struct command *c; 592 { 593 struct who *wp; 594 struct varent *vp; 595 596 USE(v); 597 USE(c); 598 vp = adrof(STRwatch); /* lint insists vp isn't used unless we */ 599 if (vp == NULL) /* unless we assign it outside the if */ 600 stderror(ERR_NOWATCH); 601 resetwatch(); 602 wp = whohead.who_next; 603 while (wp->who_next != NULL) { 604 wp->who_name[0] = '\0'; 605 wp = wp->who_next; 606 } 607 } 608 609 # ifdef UTHOST 610 char * 611 utmphost() 612 { 613 char *tty = short2str(varval(STRtty)); 614 struct who *wp; 615 char *host = NULL; 616 617 watch_login(1); 618 619 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 620 if (strcmp(tty, wp->who_tty) == 0) 621 host = wp->who_host; 622 wp->who_name[0] = '\0'; 623 } 624 resetwatch(); 625 return host; 626 } 627 # endif /* UTHOST */ 628 629 #ifdef WINNT 630 void add_to_who_list(name, mach_nm) 631 char *name; 632 char *mach_nm; 633 { 634 635 struct who *wp, *wpnew; 636 int comp = -1; 637 638 wp = whohead.who_next; 639 while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0) 640 wp = wp->who_next;/* find that tty! */ 641 642 if (wp->who_next && comp == 0) { /* found the tty... */ 643 644 if (*name == '\0') { 645 wp->who_time = 0; 646 wp->who_status = OFFLINE; 647 } 648 else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) { 649 /* someone is logged in */ 650 wp->who_time = 0; 651 wp->who_status = 0; /* same guy */ 652 } 653 else { 654 (void) strncpy(wp->who_new, name, UTNAMLEN); 655 wp->who_time = 0; 656 if (wp->who_name[0] == '\0') 657 wp->who_status = ONLINE; 658 else 659 wp->who_status = CHANGED; 660 } 661 } 662 else { 663 wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 664 (void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN); 665 wpnew->who_time = 0; 666 if (*name == '\0') 667 wpnew->who_status = OFFLINE; 668 else { 669 (void) strncpy(wpnew->who_new, name, UTNAMLEN); 670 wpnew->who_status = ONLINE; 671 } 672 #ifdef WHODEBUG 673 debugwholist(wpnew, wp); 674 #endif /* WHODEBUG */ 675 676 wpnew->who_next = wp; /* link in a new 'who' */ 677 wpnew->who_prev = wp->who_prev; 678 wpnew->who_prev->who_next = wpnew; 679 wp->who_prev = wpnew; /* linked in now */ 680 } 681 } 682 #endif /* WINNT */ 683 #endif /* HAVENOUTMP */ 684