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