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