1 /* $Header: /src/pub/tcsh/tc.who.c,v 3.44 2005/03/03 23:44:45 kim 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.44 2005/03/03 23:44:45 kim 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
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
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
66 # ifdef HAVE_STRUCT_UTMP_UT_USER
67 # define ut_name ut_user
70 # define getutent getutxent
71 # define setutent setutxent
72 # define endutent endutxent
73 # endif /* HAVE_GETUTENT */
77 # endif /* WINNT_NATIVE */
78 # endif /* TCSH_PATH_UTMP */
79 #else /* !HAVE_UTMPX_H */
82 # endif /* WINNT_NATIVE */
83 #endif /* HAVE_UTMPX_H */
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
90 # define UTHOSTLEN 100
92 # define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host)
94 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
96 /* give poor cc a little help if it needs it */
99 # define UTNAMLEN sizeof(__ut.ut_name)
100 # define UTLINLEN sizeof(__ut.ut_line)
101 # ifdef HAVE_STRUCT_UTMP_UT_HOST
103 # define UTHOSTLEN 100
105 # define UTHOSTLEN sizeof(__ut.ut_host)
107 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
108 #endif /* BROKEN_CC */
110 #ifndef TCSH_PATH_UTMP
112 # define TCSH_PATH_UTMP UTMP_FILE
113 # elif defined(_PATH_UTMP)
114 # define TCSH_PATH_UTMP _PATH_UTMP
116 # define TCSH_PATH_UTMP "/etc/utmp"
117 # endif /* UTMP_FILE */
118 #endif /* TCSH_PATH_UTMP */
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 */
134 static struct who whohead, whotail;
135 static time_t watch_period = 0;
136 static time_t stlast = 0;
138 static void debugwholist __P((struct who *, struct who *));
140 static void print_who __P((struct who *));
150 * Karl Kleinpaste, 26 Jan 1984.
151 * Initialize the dummy tty list for login watch.
152 * This dummy list eliminates boundary conditions
153 * when doing pointer-chase searches.
158 whohead.who_next = &whotail;
159 whotail.who_prev = &whohead;
162 debugwholist(NULL, NULL);
163 #endif /* WHODEBUG */
174 * Karl Kleinpaste, 26 Jan 1984.
175 * Watch /etc/utmp for login/logout changes.
181 int comp = -1, alldone;
182 int firsttime = stlast == 1;
192 struct who *wp, *wpnew;
195 time_t t, interval = MAILINTVL;
197 #if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
198 char *host, *ut_find_host();
201 static int ncbs_posted = 0;
206 #endif /* WINNT_NATIVE */
208 /* stop SIGINT, lest our login list get trashed. */
210 omask = sigblock(sigmask(SIGINT));
212 (void) sighold(SIGINT);
216 if ((v == NULL || v->vec == NULL) && !force) {
218 (void) sigsetmask(omask);
220 (void) sigrelse(SIGINT);
222 return; /* no names to watch */
226 if (blklen(vp) % 2) /* odd # args: 1st == # minutes. */
227 interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
235 * Since NCB_ASTATs take time, start em async at least 90 secs
236 * before we are due -amol 6/5/97
239 unsigned long tdiff = t - watch_period;
240 if (!watch_period || ((tdiff > 0) && (tdiff > (interval - 90)))) {
245 #endif /* WINNT_NATIVE */
246 if (t - watch_period < interval) {
248 (void) sigsetmask(omask);
250 (void) sigrelse(SIGINT);
252 return; /* not long enough yet... */
257 #else /* !WINNT_NATIVE */
260 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
261 * Don't open utmp all the time, stat it first...
263 if (stat(TCSH_PATH_UTMP, &sta)) {
266 "cannot stat %s. Please \"unset watch\".\n"),
269 (void) sigsetmask(omask);
271 (void) sigrelse(SIGINT);
275 if (stlast == sta.st_mtime) {
277 (void) sigsetmask(omask);
279 (void) sigrelse(SIGINT);
283 stlast = sta.st_mtime;
287 if ((utmpfd = open(TCSH_PATH_UTMP, O_RDONLY|O_LARGEFILE)) < 0) {
290 "%s cannot be opened. Please \"unset watch\".\n"),
293 (void) sigsetmask(omask);
295 (void) sigrelse(SIGINT);
302 * xterm clears the entire utmp entry - mark everyone on the status list
303 * OFFLINE or we won't notice X "logouts"
305 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
306 wp->who_status = OFFLINE;
311 * Read in the utmp file, sort the entries, and update existing entries or
312 * add new entries to the status list.
315 while ((uptr = getutent()) != NULL) {
316 memcpy(&utmp, uptr, sizeof (utmp));
318 while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) {
323 if (utmp.ut_type != USER_PROCESS)
326 /* Why is that? Cause the utmp file is always corrupted??? */
327 if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
330 # endif /* DEAD_PROCESS */
332 if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
333 continue; /* completely void entry */
335 if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
337 # endif /* DEAD_PROCESS */
338 wp = whohead.who_next;
339 while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
340 wp = wp->who_next;/* find that tty! */
342 if (wp->who_next && comp == 0) { /* found the tty... */
344 if (utmp.ut_type == DEAD_PROCESS) {
345 wp->who_time = utmp.ut_time;
346 wp->who_status = OFFLINE;
349 # endif /* DEAD_PROCESS */
350 if (utmp.ut_name[0] == '\0') {
351 wp->who_time = utmp.ut_time;
352 wp->who_status = OFFLINE;
354 else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
355 /* someone is logged in */
356 wp->who_time = utmp.ut_time;
357 wp->who_status = 0; /* same guy */
360 (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
361 # ifdef HAVE_STRUCT_UTMP_UT_HOST
363 host = ut_find_host(wp->who_tty);
365 (void) strncpy(wp->who_host, host, UTHOSTLEN);
369 (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
371 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
372 wp->who_time = utmp.ut_time;
373 if (wp->who_name[0] == '\0')
374 wp->who_status = ONLINE;
376 wp->who_status = CHANGED;
379 else { /* new tty in utmp */
380 wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
381 (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
382 # ifdef HAVE_STRUCT_UTMP_UT_HOST
384 host = ut_find_host(wpnew->who_tty);
386 (void) strncpy(wpnew->who_host, host, UTHOSTLEN);
388 wpnew->who_host[0] = 0;
390 (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
392 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
393 wpnew->who_time = utmp.ut_time;
395 if (utmp.ut_type == DEAD_PROCESS)
396 wpnew->who_status = OFFLINE;
398 # endif /* DEAD_PROCESS */
399 if (utmp.ut_name[0] == '\0')
400 wpnew->who_status = OFFLINE;
402 (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
403 wpnew->who_status = ONLINE;
406 debugwholist(wpnew, wp);
407 # endif /* WHODEBUG */
409 wpnew->who_next = wp; /* link in a new 'who' */
410 wpnew->who_prev = wp->who_prev;
411 wpnew->who_prev->who_next = wpnew;
412 wp->who_prev = wpnew; /* linked in now */
418 (void) close(utmpfd);
420 # if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
423 #endif /* !WINNT_NATIVE */
425 if (force || vp == NULL)
429 * The state of all logins is now known, so we can search the user's list
430 * of watchables to print the interesting ones.
432 for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
433 *(vp + 1) != NULL && **(vp + 1) != '\0';
434 vp += 2) { /* args used in pairs... */
436 if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
439 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
440 if (wp->who_status & ANNOUNCE ||
441 (!eq(STRany, vp[0]) &&
442 !Gmatch(str2short(wp->who_name), vp[0]) &&
443 !Gmatch(str2short(wp->who_new), vp[0])) ||
444 (!Gmatch(str2short(wp->who_tty), vp[1]) &&
446 continue; /* entry doesn't qualify */
447 /* already printed or not right one to print */
450 if (wp->who_time == 0)/* utmp entry was cleared */
451 wp->who_time = watch_period;
453 if ((wp->who_status & OFFLINE) &&
454 (wp->who_name[0] != '\0')) {
457 wp->who_name[0] = '\0';
458 wp->who_status |= ANNOUNCE;
461 if (wp->who_status & ONLINE) {
464 (void) strcpy(wp->who_name, wp->who_new);
465 wp->who_status |= ANNOUNCE;
468 if (wp->who_status & CHANGED) {
471 (void) strcpy(wp->who_name, wp->who_new);
472 wp->who_status |= ANNOUNCE;
478 (void) sigsetmask(omask);
480 (void) sigrelse(SIGINT);
486 debugwholist(new, wp)
487 struct who *new, *wp;
491 a = whohead.who_next;
492 while (a->who_next != NULL) {
493 xprintf("%s/%s -> ", a->who_name, a->who_tty);
498 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
501 a = whotail.who_prev;
502 xprintf(CGETS(26, 4, "backward: "));
503 while (a->who_prev != NULL) {
504 xprintf("%s/%s -> ", a->who_name, a->who_tty);
509 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
513 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
515 xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
517 #endif /* WHODEBUG */
524 #ifdef HAVE_STRUCT_UTMP_UT_HOST
525 Char *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
527 Char *cp = str2short(CGETS(26, 8, "%n has %a %l."));
528 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
529 struct varent *vp = adrof(STRwho);
532 if (vp && vp->vec && vp->vec[0])
535 tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp);
539 } /* end print_who */
543 who_info(ptr, c, wbuf, wbufsiz)
549 struct who *wp = (struct who *) ptr;
550 #ifdef HAVE_STRUCT_UTMP_UT_HOST
554 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
557 case 'n': /* user name */
558 switch (wp->who_status & STMASK) {
570 switch (wp->who_status & STMASK) {
572 return CGETS(26, 9, "logged on");
574 return CGETS(26, 10, "logged off");
576 xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"),
584 #ifdef HAVE_STRUCT_UTMP_UT_HOST
586 if (wp->who_host[0] == '\0')
587 return CGETS(26, 12, "local");
589 /* the ':' stuff is for <host>:<display>.<screen> */
590 for (pb = wp->who_host,
591 flg = isdigit((unsigned char)*pb) ? '\0' : '.';
592 *pb != '\0' && (*pb != flg || ((pb = strchr(pb, ':')) != 0));
596 *wb++ = isupper((unsigned char)*pb) ?
597 tolower((unsigned char)*pb) : *pb;
604 if (wp->who_host[0] == '\0')
605 return CGETS(26, 12, "local");
607 for (pb = wp->who_host; *pb != '\0'; pb++)
608 *wb++ = isupper((unsigned char)*pb) ?
609 tolower((unsigned char)*pb) : *pb;
613 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
638 vp = adrof(STRwatch); /* lint insists vp isn't used unless we */
639 if (vp == NULL) /* unless we assign it outside the if */
640 stderror(ERR_NOWATCH);
642 wp = whohead.who_next;
643 while (wp->who_next != NULL) {
644 wp->who_name[0] = '\0';
649 # ifdef HAVE_STRUCT_UTMP_UT_HOST
659 char *tty = short2str(varval(STRtty));
665 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
666 if (strcmp(tty, wp->who_tty) == 0)
668 wp->who_name[0] = '\0';
673 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
676 void add_to_who_list(name, mach_nm)
681 struct who *wp, *wpnew;
684 wp = whohead.who_next;
685 while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
686 wp = wp->who_next;/* find that tty! */
688 if (wp->who_next && comp == 0) { /* found the tty... */
692 wp->who_status = OFFLINE;
694 else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
695 /* someone is logged in */
697 wp->who_status = 0; /* same guy */
700 (void) strncpy(wp->who_new, name, UTNAMLEN);
702 if (wp->who_name[0] == '\0')
703 wp->who_status = ONLINE;
705 wp->who_status = CHANGED;
709 wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
710 (void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
713 wpnew->who_status = OFFLINE;
715 (void) strncpy(wpnew->who_new, name, UTNAMLEN);
716 wpnew->who_status = ONLINE;
719 debugwholist(wpnew, wp);
720 #endif /* WHODEBUG */
722 wpnew->who_next = wp; /* link in a new 'who' */
723 wpnew->who_prev = wp->who_prev;
724 wpnew->who_prev->who_next = wpnew;
725 wp->who_prev = wpnew; /* linked in now */
728 #endif /* WINNT_NATIVE */
729 #endif /* HAVENOUTMP */