source: trunk/third/tcsh/tc.who.c @ 9006

Revision 9006, 13.6 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9005, which included commits to RCS files with non-trunk default branches.
Line 
1/* $Header: /afs/dev.mit.edu/source/repository/third/tcsh/tc.who.c,v 1.1.1.1 1996-10-02 06:09:29 ghudson 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 1.1.1.1 1996-10-02 06:09:29 ghudson 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# include <utmp.h>
63#endif /* HAVEUTMPX */
64
65#ifndef BROKEN_CC
66# define UTNAMLEN       sizeof(((struct utmp *) 0)->ut_name)
67# define UTLINLEN       sizeof(((struct utmp *) 0)->ut_line)
68# ifdef UTHOST
69#  ifdef _SEQUENT_
70#   define UTHOSTLEN    100
71#  else
72#   define UTHOSTLEN    sizeof(((struct utmp *) 0)->ut_host)
73#  endif
74# endif /* UTHOST */
75#else
76/* give poor cc a little help if it needs it */
77struct utmp __ut;
78
79# define UTNAMLEN       sizeof(__ut.ut_name)
80# define UTLINLEN       sizeof(__ut.ut_line)
81# ifdef UTHOST
82#  ifdef _SEQUENT_
83#   define UTHOSTLEN    100
84#  else
85#   define UTHOSTLEN    sizeof(__ut.ut_host)
86#  endif
87# endif /* UTHOST */
88#endif /* BROKEN_CC */
89
90#ifndef _PATH_UTMP
91# ifdef UTMP_FILE
92#  define _PATH_UTMP UTMP_FILE
93# else
94#  define _PATH_UTMP "/etc/utmp"
95# endif /* UTMP_FILE */
96#endif /* _PATH_UTMP */
97
98
99struct who {
100    struct who *who_next;
101    struct who *who_prev;
102    char    who_name[UTNAMLEN + 1];
103    char    who_new[UTNAMLEN + 1];
104    char    who_tty[UTLINLEN + 1];
105#ifdef UTHOST
106    char    who_host[UTHOSTLEN + 1];
107#endif /* UTHOST */
108    time_t  who_time;
109    int     who_status;
110};
111
112static struct who whohead, whotail;
113static int watch_period = 0;
114static time_t stlast = 0;
115extern char *month_list[];
116extern char *day_list[];
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#ifdef WHODEBUG
141    debugwholist(NULL, NULL);
142#endif /* WHODEBUG */
143}
144
145void
146resetwatch()
147{
148    watch_period = 0;
149    stlast = 0;
150}
151
152/*
153 * Karl Kleinpaste, 26 Jan 1984.
154 * Watch /etc/utmp for login/logout changes.
155 */
156void
157watch_login()
158{
159    int     utmpfd, comp = -1, alldone;
160#ifdef BSDSIGS
161    sigmask_t omask;
162#endif                          /* BSDSIGS */
163    struct utmp utmp;
164    struct who *wp, *wpnew;
165    struct varent *v;
166    Char  **vp;
167    time_t  t, interval = MAILINTVL;
168    struct stat sta;
169#if defined(UTHOST) && defined(_SEQUENT_)
170    char   *host, *ut_find_host();
171#endif
172
173    /* stop SIGINT, lest our login list get trashed. */
174#ifdef BSDSIGS
175    omask = sigblock(sigmask(SIGINT));
176#else
177    (void) sighold(SIGINT);
178#endif
179
180    v = adrof(STRwatch);
181    if (v == NULL) {
182#ifdef BSDSIGS
183        (void) sigsetmask(omask);
184#else
185        (void) sigrelse(SIGINT);
186#endif
187        return;                 /* no names to watch */
188    }
189    trim(vp = v->vec);
190    if (blklen(vp) % 2)         /* odd # args: 1st == # minutes. */
191        interval = (number(*vp)) ? getn(*vp++) : MAILINTVL;
192    (void) time(&t);
193    if (t - watch_period < interval * 60) {
194#ifdef BSDSIGS
195        (void) sigsetmask(omask);
196#else
197        (void) sigrelse(SIGINT);
198#endif
199        return;                 /* not long enough yet... */
200    }
201    watch_period = t;
202
203    /*
204     * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
205     * Don't open utmp all the time, stat it first...
206     */
207    if (stat(_PATH_UTMP, &sta)) {
208        xprintf("cannot stat %s.  Please \"unset watch\".\n", _PATH_UTMP);
209#ifdef BSDSIGS
210        (void) sigsetmask(omask);
211#else
212        (void) sigrelse(SIGINT);
213#endif
214        return;
215    }
216    if (stlast == sta.st_mtime) {
217#ifdef BSDSIGS
218        (void) sigsetmask(omask);
219#else
220        (void) sigrelse(SIGINT);
221#endif
222        return;
223    }
224    stlast = sta.st_mtime;
225    if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) {
226        xprintf("%s cannot be opened.  Please \"unset watch\".\n", _PATH_UTMP);
227#ifdef BSDSIGS
228        (void) sigsetmask(omask);
229#else
230        (void) sigrelse(SIGINT);
231#endif
232        return;
233    }
234
235    /*
236     * xterm clears the entire utmp entry - mark everyone on the status list
237     * OFFLINE or we won't notice X "logouts"
238     */
239    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
240        wp->who_status = OFFLINE;
241        wp->who_time = 0;
242    }
243
244    /*
245     * Read in the utmp file, sort the entries, and update existing entries or
246     * add new entries to the status list.
247     */
248    while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) {
249
250#ifdef DEAD_PROCESS
251# ifndef IRIS4D
252        if (utmp.ut_type != USER_PROCESS)
253            continue;
254# else
255        /* Why is that? Cause the utmp file is always corrupted??? */
256        if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
257            continue;
258# endif /* IRIS4D */
259#endif /* DEAD_PROCESS */
260
261        if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
262            continue;   /* completely void entry */
263#ifdef DEAD_PROCESS
264        if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
265            continue;
266#endif /* DEAD_PROCESS */
267        wp = whohead.who_next;
268        while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
269            wp = wp->who_next;/* find that tty! */
270
271        if (wp->who_next && comp == 0) {        /* found the tty... */
272#ifdef DEAD_PROCESS
273            if (utmp.ut_type == DEAD_PROCESS) {
274                wp->who_time = utmp.ut_time;
275                wp->who_status = OFFLINE;
276            }
277            else
278#endif /* DEAD_PROCESS */
279            if (utmp.ut_name[0] == '\0') {
280                wp->who_time = utmp.ut_time;
281                wp->who_status = OFFLINE;
282            }
283            else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
284                /* someone is logged in */
285                wp->who_time = utmp.ut_time;
286                wp->who_status = 0;     /* same guy */
287            }
288            else {
289                (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
290#ifdef UTHOST
291# ifdef _SEQUENT_
292                host = ut_find_host(wp->who_tty);
293                if (host)
294                    (void) strncpy(wp->who_host, host, UTHOSTLEN);
295                else
296                    wp->who_host[0] = 0;
297# else
298                (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
299# endif
300#endif /* UTHOST */
301                wp->who_time = utmp.ut_time;
302                if (wp->who_name[0] == '\0')
303                    wp->who_status = ONLINE;
304                else
305                    wp->who_status = CHANGED;
306            }
307        }
308        else {          /* new tty in utmp */
309            wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
310            (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
311#ifdef UTHOST
312# ifdef _SEQUENT_
313            host = ut_find_host(wpnew->who_tty);
314            if (host)
315                (void) strncpy(wpnew->who_host, host, UTHOSTLEN);
316            else
317                wpnew->who_host[0] = 0;
318# else
319            (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
320# endif
321#endif /* UTHOST */
322            wpnew->who_time = utmp.ut_time;
323#ifdef DEAD_PROCESS
324            if (utmp.ut_type == DEAD_PROCESS)
325                wpnew->who_status = OFFLINE;
326            else
327#endif /* DEAD_PROCESS */
328            if (utmp.ut_name[0] == '\0')
329                wpnew->who_status = OFFLINE;
330            else {
331                (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
332                wpnew->who_status = ONLINE;
333            }
334#ifdef WHODEBUG
335            debugwholist(wpnew, wp);
336#endif /* WHODEBUG */
337
338            wpnew->who_next = wp;       /* link in a new 'who' */
339            wpnew->who_prev = wp->who_prev;
340            wpnew->who_prev->who_next = wpnew;
341            wp->who_prev = wpnew;       /* linked in now */
342        }
343    }
344    (void) close(utmpfd);
345#if defined(UTHOST) && defined(_SEQUENT_)
346    endutent();
347#endif
348
349    /*
350     * The state of all logins is now known, so we can search the user's list
351     * of watchables to print the interesting ones.
352     */
353    for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
354         *(vp + 1) != NULL && **(vp + 1) != '\0';
355         vp += 2) {             /* args used in pairs... */
356
357        if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
358            alldone = 1;
359
360        for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
361            if (wp->who_status & ANNOUNCE ||
362                (!eq(STRany, vp[0]) &&
363                 !Gmatch(str2short(wp->who_name), vp[0]) &&
364                 !Gmatch(str2short(wp->who_new),  vp[0])) ||
365                (!Gmatch(str2short(wp->who_tty),  vp[1]) &&
366                 !eq(STRany, vp[1])))
367                continue;       /* entry doesn't qualify */
368            /* already printed or not right one to print */
369
370            if (wp->who_time == 0)/* utmp entry was cleared */
371                wp->who_time = watch_period;
372
373            if ((wp->who_status & OFFLINE) &&
374                (wp->who_name[0] != '\0')) {
375                print_who(wp);
376                wp->who_name[0] = '\0';
377                wp->who_status |= ANNOUNCE;
378                continue;
379            }
380            if (wp->who_status & ONLINE) {
381                print_who(wp);
382                (void) strcpy(wp->who_name, wp->who_new);
383                wp->who_status |= ANNOUNCE;
384                continue;
385            }
386            if (wp->who_status & CHANGED) {
387                print_who(wp);
388                (void) strcpy(wp->who_name, wp->who_new);
389                wp->who_status |= ANNOUNCE;
390                continue;
391            }
392        }
393    }
394#ifdef BSDSIGS
395    (void) sigsetmask(omask);
396#else
397    (void) sigrelse(SIGINT);
398#endif
399}
400
401#ifdef WHODEBUG
402static void
403debugwholist(new, wp)
404    register struct who *new, *wp;
405{
406    register struct who *a;
407
408    a = whohead.who_next;
409    while (a->who_next != NULL) {
410        xprintf("%s/%s -> ", a->who_name, a->who_tty);
411        a = a->who_next;
412    }
413    xprintf("TAIL\n");
414    if (a != &whotail) {
415        xprintf("BUG! last element is not whotail!\n");
416        abort();
417    }
418    a = whotail.who_prev;
419    xprintf("backward: ");
420    while (a->who_prev != NULL) {
421        xprintf("%s/%s -> ", a->who_name, a->who_tty);
422        a = a->who_prev;
423    }
424    xprintf("HEAD\n");
425    if (a != &whohead) {
426        xprintf("BUG! first element is not whohead!\n");
427        abort();
428    }
429    if (new)
430        xprintf("new: %s/%s\n", new->who_name, new->who_tty);
431    if (wp)
432        xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
433}
434#endif /* WHODEBUG */
435
436
437static void
438print_who(wp)
439    struct who *wp;
440{
441#ifdef UTHOST
442    Char   *cp = str2short("%n has %a %l from %m.");
443#else
444    Char   *cp = str2short("%n has %a %l.");
445#endif /* UTHOST */
446    struct varent *vp = adrof(STRwho);
447    Char buf[BUFSIZE];
448
449    if (vp && vp->vec[0])
450        cp = vp->vec[0];
451
452    tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp);
453    for (cp = buf; *cp;)
454        xputchar(*cp++);
455    xputchar('\n');
456} /* end print_who */
457
458
459char *
460who_info(ptr, c, wbuf)
461    ptr_t ptr;
462    int c;
463    char *wbuf;
464{
465    struct who *wp = (struct who *) ptr;
466#ifdef UTHOST
467    char *wb = wbuf;
468    int flg;
469    char *pb;
470#endif /* UTHOST */
471
472    switch (c) {
473    case 'n':           /* user name */
474        switch (wp->who_status & STMASK) {
475        case ONLINE:
476        case CHANGED:
477            return wp->who_new;
478        case OFFLINE:
479            return wp->who_name;
480        default:
481            break;
482        }
483        break;
484
485    case 'a':
486        switch (wp->who_status & STMASK) {
487        case ONLINE:
488            return "logged on";
489        case OFFLINE:
490            return "logged off";
491        case CHANGED:
492            xsprintf(wbuf, "replaced %s on", wp->who_name);
493            return wbuf;
494        default:
495            break;
496        }
497        break;
498
499#ifdef UTHOST
500    case 'm':
501        if (wp->who_host[0] == '\0')
502            return "local";
503        else {
504            /* the ':' stuff is for <host>:<display>.<screen> */
505            for (pb = wp->who_host, flg = Isdigit(*pb) ? '\0' : '.';
506                 *pb != '\0' &&
507                 (*pb != flg || ((pb = strchr(pb, ':')) != 0));
508                 pb++) {
509                if (*pb == ':')
510                    flg = '\0';
511                *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb;
512            }
513            *wb = '\0';
514            return wbuf;
515        }
516
517    case 'M':
518        if (wp->who_host[0] == '\0')
519            return "local";
520        else {
521            for (pb = wp->who_host; *pb != '\0'; pb++)
522                *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb;
523            *wb = '\0';
524            return wbuf;
525        }
526#endif /* UTHOST */
527
528    case 'l':
529        return wp->who_tty;
530
531    default:
532        wbuf[0] = '%';
533        wbuf[1] = c;
534        wbuf[2] = '\0';
535        return wbuf;
536    }
537    return NULL;
538}
539
540void
541/*ARGSUSED*/
542dolog(v, c)
543Char **v;
544struct command *c;
545{
546    struct who *wp;
547    struct varent *vp;
548
549    if ((vp = adrof(STRwatch)) == NULL)
550        stderror(ERR_NOWATCH);
551    blkpr(vp->vec);
552    xputchar('\n');
553    resetwatch();
554    wp = whohead.who_next;
555    while (wp->who_next != NULL) {
556        wp->who_name[0] = '\0';
557        wp = wp->who_next;
558    }
559}
560#endif /* HAVENOUTMP */
Note: See TracBrowser for help on using the repository browser.