xref: /dragonfly/contrib/tcsh-6/tc.who.c (revision 9f3fc534)
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