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 */