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