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