tc.who.c revision 100616
11573Srgrimes/* $Header: /src/pub/tcsh/tc.who.c,v 3.35 2002/07/01 21:12:04 christos Exp $ */ 21573Srgrimes/* 31573Srgrimes * tc.who.c: Watch logins and logouts... 41573Srgrimes */ 51573Srgrimes/*- 61573Srgrimes * Copyright (c) 1980, 1991 The Regents of the University of California. 71573Srgrimes * All rights reserved. 81573Srgrimes * 91573Srgrimes * Redistribution and use in source and binary forms, with or without 101573Srgrimes * modification, are permitted provided that the following conditions 111573Srgrimes * are met: 121573Srgrimes * 1. Redistributions of source code must retain the above copyright 131573Srgrimes * notice, this list of conditions and the following disclaimer. 141573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151573Srgrimes * notice, this list of conditions and the following disclaimer in the 161573Srgrimes * documentation and/or other materials provided with the distribution. 171573Srgrimes * 3. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2950476Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311573Srgrimes * SUCH DAMAGE. 321573Srgrimes */ 3379531Sru#include "sh.h" 341573Srgrimes 351573SrgrimesRCSID("$Id: tc.who.c,v 3.35 2002/07/01 21:12:04 christos Exp $") 361573Srgrimes 3759460Sphantom#include "tc.h" 3859460Sphantom 391573Srgrimes#ifndef HAVENOUTMP 4084306Sru/* 4117226Smpp * kfk 26 Jan 1984 - for login watch functions. 4217226Smpp */ 431573Srgrimes#include <ctype.h> 441573Srgrimes 4581285Sru#ifdef HAVEUTMPX 4681285Sru# include <utmpx.h> 471573Srgrimes/* I just redefine a few words here. Changing every occurrence below 481573Srgrimes * seems like too much of work. All UTMP functions have equivalent 491573Srgrimes * UTMPX counterparts, so they can be added all here when needed. 501573Srgrimes * Kimmo Suominen, Oct 14 1991 5117226Smpp */ 5267967Sasmodai# ifndef _PATH_UTMP 5317226Smpp# if defined(__UTMPX_FILE) && !defined(UTMPX_FILE) 5417226Smpp# define _PATH_UTMP __UTMPX_FILE 5517226Smpp# else 5667967Sasmodai# define _PATH_UTMP UTMPX_FILE 5717226Smpp# endif /* __UTMPX_FILE && !UTMPX_FILE */ 581573Srgrimes# endif /* _PATH_UTMP */ 5917226Smpp# define utmp utmpx 6017226Smpp# ifdef __MVS__ 6117226Smpp# define ut_time ut_tv.tv_sec 6217226Smpp# define ut_name ut_user 6328209Sache# else 6428209Sache# define ut_time ut_xtime 6528209Sache# endif /* __MVS__ */ 6628209Sache#else /* !HAVEUTMPX */ 6728209Sache# ifndef WINNT_NATIVE 68108040Sru# include <utmp.h> 6928209Sache# endif /* WINNT_NATIVE */ 7028209Sache#endif /* HAVEUTMPX */ 7167967Sasmodai 721573Srgrimes#ifndef BROKEN_CC 7317226Smpp# define UTNAMLEN sizeof(((struct utmp *) 0)->ut_name) 7417226Smpp# define UTLINLEN sizeof(((struct utmp *) 0)->ut_line) 7517226Smpp# ifdef UTHOST 761573Srgrimes# ifdef _SEQUENT_ 7721907Swosch# define UTHOSTLEN 100 781573Srgrimes# else 79207735Sjilles# define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host) 801573Srgrimes# endif 811573Srgrimes# endif /* UTHOST */ 821573Srgrimes#else 831573Srgrimes/* give poor cc a little help if it needs it */ 8417617Smppstruct utmp __ut; 8517617Smpp 8617617Smpp# define UTNAMLEN sizeof(__ut.ut_name) 8767967Sasmodai# define UTLINLEN sizeof(__ut.ut_line) 8817617Smpp# ifdef UTHOST 891573Srgrimes# ifdef _SEQUENT_ 901573Srgrimes# define UTHOSTLEN 100 911573Srgrimes# else 9267967Sasmodai# define UTHOSTLEN sizeof(__ut.ut_host) 931573Srgrimes# endif 94# endif /* UTHOST */ 95#endif /* BROKEN_CC */ 96 97#ifndef _PATH_UTMP 98# ifdef UTMP_FILE 99# define _PATH_UTMP UTMP_FILE 100# else 101# define _PATH_UTMP "/etc/utmp" 102# endif /* UTMP_FILE */ 103#endif /* _PATH_UTMP */ 104 105 106struct who { 107 struct who *who_next; 108 struct who *who_prev; 109 char who_name[UTNAMLEN + 1]; 110 char who_new[UTNAMLEN + 1]; 111 char who_tty[UTLINLEN + 1]; 112#ifdef UTHOST 113 char who_host[UTHOSTLEN + 1]; 114#endif /* UTHOST */ 115 time_t who_time; 116 int who_status; 117}; 118 119static struct who whohead, whotail; 120static time_t watch_period = 0; 121static time_t stlast = 0; 122#ifdef WHODEBUG 123static void debugwholist __P((struct who *, struct who *)); 124#endif 125static void print_who __P((struct who *)); 126 127 128#define ONLINE 01 129#define OFFLINE 02 130#define CHANGED 04 131#define STMASK 07 132#define ANNOUNCE 010 133 134/* 135 * Karl Kleinpaste, 26 Jan 1984. 136 * Initialize the dummy tty list for login watch. 137 * This dummy list eliminates boundary conditions 138 * when doing pointer-chase searches. 139 */ 140void 141initwatch() 142{ 143 whohead.who_next = &whotail; 144 whotail.who_prev = &whohead; 145 stlast = 1; 146#ifdef WHODEBUG 147 debugwholist(NULL, NULL); 148#endif /* WHODEBUG */ 149} 150 151void 152resetwatch() 153{ 154 watch_period = 0; 155 stlast = 0; 156} 157 158/* 159 * Karl Kleinpaste, 26 Jan 1984. 160 * Watch /etc/utmp for login/logout changes. 161 */ 162void 163watch_login(force) 164 int force; 165{ 166 int utmpfd, comp = -1, alldone; 167 int firsttime = stlast == 1; 168#ifdef BSDSIGS 169 sigmask_t omask; 170#endif /* BSDSIGS */ 171 struct utmp utmp; 172 struct who *wp, *wpnew; 173 struct varent *v; 174 Char **vp = NULL; 175 time_t t, interval = MAILINTVL; 176 struct stat sta; 177#if defined(UTHOST) && defined(_SEQUENT_) 178 char *host, *ut_find_host(); 179#endif 180#ifdef WINNT_NATIVE 181 static int ncbs_posted = 0; 182 USE(utmp); 183 USE(utmpfd); 184 USE(sta); 185 USE(wpnew); 186#endif /* WINNT_NATIVE */ 187 188 /* stop SIGINT, lest our login list get trashed. */ 189#ifdef BSDSIGS 190 omask = sigblock(sigmask(SIGINT)); 191#else 192 (void) sighold(SIGINT); 193#endif 194 195 v = adrof(STRwatch); 196 if ((v == NULL || v->vec == NULL) && !force) { 197#ifdef BSDSIGS 198 (void) sigsetmask(omask); 199#else 200 (void) sigrelse(SIGINT); 201#endif 202 return; /* no names to watch */ 203 } 204 if (!force) { 205 trim(vp = v->vec); 206 if (blklen(vp) % 2) /* odd # args: 1st == # minutes. */ 207 interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL; 208 } 209 else 210 interval = 0; 211 212 (void) time(&t); 213#ifdef WINNT_NATIVE 214 /* 215 * Since NCB_ASTATs take time, start em async at least 90 secs 216 * before we are due -amol 6/5/97 217 */ 218 if (!ncbs_posted) { 219 unsigned long tdiff = t - watch_period; 220 if (!watch_period || ((tdiff > 0) && (tdiff > (interval - 90)))) { 221 start_ncbs(vp); 222 ncbs_posted = 1; 223 } 224 } 225#endif /* WINNT_NATIVE */ 226 if (t - watch_period < interval) { 227#ifdef BSDSIGS 228 (void) sigsetmask(omask); 229#else 230 (void) sigrelse(SIGINT); 231#endif 232 return; /* not long enough yet... */ 233 } 234 watch_period = t; 235#ifdef WINNT_NATIVE 236 ncbs_posted = 0; 237#else /* !WINNT_NATIVE */ 238 239 /* 240 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de> 241 * Don't open utmp all the time, stat it first... 242 */ 243 if (stat(_PATH_UTMP, &sta)) { 244 if (!force) 245 xprintf(CGETS(26, 1, 246 "cannot stat %s. Please \"unset watch\".\n"), 247 _PATH_UTMP); 248# ifdef BSDSIGS 249 (void) sigsetmask(omask); 250# else 251 (void) sigrelse(SIGINT); 252# endif 253 return; 254 } 255 if (stlast == sta.st_mtime) { 256# ifdef BSDSIGS 257 (void) sigsetmask(omask); 258# else 259 (void) sigrelse(SIGINT); 260# endif 261 return; 262 } 263 stlast = sta.st_mtime; 264 if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) { 265 if (!force) 266 xprintf(CGETS(26, 2, 267 "%s cannot be opened. Please \"unset watch\".\n"), 268 _PATH_UTMP); 269# ifdef BSDSIGS 270 (void) sigsetmask(omask); 271# else 272 (void) sigrelse(SIGINT); 273# endif 274 return; 275 } 276 277 /* 278 * xterm clears the entire utmp entry - mark everyone on the status list 279 * OFFLINE or we won't notice X "logouts" 280 */ 281 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 282 wp->who_status = OFFLINE; 283 wp->who_time = 0; 284 } 285 286 /* 287 * Read in the utmp file, sort the entries, and update existing entries or 288 * add new entries to the status list. 289 */ 290 while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) { 291 292# ifdef DEAD_PROCESS 293# ifndef IRIS4D 294 if (utmp.ut_type != USER_PROCESS) 295 continue; 296# else 297 /* Why is that? Cause the utmp file is always corrupted??? */ 298 if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS) 299 continue; 300# endif /* IRIS4D */ 301# endif /* DEAD_PROCESS */ 302 303 if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0') 304 continue; /* completely void entry */ 305# ifdef DEAD_PROCESS 306 if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0') 307 continue; 308# endif /* DEAD_PROCESS */ 309 wp = whohead.who_next; 310 while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0) 311 wp = wp->who_next;/* find that tty! */ 312 313 if (wp->who_next && comp == 0) { /* found the tty... */ 314# ifdef DEAD_PROCESS 315 if (utmp.ut_type == DEAD_PROCESS) { 316 wp->who_time = utmp.ut_time; 317 wp->who_status = OFFLINE; 318 } 319 else 320# endif /* DEAD_PROCESS */ 321 if (utmp.ut_name[0] == '\0') { 322 wp->who_time = utmp.ut_time; 323 wp->who_status = OFFLINE; 324 } 325 else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) { 326 /* someone is logged in */ 327 wp->who_time = utmp.ut_time; 328 wp->who_status = 0; /* same guy */ 329 } 330 else { 331 (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN); 332# ifdef UTHOST 333# ifdef _SEQUENT_ 334 host = ut_find_host(wp->who_tty); 335 if (host) 336 (void) strncpy(wp->who_host, host, UTHOSTLEN); 337 else 338 wp->who_host[0] = 0; 339# else 340 (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN); 341# endif 342# endif /* UTHOST */ 343 wp->who_time = utmp.ut_time; 344 if (wp->who_name[0] == '\0') 345 wp->who_status = ONLINE; 346 else 347 wp->who_status = CHANGED; 348 } 349 } 350 else { /* new tty in utmp */ 351 wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 352 (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN); 353# ifdef UTHOST 354# ifdef _SEQUENT_ 355 host = ut_find_host(wpnew->who_tty); 356 if (host) 357 (void) strncpy(wpnew->who_host, host, UTHOSTLEN); 358 else 359 wpnew->who_host[0] = 0; 360# else 361 (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN); 362# endif 363# endif /* UTHOST */ 364 wpnew->who_time = utmp.ut_time; 365# ifdef DEAD_PROCESS 366 if (utmp.ut_type == DEAD_PROCESS) 367 wpnew->who_status = OFFLINE; 368 else 369# endif /* DEAD_PROCESS */ 370 if (utmp.ut_name[0] == '\0') 371 wpnew->who_status = OFFLINE; 372 else { 373 (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN); 374 wpnew->who_status = ONLINE; 375 } 376# ifdef WHODEBUG 377 debugwholist(wpnew, wp); 378# endif /* WHODEBUG */ 379 380 wpnew->who_next = wp; /* link in a new 'who' */ 381 wpnew->who_prev = wp->who_prev; 382 wpnew->who_prev->who_next = wpnew; 383 wp->who_prev = wpnew; /* linked in now */ 384 } 385 } 386 (void) close(utmpfd); 387# if defined(UTHOST) && defined(_SEQUENT_) 388 endutent(); 389# endif 390#endif /* !WINNT_NATIVE */ 391 392 if (force || vp == NULL) 393 return; 394 395 /* 396 * The state of all logins is now known, so we can search the user's list 397 * of watchables to print the interesting ones. 398 */ 399 for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' && 400 *(vp + 1) != NULL && **(vp + 1) != '\0'; 401 vp += 2) { /* args used in pairs... */ 402 403 if (eq(*vp, STRany) && eq(*(vp + 1), STRany)) 404 alldone = 1; 405 406 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 407 if (wp->who_status & ANNOUNCE || 408 (!eq(STRany, vp[0]) && 409 !Gmatch(str2short(wp->who_name), vp[0]) && 410 !Gmatch(str2short(wp->who_new), vp[0])) || 411 (!Gmatch(str2short(wp->who_tty), vp[1]) && 412 !eq(STRany, vp[1]))) 413 continue; /* entry doesn't qualify */ 414 /* already printed or not right one to print */ 415 416 417 if (wp->who_time == 0)/* utmp entry was cleared */ 418 wp->who_time = watch_period; 419 420 if ((wp->who_status & OFFLINE) && 421 (wp->who_name[0] != '\0')) { 422 if (!firsttime) 423 print_who(wp); 424 wp->who_name[0] = '\0'; 425 wp->who_status |= ANNOUNCE; 426 continue; 427 } 428 if (wp->who_status & ONLINE) { 429 if (!firsttime) 430 print_who(wp); 431 (void) strcpy(wp->who_name, wp->who_new); 432 wp->who_status |= ANNOUNCE; 433 continue; 434 } 435 if (wp->who_status & CHANGED) { 436 if (!firsttime) 437 print_who(wp); 438 (void) strcpy(wp->who_name, wp->who_new); 439 wp->who_status |= ANNOUNCE; 440 continue; 441 } 442 } 443 } 444#ifdef BSDSIGS 445 (void) sigsetmask(omask); 446#else 447 (void) sigrelse(SIGINT); 448#endif 449} 450 451#ifdef WHODEBUG 452static void 453debugwholist(new, wp) 454 register struct who *new, *wp; 455{ 456 register struct who *a; 457 458 a = whohead.who_next; 459 while (a->who_next != NULL) { 460 xprintf("%s/%s -> ", a->who_name, a->who_tty); 461 a = a->who_next; 462 } 463 xprintf("TAIL\n"); 464 if (a != &whotail) { 465 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n")); 466 abort(); 467 } 468 a = whotail.who_prev; 469 xprintf(CGETS(26, 4, "backward: ")); 470 while (a->who_prev != NULL) { 471 xprintf("%s/%s -> ", a->who_name, a->who_tty); 472 a = a->who_prev; 473 } 474 xprintf("HEAD\n"); 475 if (a != &whohead) { 476 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n")); 477 abort(); 478 } 479 if (new) 480 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty); 481 if (wp) 482 xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty); 483} 484#endif /* WHODEBUG */ 485 486 487static void 488print_who(wp) 489 struct who *wp; 490{ 491#ifdef UTHOST 492 Char *cp = str2short(CGETS(26, 7, "%n has %a %l from %m.")); 493#else 494 Char *cp = str2short(CGETS(26, 8, "%n has %a %l.")); 495#endif /* UTHOST */ 496 struct varent *vp = adrof(STRwho); 497 Char buf[BUFSIZE]; 498 499 if (vp && vp->vec && vp->vec[0]) 500 cp = vp->vec[0]; 501 502 tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp); 503 for (cp = buf; *cp;) 504 xputchar(*cp++); 505 xputchar('\n'); 506} /* end print_who */ 507 508 509const char * 510who_info(ptr, c, wbuf, wbufsiz) 511 ptr_t ptr; 512 int c; 513 char *wbuf; 514 size_t wbufsiz; 515{ 516 struct who *wp = (struct who *) ptr; 517#ifdef UTHOST 518 char *wb = wbuf; 519 int flg; 520 char *pb; 521#endif /* UTHOST */ 522 523 switch (c) { 524 case 'n': /* user name */ 525 switch (wp->who_status & STMASK) { 526 case ONLINE: 527 case CHANGED: 528 return wp->who_new; 529 case OFFLINE: 530 return wp->who_name; 531 default: 532 break; 533 } 534 break; 535 536 case 'a': 537 switch (wp->who_status & STMASK) { 538 case ONLINE: 539 return CGETS(26, 9, "logged on"); 540 case OFFLINE: 541 return CGETS(26, 10, "logged off"); 542 case CHANGED: 543 xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"), 544 wp->who_name); 545 return wbuf; 546 default: 547 break; 548 } 549 break; 550 551#ifdef UTHOST 552 case 'm': 553 if (wp->who_host[0] == '\0') 554 return CGETS(26, 12, "local"); 555 else { 556 /* the ':' stuff is for <host>:<display>.<screen> */ 557 for (pb = wp->who_host, flg = Isdigit(*pb) ? '\0' : '.'; 558 *pb != '\0' && 559 (*pb != flg || ((pb = strchr(pb, ':')) != 0)); 560 pb++) { 561 if (*pb == ':') 562 flg = '\0'; 563 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb; 564 } 565 *wb = '\0'; 566 return wbuf; 567 } 568 569 case 'M': 570 if (wp->who_host[0] == '\0') 571 return CGETS(26, 12, "local"); 572 else { 573 for (pb = wp->who_host; *pb != '\0'; pb++) 574 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb; 575 *wb = '\0'; 576 return wbuf; 577 } 578#endif /* UTHOST */ 579 580 case 'l': 581 return wp->who_tty; 582 583 default: 584 wbuf[0] = '%'; 585 wbuf[1] = (char) c; 586 wbuf[2] = '\0'; 587 return wbuf; 588 } 589 return NULL; 590} 591 592void 593/*ARGSUSED*/ 594dolog(v, c) 595Char **v; 596struct command *c; 597{ 598 struct who *wp; 599 struct varent *vp; 600 601 USE(v); 602 USE(c); 603 vp = adrof(STRwatch); /* lint insists vp isn't used unless we */ 604 if (vp == NULL) /* unless we assign it outside the if */ 605 stderror(ERR_NOWATCH); 606 resetwatch(); 607 wp = whohead.who_next; 608 while (wp->who_next != NULL) { 609 wp->who_name[0] = '\0'; 610 wp = wp->who_next; 611 } 612} 613 614# ifdef UTHOST 615size_t 616utmphostsize() 617{ 618 return UTHOSTLEN; 619} 620 621char * 622utmphost() 623{ 624 char *tty = short2str(varval(STRtty)); 625 struct who *wp; 626 char *host = NULL; 627 628 watch_login(1); 629 630 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) { 631 if (strcmp(tty, wp->who_tty) == 0) 632 host = wp->who_host; 633 wp->who_name[0] = '\0'; 634 } 635 resetwatch(); 636 return host; 637} 638# endif /* UTHOST */ 639 640#ifdef WINNT_NATIVE 641void add_to_who_list(name, mach_nm) 642 char *name; 643 char *mach_nm; 644{ 645 646 struct who *wp, *wpnew; 647 int comp = -1; 648 649 wp = whohead.who_next; 650 while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0) 651 wp = wp->who_next;/* find that tty! */ 652 653 if (wp->who_next && comp == 0) { /* found the tty... */ 654 655 if (*name == '\0') { 656 wp->who_time = 0; 657 wp->who_status = OFFLINE; 658 } 659 else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) { 660 /* someone is logged in */ 661 wp->who_time = 0; 662 wp->who_status = 0; /* same guy */ 663 } 664 else { 665 (void) strncpy(wp->who_new, name, UTNAMLEN); 666 wp->who_time = 0; 667 if (wp->who_name[0] == '\0') 668 wp->who_status = ONLINE; 669 else 670 wp->who_status = CHANGED; 671 } 672 } 673 else { 674 wpnew = (struct who *) xcalloc(1, sizeof *wpnew); 675 (void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN); 676 wpnew->who_time = 0; 677 if (*name == '\0') 678 wpnew->who_status = OFFLINE; 679 else { 680 (void) strncpy(wpnew->who_new, name, UTNAMLEN); 681 wpnew->who_status = ONLINE; 682 } 683#ifdef WHODEBUG 684 debugwholist(wpnew, wp); 685#endif /* WHODEBUG */ 686 687 wpnew->who_next = wp; /* link in a new 'who' */ 688 wpnew->who_prev = wp->who_prev; 689 wpnew->who_prev->who_next = wpnew; 690 wp->who_prev = wpnew; /* linked in now */ 691 } 692} 693#endif /* WINNT_NATIVE */ 694#endif /* HAVENOUTMP */ 695