1 /* $Header: /src/pub/tcsh/tc.who.c,v 3.35 2002/07/01 21:12:04 christos Exp $ */
3 * tc.who.c: Watch logins and logouts...
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
35 RCSID("$Id: tc.who.c,v 3.35 2002/07/01 21:12:04 christos Exp $")
41 * kfk 26 Jan 1984 - for login watch functions.
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
53 # if defined(__UTMPX_FILE) && !defined(UTMPX_FILE)
54 # define _PATH_UTMP __UTMPX_FILE
56 # define _PATH_UTMP UTMPX_FILE
57 # endif /* __UTMPX_FILE && !UTMPX_FILE */
58 # endif /* _PATH_UTMP */
61 # define ut_time ut_tv.tv_sec
62 # define ut_name ut_user
64 # define ut_time ut_xtime
66 #else /* !HAVEUTMPX */
69 # endif /* WINNT_NATIVE */
70 #endif /* HAVEUTMPX */
73 # define UTNAMLEN sizeof(((struct utmp *) 0)->ut_name)
74 # define UTLINLEN sizeof(((struct utmp *) 0)->ut_line)
77 # define UTHOSTLEN 100
79 # define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host)
83 /* give poor cc a little help if it needs it */
86 # define UTNAMLEN sizeof(__ut.ut_name)
87 # define UTLINLEN sizeof(__ut.ut_line)
90 # define UTHOSTLEN 100
92 # define UTHOSTLEN sizeof(__ut.ut_host)
95 #endif /* BROKEN_CC */
99 # define _PATH_UTMP UTMP_FILE
101 # define _PATH_UTMP "/etc/utmp"
102 # endif /* UTMP_FILE */
103 #endif /* _PATH_UTMP */
107 struct who *who_next;
108 struct who *who_prev;
109 char who_name[UTNAMLEN + 1];
110 char who_new[UTNAMLEN + 1];
111 char who_tty[UTLINLEN + 1];
113 char who_host[UTHOSTLEN + 1];
119 static struct who whohead, whotail;
120 static time_t watch_period = 0;
121 static time_t stlast = 0;
123 static void debugwholist __P((struct who *, struct who *));
125 static void print_who __P((struct who *));
135 * Karl Kleinpaste, 26 Jan 1984.
136 * Initialize the dummy tty list for login watch.
137 * This dummy list eliminates boundary conditions
138 * when doing pointer-chase searches.
143 whohead.who_next = &whotail;
144 whotail.who_prev = &whohead;
147 debugwholist(NULL, NULL);
148 #endif /* WHODEBUG */
159 * Karl Kleinpaste, 26 Jan 1984.
160 * Watch /etc/utmp for login/logout changes.
166 int utmpfd, comp = -1, alldone;
167 int firsttime = stlast == 1;
172 struct who *wp, *wpnew;
175 time_t t, interval = MAILINTVL;
177 #if defined(UTHOST) && defined(_SEQUENT_)
178 char *host, *ut_find_host();
181 static int ncbs_posted = 0;
186 #endif /* WINNT_NATIVE */
188 /* stop SIGINT, lest our login list get trashed. */
190 omask = sigblock(sigmask(SIGINT));
192 (void) sighold(SIGINT);
196 if ((v == NULL || v->vec == NULL) && !force) {
198 (void) sigsetmask(omask);
200 (void) sigrelse(SIGINT);
202 return; /* no names to watch */
206 if (blklen(vp) % 2) /* odd # args: 1st == # minutes. */
207 interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
215 * Since NCB_ASTATs take time, start em async at least 90 secs
216 * before we are due -amol 6/5/97
219 unsigned long tdiff = t - watch_period;
220 if (!watch_period || ((tdiff > 0) && (tdiff > (interval - 90)))) {
225 #endif /* WINNT_NATIVE */
226 if (t - watch_period < interval) {
228 (void) sigsetmask(omask);
230 (void) sigrelse(SIGINT);
232 return; /* not long enough yet... */
237 #else /* !WINNT_NATIVE */
240 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
241 * Don't open utmp all the time, stat it first...
243 if (stat(_PATH_UTMP, &sta)) {
246 "cannot stat %s. Please \"unset watch\".\n"),
249 (void) sigsetmask(omask);
251 (void) sigrelse(SIGINT);
255 if (stlast == sta.st_mtime) {
257 (void) sigsetmask(omask);
259 (void) sigrelse(SIGINT);
263 stlast = sta.st_mtime;
264 if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) {
267 "%s cannot be opened. Please \"unset watch\".\n"),
270 (void) sigsetmask(omask);
272 (void) sigrelse(SIGINT);
278 * xterm clears the entire utmp entry - mark everyone on the status list
279 * OFFLINE or we won't notice X "logouts"
281 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
282 wp->who_status = OFFLINE;
287 * Read in the utmp file, sort the entries, and update existing entries or
288 * add new entries to the status list.
290 while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) {
294 if (utmp.ut_type != USER_PROCESS)
297 /* Why is that? Cause the utmp file is always corrupted??? */
298 if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
301 # endif /* DEAD_PROCESS */
303 if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
304 continue; /* completely void entry */
306 if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
308 # endif /* DEAD_PROCESS */
309 wp = whohead.who_next;
310 while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
311 wp = wp->who_next;/* find that tty! */
313 if (wp->who_next && comp == 0) { /* found the tty... */
315 if (utmp.ut_type == DEAD_PROCESS) {
316 wp->who_time = utmp.ut_time;
317 wp->who_status = OFFLINE;
320 # endif /* DEAD_PROCESS */
321 if (utmp.ut_name[0] == '\0') {
322 wp->who_time = utmp.ut_time;
323 wp->who_status = OFFLINE;
325 else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
326 /* someone is logged in */
327 wp->who_time = utmp.ut_time;
328 wp->who_status = 0; /* same guy */
331 (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
334 host = ut_find_host(wp->who_tty);
336 (void) strncpy(wp->who_host, host, UTHOSTLEN);
340 (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
343 wp->who_time = utmp.ut_time;
344 if (wp->who_name[0] == '\0')
345 wp->who_status = ONLINE;
347 wp->who_status = CHANGED;
350 else { /* new tty in utmp */
351 wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
352 (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
355 host = ut_find_host(wpnew->who_tty);
357 (void) strncpy(wpnew->who_host, host, UTHOSTLEN);
359 wpnew->who_host[0] = 0;
361 (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
364 wpnew->who_time = utmp.ut_time;
366 if (utmp.ut_type == DEAD_PROCESS)
367 wpnew->who_status = OFFLINE;
369 # endif /* DEAD_PROCESS */
370 if (utmp.ut_name[0] == '\0')
371 wpnew->who_status = OFFLINE;
373 (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
374 wpnew->who_status = ONLINE;
377 debugwholist(wpnew, wp);
378 # endif /* WHODEBUG */
380 wpnew->who_next = wp; /* link in a new 'who' */
381 wpnew->who_prev = wp->who_prev;
382 wpnew->who_prev->who_next = wpnew;
383 wp->who_prev = wpnew; /* linked in now */
386 (void) close(utmpfd);
387 # if defined(UTHOST) && defined(_SEQUENT_)
390 #endif /* !WINNT_NATIVE */
392 if (force || vp == NULL)
396 * The state of all logins is now known, so we can search the user's list
397 * of watchables to print the interesting ones.
399 for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
400 *(vp + 1) != NULL && **(vp + 1) != '\0';
401 vp += 2) { /* args used in pairs... */
403 if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
406 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
407 if (wp->who_status & ANNOUNCE ||
408 (!eq(STRany, vp[0]) &&
409 !Gmatch(str2short(wp->who_name), vp[0]) &&
410 !Gmatch(str2short(wp->who_new), vp[0])) ||
411 (!Gmatch(str2short(wp->who_tty), vp[1]) &&
413 continue; /* entry doesn't qualify */
414 /* already printed or not right one to print */
417 if (wp->who_time == 0)/* utmp entry was cleared */
418 wp->who_time = watch_period;
420 if ((wp->who_status & OFFLINE) &&
421 (wp->who_name[0] != '\0')) {
424 wp->who_name[0] = '\0';
425 wp->who_status |= ANNOUNCE;
428 if (wp->who_status & ONLINE) {
431 (void) strcpy(wp->who_name, wp->who_new);
432 wp->who_status |= ANNOUNCE;
435 if (wp->who_status & CHANGED) {
438 (void) strcpy(wp->who_name, wp->who_new);
439 wp->who_status |= ANNOUNCE;
445 (void) sigsetmask(omask);
447 (void) sigrelse(SIGINT);
453 debugwholist(new, wp)
454 register struct who *new, *wp;
456 register struct who *a;
458 a = whohead.who_next;
459 while (a->who_next != NULL) {
460 xprintf("%s/%s -> ", a->who_name, a->who_tty);
465 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
468 a = whotail.who_prev;
469 xprintf(CGETS(26, 4, "backward: "));
470 while (a->who_prev != NULL) {
471 xprintf("%s/%s -> ", a->who_name, a->who_tty);
476 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
480 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
482 xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
484 #endif /* WHODEBUG */
492 Char *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
494 Char *cp = str2short(CGETS(26, 8, "%n has %a %l."));
496 struct varent *vp = adrof(STRwho);
499 if (vp && vp->vec && vp->vec[0])
502 tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp);
506 } /* end print_who */
510 who_info(ptr, c, wbuf, wbufsiz)
516 struct who *wp = (struct who *) ptr;
524 case 'n': /* user name */
525 switch (wp->who_status & STMASK) {
537 switch (wp->who_status & STMASK) {
539 return CGETS(26, 9, "logged on");
541 return CGETS(26, 10, "logged off");
543 xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"),
553 if (wp->who_host[0] == '\0')
554 return CGETS(26, 12, "local");
556 /* the ':' stuff is for <host>:<display>.<screen> */
557 for (pb = wp->who_host, flg = Isdigit(*pb) ? '\0' : '.';
559 (*pb != flg || ((pb = strchr(pb, ':')) != 0));
563 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb;
570 if (wp->who_host[0] == '\0')
571 return CGETS(26, 12, "local");
573 for (pb = wp->who_host; *pb != '\0'; pb++)
574 *wb++ = Isupper(*pb) ? Tolower(*pb) : *pb;
603 vp = adrof(STRwatch); /* lint insists vp isn't used unless we */
604 if (vp == NULL) /* unless we assign it outside the if */
605 stderror(ERR_NOWATCH);
607 wp = whohead.who_next;
608 while (wp->who_next != NULL) {
609 wp->who_name[0] = '\0';
624 char *tty = short2str(varval(STRtty));
630 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
631 if (strcmp(tty, wp->who_tty) == 0)
633 wp->who_name[0] = '\0';
641 void add_to_who_list(name, mach_nm)
646 struct who *wp, *wpnew;
649 wp = whohead.who_next;
650 while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
651 wp = wp->who_next;/* find that tty! */
653 if (wp->who_next && comp == 0) { /* found the tty... */
657 wp->who_status = OFFLINE;
659 else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
660 /* someone is logged in */
662 wp->who_status = 0; /* same guy */
665 (void) strncpy(wp->who_new, name, UTNAMLEN);
667 if (wp->who_name[0] == '\0')
668 wp->who_status = ONLINE;
670 wp->who_status = CHANGED;
674 wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
675 (void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
678 wpnew->who_status = OFFLINE;
680 (void) strncpy(wpnew->who_new, name, UTNAMLEN);
681 wpnew->who_status = ONLINE;
684 debugwholist(wpnew, wp);
685 #endif /* WHODEBUG */
687 wpnew->who_next = wp; /* link in a new 'who' */
688 wpnew->who_prev = wp->who_prev;
689 wpnew->who_prev->who_next = wpnew;
690 wp->who_prev = wpnew; /* linked in now */
693 #endif /* WINNT_NATIVE */
694 #endif /* HAVENOUTMP */