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