tc.who.c revision 69408
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 39RCSID("$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 */ 88struct 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 110struct 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 123static struct who whohead, whotail; 124static time_t watch_period = 0; 125static time_t stlast = 0; 126#ifdef WHODEBUG 127static void debugwholist __P((struct who *, struct who *)); 128#endif 129static 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 */ 144void 145initwatch() 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 155void 156resetwatch() 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 */ 166void 167watch_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 456static void 457debugwholist(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 491static void 492print_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 513const char * 514who_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 596void 597/*ARGSUSED*/ 598dolog(v, c) 599Char **v; 600struct 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 619size_t 620utmphostsize() 621{ 622 return UTHOSTLEN; 623} 624 625char * 626utmphost() 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 645void 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