26763: fix problem on failed cd -s to relative path
[zsh.git] / Src / watch.c
blob5231579f875b10d80d6f95d809edd768e7616eaa
1 /*
2 * watch.c - login/logout watching
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
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.
30 #include "zsh.mdh"
32 /* Headers for utmp/utmpx structures */
33 #ifdef HAVE_UTMP_H
34 # include <utmp.h>
35 #endif
36 #ifdef HAVE_UTMPX_H
37 # include <utmpx.h>
38 #endif
40 /* Find utmp file */
41 #if !defined(REAL_UTMP_FILE) && defined(UTMP_FILE)
42 # define REAL_UTMP_FILE UTMP_FILE
43 #endif
44 #if !defined(REAL_UTMP_FILE) && defined(_PATH_UTMP)
45 # define REAL_UTMP_FILE _PATH_UTMP
46 #endif
47 #if !defined(REAL_UTMP_FILE) && defined(PATH_UTMP_FILE)
48 # define REAL_UTMP_FILE PATH_UTMP_FILE
49 #endif
51 /* Find wtmp file */
52 #if !defined(REAL_WTMP_FILE) && defined(WTMP_FILE)
53 # define REAL_WTMP_FILE WTMP_FILE
54 #endif
55 #if !defined(REAL_WTMP_FILE) && defined(_PATH_WTMP)
56 # define REAL_WTMP_FILE _PATH_WTMP
57 #endif
58 #if !defined(REAL_WTMP_FILE) && defined(PATH_WTMP_FILE)
59 # define REAL_WTMP_FILE PATH_WTMP_FILE
60 #endif
62 /* Find utmpx file */
63 #if !defined(REAL_UTMPX_FILE) && defined(UTMPX_FILE)
64 # define REAL_UTMPX_FILE UTMPX_FILE
65 #endif
66 #if !defined(REAL_UTMPX_FILE) && defined(_PATH_UTMPX)
67 # define REAL_UTMPX_FILE _PATH_UTMPX
68 #endif
69 #if !defined(REAL_UTMPX_FILE) && defined(PATH_UTMPX_FILE)
70 # define REAL_UTMPX_FILE PATH_UTMPX_FILE
71 #endif
73 /* Find wtmpx file */
74 #if !defined(REAL_WTMPX_FILE) && defined(WTMPX_FILE)
75 # define REAL_WTMPX_FILE WTMPX_FILE
76 #endif
77 #if !defined(REAL_WTMPX_FILE) && defined(_PATH_WTMPX)
78 # define REAL_WTMPX_FILE _PATH_WTMPX
79 #endif
80 #if !defined(REAL_WTMPX_FILE) && defined(PATH_WTMPX_FILE)
81 # define REAL_WTMPX_FILE PATH_WTMPX_FILE
82 #endif
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.
95 # ifndef ut_name
96 # define ut_name ut_user
97 # endif
98 # ifdef HAVE_STRUCT_UTMPX_UT_XTIME
99 # undef ut_time
100 # define ut_time ut_xtime
101 # else /* !HAVE_STRUCT_UTMPX_UT_XTIME */
102 # ifdef HAVE_STRUCT_UTMPX_UT_TV
103 # undef ut_time
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
110 # endif
111 # ifdef HAVE_STRUCT_UTMPX_UT_HOST
112 # define WATCH_UTMP_UT_HOST 1
113 # endif
114 #endif
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
121 # endif
122 # ifdef HAVE_STRUCT_UTMP_UT_HOST
123 # define WATCH_UTMP_UT_HOST 1
124 # endif
125 #endif
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 */
133 /**/
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"
142 # endif
144 static int wtabsz;
145 static WATCH_STRUCT_UTMP *wtab;
146 static time_t lastutmpcheck;
148 /* get the time of login/logout for WATCH */
150 /**/
151 static time_t
152 getlogtime(WATCH_STRUCT_UTMP *u, int inout)
154 FILE *in;
155 WATCH_STRUCT_UTMP uu;
156 int first = 1;
157 int srchlimit = 50; /* max number of wtmp records to search */
159 if (inout)
160 return u->ut_time;
161 if (!(in = fopen(WATCH_WTMP_FILE, "r")))
162 return time(NULL);
163 fseek(in, 0, 2);
164 do {
165 if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), 1)) {
166 fclose(in);
167 return time(NULL);
169 first = 0;
170 if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
171 fclose(in);
172 return time(NULL);
174 if (uu.ut_time < lastwatch || !srchlimit--) {
175 fclose(in);
176 return time(NULL);
179 while (memcmp(&uu, u, sizeof(uu)));
182 if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
183 fclose(in);
184 return time(NULL);
186 while (strncmp(uu.ut_line, u->ut_line, sizeof(u->ut_line)));
187 fclose(in);
188 return uu.ut_time;
191 /* Mutually recursive call to handle ternaries in $WATCHFMT */
193 # define BEGIN3 '('
194 # define END3 ')'
196 /**/
197 static char *
198 watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt)
200 int truth = 1, sep;
202 switch (*fmt++) {
203 case 'n':
204 truth = (u->ut_name[0] != 0);
205 break;
206 case 'a':
207 truth = inout;
208 break;
209 case 'l':
210 if (!strncmp(u->ut_line, "tty", 3))
211 truth = (u->ut_line[3] != 0);
212 else
213 truth = (u->ut_line[0] != 0);
214 break;
215 # ifdef WATCH_UTMP_UT_HOST
216 case 'm':
217 case 'M':
218 truth = (u->ut_host[0] != 0);
219 break;
220 # endif /* WATCH_UTMP_UT_HOST */
221 default:
222 prnt = 0; /* Skip unknown conditionals entirely */
223 break;
225 sep = *fmt++;
226 fmt = watchlog2(inout, u, fmt, (truth && prnt), sep);
227 return watchlog2(inout, u, fmt, (!truth && prnt), END3);
230 /* print a login/logout event */
232 /**/
233 static char *
234 watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
236 char buf[40], buf2[80];
237 time_t timet;
238 struct tm *tm;
239 char *fm2;
240 # ifdef WATCH_UTMP_UT_HOST
241 char *p;
242 int i;
243 # endif /* WATCH_UTMP_UT_HOST */
245 while (*fmt)
246 if (*fmt == '\\') {
247 if (*++fmt) {
248 if (prnt)
249 putchar(*fmt);
250 ++fmt;
251 } else if (fini)
252 return fmt;
253 else
254 break;
256 else if (*fmt == fini)
257 return ++fmt;
258 else if (*fmt != '%') {
259 if (prnt)
260 putchar(*fmt);
261 ++fmt;
262 } else {
263 if (*++fmt == BEGIN3)
264 fmt = watch3ary(inout, u, ++fmt, prnt);
265 else if (!prnt)
266 ++fmt;
267 else
268 switch (*(fm2 = fmt++)) {
269 case 'n':
270 printf("%.*s", (int)sizeof(u->ut_name), u->ut_name);
271 break;
272 case 'a':
273 printf("%s", (!inout) ? "logged off" : "logged on");
274 break;
275 case 'l':
276 if (!strncmp(u->ut_line, "tty", 3))
277 printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3);
278 else
279 printf("%.*s", (int)sizeof(u->ut_line), u->ut_line);
280 break;
281 # ifdef WATCH_UTMP_UT_HOST
282 case 'm':
283 for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) {
284 if (*p == '.' && !idigit(p[1]))
285 break;
286 putchar(*p);
288 break;
289 case 'M':
290 printf("%.*s", (int)sizeof(u->ut_host), u->ut_host);
291 break;
292 # endif /* WATCH_UTMP_UT_HOST */
293 case 'T':
294 case 't':
295 case '@':
296 case 'W':
297 case 'w':
298 case 'D':
299 switch (*fm2) {
300 case '@':
301 case 't':
302 fm2 = "%l:%M%p";
303 break;
304 case 'T':
305 fm2 = "%K:%M";
306 break;
307 case 'w':
308 fm2 = "%a %f";
309 break;
310 case 'W':
311 fm2 = "%m/%d/%y";
312 break;
313 case 'D':
314 if (fm2[1] == '{') {
315 char *dd, *ss;
316 int n = 79;
318 for (ss = fm2 + 2, dd = buf2;
319 n-- && *ss && *ss != '}'; ++ss, ++dd)
320 *dd = *((*ss == '\\' && ss[1]) ? ++ss : ss);
321 if (*ss == '}') {
322 *dd = '\0';
323 fmt = ss + 1;
324 fm2 = buf2;
326 else fm2 = "%y-%m-%d";
328 else fm2 = "%y-%m-%d";
329 break;
331 timet = getlogtime(u, inout);
332 tm = localtime(&timet);
333 ztrftime(buf, 40, fm2, tm);
334 printf("%s", (*buf == ' ') ? buf + 1 : buf);
335 break;
336 case '%':
337 putchar('%');
338 break;
339 case 'S':
340 txtset(TXTSTANDOUT);
341 tsetcap(TCSTANDOUTBEG, TSC_RAW);
342 break;
343 case 's':
344 txtunset(TXTSTANDOUT);
345 tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY);
346 break;
347 case 'B':
348 txtset(TXTBOLDFACE);
349 tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY);
350 break;
351 case 'b':
352 txtunset(TXTBOLDFACE);
353 tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY);
354 break;
355 case 'U':
356 txtset(TXTUNDERLINE);
357 tsetcap(TCUNDERLINEBEG, TSC_RAW);
358 break;
359 case 'u':
360 txtunset(TXTUNDERLINE);
361 tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY);
362 break;
363 default:
364 putchar('%');
365 putchar(*fm2);
366 break;
369 if (prnt)
370 putchar('\n');
372 return fmt;
375 /* check the List for login/logouts */
377 /**/
378 static void
379 watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
381 char *v, *vv, sav;
382 int bad;
384 if (!*u->ut_name)
385 return;
387 if (*w && !strcmp(*w, "all")) {
388 (void)watchlog2(inout, u, fmt, 1, 0);
389 return;
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);
394 return;
396 for (; *w; w++) {
397 bad = 0;
398 v = *w;
399 if (*v != '@' && *v != '%') {
400 for (vv = v; *vv && *vv != '@' && *vv != '%'; vv++);
401 sav = *vv;
402 *vv = '\0';
403 if (strncmp(u->ut_name, v, sizeof(u->ut_name)))
404 bad = 1;
405 *vv = sav;
406 v = vv;
408 for (;;)
409 if (*v == '%') {
410 for (vv = ++v; *vv && *vv != '@'; vv++);
411 sav = *vv;
412 *vv = '\0';
413 if (strncmp(u->ut_line, v, sizeof(u->ut_line)))
414 bad = 1;
415 *vv = sav;
416 v = vv;
418 # ifdef WATCH_UTMP_UT_HOST
419 else if (*v == '@') {
420 for (vv = ++v; *vv && *vv != '%'; vv++);
421 sav = *vv;
422 *vv = '\0';
423 if (strncmp(u->ut_host, v, strlen(v)))
424 bad = 1;
425 *vv = sav;
426 v = vv;
428 # endif /* WATCH_UTMP_UT_HOST */
429 else
430 break;
431 if (!bad) {
432 (void)watchlog2(inout, u, fmt, 1, 0);
433 return;
438 /* compare 2 utmp entries */
440 /**/
441 static int
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 */
451 /**/
452 static void
453 readwtab(void)
455 WATCH_STRUCT_UTMP *uptr;
456 int wtabmax = 32;
457 FILE *in;
459 wtabsz = 0;
460 if (!(in = fopen(WATCH_UTMP_FILE, "r")))
461 return;
462 uptr = wtab = (WATCH_STRUCT_UTMP *)zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP));
463 while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in))
464 # ifdef USER_PROCESS
465 if (uptr->ut_type == USER_PROCESS)
466 # else /* !USER_PROCESS */
467 if (uptr->ut_name[0])
468 # endif /* !USER_PROCESS */
470 uptr++;
471 if (++wtabsz == wtabmax)
472 uptr = (wtab = (WATCH_STRUCT_UTMP *)realloc((void *) wtab, (wtabmax *= 2) *
473 sizeof(WATCH_STRUCT_UTMP))) + wtabsz;
475 fclose(in);
477 if (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 */
485 /**/
486 void
487 dowatch(void)
489 FILE *in;
490 WATCH_STRUCT_UTMP *utab, *uptr, *wptr;
491 struct stat st;
492 char **s;
493 char *fmt;
494 int utabsz = 0, utabmax = wtabsz + 4;
495 int uct, wct;
497 s = watch;
499 holdintr();
500 if (!wtab) {
501 readwtab();
502 noholdintr();
503 return;
505 if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) {
506 noholdintr();
507 return;
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"))) {
513 free(utab);
514 noholdintr();
515 return;
517 while (fread(uptr, sizeof *uptr, 1, in))
518 # ifdef USER_PROCESS
519 if (uptr->ut_type == USER_PROCESS)
520 # else /* !USER_PROCESS */
521 if (uptr->ut_name[0])
522 # endif /* !USER_PROCESS */
524 uptr++;
525 if (++utabsz == utabmax)
526 uptr = (utab = (WATCH_STRUCT_UTMP *)realloc((void *) utab, (utabmax *= 2) *
527 sizeof(WATCH_STRUCT_UTMP))) + utabsz;
529 fclose(in);
530 noholdintr();
531 if (errflag) {
532 free(utab);
533 return;
535 if (utabsz)
536 qsort((void *) utab, utabsz, sizeof(WATCH_STRUCT_UTMP),
537 (int (*) _((const void *, const void *)))ucmp);
539 wct = wtabsz;
540 uct = utabsz;
541 uptr = utab;
542 wptr = wtab;
543 if (errflag) {
544 free(utab);
545 return;
547 queue_signals();
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);
555 else
556 uptr++, wptr++, wct--, uct--;
557 unqueue_signals();
558 free(wtab);
559 wtab = utab;
560 wtabsz = utabsz;
561 fflush(stdout);
564 /**/
566 bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
568 if (!watch)
569 return 1;
570 if (wtab)
571 free(wtab);
572 wtab = (WATCH_STRUCT_UTMP *)zalloc(1);
573 wtabsz = 0;
574 lastutmpcheck = 0;
575 dowatch();
576 return 0;
579 #else /* !WATCH_STRUCT_UTMP */
581 /**/
582 void dowatch(void)
586 /**/
588 bin_log(char *nam, char **argv, Options ops, int func)
590 return bin_notavail(nam, argv, ops, func);
593 #endif /* !WATCH_STRUCT_UTMP */