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