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