1: /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/tc.who.c,v 3.0 1991/07/04 21:49:28 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 "config.h" 38: #if !defined(lint) && !defined(pdp11) 39: static char *rcsid() 40: { return "$Id: tc.who.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; } 41: #endif 42: 43: #include "sh.h" 44: 45: /* 46: * kfk 26 Jan 1984 - for login watch functions. 47: */ 48: #include <ctype.h> 49: #include <utmp.h> 50: 51: #ifndef BROKEN_CC 52: # define UTNAMLEN sizeof(((struct utmp *) 0)->ut_name) 53: # define UTLINLEN sizeof(((struct utmp *) 0)->ut_line) 54: # ifdef UTHOST 55: # ifdef _SEQUENT_ 56: # define UTHOSTLEN 100 57: # else 58: # define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host) 59: # endif 60: # endif /* UTHOST */ 61: #else 62: /* give poor cc a little help if it needs it */ 63: struct utmp __ut; 64: 65: # define UTNAMLEN sizeof(__ut.ut_name) 66: # define UTLINLEN sizeof(__ut.ut_line) 67: # ifdef UTHOST 68: # ifdef _SEQUENT_ 69: # define UTHOSTLEN 100 70: # else 71: # define UTHOSTLEN sizeof(__ut.ut_host) 72: # endif 73: # endif /* UTHOST */ 74: #endif /* BROKEN_CC */ 75: 76: #ifndef _PATH_UTMP 77: # ifdef UTMP_FILE 78: # define _PATH_UTMP UTMP_FILE 79: # else 80: # define _PATH_UTMP "/var/run/utmp" 81: # endif /* UTMP_FILE */ 82: #endif /* _PATH_UTMP */ 83: 84: 85: struct who { 86: struct who *w_next; 87: struct who *w_prev; 88: char w_name[UTNAMLEN + 1]; 89: char w_new[UTNAMLEN + 1]; 90: char w_tty[UTLINLEN + 1]; 91: #ifdef UTHOST 92: char w_host[UTHOSTLEN + 1]; 93: #endif /* UTHOST */ 94: long w_time; 95: int w_status; 96: }; 97: 98: static struct who *wholist = NULL; 99: static int watch_period = 0; 100: static time_t stlast = 0; 101: extern char *month_list[]; 102: #ifdef WHODEBUG 103: static void debugwholist __P((struct who *, struct who *)); 104: #endif 105: static void print_who __P((struct who *)); 106: 107: 108: #define ONLINE 01 109: #define OFFLINE 02 110: #define CHANGED 04 111: #define STMASK 07 112: #define ANNOUNCE 010 113: 114: /* 115: * Karl Kleinpaste, 26 Jan 1984. 116: * Initialize the dummy tty list for login watch. 117: * This dummy list eliminates boundary conditions 118: * when doing pointer-chase searches. 119: */ 120: void 121: initwatch() 122: { 123: register int i; 124: 125: wholist = (struct who *) xcalloc(1, sizeof *wholist); 126: wholist->w_next = (struct who *) xcalloc(1, sizeof *wholist); 127: wholist->w_next->w_prev = wholist; 128: for (i = 0; i < UTLINLEN; i++) { 129: wholist->w_tty[i] = '\01'; 130: wholist->w_next->w_tty[i] = '~'; 131: } 132: wholist->w_tty[i] = '\0'; 133: wholist->w_next->w_tty[i] = '\0'; 134: 135: #ifdef WHODEBUG 136: debugwholist(NULL, NULL); 137: #endif /* WHODEBUG */ 138: } 139: 140: void 141: resetwatch() 142: { 143: watch_period = 0; 144: stlast = 0; 145: } 146: 147: /* 148: * Karl Kleinpaste, 26 Jan 1984. 149: * Watch /var/run/utmp for login/logout changes. 150: */ 151: void 152: watch_login() 153: { 154: int utmpfd, comp, alldone; 155: #ifdef BSDSIGS 156: sigmask_t omask; 157: #endif /* BSDSIGS */ 158: struct utmp utmp; 159: struct who *wp, *wpnew; 160: struct varent *v; 161: Char **vp; 162: time_t t, interval = MAILINTVL; 163: struct stat sta; 164: #if defined(UTHOST) && defined(_SEQUENT_) 165: char *host, *ut_find_host(); 166: #endif 167: 168: /* stop SIGINT, lest our login list get trashed. */ 169: #ifdef BSDSIGS 170: omask = sigblock(sigmask(SIGINT)); 171: #else 172: (void) sighold(SIGINT); 173: #endif 174: 175: v = adrof(STRwatch); 176: if (v == NULL) { 177: #ifdef BSDSIGS 178: (void) sigsetmask(omask); 179: #else 180: (void) sigrelse(SIGINT); 181: #endif 182: return; /* no names to watch */ 183: } 184: vp = v->vec; 185: if (blklen(vp) % 2) /* odd # args: 1st == # minutes. */ 186: interval = (number(*vp)) ? getn(*vp++) : MAILINTVL; 187: (void) time(&t); 188: if (t - watch_period < interval * 60) { 189: #ifdef BSDSIGS 190: (void) sigsetmask(omask); 191: #else 192: (void) sigrelse(SIGINT); 193: #endif 194: return; /* not long enough yet... */ 195: } 196: watch_period = t; 197: 198: /* 199: * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de> 200: * Don't open utmp all the time, stat it first... 201: */ 202: if (stat(_PATH_UTMP, &sta)) { 203: xprintf("cannot stat %s. Please \"unset watch\".\n", _PATH_UTMP); 204: #ifdef BSDSIGS 205: (void) sigsetmask(omask); 206: #else 207: (void) sigrelse(SIGINT); 208: #endif 209: return; 210: } 211: if (stlast == sta.st_mtime) { 212: #ifdef BSDSIGS 213: (void) sigsetmask(omask); 214: #else 215: (void) sigrelse(SIGINT); 216: #endif 217: return; 218: } 219: stlast = sta.st_mtime; 220: if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) { 221: xprintf("%s cannot be opened. Please \"unset watch\".\n", _PATH_UTMP); 222: #ifdef BSDSIGS 223: (void) sigsetmask(omask); 224: #else 225: (void) sigrelse(SIGINT); 226: #endif 227: return; 228: } 229: 230: /* 231: * xterm clears the entire utmp entry - mark everyone on the status list 232: * OFFLINE or we won't notice X "logouts" 233: */ 234: for (wp = wholist; wp != NULL; wp = wp->w_next) { 235: wp->w_status = OFFLINE; 236: wp->w_time = 0; 237: } 238: 239: /* 240: * Read in the utmp file, sort the entries, and update existing entries or 241: * add new entries to the status list. 242: */ 243: while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) { 244: 245: #ifdef DEAD_PROCESS 246: # ifndef IRIS4D 247: if (utmp.ut_type != USER_PROCESS) 248: continue; 249: # else 250: /* Why is that? Cause the utmp file is always corrupted??? */ 251: if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS) 252: continue; 253: # endif /* IRIS4D */ 254: #endif /* DEAD_PROCESS */ 255: 256: if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0') 257: continue; /* completely void entry */ 258: #ifdef DEAD_PROCESS 259: if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0') 260: continue; 261: #endif /* DEAD_PROCESS */ 262: wp = wholist; 263: while ((comp = strncmp(wp->w_tty, utmp.ut_line, UTLINLEN)) < 0) 264: wp = wp->w_next;/* find that tty! */ 265: 266: if (comp == 0) { /* found the tty... */ 267: #ifdef DEAD_PROCESS 268: if (utmp.ut_type == DEAD_PROCESS) { 269: wp->w_time = utmp.ut_time; 270: wp->w_status = OFFLINE; 271: } 272: else 273: #endif /* DEAD_PROCESS */ 274: if (utmp.ut_name[0] == '\0') { 275: wp->w_time = utmp.ut_time; 276: wp->w_status = OFFLINE; 277: } 278: else if (strncmp(utmp.ut_name, wp->w_name, UTNAMLEN) == 0) { 279: /* someone is logged in */ 280: wp->w_time = utmp.ut_time; 281: wp->w_status = 0; /* same guy */ 282: } 283: else { 284: (void) strncpy(wp->w_new, utmp.ut_name, UTNAMLEN); 285: #ifdef UTHOST 286: # ifdef _SEQUENT_ 287: host = ut_find_host(wp->w_tty); 288: if (host) 289: (void) strncpy(wp->w_host, host, UTHOSTLEN); 290: else 291: wp->w_host[0] = 0; 292: # else 293: (void) strncpy(wp->w_host, utmp.ut_host, UTHOSTLEN); 294: # endif 295: #endif /* UTHOST */ 296: wp->w_time = utmp.ut_time; 297: if (wp->w_name[0] == '\0') 298: wp->w_status = ONLINE; 299: else 300: wp->w_status = CHANGED; 301: } 302: } 303: else { /* new tty in utmp */ 304: wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 305: (void) strncpy(wpnew->w_tty, utmp.ut_line, UTLINLEN); 306: #ifdef UTHOST 307: # ifdef _SEQUENT_ 308: host = ut_find_host(wpnew->w_tty); 309: if (host) 310: (void) strncpy(wpnew->w_host, host, UTHOSTLEN); 311: else 312: wpnew->w_host[0] = 0; 313: # else 314: (void) strncpy(wpnew->w_host, utmp.ut_host, UTHOSTLEN); 315: # endif 316: #endif /* UTHOST */ 317: wpnew->w_time = utmp.ut_time; 318: #ifdef DEAD_PROCESS 319: if (utmp.ut_type == DEAD_PROCESS) 320: wpnew->w_status = OFFLINE; 321: else 322: #endif /* DEAD_PROCESS */ 323: if (utmp.ut_name[0] == '\0') 324: wpnew->w_status = OFFLINE; 325: else { 326: (void) strncpy(wpnew->w_new, utmp.ut_name, UTNAMLEN); 327: wpnew->w_status = ONLINE; 328: } 329: #ifdef WHODEBUG 330: debugwholist(wpnew, wp); 331: #endif /* WHODEBUG */ 332: 333: wpnew->w_next = wp; /* link in a new 'who' */ 334: wpnew->w_prev = wp->w_prev; 335: wpnew->w_prev->w_next = wpnew; 336: wp->w_prev = wpnew; /* linked in now */ 337: } 338: } 339: (void) close(utmpfd); 340: #if defined(UTHOST) && defined(_SEQUENT_) 341: endutent(); 342: #endif 343: 344: /* 345: * The state of all logins is now known, so we can search the user's list 346: * of watchables to print the interesting ones. 347: */ 348: for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' && 349: *(vp + 1) != NULL && **(vp + 1) != '\0'; 350: vp += 2) { /* args used in pairs... */ 351: 352: if (eq(*vp, STRany) && eq(*(vp + 1), STRany)) 353: alldone = 1; 354: 355: for (wp = wholist; wp != NULL; wp = wp->w_next) { 356: if (wp->w_status & ANNOUNCE || 357: (!eq(*vp, STRany) && 358: !eq(*vp, str2short(wp->w_name)) && 359: !eq(*vp, str2short(wp->w_new))) || 360: (!eq(*(vp + 1), str2short(wp->w_tty)) && 361: !eq(*(vp + 1), STRany))) 362: continue; /* entry doesn't qualify */ 363: /* already printed or not right one to print */ 364: 365: if (wp->w_time == 0)/* utmp entry was cleared */ 366: wp->w_time = watch_period; 367: 368: if ((wp->w_status & OFFLINE) && 369: (wp->w_name[0] != '\0')) { 370: print_who(wp); 371: wp->w_name[0] = '\0'; 372: wp->w_status |= ANNOUNCE; 373: continue; 374: } 375: if (wp->w_status & ONLINE) { 376: print_who(wp); 377: (void) strcpy(wp->w_name, wp->w_new); 378: wp->w_status |= ANNOUNCE; 379: continue; 380: } 381: if (wp->w_status & CHANGED) { 382: print_who(wp); 383: (void) strcpy(wp->w_name, wp->w_new); 384: wp->w_status |= ANNOUNCE; 385: continue; 386: } 387: } 388: } 389: #ifdef BSDSIGS 390: (void) sigsetmask(omask); 391: #else 392: (void) sigrelse(SIGINT); 393: #endif 394: } 395: 396: #ifdef WHODEBUG 397: static oid 398: debugwholist(new, wp) 399: register struct who *new, *wp; 400: { 401: register struct who *a; 402: 403: a = wholist; 404: while (a != NULL) { 405: xprintf("%s/%s -> ", a->w_name, a->w_tty); 406: a = a->w_next; 407: } 408: xprintf("NULL\n"); 409: a = wholist; 410: xprintf("backward: "); 411: while (a->w_next != NULL) 412: a = a->w_next; 413: while (a != NULL) { 414: xprintf("%s/%s -> ", a->w_name, a->w_tty); 415: a = a->w_prev; 416: } 417: xprintf("NULL\n"); 418: if (new) 419: xprintf("new: %s/%s\n", new->w_name, new->w_tty); 420: if (wp) 421: xprintf("wp: %s/%s\n", wp->w_name, wp->w_tty); 422: } 423: #endif /* WHODEBUG */ 424: 425: 426: static void 427: print_who(wp) 428: struct who *wp; 429: { 430: #ifdef UTHOST 431: char *cp = "%n has %a %l from %m."; 432: char *ptr, flg; 433: #else 434: char *cp = "%n has %a %l."; 435: #endif /* UTHOST */ 436: 437: struct varent *vp = adrof(STRwho); 438: struct tm *t; 439: char ampm = 'a'; 440: int attributes = 0; 441: 442: t = localtime(&wp->w_time); 443: if (vp && vp->vec[0]) 444: cp = short2str(vp->vec[0]); 445: 446: for (; *cp; cp++) 447: if (*cp != '%') 448: xputchar(*cp | attributes); 449: else 450: switch (*++cp) { 451: case 'n': /* user name */ 452: switch (wp->w_status & STMASK) { 453: case ONLINE: 454: case CHANGED: 455: xprintf("%a%s", attributes, wp->w_new); 456: break; 457: case OFFLINE: 458: xprintf("%a%s", attributes, wp->w_name); 459: break; 460: } 461: break; 462: case 'a': 463: switch (wp->w_status & STMASK) { 464: case ONLINE: 465: xprintf("%a%s", attributes, "logged on"); 466: break; 467: case OFFLINE: 468: xprintf("%a%s", attributes, "logged off"); 469: break; 470: case CHANGED: 471: xprintf("%areplaced %s on", attributes, wp->w_name); 472: break; 473: } 474: break; 475: case 'S': /* start standout */ 476: attributes |= STANDOUT; 477: break; 478: case 'B': /* start bold */ 479: attributes |= BOLD; 480: break; 481: case 'U': /* start underline */ 482: attributes |= UNDER; 483: break; 484: case 's': /* end standout */ 485: attributes &= ~STANDOUT; 486: break; 487: case 'b': /* end bold */ 488: attributes &= ~BOLD; 489: break; 490: case 'u': /* end underline */ 491: attributes &= ~UNDER; 492: break; 493: case 't': 494: case 'T': 495: case '@': 496: if (adrof(STRampm) || *cp != 'T') { 497: int hr = t->tm_hour; 498: 499: if (hr >= 12) { 500: if (hr > 12) 501: hr -= 12; 502: ampm = 'p'; 503: } 504: else if (hr == 0) 505: hr = 12; 506: xprintf("%a%d:%02d%cm", attributes, hr, t->tm_min, ampm); 507: } 508: else 509: xprintf("%a%d:%02d", attributes, t->tm_hour, t->tm_min); 510: break; 511: case 'w': 512: xprintf("%a%s %d", attributes, month_list[t->tm_mon], 513: t->tm_mday); 514: break; 515: case 'W': 516: xprintf("%a%02d/%02d/%02d", attributes, 517: t->tm_mon + 1, t->tm_mday, t->tm_year); 518: break; 519: case 'D': 520: xprintf("%a%02d-%02d-%02d", attributes, 521: t->tm_year, t->tm_mon + 1, t->tm_mday); 522: break; 523: case 'l': 524: xprintf("%a%s", attributes, wp->w_tty); 525: break; 526: #ifdef UTHOST 527: case 'm': 528: if (wp->w_host[0] == '\0') 529: xprintf("%alocal", attributes); 530: else 531: /* the ':' stuff is for <host>:<display>.<screen> */ 532: for (ptr = wp->w_host, flg = Isdigit(*ptr) ? '\0' : '.'; 533: *ptr != '\0' && 534: (*ptr != flg || ((ptr = strchr(ptr, ':')) != 0)); 535: ptr++) { 536: if (*ptr == ':') 537: flg = '\0'; 538: xputchar((int) 539: ((Isupper(*ptr) ? Tolower(*ptr) : *ptr) | 540: attributes)); 541: } 542: break; 543: case 'M': 544: if (wp->w_host[0] == '\0') 545: xprintf("%alocal", attributes); 546: else 547: for (ptr = wp->w_host; *ptr != '\0'; ptr++) 548: xputchar((int) 549: ((Isupper(*ptr) ? Tolower(*ptr) : *ptr) | 550: attributes)); 551: break; 552: #endif /* UTHOST */ 553: default: 554: xputchar('%' | attributes); 555: xputchar(*cp | attributes); 556: break; 557: } 558: xputchar('\n'); 559: } /* end print_who */ 560: 561: void 562: /*ARGSUSED*/ 563: dolog(v) 564: Char **v; 565: { 566: struct who *wp; 567: struct varent *vp; 568: 569: if ((vp = adrof(STRwatch)) == NULL) 570: stderror(ERR_NOWATCH); 571: blkpr(vp->vec); 572: xprintf("\n"); 573: watch_period = 0; 574: stlast = 0; 575: wp = wholist; 576: while (wp != NULL) { 577: wp->w_name[0] = '\0'; 578: wp = wp->w_next; 579: } 580: }