2.11BSD/src/bin/tcsh/tc.who.c
/* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/tc.who.c,v 3.0 1991/07/04 21:49:28 christos Exp $ */
/*
* tc.who.c: Watch logins and logouts...
*/
/*-
* Copyright (c) 1980, 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#if !defined(lint) && !defined(pdp11)
static char *rcsid()
{ return "$Id: tc.who.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; }
#endif
#include "sh.h"
/*
* kfk 26 Jan 1984 - for login watch functions.
*/
#include <ctype.h>
#include <utmp.h>
#ifndef BROKEN_CC
# define UTNAMLEN sizeof(((struct utmp *) 0)->ut_name)
# define UTLINLEN sizeof(((struct utmp *) 0)->ut_line)
# ifdef UTHOST
# ifdef _SEQUENT_
# define UTHOSTLEN 100
# else
# define UTHOSTLEN sizeof(((struct utmp *) 0)->ut_host)
# endif
# endif /* UTHOST */
#else
/* give poor cc a little help if it needs it */
struct utmp __ut;
# define UTNAMLEN sizeof(__ut.ut_name)
# define UTLINLEN sizeof(__ut.ut_line)
# ifdef UTHOST
# ifdef _SEQUENT_
# define UTHOSTLEN 100
# else
# define UTHOSTLEN sizeof(__ut.ut_host)
# endif
# endif /* UTHOST */
#endif /* BROKEN_CC */
#ifndef _PATH_UTMP
# ifdef UTMP_FILE
# define _PATH_UTMP UTMP_FILE
# else
# define _PATH_UTMP "/var/run/utmp"
# endif /* UTMP_FILE */
#endif /* _PATH_UTMP */
struct who {
struct who *w_next;
struct who *w_prev;
char w_name[UTNAMLEN + 1];
char w_new[UTNAMLEN + 1];
char w_tty[UTLINLEN + 1];
#ifdef UTHOST
char w_host[UTHOSTLEN + 1];
#endif /* UTHOST */
long w_time;
int w_status;
};
static struct who *wholist = NULL;
static int watch_period = 0;
static time_t stlast = 0;
extern char *month_list[];
#ifdef WHODEBUG
static void debugwholist __P((struct who *, struct who *));
#endif
static void print_who __P((struct who *));
#define ONLINE 01
#define OFFLINE 02
#define CHANGED 04
#define STMASK 07
#define ANNOUNCE 010
/*
* Karl Kleinpaste, 26 Jan 1984.
* Initialize the dummy tty list for login watch.
* This dummy list eliminates boundary conditions
* when doing pointer-chase searches.
*/
void
initwatch()
{
register int i;
wholist = (struct who *) xcalloc(1, sizeof *wholist);
wholist->w_next = (struct who *) xcalloc(1, sizeof *wholist);
wholist->w_next->w_prev = wholist;
for (i = 0; i < UTLINLEN; i++) {
wholist->w_tty[i] = '\01';
wholist->w_next->w_tty[i] = '~';
}
wholist->w_tty[i] = '\0';
wholist->w_next->w_tty[i] = '\0';
#ifdef WHODEBUG
debugwholist(NULL, NULL);
#endif /* WHODEBUG */
}
void
resetwatch()
{
watch_period = 0;
stlast = 0;
}
/*
* Karl Kleinpaste, 26 Jan 1984.
* Watch /var/run/utmp for login/logout changes.
*/
void
watch_login()
{
int utmpfd, comp, alldone;
#ifdef BSDSIGS
sigmask_t omask;
#endif /* BSDSIGS */
struct utmp utmp;
struct who *wp, *wpnew;
struct varent *v;
Char **vp;
time_t t, interval = MAILINTVL;
struct stat sta;
#if defined(UTHOST) && defined(_SEQUENT_)
char *host, *ut_find_host();
#endif
/* stop SIGINT, lest our login list get trashed. */
#ifdef BSDSIGS
omask = sigblock(sigmask(SIGINT));
#else
(void) sighold(SIGINT);
#endif
v = adrof(STRwatch);
if (v == NULL) {
#ifdef BSDSIGS
(void) sigsetmask(omask);
#else
(void) sigrelse(SIGINT);
#endif
return; /* no names to watch */
}
vp = v->vec;
if (blklen(vp) % 2) /* odd # args: 1st == # minutes. */
interval = (number(*vp)) ? getn(*vp++) : MAILINTVL;
(void) time(&t);
if (t - watch_period < interval * 60) {
#ifdef BSDSIGS
(void) sigsetmask(omask);
#else
(void) sigrelse(SIGINT);
#endif
return; /* not long enough yet... */
}
watch_period = t;
/*
* From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
* Don't open utmp all the time, stat it first...
*/
if (stat(_PATH_UTMP, &sta)) {
xprintf("cannot stat %s. Please \"unset watch\".\n", _PATH_UTMP);
#ifdef BSDSIGS
(void) sigsetmask(omask);
#else
(void) sigrelse(SIGINT);
#endif
return;
}
if (stlast == sta.st_mtime) {
#ifdef BSDSIGS
(void) sigsetmask(omask);
#else
(void) sigrelse(SIGINT);
#endif
return;
}
stlast = sta.st_mtime;
if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) {
xprintf("%s cannot be opened. Please \"unset watch\".\n", _PATH_UTMP);
#ifdef BSDSIGS
(void) sigsetmask(omask);
#else
(void) sigrelse(SIGINT);
#endif
return;
}
/*
* xterm clears the entire utmp entry - mark everyone on the status list
* OFFLINE or we won't notice X "logouts"
*/
for (wp = wholist; wp != NULL; wp = wp->w_next) {
wp->w_status = OFFLINE;
wp->w_time = 0;
}
/*
* Read in the utmp file, sort the entries, and update existing entries or
* add new entries to the status list.
*/
while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) {
#ifdef DEAD_PROCESS
# ifndef IRIS4D
if (utmp.ut_type != USER_PROCESS)
continue;
# else
/* Why is that? Cause the utmp file is always corrupted??? */
if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
continue;
# endif /* IRIS4D */
#endif /* DEAD_PROCESS */
if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
continue; /* completely void entry */
#ifdef DEAD_PROCESS
if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
continue;
#endif /* DEAD_PROCESS */
wp = wholist;
while ((comp = strncmp(wp->w_tty, utmp.ut_line, UTLINLEN)) < 0)
wp = wp->w_next;/* find that tty! */
if (comp == 0) { /* found the tty... */
#ifdef DEAD_PROCESS
if (utmp.ut_type == DEAD_PROCESS) {
wp->w_time = utmp.ut_time;
wp->w_status = OFFLINE;
}
else
#endif /* DEAD_PROCESS */
if (utmp.ut_name[0] == '\0') {
wp->w_time = utmp.ut_time;
wp->w_status = OFFLINE;
}
else if (strncmp(utmp.ut_name, wp->w_name, UTNAMLEN) == 0) {
/* someone is logged in */
wp->w_time = utmp.ut_time;
wp->w_status = 0; /* same guy */
}
else {
(void) strncpy(wp->w_new, utmp.ut_name, UTNAMLEN);
#ifdef UTHOST
# ifdef _SEQUENT_
host = ut_find_host(wp->w_tty);
if (host)
(void) strncpy(wp->w_host, host, UTHOSTLEN);
else
wp->w_host[0] = 0;
# else
(void) strncpy(wp->w_host, utmp.ut_host, UTHOSTLEN);
# endif
#endif /* UTHOST */
wp->w_time = utmp.ut_time;
if (wp->w_name[0] == '\0')
wp->w_status = ONLINE;
else
wp->w_status = CHANGED;
}
}
else { /* new tty in utmp */
wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
(void) strncpy(wpnew->w_tty, utmp.ut_line, UTLINLEN);
#ifdef UTHOST
# ifdef _SEQUENT_
host = ut_find_host(wpnew->w_tty);
if (host)
(void) strncpy(wpnew->w_host, host, UTHOSTLEN);
else
wpnew->w_host[0] = 0;
# else
(void) strncpy(wpnew->w_host, utmp.ut_host, UTHOSTLEN);
# endif
#endif /* UTHOST */
wpnew->w_time = utmp.ut_time;
#ifdef DEAD_PROCESS
if (utmp.ut_type == DEAD_PROCESS)
wpnew->w_status = OFFLINE;
else
#endif /* DEAD_PROCESS */
if (utmp.ut_name[0] == '\0')
wpnew->w_status = OFFLINE;
else {
(void) strncpy(wpnew->w_new, utmp.ut_name, UTNAMLEN);
wpnew->w_status = ONLINE;
}
#ifdef WHODEBUG
debugwholist(wpnew, wp);
#endif /* WHODEBUG */
wpnew->w_next = wp; /* link in a new 'who' */
wpnew->w_prev = wp->w_prev;
wpnew->w_prev->w_next = wpnew;
wp->w_prev = wpnew; /* linked in now */
}
}
(void) close(utmpfd);
#if defined(UTHOST) && defined(_SEQUENT_)
endutent();
#endif
/*
* The state of all logins is now known, so we can search the user's list
* of watchables to print the interesting ones.
*/
for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
*(vp + 1) != NULL && **(vp + 1) != '\0';
vp += 2) { /* args used in pairs... */
if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
alldone = 1;
for (wp = wholist; wp != NULL; wp = wp->w_next) {
if (wp->w_status & ANNOUNCE ||
(!eq(*vp, STRany) &&
!eq(*vp, str2short(wp->w_name)) &&
!eq(*vp, str2short(wp->w_new))) ||
(!eq(*(vp + 1), str2short(wp->w_tty)) &&
!eq(*(vp + 1), STRany)))
continue; /* entry doesn't qualify */
/* already printed or not right one to print */
if (wp->w_time == 0)/* utmp entry was cleared */
wp->w_time = watch_period;
if ((wp->w_status & OFFLINE) &&
(wp->w_name[0] != '\0')) {
print_who(wp);
wp->w_name[0] = '\0';
wp->w_status |= ANNOUNCE;
continue;
}
if (wp->w_status & ONLINE) {
print_who(wp);
(void) strcpy(wp->w_name, wp->w_new);
wp->w_status |= ANNOUNCE;
continue;
}
if (wp->w_status & CHANGED) {
print_who(wp);
(void) strcpy(wp->w_name, wp->w_new);
wp->w_status |= ANNOUNCE;
continue;
}
}
}
#ifdef BSDSIGS
(void) sigsetmask(omask);
#else
(void) sigrelse(SIGINT);
#endif
}
#ifdef WHODEBUG
static oid
debugwholist(new, wp)
register struct who *new, *wp;
{
register struct who *a;
a = wholist;
while (a != NULL) {
xprintf("%s/%s -> ", a->w_name, a->w_tty);
a = a->w_next;
}
xprintf("NULL\n");
a = wholist;
xprintf("backward: ");
while (a->w_next != NULL)
a = a->w_next;
while (a != NULL) {
xprintf("%s/%s -> ", a->w_name, a->w_tty);
a = a->w_prev;
}
xprintf("NULL\n");
if (new)
xprintf("new: %s/%s\n", new->w_name, new->w_tty);
if (wp)
xprintf("wp: %s/%s\n", wp->w_name, wp->w_tty);
}
#endif /* WHODEBUG */
static void
print_who(wp)
struct who *wp;
{
#ifdef UTHOST
char *cp = "%n has %a %l from %m.";
char *ptr, flg;
#else
char *cp = "%n has %a %l.";
#endif /* UTHOST */
struct varent *vp = adrof(STRwho);
struct tm *t;
char ampm = 'a';
int attributes = 0;
t = localtime(&wp->w_time);
if (vp && vp->vec[0])
cp = short2str(vp->vec[0]);
for (; *cp; cp++)
if (*cp != '%')
xputchar(*cp | attributes);
else
switch (*++cp) {
case 'n': /* user name */
switch (wp->w_status & STMASK) {
case ONLINE:
case CHANGED:
xprintf("%a%s", attributes, wp->w_new);
break;
case OFFLINE:
xprintf("%a%s", attributes, wp->w_name);
break;
}
break;
case 'a':
switch (wp->w_status & STMASK) {
case ONLINE:
xprintf("%a%s", attributes, "logged on");
break;
case OFFLINE:
xprintf("%a%s", attributes, "logged off");
break;
case CHANGED:
xprintf("%areplaced %s on", attributes, wp->w_name);
break;
}
break;
case 'S': /* start standout */
attributes |= STANDOUT;
break;
case 'B': /* start bold */
attributes |= BOLD;
break;
case 'U': /* start underline */
attributes |= UNDER;
break;
case 's': /* end standout */
attributes &= ~STANDOUT;
break;
case 'b': /* end bold */
attributes &= ~BOLD;
break;
case 'u': /* end underline */
attributes &= ~UNDER;
break;
case 't':
case 'T':
case '@':
if (adrof(STRampm) || *cp != 'T') {
int hr = t->tm_hour;
if (hr >= 12) {
if (hr > 12)
hr -= 12;
ampm = 'p';
}
else if (hr == 0)
hr = 12;
xprintf("%a%d:%02d%cm", attributes, hr, t->tm_min, ampm);
}
else
xprintf("%a%d:%02d", attributes, t->tm_hour, t->tm_min);
break;
case 'w':
xprintf("%a%s %d", attributes, month_list[t->tm_mon],
t->tm_mday);
break;
case 'W':
xprintf("%a%02d/%02d/%02d", attributes,
t->tm_mon + 1, t->tm_mday, t->tm_year);
break;
case 'D':
xprintf("%a%02d-%02d-%02d", attributes,
t->tm_year, t->tm_mon + 1, t->tm_mday);
break;
case 'l':
xprintf("%a%s", attributes, wp->w_tty);
break;
#ifdef UTHOST
case 'm':
if (wp->w_host[0] == '\0')
xprintf("%alocal", attributes);
else
/* the ':' stuff is for <host>:<display>.<screen> */
for (ptr = wp->w_host, flg = Isdigit(*ptr) ? '\0' : '.';
*ptr != '\0' &&
(*ptr != flg || ((ptr = strchr(ptr, ':')) != 0));
ptr++) {
if (*ptr == ':')
flg = '\0';
xputchar((int)
((Isupper(*ptr) ? Tolower(*ptr) : *ptr) |
attributes));
}
break;
case 'M':
if (wp->w_host[0] == '\0')
xprintf("%alocal", attributes);
else
for (ptr = wp->w_host; *ptr != '\0'; ptr++)
xputchar((int)
((Isupper(*ptr) ? Tolower(*ptr) : *ptr) |
attributes));
break;
#endif /* UTHOST */
default:
xputchar('%' | attributes);
xputchar(*cp | attributes);
break;
}
xputchar('\n');
} /* end print_who */
void
/*ARGSUSED*/
dolog(v)
Char **v;
{
struct who *wp;
struct varent *vp;
if ((vp = adrof(STRwatch)) == NULL)
stderror(ERR_NOWATCH);
blkpr(vp->vec);
xprintf("\n");
watch_period = 0;
stlast = 0;
wp = wholist;
while (wp != NULL) {
wp->w_name[0] = '\0';
wp = wp->w_next;
}
}