1
0
mirror of https://github.com/zsh-users/zsh synced 2024-07-08 19:55:44 +00:00
zsh/Src/watch.c
Peter Stephenson 9634760d5e 17582: Improved option argument handling.
unposted: Updated version to 4.1.0-dev-6 because of interface change.
2002-08-27 21:10:30 +00:00

590 lines
13 KiB
C

/*
* 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;
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;
}
queue_signals();
if (!(fmt = getsparam("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(char *nam, char **argv, Options 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, Options ops, int func)
{
return bin_notavail(nam, argv, ops, func);
}
#endif /* !WATCH_STRUCT_UTMP */