tc.who.c revision 59243
1/* $Header: /src/pub/tcsh/tc.who.c,v 3.28 1998/04/08 13:59:13 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 39RCSID("$Id: tc.who.c,v 3.28 1998/04/08 13:59:13 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# 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 */ 79struct 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 101struct 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 114static struct who whohead, whotail; 115static time_t watch_period = 0; 116static time_t stlast = 0; 117#ifdef WHODEBUG 118static void debugwholist __P((struct who *, struct who *)); 119#endif 120static 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 */ 135void 136initwatch() 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 146void 147resetwatch() 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 */ 157void 158watch_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 xprintf(CGETS(26, 1, "cannot stat %s. Please \"unset watch\".\n"), 240 _PATH_UTMP); 241# ifdef BSDSIGS 242 (void) sigsetmask(omask); 243# else 244 (void) sigrelse(SIGINT); 245# endif 246 return; 247 } 248 if (stlast == sta.st_mtime) { 249# ifdef BSDSIGS 250 (void) sigsetmask(omask); 251# else 252 (void) sigrelse(SIGINT); 253# endif 254 return; 255 } 256 stlast = sta.st_mtime; 257 if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) { 258 xprintf(CGETS(26, 2, "%s cannot be opened. Please \"unset watch\".\n"), 259 _PATH_UTMP); 260# ifdef BSDSIGS 261 (void) sigsetmask(omask); 262# else 263 (void) sigrelse(SIGINT); 264# endif 265 return; 266 } 267 268 /* 269 * xterm clears the entire utmp entry - mark everyone on the status list 270 * OFFLINE or we won't notice X "logouts" 271 */ 272 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 273 wp->who_status = OFFLINE; 274 wp->who_time = 0; 275 } 276 277 /* 278 * Read in the utmp file, sort the entries, and update existing entries or 279 * add new entries to the status list. 280 */ 281 while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) { 282 283# ifdef DEAD_PROCESS 284# ifndef IRIS4D 285 if (utmp.ut_type != USER_PROCESS) 286 continue; 287# else 288 /* Why is that? Cause the utmp file is always corrupted??? */ 289 if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS) 290 continue; 291# endif /* IRIS4D */ 292# endif /* DEAD_PROCESS */ 293 294 if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0') 295 continue; /* completely void entry */ 296# ifdef DEAD_PROCESS 297 if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0') 298 continue; 299# endif /* DEAD_PROCESS */ 300 wp = whohead.who_next; 301 while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0) 302 wp = wp->who_next;/* find that tty! */ 303 304 if (wp->who_next && comp == 0) { /* found the tty... */ 305# ifdef DEAD_PROCESS 306 if (utmp.ut_type == DEAD_PROCESS) { 307 wp->who_time = utmp.ut_time; 308 wp->who_status = OFFLINE; 309 } 310 else 311# endif /* DEAD_PROCESS */ 312 if (utmp.ut_name[0] == '\0') { 313 wp->who_time = utmp.ut_time; 314 wp->who_status = OFFLINE; 315 } 316 else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) { 317 /* someone is logged in */ 318 wp->who_time = utmp.ut_time; 319 wp->who_status = 0; /* same guy */ 320 } 321 else { 322 (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN); 323# ifdef UTHOST 324# ifdef _SEQUENT_ 325 host = ut_find_host(wp->who_tty); 326 if (host) 327 (void) strncpy(wp->who_host, host, UTHOSTLEN); 328 else 329 wp->who_host[0] = 0; 330# else 331 (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN); 332# endif 333# endif /* UTHOST */ 334 wp->who_time = utmp.ut_time; 335 if (wp->who_name[0] == '\0') 336 wp->who_status = ONLINE; 337 else 338 wp->who_status = CHANGED; 339 } 340 } 341 else { /* new tty in utmp */ 342 wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 343 (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN); 344# ifdef UTHOST 345# ifdef _SEQUENT_ 346 host = ut_find_host(wpnew->who_tty); 347 if (host) 348 (void) strncpy(wpnew->who_host, host, UTHOSTLEN); 349 else 350 wpnew->who_host[0] = 0; 351# else 352 (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN); 353# endif 354# endif /* UTHOST */ 355 wpnew->who_time = utmp.ut_time; 356# ifdef DEAD_PROCESS 357 if (utmp.ut_type == DEAD_PROCESS) 358 wpnew->who_status = OFFLINE; 359 else 360# endif /* DEAD_PROCESS */ 361 if (utmp.ut_name[0] == '\0') 362 wpnew->who_status = OFFLINE; 363 else { 364 (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN); 365 wpnew->who_status = ONLINE; 366 } 367# ifdef WHODEBUG 368 debugwholist(wpnew, wp); 369# endif /* WHODEBUG */ 370 371 wpnew->who_next = wp; /* link in a new 'who' */ 372 wpnew->who_prev = wp->who_prev; 373 wpnew->who_prev->who_next = wpnew; 374 wp->who_prev = wpnew; /* linked in now */ 375 } 376 } 377 (void) close(utmpfd); 378# if defined(UTHOST) && defined(_SEQUENT_) 379 endutent(); 380# endif 381#endif /* !WINNT */ 382 383 if (force || vp == NULL) 384 return; 385 386 /* 387 * The state of all logins is now known, so we can search the user's list 388 * of watchables to print the interesting ones. 389 */ 390 for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' && 391 *(vp + 1) != NULL && **(vp + 1) != '\0'; 392 vp += 2) { /* args used in pairs... */ 393 394 if (eq(*vp, STRany) && eq(*(vp + 1), STRany)) 395 alldone = 1; 396 397 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 398 if (wp->who_status & ANNOUNCE || 399 (!eq(STRany, vp[0]) && 400 !Gmatch(str2short(wp->who_name), vp[0]) && 401 !Gmatch(str2short(wp->who_new), vp[0])) || 402 (!Gmatch(str2short(wp->who_tty), vp[1]) && 403 !eq(STRany, vp[1]))) 404 continue; /* entry doesn't qualify */ 405 /* already printed or not right one to print */ 406 407 408 if (wp->who_time == 0)/* utmp entry was cleared */ 409 wp->who_time = watch_period; 410 411 if ((wp->who_status & OFFLINE) && 412 (wp->who_name[0] != '\0')) { 413 if (!firsttime) 414 print_who(wp); 415 wp->who_name[0] = '\0'; 416 wp->who_status |= ANNOUNCE; 417 continue; 418 } 419 if (wp->who_status & ONLINE) { 420 if (!firsttime) 421 print_who(wp); 422 (void) strcpy(wp->who_name, wp->who_new); 423 wp->who_status |= ANNOUNCE; 424 continue; 425 } 426 if (wp->who_status & CHANGED) { 427 if (!firsttime) 428 print_who(wp); 429 (void) strcpy(wp->who_name, wp->who_new); 430 wp->who_status |= ANNOUNCE; 431 continue; 432 } 433 } 434 } 435#ifdef BSDSIGS 436 (void) sigsetmask(omask); 437#else 438 (void) sigrelse(SIGINT); 439#endif 440} 441 442#ifdef WHODEBUG 443static void 444debugwholist(new, wp) 445 register struct who *new, *wp; 446{ 447 register struct who *a; 448 449 a = whohead.who_next; 450 while (a->who_next != NULL) { 451 xprintf("%s/%s -> ", a->who_name, a->who_tty); 452 a = a->who_next; 453 } 454 xprintf("TAIL\n"); 455 if (a != &whotail) { 456 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n")); 457 abort(); 458 } 459 a = whotail.who_prev; 460 xprintf(CGETS(26, 4, "backward: ")); 461 while (a->who_prev != NULL) { 462 xprintf("%s/%s -> ", a->who_name, a->who_tty); 463 a = a->who_prev; 464 } 465 xprintf("HEAD\n"); 466 if (a != &whohead) { 467 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n")); 468 abort(); 469 } 470 if (new) 471 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty); 472 if (wp) 473 xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty); 474} 475#endif /* WHODEBUG */ 476 477 478static void 479print_who(wp) 480 struct who *wp; 481{ 482#ifdef UTHOST 483 Char *cp = str2short(CGETS(26, 7, "%n has %a %l from %m.")); 484#else 485 Char *cp = str2short(CGETS(26, 8, "%n has %a %l.")); 486#endif /* UTHOST */ 487 struct varent *vp = adrof(STRwho); 488 Char buf[BUFSIZE]; 489 490 if (vp && vp->vec[0]) 491 cp = vp->vec[0]; 492 493 tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp); 494 for (cp = buf; *cp;) 495 xputchar(*cp++); 496 xputchar('\n'); 497} /* end print_who */ 498 499 500const char * 501who_info(ptr, c, wbuf, wbufsiz) 502 ptr_t ptr; 503 int c; 504 char *wbuf; 505 size_t wbufsiz; 506{ 507 struct who *wp = (struct who *) ptr; 508#ifdef UTHOST 509 char *wb = wbuf; 510 int flg; 511 char *pb; 512#endif /* UTHOST */ 513 514 switch (c) { 515 case 'n': /* user name */ 516 switch (wp->who_status & STMASK) { 517 case ONLINE: 518 case CHANGED: 519 return wp->who_new; 520 case OFFLINE: 521 return wp->who_name; 522 default: 523 break; 524 } 525 break; 526 527 case 'a': 528 switch (wp->who_status & STMASK) { 529 case ONLINE: 530 return CGETS(26, 9, "logged on"); 531 case OFFLINE: 532 return CGETS(26, 10, "logged off"); 533 case CHANGED: 534 xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"), 535 wp->who_name); 536 return wbuf; 537 default: 538 break; 539 } 540 break; 541 542#ifdef UTHOST 543 case 'm': 544 if (wp->who_host[0] == '\0') 545 return CGETS(26, 12, "local"); 546 else { 547 /* the ':' stuff is for <host>:<display>.<screen> */ 548 for (pb = wp->who_host, flg = Isdigit(*pb) ? '\0' : '.'; 549 *pb != '\0' && 550 (*pb != flg || ((pb = strchr(pb, ':')) != 0)); 551 pb++) { 552 if (*pb == ':') 553 flg = '\0'; 554 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb; 555 } 556 *wb = '\0'; 557 return wbuf; 558 } 559 560 case 'M': 561 if (wp->who_host[0] == '\0') 562 return CGETS(26, 12, "local"); 563 else { 564 for (pb = wp->who_host; *pb != '\0'; pb++) 565 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb; 566 *wb = '\0'; 567 return wbuf; 568 } 569#endif /* UTHOST */ 570 571 case 'l': 572 return wp->who_tty; 573 574 default: 575 wbuf[0] = '%'; 576 wbuf[1] = (char) c; 577 wbuf[2] = '\0'; 578 return wbuf; 579 } 580 return NULL; 581} 582 583void 584/*ARGSUSED*/ 585dolog(v, c) 586Char **v; 587struct command *c; 588{ 589 struct who *wp; 590 struct varent *vp; 591 592 USE(v); 593 USE(c); 594 vp = adrof(STRwatch); /* lint insists vp isn't used unless we */ 595 if (vp == NULL) /* unless we assign it outside the if */ 596 stderror(ERR_NOWATCH); 597 resetwatch(); 598 wp = whohead.who_next; 599 while (wp->who_next != NULL) { 600 wp->who_name[0] = '\0'; 601 wp = wp->who_next; 602 } 603} 604 605# ifdef UTHOST 606char * 607utmphost() 608{ 609 char *tty = short2str(varval(STRtty)); 610 struct who *wp; 611 char *host = NULL; 612 613 watch_login(1); 614 615 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 616 if (strcmp(tty, wp->who_tty) == 0) 617 host = wp->who_host; 618 wp->who_name[0] = '\0'; 619 } 620 resetwatch(); 621 return host; 622} 623# endif /* UTHOST */ 624 625#ifdef WINNT 626void add_to_who_list(name, mach_nm) 627 char *name; 628 char *mach_nm; 629{ 630 631 struct who *wp, *wpnew; 632 int comp = -1; 633 634 wp = whohead.who_next; 635 while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0) 636 wp = wp->who_next;/* find that tty! */ 637 638 if (wp->who_next && comp == 0) { /* found the tty... */ 639 640 if (*name == '\0') { 641 wp->who_time = 0; 642 wp->who_status = OFFLINE; 643 } 644 else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) { 645 /* someone is logged in */ 646 wp->who_time = 0; 647 wp->who_status = 0; /* same guy */ 648 } 649 else { 650 (void) strncpy(wp->who_new, name, UTNAMLEN); 651 wp->who_time = 0; 652 if (wp->who_name[0] == '\0') 653 wp->who_status = ONLINE; 654 else 655 wp->who_status = CHANGED; 656 } 657 } 658 else { 659 wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 660 (void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN); 661 wpnew->who_time = 0; 662 if (*name == '\0') 663 wpnew->who_status = OFFLINE; 664 else { 665 (void) strncpy(wpnew->who_new, name, UTNAMLEN); 666 wpnew->who_status = ONLINE; 667 } 668#ifdef WHODEBUG 669 debugwholist(wpnew, wp); 670#endif /* WHODEBUG */ 671 672 wpnew->who_next = wp; /* link in a new 'who' */ 673 wpnew->who_prev = wp->who_prev; 674 wpnew->who_prev->who_next = wpnew; 675 wp->who_prev = wpnew; /* linked in now */ 676 } 677} 678#endif /* WINNT */ 679#endif /* HAVENOUTMP */ 680