diff options
Diffstat (limited to 'Src/watch.c')
-rw-r--r-- | Src/watch.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/Src/watch.c b/Src/watch.c new file mode 100644 index 000000000..2532e0a63 --- /dev/null +++ b/Src/watch.c @@ -0,0 +1,586 @@ +/* + * watch.c - login/logout watching + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" + +/* Headers for utmp/utmpx structures */ +#ifdef HAVE_UTMP_H +# include <utmp.h> +#endif +#ifdef HAVE_UTMPX_H +# include <utmpx.h> +#endif + +/* Find utmp file */ +#if !defined(REAL_UTMP_FILE) && defined(UTMP_FILE) +# define REAL_UTMP_FILE UTMP_FILE +#endif +#if !defined(REAL_UTMP_FILE) && defined(_PATH_UTMP) +# define REAL_UTMP_FILE _PATH_UTMP +#endif +#if !defined(REAL_UTMP_FILE) && defined(PATH_UTMP_FILE) +# define REAL_UTMP_FILE PATH_UTMP_FILE +#endif + +/* Find wtmp file */ +#if !defined(REAL_WTMP_FILE) && defined(WTMP_FILE) +# define REAL_WTMP_FILE WTMP_FILE +#endif +#if !defined(REAL_WTMP_FILE) && defined(_PATH_WTMP) +# define REAL_WTMP_FILE _PATH_WTMP +#endif +#if !defined(REAL_WTMP_FILE) && defined(PATH_WTMP_FILE) +# define REAL_WTMP_FILE PATH_WTMP_FILE +#endif + +/* Find utmpx file */ +#if !defined(REAL_UTMPX_FILE) && defined(UTMPX_FILE) +# define REAL_UTMPX_FILE UTMPX_FILE +#endif +#if !defined(REAL_UTMPX_FILE) && defined(_PATH_UTMPX) +# define REAL_UTMPX_FILE _PATH_UTMPX +#endif +#if !defined(REAL_UTMPX_FILE) && defined(PATH_UTMPX_FILE) +# define REAL_UTMPX_FILE PATH_UTMPX_FILE +#endif + +/* Find wtmpx file */ +#if !defined(REAL_WTMPX_FILE) && defined(WTMPX_FILE) +# define REAL_WTMPX_FILE WTMPX_FILE +#endif +#if !defined(REAL_WTMPX_FILE) && defined(_PATH_WTMPX) +# define REAL_WTMPX_FILE _PATH_WTMPX +#endif +#if !defined(REAL_WTMPX_FILE) && defined(PATH_WTMPX_FILE) +# define REAL_WTMPX_FILE PATH_WTMPX_FILE +#endif + +/* Decide which structure to use. We use a structure that exists in * + * the headers, and require that its corresponding utmp file exist. * + * (wtmp is less important.) */ + +#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE) +# define WATCH_STRUCT_UTMP struct utmpx +# ifdef HAVE_STRUCT_UTMPX_UT_XTIME +# undef ut_time +# define ut_time ut_xtime +# else /* !HAVE_STRUCT_UTMPX_UT_XTIME */ +# ifdef HAVE_STRUCT_UTMPX_UT_TV +# undef ut_time +# define ut_time ut_tv.tv_sec +# endif /* HAVE_STRUCT_UTMPX_UT_TV */ +# endif /* !HAVE_STRUCT_UTMPX_UT_XTIME */ +# define WATCH_UTMP_FILE REAL_UTMPX_FILE +# ifdef REAL_WTMPX_FILE +# define WATCH_WTMP_FILE REAL_WTMPX_FILE +# endif +# ifdef HAVE_STRUCT_UTMPX_UT_HOST +# define WATCH_UTMP_UT_HOST 1 +# endif +#endif + +#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMP) && defined(REAL_UTMP_FILE) +# define WATCH_STRUCT_UTMP struct utmp +# define WATCH_UTMP_FILE REAL_UTMP_FILE +# ifdef REAL_WTMP_FILE +# define WATCH_WTMP_FILE REAL_WTMP_FILE +# endif +# ifdef HAVE_STRUCT_UTMP_UT_HOST +# define WATCH_UTMP_UT_HOST 1 +# endif +#endif + +#ifdef WATCH_UTMP_UT_HOST +# define DEFAULT_WATCHFMT "%n has %a %l from %m." +#else /* !WATCH_UTMP_UT_HOST */ +# define DEFAULT_WATCHFMT "%n has %a %l." +#endif /* !WATCH_UTMP_UT_HOST */ + +/**/ +char const * const default_watchfmt = DEFAULT_WATCHFMT; + +#ifdef WATCH_STRUCT_UTMP + +# include "watch.pro" + +# ifndef WATCH_WTMP_FILE +# define WATCH_WTMP_FILE "/dev/null" +# endif + +static int wtabsz; +static WATCH_STRUCT_UTMP *wtab; +static time_t lastutmpcheck; + +/* get the time of login/logout for WATCH */ + +/**/ +static time_t +getlogtime(WATCH_STRUCT_UTMP *u, int inout) +{ + FILE *in; + WATCH_STRUCT_UTMP uu; + int first = 1; + int srchlimit = 50; /* max number of wtmp records to search */ + + if (inout) + return u->ut_time; + if (!(in = fopen(WATCH_WTMP_FILE, "r"))) + return time(NULL); + fseek(in, 0, 2); + do { + if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), 1)) { + fclose(in); + return time(NULL); + } + first = 0; + if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) { + fclose(in); + return time(NULL); + } + if (uu.ut_time < lastwatch || !srchlimit--) { + fclose(in); + return time(NULL); + } + } + while (memcmp(&uu, u, sizeof(uu))); + + do + if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) { + fclose(in); + return time(NULL); + } + while (strncmp(uu.ut_line, u->ut_line, sizeof(u->ut_line))); + fclose(in); + return uu.ut_time; +} + +/* Mutually recursive call to handle ternaries in $WATCHFMT */ + +# define BEGIN3 '(' +# define END3 ')' + +/**/ +static char * +watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt) +{ + int truth = 1, sep; + + switch (*fmt++) { + case 'n': + truth = (u->ut_name[0] != 0); + break; + case 'a': + truth = inout; + break; + case 'l': + if (!strncmp(u->ut_line, "tty", 3)) + truth = (u->ut_line[3] != 0); + else + truth = (u->ut_line[0] != 0); + break; +# ifdef WATCH_UTMP_UT_HOST + case 'm': + case 'M': + truth = (u->ut_host[0] != 0); + break; +# endif /* WATCH_UTMP_UT_HOST */ + default: + prnt = 0; /* Skip unknown conditionals entirely */ + break; + } + sep = *fmt++; + fmt = watchlog2(inout, u, fmt, (truth && prnt), sep); + return watchlog2(inout, u, fmt, (!truth && prnt), END3); +} + +/* print a login/logout event */ + +/**/ +static char * +watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) +{ + char buf[40], buf2[80]; + time_t timet; + struct tm *tm; + char *fm2; +# ifdef WATCH_UTMP_UT_HOST + char *p; + int i; +# endif /* WATCH_UTMP_UT_HOST */ + + while (*fmt) + if (*fmt == '\\') + if (*++fmt) { + if (prnt) + putchar(*fmt); + ++fmt; + } else if (fini) + return fmt; + else + break; + else if (*fmt == fini) + return ++fmt; + else if (*fmt != '%') { + if (prnt) + putchar(*fmt); + ++fmt; + } else { + if (*++fmt == BEGIN3) + fmt = watch3ary(inout, u, ++fmt, prnt); + else if (!prnt) + ++fmt; + else + switch (*(fm2 = fmt++)) { + case 'n': + printf("%.*s", (int)sizeof(u->ut_name), u->ut_name); + break; + case 'a': + printf("%s", (!inout) ? "logged off" : "logged on"); + break; + case 'l': + if (!strncmp(u->ut_line, "tty", 3)) + printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3); + else + printf("%.*s", (int)sizeof(u->ut_line), u->ut_line); + break; +# ifdef WATCH_UTMP_UT_HOST + case 'm': + for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) { + if (*p == '.' && !idigit(p[1])) + break; + putchar(*p); + } + break; + case 'M': + printf("%.*s", (int)sizeof(u->ut_host), u->ut_host); + break; +# endif /* WATCH_UTMP_UT_HOST */ + case 'T': + case 't': + case '@': + case 'W': + case 'w': + case 'D': + switch (*fm2) { + case '@': + case 't': + fm2 = "%l:%M%p"; + break; + case 'T': + fm2 = "%K:%M"; + break; + case 'w': + fm2 = "%a %f"; + break; + case 'W': + fm2 = "%m/%d/%y"; + break; + case 'D': + if (fm2[1] == '{') { + char *dd, *ss; + int n = 79; + + for (ss = fm2 + 2, dd = buf2; + n-- && *ss && *ss != '}'; ++ss, ++dd) + *dd = *((*ss == '\\' && ss[1]) ? ++ss : ss); + if (*ss == '}') { + *dd = '\0'; + fmt = ss + 1; + fm2 = buf2; + } + else fm2 = "%y-%m-%d"; + } + else fm2 = "%y-%m-%d"; + break; + } + timet = getlogtime(u, inout); + tm = localtime(&timet); + ztrftime(buf, 40, fm2, tm); + printf("%s", (*buf == ' ') ? buf + 1 : buf); + break; + case '%': + putchar('%'); + break; + case 'S': + txtset(TXTSTANDOUT); + tsetcap(TCSTANDOUTBEG, -1); + break; + case 's': + txtset(TXTDIRTY); + txtunset(TXTSTANDOUT); + tsetcap(TCSTANDOUTEND, -1); + break; + case 'B': + txtset(TXTDIRTY); + txtset(TXTBOLDFACE); + tsetcap(TCBOLDFACEBEG, -1); + break; + case 'b': + txtset(TXTDIRTY); + txtunset(TXTBOLDFACE); + tsetcap(TCALLATTRSOFF, -1); + break; + case 'U': + txtset(TXTUNDERLINE); + tsetcap(TCUNDERLINEBEG, -1); + break; + case 'u': + txtset(TXTDIRTY); + txtunset(TXTUNDERLINE); + tsetcap(TCUNDERLINEEND, -1); + break; + default: + putchar('%'); + putchar(*fm2); + break; + } + } + if (prnt) + putchar('\n'); + + return fmt; +} + +/* check the List for login/logouts */ + +/**/ +static void +watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) +{ + char *v, *vv, sav; + int bad; + + if (!*u->ut_name) + return; + + if (*w && !strcmp(*w, "all")) { + (void)watchlog2(inout, u, fmt, 1, 0); + return; + } + if (*w && !strcmp(*w, "notme") && + strncmp(u->ut_name, get_username(), sizeof(u->ut_name))) { + (void)watchlog2(inout, u, fmt, 1, 0); + return; + } + for (; *w; w++) { + bad = 0; + v = *w; + if (*v != '@' && *v != '%') { + for (vv = v; *vv && *vv != '@' && *vv != '%'; vv++); + sav = *vv; + *vv = '\0'; + if (strncmp(u->ut_name, v, sizeof(u->ut_name))) + bad = 1; + *vv = sav; + v = vv; + } + for (;;) + if (*v == '%') { + for (vv = ++v; *vv && *vv != '@'; vv++); + sav = *vv; + *vv = '\0'; + if (strncmp(u->ut_line, v, sizeof(u->ut_line))) + bad = 1; + *vv = sav; + v = vv; + } +# ifdef WATCH_UTMP_UT_HOST + else if (*v == '@') { + for (vv = ++v; *vv && *vv != '%'; vv++); + sav = *vv; + *vv = '\0'; + if (strncmp(u->ut_host, v, strlen(v))) + bad = 1; + *vv = sav; + v = vv; + } +# endif /* WATCH_UTMP_UT_HOST */ + else + break; + if (!bad) { + (void)watchlog2(inout, u, fmt, 1, 0); + return; + } + } +} + +/* compare 2 utmp entries */ + +/**/ +static int +ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v) +{ + if (u->ut_time == v->ut_time) + return strncmp(u->ut_line, v->ut_line, sizeof(u->ut_line)); + return u->ut_time - v->ut_time; +} + +/* initialize the user List */ + +/**/ +static void +readwtab(void) +{ + WATCH_STRUCT_UTMP *uptr; + int wtabmax = 32; + FILE *in; + + wtabsz = 0; + if (!(in = fopen(WATCH_UTMP_FILE, "r"))) + return; + uptr = wtab = (WATCH_STRUCT_UTMP *)zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP)); + while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) +# ifdef USER_PROCESS + if (uptr->ut_type == USER_PROCESS) +# else /* !USER_PROCESS */ + if (uptr->ut_name[0]) +# endif /* !USER_PROCESS */ + { + uptr++; + if (++wtabsz == wtabmax) + uptr = (wtab = (WATCH_STRUCT_UTMP *)realloc((void *) wtab, (wtabmax *= 2) * + sizeof(WATCH_STRUCT_UTMP))) + wtabsz; + } + fclose(in); + + if (wtabsz) + qsort((void *) wtab, wtabsz, sizeof(WATCH_STRUCT_UTMP), + (int (*) _((const void *, const void *)))ucmp); +} + +/* Check for login/logout events; executed before * + * each prompt if WATCH is set */ + +/**/ +void +dowatch(void) +{ + FILE *in; + WATCH_STRUCT_UTMP *utab, *uptr, *wptr; + struct stat st; + char **s; + char *fmt; + int utabsz = 0, utabmax = wtabsz + 4; + int uct, wct; + + s = watch; + if (!(fmt = getsparam("WATCHFMT"))) + fmt = DEFAULT_WATCHFMT; + + holdintr(); + if (!wtab) { + readwtab(); + noholdintr(); + return; + } + if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) { + noholdintr(); + return; + } + lastutmpcheck = st.st_mtime; + uptr = utab = (WATCH_STRUCT_UTMP *) zalloc(utabmax * sizeof(WATCH_STRUCT_UTMP)); + + if (!(in = fopen(WATCH_UTMP_FILE, "r"))) { + free(utab); + noholdintr(); + return; + } + while (fread(uptr, sizeof *uptr, 1, in)) +# ifdef USER_PROCESS + if (uptr->ut_type == USER_PROCESS) +# else /* !USER_PROCESS */ + if (uptr->ut_name[0]) +# endif /* !USER_PROCESS */ + { + uptr++; + if (++utabsz == utabmax) + uptr = (utab = (WATCH_STRUCT_UTMP *)realloc((void *) utab, (utabmax *= 2) * + sizeof(WATCH_STRUCT_UTMP))) + utabsz; + } + fclose(in); + noholdintr(); + if (errflag) { + free(utab); + return; + } + if (utabsz) + qsort((void *) utab, utabsz, sizeof(WATCH_STRUCT_UTMP), + (int (*) _((const void *, const void *)))ucmp); + + wct = wtabsz; + uct = utabsz; + uptr = utab; + wptr = wtab; + if (errflag) { + free(utab); + return; + } + while ((uct || wct) && !errflag) + if (!uct || (wct && ucmp(uptr, wptr) > 0)) + wct--, watchlog(0, wptr++, s, fmt); + else if (!wct || (uct && ucmp(uptr, wptr) < 0)) + uct--, watchlog(1, uptr++, s, fmt); + else + uptr++, wptr++, wct--, uct--; + free(wtab); + wtab = utab; + wtabsz = utabsz; + fflush(stdout); +} + +/**/ +int +bin_log(char *nam, char **argv, char *ops, int func) +{ + if (!watch) + return 1; + if (wtab) + free(wtab); + wtab = (WATCH_STRUCT_UTMP *)zalloc(1); + wtabsz = 0; + lastutmpcheck = 0; + dowatch(); + return 0; +} + +#else /* !WATCH_STRUCT_UTMP */ + +/**/ +void dowatch(void) +{ +} + +/**/ +int +bin_log(char *nam, char **argv, char *ops, int func) +{ + return bin_notavail(nam, argv, ops, func); +} + +#endif /* !WATCH_STRUCT_UTMP */ |