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