From 271cfc685b17938e67a8212f2df78c32989411d7 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 2 Nov 2021 21:39:52 +0100 Subject: 49534, 49539: separate watch/log functionality out into a module --- Src/Modules/watch.c | 716 ++++++++++++++++++++++++++++++++++++++++++++++++++ Src/Modules/watch.mdd | 7 + Src/builtin.c | 1 - Src/init.c | 1 - Src/params.c | 16 +- Src/utils.c | 16 -- Src/watch.c | 626 ------------------------------------------- Src/zsh.mdd | 2 +- 8 files changed, 730 insertions(+), 655 deletions(-) create mode 100644 Src/Modules/watch.c create mode 100644 Src/Modules/watch.mdd delete mode 100644 Src/watch.c (limited to 'Src') diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c new file mode 100644 index 000000000..02f0562fc --- /dev/null +++ b/Src/Modules/watch.c @@ -0,0 +1,716 @@ +/* + * 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 "watch.mdh" + +/* Headers for utmp/utmpx structures */ +#ifdef HAVE_UTMP_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#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 +# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT) +# define setutent setutxent +# define getutent getutxent +# define endutent endutxent +# ifndef HAVE_GETUTENT +# define HAVE_GETUTENT 1 +# endif +# endif + +/* + * In utmpx, the ut_name field is replaced by ut_user. + * However, on some systems ut_name may already be defined this + * way for the purposes of utmp. + */ +# ifndef ut_name +# define ut_name ut_user +# endif +# 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 */ + +#ifdef WATCH_STRUCT_UTMP + +# include "watch.pro" + +# ifndef WATCH_WTMP_FILE +# define WATCH_WTMP_FILE "/dev/null" +# endif + +static int wtabsz = 0; +static WATCH_STRUCT_UTMP *wtab = NULL; + +/* the last time we checked the people in the WATCH variable */ +static time_t lastwatch; + +static time_t lastutmpcheck = 0; + +/* 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, SEEK_END); + do { + if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) { + 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; + int len; +# 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); + len = ztrftime(buf, 40, fm2, tm, 0L); + if (len > 0) + metafy(buf, len, META_NOALLOC); + printf("%s", (*buf == ' ') ? buf + 1 : buf); + break; + case '%': + putchar('%'); + break; + case 'S': + txtset(TXTSTANDOUT); + tsetcap(TCSTANDOUTBEG, TSC_RAW); + break; + case 's': + txtunset(TXTSTANDOUT); + tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); + break; + case 'B': + txtset(TXTBOLDFACE); + tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); + break; + case 'b': + txtunset(TXTBOLDFACE); + tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); + break; + case 'U': + txtset(TXTUNDERLINE); + tsetcap(TCUNDERLINEBEG, TSC_RAW); + break; + case 'u': + txtunset(TXTUNDERLINE); + tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); + break; + default: + putchar('%'); + putchar(*fm2); + break; + } + } + if (prnt) + putchar('\n'); + + return fmt; +} + +/* See if the watch entry matches */ + +static int +watchlog_match(char *teststr, char *actual, int len) +{ + int ret = 0; + Patprog pprog; + char *str = dupstring(teststr); + + tokenize(str); + + if ((pprog = patcompile(str, PAT_STATIC, 0))) { + queue_signals(); + if (pattry(pprog, actual)) + ret = 1; + unqueue_signals(); + } else if (!strncmp(actual, teststr, len)) + ret = 1; + return ret; +} + +/* 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 (!watchlog_match(v, u->ut_name, sizeof(u->ut_name))) + bad = 1; + *vv = sav; + v = vv; + } + for (;;) + if (*v == '%') { + for (vv = ++v; *vv && *vv != '@'; vv++); + sav = *vv; + *vv = '\0'; + if (!watchlog_match(v, u->ut_line, 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 (!watchlog_match(v, u->ut_host, 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 int +readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) +{ + WATCH_STRUCT_UTMP *uptr; + int wtabmax = initial_sz < 2 ? 32 : initial_sz; + int sz = 0; +# ifdef HAVE_GETUTENT + WATCH_STRUCT_UTMP *tmp; +# else + FILE *in; +# endif + + uptr = *head = (WATCH_STRUCT_UTMP *) + zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP)); +# ifdef HAVE_GETUTENT + setutent(); + while ((tmp = getutent()) != NULL) { + memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP)); +# else + if (!(in = fopen(WATCH_UTMP_FILE, "r"))) + return 0; + while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) { +# endif +# ifdef USER_PROCESS + if (uptr->ut_type == USER_PROCESS) +# else /* !USER_PROCESS */ + if (uptr->ut_name[0]) +# endif /* !USER_PROCESS */ + { + uptr++; + if (++sz == wtabmax) { + uptr = (WATCH_STRUCT_UTMP *) + realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP)); + if (uptr == NULL) { + /* memory pressure - so stop consuming and use, what we have + * Other option is to exit() here, as zmalloc does on error */ + sz--; + break; + } + *head = uptr; + uptr += sz; + } + } + } +# ifdef HAVE_GETUTENT + endutent(); +# else + fclose(in); +# endif + + if (sz) + qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP), + (int (*) _((const void *, const void *)))ucmp); + return sz; +} + +/* Check for login/logout events; executed before * + * each prompt if WATCH is set */ + +/**/ +void +dowatch(void) +{ + WATCH_STRUCT_UTMP *utab, *uptr, *wptr; + struct stat st; + char **s; + char *fmt; + int utabsz, uct, wct; + + s = watch; + + holdintr(); + if (!wtab) + wtabsz = readwtab(&wtab, 32); + if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) { + noholdintr(); + return; + } + lastutmpcheck = st.st_mtime; + utabsz = readwtab(&utab, wtabsz + 4); + noholdintr(); + if (errflag) { + free(utab); + return; + } + + wct = wtabsz; + uct = utabsz; + uptr = utab; + wptr = wtab; + if (errflag) { + free(utab); + return; + } + queue_signals(); + if (!(fmt = getsparam_u("WATCHFMT"))) + fmt = DEFAULT_WATCHFMT; + 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--; + } + unqueue_signals(); + free(wtab); + wtab = utab; + wtabsz = utabsz; + fflush(stdout); + lastwatch = time(NULL); +} + +static void +checksched(void) +{ + /* Do nothing if WATCH is not set, or LOGCHECK has not elapsed */ + if (watch && (int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) + dowatch(); +} + +/**/ +static int +bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(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 */ + +static void +checksched(void) +{ +} + +/**/ +static int +bin_log(char *nam, char **argv, Options ops, int func) +{ + return bin_notavail(nam, argv, ops, func); +} + +#endif /* !WATCH_STRUCT_UTMP */ + +/**/ +static char **watch; /* $watch */ + +/* module setup */ + +static struct builtin bintab[] = { + BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), +}; + +static struct paramdef partab[] = { + PARAMDEF("WATCH", PM_TIED|PM_SCALAR|PM_SPECIAL, &watch, &colonarr_gsu), + PARAMDEF("watch", PM_TIED|PM_ARRAY|PM_SPECIAL, &watch, &vararray_gsu), +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(UNUSED(Module m)) +{ + static char const * const default_watchfmt = DEFAULT_WATCHFMT; + Param pm; + + if ((pm = (Param) paramtab->getnode(paramtab, "watch"))) + pm->ename = "WATCH"; + if ((pm = (Param) paramtab->getnode(paramtab, "WATCH"))) + pm->ename = "watch"; + watch = mkarray(NULL); + + /* These two parameters are only set to defaults if not set. + * So setting them in .zshrc will not be enough to load the + * module. It's useless until the watch array is set anyway. */ + if (!paramtab->getnode(paramtab, "WATCHFMT")) + setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); + if (!paramtab->getnode(paramtab, "LOGCHECK")) + setiparam("LOGCHECK", 60); + + addprepromptfn(&checksched); + + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + delprepromptfn(&checksched); + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/watch.mdd b/Src/Modules/watch.mdd new file mode 100644 index 000000000..7e8454ede --- /dev/null +++ b/Src/Modules/watch.mdd @@ -0,0 +1,7 @@ +name=zsh/watch +link=dynamic +load=yes + +autofeatures="b:log p:WATCH p:watch" + +objects="watch.o" diff --git a/Src/builtin.c b/Src/builtin.c index 89bcd98db..8ef678b22 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -89,7 +89,6 @@ static struct builtin builtins[] = BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), - BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) diff --git a/Src/init.c b/Src/init.c index 878a53a37..871d46b12 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1042,7 +1042,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #endif /* FPATH_NEEDS_INIT */ mailpath = mkarray(NULL); - watch = mkarray(NULL); psvar = mkarray(NULL); module_path = mkarray(ztrdup(MODULE_DIR)); modulestab = newmoduletable(17, "modules"); diff --git a/Src/params.c b/Src/params.c index b703a97ce..dadf83129 100644 --- a/Src/params.c +++ b/Src/params.c @@ -63,7 +63,6 @@ char **pparams, /* $argv */ **mailpath, /* $mailpath */ **manpath, /* $manpath */ **psvar, /* $psvar */ - **watch, /* $watch */ **zsh_eval_context; /* $zsh_eval_context */ /**/ mod_export @@ -194,6 +193,10 @@ mod_export const struct gsu_hash stdhash_gsu = mod_export const struct gsu_hash nullsethash_gsu = { hashgetfn, nullsethashfn, nullunsetfn }; +/**/ +mod_export const struct gsu_scalar colonarr_gsu = +{ colonarrgetfn, colonarrsetfn, stdunsetfn }; + /* Non standard methods (not exported) */ static const struct gsu_integer pound_gsu = @@ -259,9 +262,6 @@ static const struct gsu_integer varint_readonly_gsu = static const struct gsu_integer zlevar_gsu = { intvargetfn, zlevarsetfn, stdunsetfn }; -static const struct gsu_scalar colonarr_gsu = -{ colonarrgetfn, colonarrsetfn, stdunsetfn }; - static const struct gsu_integer argc_gsu = { poundgetfn, nullintsetfn, stdunsetfn }; static const struct gsu_array pipestatus_gsu = @@ -398,7 +398,6 @@ IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED), IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED), IPDEF8("FPATH", &fpath, "fpath", PM_TIED), IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED), -IPDEF8("WATCH", &watch, "watch", PM_TIED), IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED), IPDEF8("PSVAR", &psvar, "psvar", PM_TIED), IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED), @@ -430,7 +429,6 @@ IPDEF9("fpath", &fpath, "FPATH", PM_TIED), IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED), IPDEF9("manpath", &manpath, "MANPATH", PM_TIED), IPDEF9("psvar", &psvar, "PSVAR", PM_TIED), -IPDEF9("watch", &watch, "WATCH", PM_TIED), IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL), @@ -453,7 +451,6 @@ IPDEF8("CDPATH", &cdpath, NULL, 0), IPDEF8("FIGNORE", &fignore, NULL, 0), IPDEF8("FPATH", &fpath, NULL, 0), IPDEF8("MAILPATH", &mailpath, NULL, 0), -IPDEF8("WATCH", &watch, NULL, 0), IPDEF8("PATH", &path, NULL, PM_RESTRICTED), IPDEF8("PSVAR", &psvar, NULL, 0), IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL), @@ -836,7 +833,6 @@ createparamtable(void) */ setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX)); setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT)); - setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); hostnam = (char *)zalloc(256); gethostname(hostnam, 256); @@ -4093,7 +4089,7 @@ arrvarsetfn(Param pm, char **x) } /**/ -char * +mod_export char * colonarrgetfn(Param pm) { char ***dptr = (char ***)pm->u.data; @@ -4101,7 +4097,7 @@ colonarrgetfn(Param pm) } /**/ -void +mod_export void colonarrsetfn(Param pm, char *x) { char ***dptr = (char ***)pm->u.data; diff --git a/Src/utils.c b/Src/utils.c index ed3690172..8adab2bd7 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1494,11 +1494,6 @@ deltimedfn(voidvoidfnptr_t func) /**/ time_t lastmailcheck; -/* the last time we checked the people in the WATCH variable */ - -/**/ -time_t lastwatch; - /* * Call a function given by "name" with optional arguments * "lnklst". If these are present the first argument is the function name. @@ -1637,17 +1632,6 @@ preprompt(void) if (errflag) return; - /* If WATCH is set, then check for the * - * specified login/logout events. */ - if (watch) { - if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) { - dowatch(); - lastwatch = time(NULL); - } - } - if (errflag) - return; - /* Check mail */ currentmailcheck = time(NULL); if (mailcheck && diff --git a/Src/watch.c b/Src/watch.c deleted file mode 100644 index c41704315..000000000 --- a/Src/watch.c +++ /dev/null @@ -1,626 +0,0 @@ -/* - * 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 -#endif -#ifdef HAVE_UTMPX_H -# include -#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 -# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT) -# define setutent setutxent -# define getutent getutxent -# define endutent endutxent -# ifndef HAVE_GETUTENT -# define HAVE_GETUTENT 1 -# endif -# endif - -/* - * In utmpx, the ut_name field is replaced by ut_user. - * However, on some systems ut_name may already be defined this - * way for the purposes of utmp. - */ -# ifndef ut_name -# define ut_name ut_user -# endif -# 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 = 0; -static WATCH_STRUCT_UTMP *wtab = NULL; -static time_t lastutmpcheck = 0; - -/* 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, SEEK_END); - do { - if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) { - 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; - int len; -# 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); - len = ztrftime(buf, 40, fm2, tm, 0L); - if (len > 0) - metafy(buf, len, META_NOALLOC); - printf("%s", (*buf == ' ') ? buf + 1 : buf); - break; - case '%': - putchar('%'); - break; - case 'S': - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_RAW); - break; - case 's': - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); - break; - case 'B': - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); - break; - case 'b': - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); - break; - case 'U': - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_RAW); - break; - case 'u': - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); - break; - default: - putchar('%'); - putchar(*fm2); - break; - } - } - if (prnt) - putchar('\n'); - - return fmt; -} - -/* See if the watch entry matches */ - -static int -watchlog_match(char *teststr, char *actual, int len) -{ - int ret = 0; - Patprog pprog; - char *str = dupstring(teststr); - - tokenize(str); - - if ((pprog = patcompile(str, PAT_STATIC, 0))) { - queue_signals(); - if (pattry(pprog, actual)) - ret = 1; - unqueue_signals(); - } else if (!strncmp(actual, teststr, len)) - ret = 1; - return ret; -} - -/* 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 (!watchlog_match(v, u->ut_name, sizeof(u->ut_name))) - bad = 1; - *vv = sav; - v = vv; - } - for (;;) - if (*v == '%') { - for (vv = ++v; *vv && *vv != '@'; vv++); - sav = *vv; - *vv = '\0'; - if (!watchlog_match(v, u->ut_line, 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 (!watchlog_match(v, u->ut_host, 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 int -readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) -{ - WATCH_STRUCT_UTMP *uptr; - int wtabmax = initial_sz < 2 ? 32 : initial_sz; - int sz = 0; -# ifdef HAVE_GETUTENT - WATCH_STRUCT_UTMP *tmp; -# else - FILE *in; -# endif - - uptr = *head = (WATCH_STRUCT_UTMP *) - zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP)); -# ifdef HAVE_GETUTENT - setutent(); - while ((tmp = getutent()) != NULL) { - memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP)); -# else - if (!(in = fopen(WATCH_UTMP_FILE, "r"))) - return 0; - while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) { -# endif -# ifdef USER_PROCESS - if (uptr->ut_type == USER_PROCESS) -# else /* !USER_PROCESS */ - if (uptr->ut_name[0]) -# endif /* !USER_PROCESS */ - { - uptr++; - if (++sz == wtabmax) { - uptr = (WATCH_STRUCT_UTMP *) - realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP)); - if (uptr == NULL) { - /* memory pressure - so stop consuming and use, what we have - * Other option is to exit() here, as zmalloc does on error */ - sz--; - break; - } - *head = uptr; - uptr += sz; - } - } - } -# ifdef HAVE_GETUTENT - endutent(); -# else - fclose(in); -# endif - - if (sz) - qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP), - (int (*) _((const void *, const void *)))ucmp); - return sz; -} - -/* Check for login/logout events; executed before * - * each prompt if WATCH is set */ - -/**/ -void -dowatch(void) -{ - WATCH_STRUCT_UTMP *utab, *uptr, *wptr; - struct stat st; - char **s; - char *fmt; - int utabsz, uct, wct; - - s = watch; - - holdintr(); - if (!wtab) - wtabsz = readwtab(&wtab, 32); - if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) { - noholdintr(); - return; - } - lastutmpcheck = st.st_mtime; - utabsz = readwtab(&utab, wtabsz + 4); - noholdintr(); - if (errflag) { - free(utab); - return; - } - - wct = wtabsz; - uct = utabsz; - uptr = utab; - wptr = wtab; - if (errflag) { - free(utab); - return; - } - queue_signals(); - if (!(fmt = getsparam_u("WATCHFMT"))) - fmt = DEFAULT_WATCHFMT; - 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--; - } - unqueue_signals(); - free(wtab); - wtab = utab; - wtabsz = utabsz; - fflush(stdout); -} - -/**/ -int -bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(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, Options ops, int func) -{ - return bin_notavail(nam, argv, ops, func); -} - -#endif /* !WATCH_STRUCT_UTMP */ diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 9bcaccae5..da8d58322 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -13,7 +13,7 @@ objects="builtin.o compat.o cond.o context.o \ exec.o glob.o hashtable.o hashnameddir.o \ hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \ mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \ -signames.o sort.o string.o subst.o text.o utils.o watch.o \ +signames.o sort.o string.o subst.o text.o utils.o \ openssh_bsd_setres_id.o" headers="../config.h zsh_system.h zsh.h sigcount.h signals.h \ -- cgit 1.4.1