2 * watch.c - login/logout watching
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1992-1997 Paul Falstad
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose. The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
32 /* Headers for utmp/utmpx structures */
41 #if !defined(REAL_UTMP_FILE) && defined(UTMP_FILE)
42 # define REAL_UTMP_FILE UTMP_FILE
44 #if !defined(REAL_UTMP_FILE) && defined(_PATH_UTMP)
45 # define REAL_UTMP_FILE _PATH_UTMP
47 #if !defined(REAL_UTMP_FILE) && defined(PATH_UTMP_FILE)
48 # define REAL_UTMP_FILE PATH_UTMP_FILE
52 #if !defined(REAL_WTMP_FILE) && defined(WTMP_FILE)
53 # define REAL_WTMP_FILE WTMP_FILE
55 #if !defined(REAL_WTMP_FILE) && defined(_PATH_WTMP)
56 # define REAL_WTMP_FILE _PATH_WTMP
58 #if !defined(REAL_WTMP_FILE) && defined(PATH_WTMP_FILE)
59 # define REAL_WTMP_FILE PATH_WTMP_FILE
63 #if !defined(REAL_UTMPX_FILE) && defined(UTMPX_FILE)
64 # define REAL_UTMPX_FILE UTMPX_FILE
66 #if !defined(REAL_UTMPX_FILE) && defined(_PATH_UTMPX)
67 # define REAL_UTMPX_FILE _PATH_UTMPX
69 #if !defined(REAL_UTMPX_FILE) && defined(PATH_UTMPX_FILE)
70 # define REAL_UTMPX_FILE PATH_UTMPX_FILE
74 #if !defined(REAL_WTMPX_FILE) && defined(WTMPX_FILE)
75 # define REAL_WTMPX_FILE WTMPX_FILE
77 #if !defined(REAL_WTMPX_FILE) && defined(_PATH_WTMPX)
78 # define REAL_WTMPX_FILE _PATH_WTMPX
80 #if !defined(REAL_WTMPX_FILE) && defined(PATH_WTMPX_FILE)
81 # define REAL_WTMPX_FILE PATH_WTMPX_FILE
84 /* Decide which structure to use. We use a structure that exists in *
85 * the headers, and require that its corresponding utmp file exist. *
86 * (wtmp is less important.) */
88 #if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE)
89 # define WATCH_STRUCT_UTMP struct utmpx
91 * In utmpx, the ut_name field is replaced by ut_user.
92 * Howver, on some systems ut_name may already be defined this
93 * way for the purposes of utmp.
96 # define ut_name ut_user
98 # ifdef HAVE_STRUCT_UTMPX_UT_XTIME
100 # define ut_time ut_xtime
101 # else /* !HAVE_STRUCT_UTMPX_UT_XTIME */
102 # ifdef HAVE_STRUCT_UTMPX_UT_TV
104 # define ut_time ut_tv.tv_sec
105 # endif /* HAVE_STRUCT_UTMPX_UT_TV */
106 # endif /* !HAVE_STRUCT_UTMPX_UT_XTIME */
107 # define WATCH_UTMP_FILE REAL_UTMPX_FILE
108 # ifdef REAL_WTMPX_FILE
109 # define WATCH_WTMP_FILE REAL_WTMPX_FILE
111 # ifdef HAVE_STRUCT_UTMPX_UT_HOST
112 # define WATCH_UTMP_UT_HOST 1
116 #if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMP) && defined(REAL_UTMP_FILE)
117 # define WATCH_STRUCT_UTMP struct utmp
118 # define WATCH_UTMP_FILE REAL_UTMP_FILE
119 # ifdef REAL_WTMP_FILE
120 # define WATCH_WTMP_FILE REAL_WTMP_FILE
122 # ifdef HAVE_STRUCT_UTMP_UT_HOST
123 # define WATCH_UTMP_UT_HOST 1
127 #ifdef WATCH_UTMP_UT_HOST
128 # define DEFAULT_WATCHFMT "%n has %a %l from %m."
129 #else /* !WATCH_UTMP_UT_HOST */
130 # define DEFAULT_WATCHFMT "%n has %a %l."
131 #endif /* !WATCH_UTMP_UT_HOST */
134 char const * const default_watchfmt
= DEFAULT_WATCHFMT
;
136 #ifdef WATCH_STRUCT_UTMP
138 # include "watch.pro"
140 # ifndef WATCH_WTMP_FILE
141 # define WATCH_WTMP_FILE "/dev/null"
145 static WATCH_STRUCT_UTMP
*wtab
;
146 static time_t lastutmpcheck
;
148 /* get the time of login/logout for WATCH */
152 getlogtime(WATCH_STRUCT_UTMP
*u
, int inout
)
155 WATCH_STRUCT_UTMP uu
;
157 int srchlimit
= 50; /* max number of wtmp records to search */
161 if (!(in
= fopen(WATCH_WTMP_FILE
, "r")))
165 if (fseek(in
, ((first
) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP
), 1)) {
170 if (!fread(&uu
, sizeof(WATCH_STRUCT_UTMP
), 1, in
)) {
174 if (uu
.ut_time
< lastwatch
|| !srchlimit
--) {
179 while (memcmp(&uu
, u
, sizeof(uu
)));
182 if (!fread(&uu
, sizeof(WATCH_STRUCT_UTMP
), 1, in
)) {
186 while (strncmp(uu
.ut_line
, u
->ut_line
, sizeof(u
->ut_line
)));
191 /* Mutually recursive call to handle ternaries in $WATCHFMT */
198 watch3ary(int inout
, WATCH_STRUCT_UTMP
*u
, char *fmt
, int prnt
)
204 truth
= (u
->ut_name
[0] != 0);
210 if (!strncmp(u
->ut_line
, "tty", 3))
211 truth
= (u
->ut_line
[3] != 0);
213 truth
= (u
->ut_line
[0] != 0);
215 # ifdef WATCH_UTMP_UT_HOST
218 truth
= (u
->ut_host
[0] != 0);
220 # endif /* WATCH_UTMP_UT_HOST */
222 prnt
= 0; /* Skip unknown conditionals entirely */
226 fmt
= watchlog2(inout
, u
, fmt
, (truth
&& prnt
), sep
);
227 return watchlog2(inout
, u
, fmt
, (!truth
&& prnt
), END3
);
230 /* print a login/logout event */
234 watchlog2(int inout
, WATCH_STRUCT_UTMP
*u
, char *fmt
, int prnt
, int fini
)
236 char buf
[40], buf2
[80];
240 # ifdef WATCH_UTMP_UT_HOST
243 # endif /* WATCH_UTMP_UT_HOST */
256 else if (*fmt
== fini
)
258 else if (*fmt
!= '%') {
263 if (*++fmt
== BEGIN3
)
264 fmt
= watch3ary(inout
, u
, ++fmt
, prnt
);
268 switch (*(fm2
= fmt
++)) {
270 printf("%.*s", (int)sizeof(u
->ut_name
), u
->ut_name
);
273 printf("%s", (!inout
) ? "logged off" : "logged on");
276 if (!strncmp(u
->ut_line
, "tty", 3))
277 printf("%.*s", (int)sizeof(u
->ut_line
) - 3, u
->ut_line
+ 3);
279 printf("%.*s", (int)sizeof(u
->ut_line
), u
->ut_line
);
281 # ifdef WATCH_UTMP_UT_HOST
283 for (p
= u
->ut_host
, i
= sizeof(u
->ut_host
); i
&& *p
; i
--, p
++) {
284 if (*p
== '.' && !idigit(p
[1]))
290 printf("%.*s", (int)sizeof(u
->ut_host
), u
->ut_host
);
292 # endif /* WATCH_UTMP_UT_HOST */
318 for (ss
= fm2
+ 2, dd
= buf2
;
319 n
-- && *ss
&& *ss
!= '}'; ++ss
, ++dd
)
320 *dd
= *((*ss
== '\\' && ss
[1]) ? ++ss
: ss
);
326 else fm2
= "%y-%m-%d";
328 else fm2
= "%y-%m-%d";
331 timet
= getlogtime(u
, inout
);
332 tm
= localtime(&timet
);
333 ztrftime(buf
, 40, fm2
, tm
);
334 printf("%s", (*buf
== ' ') ? buf
+ 1 : buf
);
341 tsetcap(TCSTANDOUTBEG
, TSC_RAW
);
344 txtunset(TXTSTANDOUT
);
345 tsetcap(TCSTANDOUTEND
, TSC_RAW
|TSC_DIRTY
);
349 tsetcap(TCBOLDFACEBEG
, TSC_RAW
|TSC_DIRTY
);
352 txtunset(TXTBOLDFACE
);
353 tsetcap(TCALLATTRSOFF
, TSC_RAW
|TSC_DIRTY
);
356 txtset(TXTUNDERLINE
);
357 tsetcap(TCUNDERLINEBEG
, TSC_RAW
);
360 txtunset(TXTUNDERLINE
);
361 tsetcap(TCUNDERLINEEND
, TSC_RAW
|TSC_DIRTY
);
375 /* check the List for login/logouts */
379 watchlog(int inout
, WATCH_STRUCT_UTMP
*u
, char **w
, char *fmt
)
387 if (*w
&& !strcmp(*w
, "all")) {
388 (void)watchlog2(inout
, u
, fmt
, 1, 0);
391 if (*w
&& !strcmp(*w
, "notme") &&
392 strncmp(u
->ut_name
, get_username(), sizeof(u
->ut_name
))) {
393 (void)watchlog2(inout
, u
, fmt
, 1, 0);
399 if (*v
!= '@' && *v
!= '%') {
400 for (vv
= v
; *vv
&& *vv
!= '@' && *vv
!= '%'; vv
++);
403 if (strncmp(u
->ut_name
, v
, sizeof(u
->ut_name
)))
410 for (vv
= ++v
; *vv
&& *vv
!= '@'; vv
++);
413 if (strncmp(u
->ut_line
, v
, sizeof(u
->ut_line
)))
418 # ifdef WATCH_UTMP_UT_HOST
419 else if (*v
== '@') {
420 for (vv
= ++v
; *vv
&& *vv
!= '%'; vv
++);
423 if (strncmp(u
->ut_host
, v
, strlen(v
)))
428 # endif /* WATCH_UTMP_UT_HOST */
432 (void)watchlog2(inout
, u
, fmt
, 1, 0);
438 /* compare 2 utmp entries */
442 ucmp(WATCH_STRUCT_UTMP
*u
, WATCH_STRUCT_UTMP
*v
)
444 if (u
->ut_time
== v
->ut_time
)
445 return strncmp(u
->ut_line
, v
->ut_line
, sizeof(u
->ut_line
));
446 return u
->ut_time
- v
->ut_time
;
449 /* initialize the user List */
455 WATCH_STRUCT_UTMP
*uptr
;
460 if (!(in
= fopen(WATCH_UTMP_FILE
, "r")))
462 uptr
= wtab
= (WATCH_STRUCT_UTMP
*)zalloc(wtabmax
* sizeof(WATCH_STRUCT_UTMP
));
463 while (fread(uptr
, sizeof(WATCH_STRUCT_UTMP
), 1, in
))
465 if (uptr
->ut_type
== USER_PROCESS
)
466 # else /* !USER_PROCESS */
467 if (uptr
->ut_name
[0])
468 # endif /* !USER_PROCESS */
471 if (++wtabsz
== wtabmax
)
472 uptr
= (wtab
= (WATCH_STRUCT_UTMP
*)realloc((void *) wtab
, (wtabmax
*= 2) *
473 sizeof(WATCH_STRUCT_UTMP
))) + wtabsz
;
478 qsort((void *) wtab
, wtabsz
, sizeof(WATCH_STRUCT_UTMP
),
479 (int (*) _((const void *, const void *)))ucmp
);
482 /* Check for login/logout events; executed before *
483 * each prompt if WATCH is set */
490 WATCH_STRUCT_UTMP
*utab
, *uptr
, *wptr
;
494 int utabsz
= 0, utabmax
= wtabsz
+ 4;
505 if ((stat(WATCH_UTMP_FILE
, &st
) == -1) || (st
.st_mtime
<= lastutmpcheck
)) {
509 lastutmpcheck
= st
.st_mtime
;
510 uptr
= utab
= (WATCH_STRUCT_UTMP
*) zalloc(utabmax
* sizeof(WATCH_STRUCT_UTMP
));
512 if (!(in
= fopen(WATCH_UTMP_FILE
, "r"))) {
517 while (fread(uptr
, sizeof *uptr
, 1, in
))
519 if (uptr
->ut_type
== USER_PROCESS
)
520 # else /* !USER_PROCESS */
521 if (uptr
->ut_name
[0])
522 # endif /* !USER_PROCESS */
525 if (++utabsz
== utabmax
)
526 uptr
= (utab
= (WATCH_STRUCT_UTMP
*)realloc((void *) utab
, (utabmax
*= 2) *
527 sizeof(WATCH_STRUCT_UTMP
))) + utabsz
;
536 qsort((void *) utab
, utabsz
, sizeof(WATCH_STRUCT_UTMP
),
537 (int (*) _((const void *, const void *)))ucmp
);
548 if (!(fmt
= getsparam("WATCHFMT")))
549 fmt
= DEFAULT_WATCHFMT
;
550 while ((uct
|| wct
) && !errflag
)
551 if (!uct
|| (wct
&& ucmp(uptr
, wptr
) > 0))
552 wct
--, watchlog(0, wptr
++, s
, fmt
);
553 else if (!wct
|| (uct
&& ucmp(uptr
, wptr
) < 0))
554 uct
--, watchlog(1, uptr
++, s
, fmt
);
556 uptr
++, wptr
++, wct
--, uct
--;
566 bin_log(UNUSED(char *nam
), UNUSED(char **argv
), UNUSED(Options ops
), UNUSED(int func
))
572 wtab
= (WATCH_STRUCT_UTMP
*)zalloc(1);
579 #else /* !WATCH_STRUCT_UTMP */
588 bin_log(char *nam
, char **argv
, Options ops
, int func
)
590 return bin_notavail(nam
, argv
, ops
, func
);
593 #endif /* !WATCH_STRUCT_UTMP */