1 /* $Header: /p/tcsh/cvsroot/tcsh/tc.who.c,v 3.51 2006/03/03 22:08:45 amold 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("$tcsh: tc.who.c,v 3.51 2006/03/03 22:08:45 amold 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 (struct who
*, struct who
*);
140 static void print_who (struct who
*);
151 * Karl Kleinpaste, 26 Jan 1984.
152 * Initialize the dummy tty list for login watch.
153 * This dummy list eliminates boundary conditions
154 * when doing pointer-chase searches.
159 whohead
.who_next
= &whotail
;
160 whotail
.who_prev
= &whohead
;
163 debugwholist(NULL
, NULL
);
164 #endif /* WHODEBUG */
175 * Karl Kleinpaste, 26 Jan 1984.
176 * Watch /etc/utmp for login/logout changes.
179 watch_login(int force
)
181 int comp
= -1, alldone
;
182 int firsttime
= stlast
== 1;
189 struct who
*wp
, *wpnew
;
192 time_t t
, interval
= MAILINTVL
;
194 #if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
195 char *host
, *ut_find_host();
198 static int ncbs_posted
= 0;
203 #endif /* WINNT_NATIVE */
205 /* stop SIGINT, lest our login list get trashed. */
207 cleanup_push(&pintr_disabled
, disabled_cleanup
);
210 if ((v
== NULL
|| v
->vec
== NULL
) && !force
) {
211 cleanup_until(&pintr_disabled
);
212 return; /* no names to watch */
216 if (blklen(vp
) % 2) /* odd # args: 1st == # minutes. */
217 interval
= (number(*vp
)) ? (getn(*vp
++) * 60) : MAILINTVL
;
225 * Since NCB_ASTATs take time, start em async at least 90 secs
226 * before we are due -amol 6/5/97
229 time_t tdiff
= t
- watch_period
;
230 if (!watch_period
|| ((tdiff
> 0) && (tdiff
> (interval
- 90)))) {
235 #endif /* WINNT_NATIVE */
236 if (t
- watch_period
< interval
) {
237 cleanup_until(&pintr_disabled
);
238 return; /* not long enough yet... */
243 #else /* !WINNT_NATIVE */
246 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
247 * Don't open utmp all the time, stat it first...
249 if (stat(TCSH_PATH_UTMP
, &sta
)) {
252 "cannot stat %s. Please \"unset watch\".\n"),
254 cleanup_until(&pintr_disabled
);
257 if (stlast
== sta
.st_mtime
) {
258 cleanup_until(&pintr_disabled
);
261 stlast
= sta
.st_mtime
;
265 if ((utmpfd
= xopen(TCSH_PATH_UTMP
, O_RDONLY
|O_LARGEFILE
)) < 0) {
268 "%s cannot be opened. Please \"unset watch\".\n"),
270 cleanup_until(&pintr_disabled
);
273 cleanup_push(&utmpfd
, open_cleanup
);
277 * xterm clears the entire utmp entry - mark everyone on the status list
278 * OFFLINE or we won't notice X "logouts"
280 for (wp
= whohead
.who_next
; wp
->who_next
!= NULL
; wp
= wp
->who_next
)
281 wp
->who_status
= OFFLINE
| CLEARED
;
284 * Read in the utmp file, sort the entries, and update existing entries or
285 * add new entries to the status list.
288 while ((uptr
= getutent()) != NULL
) {
289 memcpy(&utmp
, uptr
, sizeof (utmp
));
291 while (xread(utmpfd
, &utmp
, sizeof utmp
) == sizeof utmp
) {
296 if (utmp
.ut_type
!= USER_PROCESS
)
299 /* Why is that? Cause the utmp file is always corrupted??? */
300 if (utmp
.ut_type
!= USER_PROCESS
&& utmp
.ut_type
!= DEAD_PROCESS
)
303 # endif /* DEAD_PROCESS */
305 if (utmp
.ut_name
[0] == '\0' && utmp
.ut_line
[0] == '\0')
306 continue; /* completely void entry */
308 if (utmp
.ut_type
== DEAD_PROCESS
&& utmp
.ut_line
[0] == '\0')
310 # endif /* DEAD_PROCESS */
311 wp
= whohead
.who_next
;
312 while (wp
->who_next
&& (comp
= strncmp(wp
->who_tty
, utmp
.ut_line
, UTLINLEN
)) < 0)
313 wp
= wp
->who_next
;/* find that tty! */
315 if (wp
->who_next
&& comp
== 0) { /* found the tty... */
316 if (utmp
.ut_time
< wp
->who_time
)
319 if (utmp
.ut_type
== DEAD_PROCESS
) {
320 wp
->who_time
= utmp
.ut_time
;
321 wp
->who_status
= OFFLINE
;
324 # endif /* DEAD_PROCESS */
325 if (utmp
.ut_name
[0] == '\0') {
326 wp
->who_time
= utmp
.ut_time
;
327 wp
->who_status
= OFFLINE
;
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
= ONLINE
| ANNOUNCE
; /* same guy */
335 (void) strncpy(wp
->who_new
, utmp
.ut_name
, UTNAMLEN
);
336 # ifdef HAVE_STRUCT_UTMP_UT_HOST
338 host
= ut_find_host(wp
->who_tty
);
340 (void) strncpy(wp
->who_host
, host
, UTHOSTLEN
);
344 (void) strncpy(wp
->who_host
, utmp
.ut_host
, UTHOSTLEN
);
346 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
347 wp
->who_time
= utmp
.ut_time
;
348 if (wp
->who_name
[0] == '\0')
349 wp
->who_status
= ONLINE
;
351 wp
->who_status
= CHANGED
;
354 else { /* new tty in utmp */
355 wpnew
= xcalloc(1, sizeof *wpnew
);
356 (void) strncpy(wpnew
->who_tty
, utmp
.ut_line
, UTLINLEN
);
357 # ifdef HAVE_STRUCT_UTMP_UT_HOST
359 host
= ut_find_host(wpnew
->who_tty
);
361 (void) strncpy(wpnew
->who_host
, host
, UTHOSTLEN
);
363 wpnew
->who_host
[0] = 0;
365 (void) strncpy(wpnew
->who_host
, utmp
.ut_host
, UTHOSTLEN
);
367 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
368 wpnew
->who_time
= utmp
.ut_time
;
370 if (utmp
.ut_type
== DEAD_PROCESS
)
371 wpnew
->who_status
= OFFLINE
;
373 # endif /* DEAD_PROCESS */
374 if (utmp
.ut_name
[0] == '\0')
375 wpnew
->who_status
= OFFLINE
;
377 (void) strncpy(wpnew
->who_new
, utmp
.ut_name
, UTNAMLEN
);
378 wpnew
->who_status
= ONLINE
;
381 debugwholist(wpnew
, wp
);
382 # endif /* WHODEBUG */
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 */
393 cleanup_until(&utmpfd
);
395 # if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
398 #endif /* !WINNT_NATIVE */
400 if (force
|| vp
== NULL
) {
401 cleanup_until(&pintr_disabled
);
406 * The state of all logins is now known, so we can search the user's list
407 * of watchables to print the interesting ones.
409 for (alldone
= 0; !alldone
&& *vp
!= NULL
&& **vp
!= '\0' &&
410 *(vp
+ 1) != NULL
&& **(vp
+ 1) != '\0';
411 vp
+= 2) { /* args used in pairs... */
413 if (eq(*vp
, STRany
) && eq(*(vp
+ 1), STRany
))
416 for (wp
= whohead
.who_next
; wp
->who_next
!= NULL
; wp
= wp
->who_next
) {
417 if (wp
->who_status
& ANNOUNCE
||
418 (!eq(STRany
, vp
[0]) &&
419 !Gmatch(str2short(wp
->who_name
), vp
[0]) &&
420 !Gmatch(str2short(wp
->who_new
), vp
[0])) ||
421 (!Gmatch(str2short(wp
->who_tty
), vp
[1]) &&
423 continue; /* entry doesn't qualify */
424 /* already printed or not right one to print */
427 if (wp
->who_status
& CLEARED
) {/* utmp entry was cleared */
428 wp
->who_time
= watch_period
;
429 wp
->who_status
&= ~CLEARED
;
432 if ((wp
->who_status
& OFFLINE
) &&
433 (wp
->who_name
[0] != '\0')) {
436 wp
->who_name
[0] = '\0';
437 wp
->who_status
|= ANNOUNCE
;
440 if (wp
->who_status
& ONLINE
) {
443 (void) strcpy(wp
->who_name
, wp
->who_new
);
444 wp
->who_status
|= ANNOUNCE
;
447 if (wp
->who_status
& CHANGED
) {
450 (void) strcpy(wp
->who_name
, wp
->who_new
);
451 wp
->who_status
|= ANNOUNCE
;
456 cleanup_until(&pintr_disabled
);
461 debugwholist(struct who
*new, struct who
*wp
)
465 a
= whohead
.who_next
;
466 while (a
->who_next
!= NULL
) {
467 xprintf("%s/%s -> ", a
->who_name
, a
->who_tty
);
472 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
475 a
= whotail
.who_prev
;
476 xprintf(CGETS(26, 4, "backward: "));
477 while (a
->who_prev
!= NULL
) {
478 xprintf("%s/%s -> ", a
->who_name
, a
->who_tty
);
483 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
487 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name
, new->who_tty
);
489 xprintf("wp: %s/%s\n", wp
->who_name
, wp
->who_tty
);
491 #endif /* WHODEBUG */
495 print_who(struct who
*wp
)
497 #ifdef HAVE_STRUCT_UTMP_UT_HOST
498 Char
*cp
= str2short(CGETS(26, 7, "%n has %a %l from %m."));
500 Char
*cp
= str2short(CGETS(26, 8, "%n has %a %l."));
501 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
502 struct varent
*vp
= adrof(STRwho
);
505 if (vp
&& vp
->vec
&& vp
->vec
[0])
508 str
= tprintf(FMT_WHO
, cp
, NULL
, wp
->who_time
, wp
);
509 cleanup_push(str
, xfree
);
514 } /* end print_who */
518 who_info(ptr_t ptr
, int c
)
520 struct who
*wp
= ptr
;
522 #ifdef HAVE_STRUCT_UTMP_UT_HOST
526 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
529 case 'n': /* user name */
530 switch (wp
->who_status
& STMASK
) {
533 return strsave(wp
->who_new
);
535 return strsave(wp
->who_name
);
542 switch (wp
->who_status
& STMASK
) {
544 return strsave(CGETS(26, 9, "logged on"));
546 return strsave(CGETS(26, 10, "logged off"));
548 return xasprintf(CGETS(26, 11, "replaced %s on"), wp
->who_name
);
554 #ifdef HAVE_STRUCT_UTMP_UT_HOST
556 if (wp
->who_host
[0] == '\0')
557 return strsave(CGETS(26, 12, "local"));
560 wbuf
= xmalloc(strlen(pb
) + 1);
562 /* the ':' stuff is for <host>:<display>.<screen> */
563 for (flg
= isdigit((unsigned char)*pb
) ? '\0' : '.';
564 *pb
!= '\0' && (*pb
!= flg
|| ((pb
= strchr(pb
, ':')) != 0));
568 *wb
++ = isupper((unsigned char)*pb
) ?
569 tolower((unsigned char)*pb
) : *pb
;
576 if (wp
->who_host
[0] == '\0')
577 return strsave(CGETS(26, 12, "local"));
580 wbuf
= xmalloc(strlen(pb
) + 1);
582 for (; *pb
!= '\0'; pb
++)
583 *wb
++ = isupper((unsigned char)*pb
) ?
584 tolower((unsigned char)*pb
) : *pb
;
588 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
591 return strsave(wp
->who_tty
);
605 dolog(Char
**v
, struct command
*c
)
612 vp
= adrof(STRwatch
); /* lint insists vp isn't used unless we */
613 if (vp
== NULL
) /* unless we assign it outside the if */
614 stderror(ERR_NOWATCH
);
616 wp
= whohead
.who_next
;
617 while (wp
->who_next
!= NULL
) {
618 wp
->who_name
[0] = '\0';
623 # ifdef HAVE_STRUCT_UTMP_UT_HOST
633 char *tty
= short2str(varval(STRtty
));
639 for (wp
= whohead
.who_next
; wp
->who_next
!= NULL
; wp
= wp
->who_next
) {
640 if (strcmp(tty
, wp
->who_tty
) == 0)
642 wp
->who_name
[0] = '\0';
647 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
650 void add_to_who_list(name
, mach_nm
)
655 struct who
*wp
, *wpnew
;
658 wp
= whohead
.who_next
;
659 while (wp
->who_next
&& (comp
= strncmp(wp
->who_tty
,mach_nm
,UTLINLEN
)) < 0)
660 wp
= wp
->who_next
;/* find that tty! */
662 if (wp
->who_next
&& comp
== 0) { /* found the tty... */
666 wp
->who_status
= OFFLINE
;
668 else if (strncmp(name
, wp
->who_name
, UTNAMLEN
) == 0) {
669 /* someone is logged in */
671 wp
->who_status
= 0; /* same guy */
674 (void) strncpy(wp
->who_new
, name
, UTNAMLEN
);
676 if (wp
->who_name
[0] == '\0')
677 wp
->who_status
= ONLINE
;
679 wp
->who_status
= CHANGED
;
683 wpnew
= xcalloc(1, sizeof *wpnew
);
684 (void) strncpy(wpnew
->who_tty
, mach_nm
, UTLINLEN
);
687 wpnew
->who_status
= OFFLINE
;
689 (void) strncpy(wpnew
->who_new
, name
, UTNAMLEN
);
690 wpnew
->who_status
= ONLINE
;
693 debugwholist(wpnew
, wp
);
694 #endif /* WHODEBUG */
696 wpnew
->who_next
= wp
; /* link in a new 'who' */
697 wpnew
->who_prev
= wp
->who_prev
;
698 wpnew
->who_prev
->who_next
= wpnew
;
699 wp
->who_prev
= wpnew
; /* linked in now */
702 #endif /* WINNT_NATIVE */
703 #endif /* HAVENOUTMP */