home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume16
/
fido
/
fido.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-01
|
19KB
|
808 lines
/*
* Copyright 1988 Philip L. Budne.
* All rights reserved.
*
* This program may be freely distributed as long as this notice
* and the above Copyright remain intact.
*
* The author makes no claims of suitability for use.
*/
/*
* Phil Budne @ BU/DSG
*
* prehistoric users list compiled into struct. machine
* watching was a seperate program.
* 8/1/86 read .fido file
* 1/87 check ppid
* 8/14/87 read rwho files directly (used to popen rwho!)
* 8/20/87 add beep again, add idletime
* rework -- put found structure in target
* report found users once -- now way to
* track where someone is gone from. report idle.
* 8/21/87 add machines, hungry
* 4/19/88 look in HOME for .fido, quit if not found
* stat WHODIR, keep list of files to avoid
* re-reading spooldir constantly!
* 8/6/88 convert to open mhash. malloc targets.
* add stomp. remove BEEP, STOMP, MT, HOST, AT
* use static filenamestrings.
* 8/8/88! keep boottime and sendtime in struct machine.
* report reboots.
* 8/9/88 All dressed up. Ready to go!
*/
/* do reverse video for machines? */
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/dir.h>
# include <strings.h>
# include <stdio.h>
# include <ctype.h>
# include <protocols/rwhod.h> /* But not on 4.2 systems!! */
# define EOS '\0'
extern char *malloc(); /* from libc */
# define bool int
# define TRUE 1
# define FALSE 0
# define INITFILE ".fido"
# define DEF_BEEP FALSE
# define DEF_STAMP TRUE
# define DEF_MACHINES TRUE
# define DEF_DOWNTIME 8 /* minutes (less than 4 is noisy) */
# define DEF_IDLETIME 15 /* minutes */
# define DEF_SLEEPTIME 60 /* seconds */
# define MIN_SLEEPTIME 32
# define MAXBUF 132 /* various buffers */
/* static lengths in target struct (fields from utmp) */
# define MAXHST 33
# define MAXUSR 9
# define MAXTTY 9
# define NUMFILES 100 /* grrr -- a hardwired limit!! */
# define BPHOST 32 /* avg length for hostnames ick!! */
struct target {
char *host, *user;
struct found { /* count of entries found in each state */
int nidle, idle, hungry;
} found[2]; /* now and before */
struct target *tnext;
};
# define TOTAL(tp,t) ((tp)->found[t].idle + (tp)->found[t].nidle)
struct target *targets; /* list of targets */
struct target *lasttarget; /* last target created */
int numt = 0; /* number of targets */
int this = 0; /* toggling index (0/1) into found array */
char wildhost[] = "*";
# define WILDHOST wildhost /* empty host for targets */
int beep = DEF_BEEP;
int stamp = DEF_STAMP;
int machines = DEF_MACHINES;
int downtime = DEF_DOWNTIME * 60;
int idletime = DEF_IDLETIME * 60;
int sleeptime = DEF_SLEEPTIME;
char nowstr[ 50 ]; /* for time stomping */
struct machine { /* hash table of machines */
char name[40];
enum { UP=1, DOWN=2 } state;
time_t latest; /* last time seen up */
int boottime, sendtime; /* from whod packet */
struct machine
*hnext, /* next in hash chain */
*anext; /* next in list of all hosts */
};
# define MHSIZE 101 /* small prime */
struct machine *mhash[ MHSIZE ], *allmachines;
int firstpass = TRUE;
# if DEBUG > 0
char *state_name[] = { "??", "UP", "DOWN" };
# endif /* DEBUG > 0 */
# define WHODIR "/usr/spool/rwho"
# define ISHUNGRY(we) \
((we->we_utmp.out_line[sizeof(we->we_utmp.out_line)-1] & 0200) != 0 )
# define CLEARHUNGRY(we) \
we->we_utmp.out_line[sizeof(we->we_utmp.out_line)-1] &= ~0200
main() {
int i;
if( !readinit( INITFILE ) ) { /* check current dir */
extern char *getenv();
char tname[ 1024 ], *hp;
if( (hp = getenv("HOME")) == NULL ) { /* check 'home' */
/* getpwuid(getuid()) */
fprintf( stderr, "No ./.fido and HOME not set\n" );
exit( 1 );
} /* no HOME */
sprintf( tname, "%s/%s", hp, INITFILE );
if( !readinit( tname ) ) {
fprintf( stderr, "No ./.fido or %s\n", tname );
exit( 1 );
} /* readinit failed */
} /* no .fido in "." */
# if DEBUG > 0
dumpinit();
# endif /* DEBUG > 0 */
if( numt == 0 && !machines ) {
fprintf(stderr, "Nothing to do!!\n" );
exit( 1 );
} /* nothing to do!! */
if( chdir( WHODIR ) < 0 ) {
perror( WHODIR );
exit( 1 );
} /* chdir failed */
nice( 10 ); /* be nice to real users */
for( ; ; ) {
dowho(); /* prowl rwho spool dir */
if( machines ) /* watching machines? */
scan(); /* yell about them */
this = !this; /* toggle found index */
fflush( stdout );
sleep( sleeptime ); /* go to sleep */
if( getppid() == 1 ) /* child of init? */
exit( 1 ); /* quitting time */
} /* for ever */
} /* main */
dowho() { /* poke thru rwho spool */
register int i;
register struct target *tp;
int now;
char **fp;
struct whod wd;
struct stat newstat;
char buf[MAXBUF], user[MAXUSR], host[MAXHST], tty[MAXTTY];
static int nfiles;
static struct stat oldstat;
static char *filenames[ NUMFILES ]; /* ACK!! hardwired limits! */
static char filenamestrings[ NUMFILES * BPHOST ]; /* avoid malloc/free */
if( stat( ".", &newstat ) < 0 )
return;
if( newstat.st_mtime != oldstat.st_mtime ) {
DIR *dirp;
struct direct *d;
register char *dp;
oldstat = newstat;
# if DEBUG > 0
puts("(re)reading RWHODIR");
# endif /* DEBUG > 0 */
if( (dirp = opendir( "." )) == NULL )
return;
nfiles = 0;
fp = filenames;
dp = filenamestrings;
while( (d = readdir( dirp )) != NULL ) {
register char *sp;
if( strncmp( d->d_name, "whod.", 5 ) != 0 )
continue;
# if DEBUG > 4
printf("readdir: %s\n", d->d_name );
# endif /* DEBUG > 4 */
if( nfiles == NUMFILES ) {
fprintf( stderr, "Too many files!! Increase NUMFILES!!\n");
break;
} /* too many files */
nfiles++;
*fp++ = dp;
sp = d->d_name;
while( *dp++ = *sp++ )
;
} /* while readdir */
closedir( dirp );
} /* rwhod mtime has changed */
for( tp = targets, i = 0; i < numt; tp = tp->tnext, i++ ) {
tp->found[this].hungry = 0;
tp->found[this].nidle = 0;
tp->found[this].idle = 0;
} /* for i */
time( &now );
if( stamp ) {
strcpy( nowstr, ctime( &now ) );
nowstr[ 16 ] = ' ';
nowstr[ 17 ] = EOS; /* kill year and seconds */
} /* stamping */
for( fp = filenames, i = 0; i < nfiles; i++, fp++ ) {
register struct whoent *we;
int f, cc, down;
time_t recvtime;
int try;
# ifdef USEFILETIME
struct stat ftime;
# endif /* USEFILETIME defined */
# if DEBUG > 19
printf("=== %s ===\n", *fp );
# endif /* DEBUG > 19 */
for( try = 0; try < 5; try++, sleep(1) ) {
cc = -1;
if( (f = open( *fp, 0 )) < 0 )
continue; /* try to read again */
cc = read( f, &wd, sizeof( wd ));
# ifdef USEFILETIME
fstat( f, &ftime );
# endif /* USEFILETIME defined */
close( f );
cc -= sizeof( wd ) - sizeof ( wd.wd_we );
# if DEBUG > 19
printf("%d chars\n", cc );
# endif /* DEBUG > 19 */
if( cc < 0 ) {
# if DEBUG > 0 && DEBUG < 20
printf("%s: %d chars\n", wd.wd_hostname, cc );
# endif /* DEBUG > 0 && DEBUG < 20 */
continue; /* re-read file */
} /* short file */
else
break;
} /* for each try */
if( cc < 0 )
continue; /* re-read */
# ifdef USEFILETIME
recvtime = ftime.st_mtime;
# else /* USEFILETIME not defined */
recvtime = wd.wd_recvtime;
# endif /* USEFILETIME not defined */
down = now - recvtime;
# if DEBUG > 19
printf( "down %d\n", down );
# endif /* DEBUG > 19 */
if( wd.wd_hostname[0] == EOS ) {
# if DEBUG > 0
printf( "null hostname in file %s\n", *fp );
# endif /* DEBUG > 0 */
continue; /* re-read!? */
} /* hostname empty */
if( machines )
if( wd.wd_hostname[0] != EOS )
process( &wd, recvtime );
if( down > downtime ) /* host is down? */
continue;
for( we = wd.wd_we; cc > 0 ; we++, cc -= sizeof( *we ) ) {
int tcnt;
int washungry;
register char *cp, *dp;
washungry = ISHUNGRY(we); /* save hungry bit */
CLEARHUNGRY(we); /* clear to avoid bad comparisons */
strncpy( user, we->we_utmp.out_name, 8 );
strcpy( host, wd.wd_hostname );
strcpy( tty, we->we_utmp.out_line );
# if DEBUG > 10
printf("%s@%s %s idle %d\n", user, host, tty, we->we_idle );
# endif /* DEBUG > 10 */
for( tp = targets, tcnt = 0; tcnt < numt; tcnt++, tp = tp->tnext ) {
if( strcmp(user, tp->user) == 0 ) { /* matching user */
if( (dp = index( host, '.' )) != NULL )
*dp = EOS; /* strip domains */
# if DEBUG > 4
# if DEBUG < 20
printf("%s@%s %s idle %d\n",
user, host, tty, we->we_idle );
# endif /* DEBUG < 20 */
printf("%s %d %d\n", tp->user,
tp->found[this].nidle,
tp->found[this].idle );
# endif /* DEBUG > 4 */
if( tp->host == WILDHOST || /* wild or */
strcmp(host, tp->host) == 0 ) { /*matching host*/
if( we->we_idle > idletime )
tp->found[this].idle++;
else { /* not idle */
if( tp->found[this].nidle++ == 0 && /*first*/
tp->found[!this].nidle == 0 ) { /*none b4*/
# if DEBUG > 0 && DEBUG < 5
tprint( tp );
# endif /* DEBUG > 0 && DEBUG < 5 */
/* nothing before or, imbalance */
if( tp->found[!this].idle == 0 ||
TOTAL(tp,this) > TOTAL(tp,!this) )
now_on( user, host, tty );
else /* return from idleness */
now_active( user, host, tty );
} /* first non idle where none before */
} /* not idle */
if( washungry )
if( tp->found[this].hungry++ == 0 &&
tp->found[!this].hungry == 0 )
now_hungry( user, host, tty );
} /* matching host */
goto nextwhoent;
} /* matching user */
} /* for all targets */
nextwhoent: ;
} /* for we */
} /* for each file */
for( tp = targets, i = 0; i < numt; i++, tp = tp->tnext ) {
# if DEBUG > 49
tprint( tp );
# endif /* DEBUG > 49 */
if( tp->found[!this].nidle > 0 ) { /* was here before */
# if DEBUG > 4 && DEBUG < 50
tprint( tp );
# endif /* DEBUG > 4 && DEBUG < 50 */
if( tp->found[this].nidle == 0 ) { /* no non idle users */
# if DEBUG > 0 && DEBUG < 5
tprint( tp );
# endif /* DEBUG > 0 && DEBUG < 5 */
if( tp->found[this].idle > 0 && /* have idle users */
TOTAL(tp,this) == TOTAL(tp,!this) ) /* no net change */
now_idle( tp->user, tp->host );
else
now_gone( tp->user, tp->host ); /* none idle or */
/* net change. */
/* must have logged out */
} /* no non-idle */
} /* prev had non-idle */
else if( tp->found[!this].idle > 0 ) { /* prev idle? */
if( TOTAL(tp,this) == 0 ) { /* no current */
# if DEBUG > 0 && DEBUG < 5
tprint( tp );
# endif /* DEBUG > 0 && DEBUG < 5 */
now_gone( tp->user, tp->host );
}
} /* here but idle before */
} /* for tp */
} /* do who */
now_on( user, host, tty )
char *user, *host, *tty;
{
stomp();
printf("bow wow!! %s now on %s %s\n", user, host, tty );
} /* now on */
now_active( user, host, tty )
char *user, *host, *tty;
{
stomp();
printf("arf! %s active on %s %s\n", user, host, tty );
} /* now active */
now_hungry( user, host, tty )
char *user, *host, *tty;
{
stomp();
printf("woof! %s hungry on %s %s\n", user, host, tty );
} /* now hungry */
now_idle( user, host )
char *user, *host;
{
stomp();
/* doggie noise here? */
fputs( user, stdout );
if( host != WILDHOST ) {
putchar('@');
fputs( host, stdout );
} /* have host */
puts(" is idle");
} /* now idle */
now_gone( user, host )
char *user, *host;
{
stomp();
/* doggie noise here? */
fputs( user, stdout );
if( host != WILDHOST ) {
putchar('@');
fputs( host, stdout );
} /* have host */
puts(" is gone");
} /* now gone */
now_sated( user, host )
char *user, *host;
{
stomp();
/* doggie noise here? */
fputs( user, stdout );
if( host != WILDHOST ) {
putchar('@');
fputs( host, stdout );
} /* have host */
puts(" is sated!!");
} /* now sated */
bool
checkflag( buf, name, ptr )
char *name, *buf;
bool *ptr;
{
int l;
l = strlen( name );
if( strncmp( name, buf, l ) == 0 )
*ptr = TRUE;
else if( buf[0] == 'n' && buf[1] == 'o' && strncmp( name, buf+2, l ) == 0 )
*ptr = FALSE;
else
return( FALSE );
return( TRUE );
} /* checkflag */
bool
numarg( buf, name, ptr, scale )
char *name, *buf;
int *ptr, scale;
{
if( strncmp( name, buf, strlen( name ) ) == 0 ) {
int i;
if( sscanf( buf, "%*s %d", &i ) != 1 )
fprintf( stderr, "fido: bad line: %s\n", buf );
else
*ptr = i * scale;
return( TRUE );
} /* matches */
return( FALSE );
} /* numarg */
bool
readinit( file )
char *file;
{
FILE *f;
char buf[ MAXBUF ];
if( (f = fopen(file, "r")) == NULL )
return( FALSE );
while( fgets(buf, sizeof buf, f) != NULL ) {
register char *cp;
if( (cp = index(buf,'\n')) != NULL )
*cp = EOS;
# if DEBUG > 9
printf("%s\n", buf );
# endif /* DEBUG > 9 */
if( buf[0] == '#' )
continue;
if( strncmp(buf, "user", 4) == 0 )
r_user( buf );
else if( checkflag(buf, "beep", &beep) )
continue;
else if( checkflag(buf, "machines", &machines) )
continue;
else if( checkflag(buf, "stamp", &stamp) )
continue;
else if( numarg(buf, "idletime", &idletime, 60) )
continue;
else if( numarg(buf, "downtime", &downtime, 60) )
continue;
else if( numarg(buf, "sleeptime", &sleeptime, 1) )
continue;
else
fprintf(stderr, "fido: bad line in %s: %s\n", file, buf );
} /* while */
if( sleeptime < MIN_SLEEPTIME )
sleeptime = MIN_SLEEPTIME;
fclose ( f );
return( TRUE );
} /* readinit */
char *savestr( s )
register char *s;
{
register char *t;
while( *s != EOS && isspace( *s ) )
s++;
if( *s == EOS )
return( "" );
t = s;
while( *t != EOS && !isspace( *t ) && *t != '\n' )
t++;
if( *t != EOS )
*t = EOS;
# if DEBUG > 10
printf("savestr '%s'\n", s);
# endif /* DEBUG > 10 */
return( strcpy( malloc( strlen( s ) + 1 ), s) );
} /* savestr */
r_user( l )
register char *l;
{
register char *cp;
register struct target *tp;
while( *l != EOS && !isspace( *l ) )
l++;
while( *l != EOS && isspace( *l ) )
l++;
if( *l == EOS )
return;
tp = (struct target *) malloc( sizeof( struct target ) );
tp->tnext = NULL;
if( targets == NULL )
targets = tp;
else
lasttarget->tnext = tp;
if( (cp = index(l, '@')) != NULL ) {
*cp++ = EOS; /* blast @ to tie off user */
if( *cp != EOS ) /* have host? */
tp->host = savestr( cp ); /* save it */
else /* none? */
tp->host = WILDHOST; /* WTF -- save as wild */
} /* have host */
else
tp->host = WILDHOST;
tp->user = savestr( l );
tp->found[0].idle = 0;
tp->found[0].nidle = 0;
tp->found[0].hungry = 0;
tp->found[1].idle = 0;
tp->found[1].nidle = 0;
tp->found[1].hungry = 0;
lasttarget = tp;
numt++;
} /* r_user */
dumpinit() {
register int i;
register struct target *tp;
for( tp = targets, i = 0; i < numt; tp = tp->tnext, i++ )
if( tp->host == WILDHOST )
printf("%s\n", tp->user );
else
printf("%s@%s\n", tp->user, tp->host );
} /* dumpinit */
/**************** machine stuff ****************/
# define SH 7
# define WS 32
unsigned
hash( n )
register char *n;
{
register unsigned l, i;
i = l = 0;
while( *n != EOS ) {
i = ((i << SH) | (i >> (WS-SH))) ^ *n++;
l++;
} /* while not end of string */
return( (i + l) % MHSIZE );
} /* hash */
struct machine *
findmachine( n )
char *n;
{
int h;
register struct machine *mp;
h = hash( n );
for( mp = mhash[ h ]; mp != NULL; mp = mp->hnext )
if( strcmp(mp->name, n) == 0 )
return( mp );
return( NULL );
} /* findmachine */
struct machine *
newmachine( n )
char *n;
{
int h;
register struct machine *mp;
mp = (struct machine *) malloc( sizeof( struct machine ) );
strcpy(mp->name, n);
mp->state = UP;
mp->anext = allmachines; /* put in list of all machines */
allmachines = mp;
h = hash( n ); /* put in hash list */
mp->hnext = mhash[ h ];
mhash[ h ] = mp;
if( !firstpass )
saynew( mp );
return( mp );
} /* newmachine */
process( wd, t )
struct whod *wd;
time_t t;
{
struct machine *mp;
if( (mp = findmachine( wd->wd_hostname )) != NULL ) {
# if DEBUG > 10
printf("found machine: %s\n", wd->wd_hostname);
# endif /* DEBUG > 10 */
if( wd->wd_boottime - mp->boottime > 120 ) { /* filter out freaks */
sayreboot( mp );
# if DEBUG > 0
printf("new %d old %d diff %d\n",
wd->wd_boottime,
mp->boottime,
wd->wd_boottime - mp->boottime );
# endif /* DEBUG > 0 */
} /* new boottime */
} /* machine found */
else
mp = newmachine( wd->wd_hostname );
mp->latest = t; /* packet sendtime or file time */
mp->boottime = wd->wd_boottime;
mp->sendtime = wd->wd_sendtime;
} /* process */
scan() {
register struct machine *mp;
int i;
time_t now;
time( &now );
for( mp = allmachines; mp != NULL; mp = mp->anext ) {
# if DEBUG > 4
printf("scan: %s (%s)\n", mp->name, state_name[ mp->state+1 ] );
# endif /* DEBUG > 4 */
/*
* if /usr/spool/rwho is on another machine (via NFS) and our
* clocks differ we get alot of up/down noise. there must be
* a good way to dampen this!!
*
* perhaps keep a new state "may be down" as a cushion
*
* perhaps keep threshold per host and increase each time the
* host comes "up" without having been rebooted (see
* wd_boottime)
*/
if( now - mp->latest > downtime ) { /* now down */
if( mp->state != DOWN ) {
saydown( mp, now - mp->latest );
mp->state = DOWN;
} /* new state: down */
} /* now down */
else { /* now up */
if( mp->state != UP ) {
sayup( mp );
mp->state = UP;
} /* new state: up */
} /* now up */
} /* for all machines */
firstpass = FALSE;
} /* scan */
stomp() {
if( beep )
putchar('\007');
if( stamp )
fputs( nowstr, stdout );
} /* stomp */
saydown( mp, t )
struct machine *mp;
int t;
{
int d, h, m;
stomp();
printf("%s is down (", mp->name );
t /= 60; /* toss seconds */
m = t % 60; /* get mins */
t /= 60; /* toss mins */
d = t % 24; /* get days */
t /= 24; /* cast out days */
t %= 7; /* get weeks!! */
if( t > 0 ) printf("%dw", t );
if( d > 0 ) printf("%dd", d );
if( h > 0 ) printf("%dh", h );
if( m > 0 ) printf("%dm", m );
puts(")");
} /* saydown */
sayup( mp )
struct machine *mp;
{
stomp();
printf("%s is up\n", mp->name );
} /* sayup */
saynew( mp )
struct machine *mp;
{
stomp();
printf("new machine: %s\n", mp->name );
} /* saynew sayme */
sayreboot( mp )
struct machine *mp;
{
stomp();
printf("%s rebooted\n", mp->name );
} /* saynew sayme */
# if DEBUG > 0
tprint( tp )
register struct target *tp;
{
printf("%s (%d %d %d) (%d %d %d)",
tp->user,
tp->found[this].nidle,
tp->found[this].idle,
tp->found[this].hungry,
tp->found[!this].nidle,
tp->found[!this].idle,
tp->found[!this].hungry
);
if( TOTAL(tp,this) != TOTAL(tp,!this) )
printf(" ***** net imbalance!! *****");
puts("");
} /* tprint */
# endif /* DEBUG > 0 */