mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-18 21:14:11 +01:00
8776b52d7d
ut_name needs to be ut_user in struct utmpx
598 lines
14 KiB
C
598 lines
14 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
|
|
/*
|
|
* In utmpx, the ut_name field is replaced by ut_user.
|
|
* Howver, 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;
|
|
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(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 */
|