2 * tc.who.c: Watch logins and logouts...
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * kfk 26 Jan 1984 - for login watch functions.
43 # define UTNAMLEN sizeof(((struct utmpx *) 0)->ut_name)
44 # define UTLINLEN sizeof(((struct utmpx *) 0)->ut_line)
45 # ifdef HAVE_STRUCT_UTMPX_UT_HOST
46 # define UTHOSTLEN sizeof(((struct utmpx *) 0)->ut_host)
48 /* I just redefine a few words here. Changing every occurrence below
49 * seems like too much of work. All UTMP functions have equivalent
50 * UTMPX counterparts, so they can be added all here when needed.
51 * Kimmo Suominen, Oct 14 1991
53 # if defined(__UTMPX_FILE) && !defined(UTMPX_FILE)
54 # define TCSH_PATH_UTMP __UTMPX_FILE
55 # elif defined(_PATH_UTMPX)
56 # define TCSH_PATH_UTMP _PATH_UTMPX
57 # elif defined(UTMPX_FILE)
58 # define TCSH_PATH_UTMP UTMPX_FILE
59 # elif __FreeBSD_version >= 900000
60 # /* Why isn't this defined somewhere? */
61 # define TCSH_PATH_UTMP "/var/run/utx.active"
62 # elif defined(__hpux)
63 # define TCSH_PATH_UTMP "/etc/utmpx"
64 # elif defined(IBMAIX) && defined(UTMP_FILE)
65 # define TCSH_PATH_UTMP UTMP_FILE
67 # if defined(TCSH_PATH_UTMP) || !defined(HAVE_UTMP_H)
69 # define TCSH_USE_UTMPX
70 # if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
71 # define getutent getutxent
72 # define setutent setutxent
73 # define endutent endutxent
74 # endif /* HAVE_GETUTENT || HAVE_GETUTXENT */
75 # if defined(HAVE_STRUCT_UTMPX_UT_TV)
76 # define ut_time ut_tv.tv_sec
77 # elif defined(HAVE_STRUCT_UTMPX_UT_XTIME)
78 # define ut_time ut_xtime
80 # if defined(HAVE_STRUCT_UTMPX_UT_USER)
81 # define ut_name ut_user
83 # endif /* TCSH_PATH_UTMP || !HAVE_UTMP_H */
84 #endif /* HAVE_UTMPX_H */
86 #if !defined(TCSH_USE_UTMPX) && defined(HAVE_UTMP_H)
88 # if defined(HAVE_STRUCT_UTMP_UT_TV)
89 # define ut_time ut_tv.tv_sec
90 # elif defined(HAVE_STRUCT_UTMP_UT_XTIME)
91 # define ut_time ut_xtime
93 # if defined(HAVE_STRUCT_UTMP_UT_USER)
94 # define ut_name ut_user
97 # define UTNAMLEN sizeof(((struct utmp *) 0)->ut_name)
98 # define UTLINLEN sizeof(((struct utmp *) 0)->ut_line)
99 # ifdef HAVE_STRUCT_UTMP_UT_HOST
101 # define UTHOSTLEN 100
103 # define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host)
105 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
107 /* give poor cc a little help if it needs it */
109 # define UTNAMLEN sizeof(__ut.ut_name)
110 # define UTLINLEN sizeof(__ut.ut_line)
111 # ifdef HAVE_STRUCT_UTMP_UT_HOST
113 # define UTHOSTLEN 100
115 # define UTHOSTLEN sizeof(__ut.ut_host)
117 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
118 # endif /* BROKEN_CC */
119 # ifndef TCSH_PATH_UTMP
121 # define TCSH_PATH_UTMP UTMP_FILE
122 # elif defined(_PATH_UTMP)
123 # define TCSH_PATH_UTMP _PATH_UTMP
125 # define TCSH_PATH_UTMP "/etc/utmp"
126 # endif /* UTMP_FILE */
127 # endif /* TCSH_PATH_UTMP */
128 #endif /* !TCSH_USE_UTMPX && HAVE_UTMP_H */
138 struct who
*who_next
;
139 struct who
*who_prev
;
140 char who_name
[UTNAMLEN
+ 1];
141 char who_new
[UTNAMLEN
+ 1];
142 char who_tty
[UTLINLEN
+ 1];
144 char who_host
[UTHOSTLEN
+ 1];
145 #endif /* UTHOSTLEN */
150 static struct who whohead
, whotail
;
151 static time_t watch_period
= 0;
152 static time_t stlast
= 0;
154 static void debugwholist (struct who
*, struct who
*);
156 static void print_who (struct who
*);
167 * Karl Kleinpaste, 26 Jan 1984.
168 * Initialize the dummy tty list for login watch.
169 * This dummy list eliminates boundary conditions
170 * when doing pointer-chase searches.
175 whohead
.who_next
= &whotail
;
176 whotail
.who_prev
= &whohead
;
179 debugwholist(NULL
, NULL
);
180 #endif /* WHODEBUG */
191 * Karl Kleinpaste, 26 Jan 1984.
192 * Watch /etc/utmp for login/logout changes.
195 watch_login(int force
)
197 int comp
= -1, alldone
;
198 int firsttime
= stlast
== 1;
199 #if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
205 struct who
*wp
, *wpnew
;
208 time_t t
, interval
= MAILINTVL
;
210 #if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
211 char *host
, *ut_find_host();
218 #endif /* WINNT_NATIVE */
220 /* stop SIGINT, lest our login list get trashed. */
222 cleanup_push(&pintr_disabled
, disabled_cleanup
);
225 if ((v
== NULL
|| v
->vec
== NULL
) && !force
) {
226 cleanup_until(&pintr_disabled
);
227 return; /* no names to watch */
231 if (blklen(vp
) % 2) /* odd # args: 1st == # minutes. */
232 interval
= (number(*vp
)) ? (getn(*vp
++) * 60) : MAILINTVL
;
238 if (t
- watch_period
< interval
) {
239 cleanup_until(&pintr_disabled
);
240 return; /* not long enough yet... */
245 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
246 * Don't open utmp all the time, stat it first...
248 if (stat(TCSH_PATH_UTMP
, &sta
)) {
251 "cannot stat %s. Please \"unset watch\".\n"),
253 cleanup_until(&pintr_disabled
);
256 if (stlast
== sta
.st_mtime
) {
257 cleanup_until(&pintr_disabled
);
260 stlast
= sta
.st_mtime
;
261 #if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
264 if ((utmpfd
= xopen(TCSH_PATH_UTMP
, O_RDONLY
|O_LARGEFILE
)) < 0) {
267 "%s cannot be opened. Please \"unset watch\".\n"),
269 cleanup_until(&pintr_disabled
);
272 cleanup_push(&utmpfd
, open_cleanup
);
276 * xterm clears the entire utmp entry - mark everyone on the status list
277 * OFFLINE or we won't notice X "logouts"
279 for (wp
= whohead
.who_next
; wp
->who_next
!= NULL
; wp
= wp
->who_next
)
280 wp
->who_status
= OFFLINE
| CLEARED
;
283 * Read in the utmp file, sort the entries, and update existing entries or
284 * add new entries to the status list.
286 #if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
287 while ((uptr
= getutent()) != NULL
) {
288 memcpy(&utmp
, uptr
, sizeof (utmp
));
290 while (xread(utmpfd
, &utmp
, sizeof utmp
) == sizeof utmp
) {
295 if (utmp
.ut_type
!= USER_PROCESS
)
298 /* Why is that? Cause the utmp file is always corrupted??? */
299 if (utmp
.ut_type
!= USER_PROCESS
&& utmp
.ut_type
!= DEAD_PROCESS
)
302 # endif /* DEAD_PROCESS */
304 if (utmp
.ut_name
[0] == '\0' && utmp
.ut_line
[0] == '\0')
305 continue; /* completely void entry */
307 if (utmp
.ut_type
== DEAD_PROCESS
&& utmp
.ut_line
[0] == '\0')
309 # endif /* DEAD_PROCESS */
310 wp
= whohead
.who_next
;
311 while (wp
->who_next
&& (comp
= strncmp(wp
->who_tty
, utmp
.ut_line
, UTLINLEN
)) < 0)
312 wp
= wp
->who_next
;/* find that tty! */
314 if (wp
->who_next
&& comp
== 0) { /* found the tty... */
315 if (utmp
.ut_time
< wp
->who_time
)
318 if (utmp
.ut_type
== DEAD_PROCESS
) {
319 wp
->who_time
= utmp
.ut_time
;
320 wp
->who_status
= OFFLINE
;
323 # endif /* DEAD_PROCESS */
324 if (utmp
.ut_name
[0] == '\0') {
325 wp
->who_time
= utmp
.ut_time
;
326 wp
->who_status
= OFFLINE
;
328 else if (strncmp(utmp
.ut_name
, wp
->who_name
, UTNAMLEN
) == 0) {
329 /* someone is logged in */
330 wp
->who_time
= utmp
.ut_time
;
331 wp
->who_status
= ONLINE
| ANNOUNCE
; /* same guy */
334 (void) strncpy(wp
->who_new
, utmp
.ut_name
, UTNAMLEN
);
337 host
= ut_find_host(wp
->who_tty
);
339 (void) strncpy(wp
->who_host
, host
, UTHOSTLEN
);
343 (void) strncpy(wp
->who_host
, utmp
.ut_host
, UTHOSTLEN
);
345 # endif /* UTHOSTLEN */
346 wp
->who_time
= utmp
.ut_time
;
347 if (wp
->who_name
[0] == '\0')
348 wp
->who_status
= ONLINE
;
350 wp
->who_status
= CHANGED
;
353 else { /* new tty in utmp */
354 wpnew
= xcalloc(1, sizeof *wpnew
);
355 (void) strncpy(wpnew
->who_tty
, utmp
.ut_line
, UTLINLEN
);
358 host
= ut_find_host(wpnew
->who_tty
);
360 (void) strncpy(wpnew
->who_host
, host
, UTHOSTLEN
);
362 wpnew
->who_host
[0] = 0;
364 (void) strncpy(wpnew
->who_host
, utmp
.ut_host
, UTHOSTLEN
);
366 # endif /* UTHOSTLEN */
367 wpnew
->who_time
= utmp
.ut_time
;
369 if (utmp
.ut_type
== DEAD_PROCESS
)
370 wpnew
->who_status
= OFFLINE
;
372 # endif /* DEAD_PROCESS */
373 if (utmp
.ut_name
[0] == '\0')
374 wpnew
->who_status
= OFFLINE
;
376 (void) strncpy(wpnew
->who_new
, utmp
.ut_name
, UTNAMLEN
);
377 wpnew
->who_status
= ONLINE
;
380 debugwholist(wpnew
, wp
);
381 # endif /* WHODEBUG */
383 wpnew
->who_next
= wp
; /* link in a new 'who' */
384 wpnew
->who_prev
= wp
->who_prev
;
385 wpnew
->who_prev
->who_next
= wpnew
;
386 wp
->who_prev
= wpnew
; /* linked in now */
389 #if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
392 cleanup_until(&utmpfd
);
394 #endif /* !WINNT_NATIVE */
396 if (force
|| vp
== NULL
) {
397 cleanup_until(&pintr_disabled
);
402 * The state of all logins is now known, so we can search the user's list
403 * of watchables to print the interesting ones.
405 for (alldone
= 0; !alldone
&& *vp
!= NULL
&& **vp
!= '\0' &&
406 *(vp
+ 1) != NULL
&& **(vp
+ 1) != '\0';
407 vp
+= 2) { /* args used in pairs... */
409 if (eq(*vp
, STRany
) && eq(*(vp
+ 1), STRany
))
412 for (wp
= whohead
.who_next
; wp
->who_next
!= NULL
; wp
= wp
->who_next
) {
413 if (wp
->who_status
& ANNOUNCE
||
414 (!eq(STRany
, vp
[0]) &&
415 !Gmatch(str2short(wp
->who_name
), vp
[0]) &&
416 !Gmatch(str2short(wp
->who_new
), vp
[0])) ||
417 (!Gmatch(str2short(wp
->who_tty
), vp
[1]) &&
419 continue; /* entry doesn't qualify */
420 /* already printed or not right one to print */
423 if (wp
->who_status
& CLEARED
) {/* utmp entry was cleared */
424 wp
->who_time
= watch_period
;
425 wp
->who_status
&= ~CLEARED
;
428 if ((wp
->who_status
& OFFLINE
) &&
429 (wp
->who_name
[0] != '\0')) {
432 wp
->who_name
[0] = '\0';
433 wp
->who_status
|= ANNOUNCE
;
436 if (wp
->who_status
& ONLINE
) {
439 (void) strcpy(wp
->who_name
, wp
->who_new
);
440 wp
->who_status
|= ANNOUNCE
;
443 if (wp
->who_status
& CHANGED
) {
446 (void) strcpy(wp
->who_name
, wp
->who_new
);
447 wp
->who_status
|= ANNOUNCE
;
452 cleanup_until(&pintr_disabled
);
457 debugwholist(struct who
*new, struct who
*wp
)
461 a
= whohead
.who_next
;
462 while (a
->who_next
!= NULL
) {
463 xprintf("%s/%s -> ", a
->who_name
, a
->who_tty
);
468 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
471 a
= whotail
.who_prev
;
472 xprintf(CGETS(26, 4, "backward: "));
473 while (a
->who_prev
!= NULL
) {
474 xprintf("%s/%s -> ", a
->who_name
, a
->who_tty
);
479 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
483 xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name
, new->who_tty
);
485 xprintf("wp: %s/%s\n", wp
->who_name
, wp
->who_tty
);
487 #endif /* WHODEBUG */
491 print_who(struct who
*wp
)
494 Char
*cp
= str2short(CGETS(26, 7, "%n has %a %l from %m."));
496 Char
*cp
= str2short(CGETS(26, 8, "%n has %a %l."));
497 #endif /* UTHOSTLEN */
498 struct varent
*vp
= adrof(STRwho
);
501 if (vp
&& vp
->vec
&& vp
->vec
[0])
504 str
= tprintf(FMT_WHO
, cp
, NULL
, wp
->who_time
, wp
);
505 cleanup_push(str
, xfree
);
510 } /* end print_who */
514 who_info(ptr_t ptr
, int c
)
516 struct who
*wp
= ptr
;
522 #endif /* UTHOSTLEN */
525 case 'n': /* user name */
526 switch (wp
->who_status
& STMASK
) {
529 return strsave(wp
->who_new
);
531 return strsave(wp
->who_name
);
538 switch (wp
->who_status
& STMASK
) {
540 return strsave(CGETS(26, 9, "logged on"));
542 return strsave(CGETS(26, 10, "logged off"));
544 return xasprintf(CGETS(26, 11, "replaced %s on"), wp
->who_name
);
552 if (wp
->who_host
[0] == '\0')
553 return strsave(CGETS(26, 12, "local"));
556 wbuf
= xmalloc(strlen(pb
) + 1);
558 /* the ':' stuff is for <host>:<display>.<screen> */
559 for (flg
= isdigit((unsigned char)*pb
) ? '\0' : '.';
560 *pb
!= '\0' && (*pb
!= flg
|| ((pb
= strchr(pb
, ':')) != 0));
564 *wb
++ = isupper((unsigned char)*pb
) ?
565 tolower((unsigned char)*pb
) : *pb
;
572 if (wp
->who_host
[0] == '\0')
573 return strsave(CGETS(26, 12, "local"));
576 wbuf
= xmalloc(strlen(pb
) + 1);
578 for (; *pb
!= '\0'; pb
++)
579 *wb
++ = isupper((unsigned char)*pb
) ?
580 tolower((unsigned char)*pb
) : *pb
;
584 #endif /* UTHOSTLEN */
587 return strsave(wp
->who_tty
);
601 dolog(Char
**v
, struct command
*c
)
608 vp
= adrof(STRwatch
); /* lint insists vp isn't used unless we */
609 if (vp
== NULL
) /* unless we assign it outside the if */
610 stderror(ERR_NOWATCH
);
612 wp
= whohead
.who_next
;
613 while (wp
->who_next
!= NULL
) {
614 wp
->who_name
[0] = '\0';
629 char *tty
= short2str(varval(STRtty
));
635 for (wp
= whohead
.who_next
; wp
->who_next
!= NULL
; wp
= wp
->who_next
) {
636 if (strcmp(tty
, wp
->who_tty
) == 0)
638 wp
->who_name
[0] = '\0';
643 # endif /* UTHOSTLEN */
645 #endif /* HAVENOUTMP */