[comp.sources.unix] v16i053: Watchdog for users and machines

rsalz@uunet.uu.net (Rich Salz) (11/02/88)

Submitted-by: budd@bu-it.bu.edu (Phil Budne)
Posting-number: Volume 16, Issue 53
Archive-name: fido

Fido periodically scans the files created by rwhod and reports the
comings and goings of people and machines.  It reads a .fido init file to
determine what to watch.  I run it as "fido &" in my console window.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile fido.c fido.1
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(331 characters\)
sed "s/^X//" >README <<'END_OF_README'
XFido is a program I wrote in anticipation of getting a workstation.
XI keep it running in my console window.  Recently it has become popular
Xwith more users, and so I've done some cleanup and sent it out.
X
XI don't doubt that it is a great waste of CPU, but Hey, I like it.
X
X		Phil Budne
X		Boston University, Boston Ma
X		August 1988
END_OF_README
if test 331 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(341 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X# Makefile for fido.
X# typing "make DEBUG=n" builds a fido of debug level n
X#
X# GNU's fantastic CC
XCC=gcc
XDFLAGS=-g
XCFLAGS=-O
XDEBUG=4
X
Xdfido$(DEBUG):	fido.c
X	$(CC) $(DFLAGS) -DDEBUG=$(DEBUG) -o dfido$(DEBUG) fido.c
X
Xfido:	fido.c
X	$(CC) $(CFLAGS) -o fido fido.c
X
XSHAR:	README Makefile fido.c fido.1
X	shar README Makefile fido.c fido.1 > SHAR
END_OF_Makefile
if test 341 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f fido.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"fido.c\"
else
echo shar: Extracting \"fido.c\" \(19284 characters\)
sed "s/^X//" >fido.c <<'END_OF_fido.c'
X/*
X *	Copyright 1988 Philip L. Budne.
X *	All rights reserved.
X *
X *	This program may be freely distributed as long as this notice
X *	and the above Copyright remain intact.
X *
X *	The author makes no claims of suitability for use.
X */
X
X/*
X *	Phil Budne @ BU/DSG
X *
X *	prehistoric	users list compiled into struct. machine
X *			watching was a seperate program.
X *	8/1/86		read .fido file
X *	1/87		check ppid
X *	8/14/87		read rwho files directly (used to popen rwho!)
X *	8/20/87		add beep again, add idletime
X *			rework -- put found structure in target
X *			report found users once -- now way to
X *			track where someone is gone from. report idle.
X *	8/21/87		add machines, hungry
X *	4/19/88		look in HOME for .fido, quit if not found
X *			stat WHODIR, keep list of files to avoid
X *			re-reading spooldir constantly!
X *	8/6/88		convert to open mhash. malloc targets.
X *			add stomp. remove BEEP, STOMP, MT, HOST, AT
X *			use static filenamestrings.
X *	8/8/88!		keep boottime and sendtime in struct machine.
X *			report reboots.
X *	8/9/88		All dressed up. Ready to go!
X */
X
X/* do reverse video for machines? */
X
X# include <sys/types.h>
X# include <sys/stat.h>
X# include <sys/dir.h>
X# include <strings.h>
X# include <stdio.h>
X# include <ctype.h>
X# include <protocols/rwhod.h>		/* But not on 4.2 systems!! */
X
X# define EOS '\0'
Xextern char *malloc();			/* from libc */
X
X# define bool int
X# define TRUE 1
X# define FALSE 0
X
X# define INITFILE ".fido"
X
X# define DEF_BEEP	FALSE
X# define DEF_STAMP	TRUE
X# define DEF_MACHINES	TRUE
X# define DEF_DOWNTIME	8		/* minutes (less than 4 is noisy) */
X# define DEF_IDLETIME	15		/* minutes */
X# define DEF_SLEEPTIME	60		/* seconds */
X# define MIN_SLEEPTIME	32
X
X# define MAXBUF 132			/* various buffers */
X
X/* static lengths in target struct (fields from utmp) */
X# define MAXHST 33
X# define MAXUSR 9
X# define MAXTTY 9
X
X# define NUMFILES 100		/* grrr -- a hardwired limit!! */
X# define BPHOST 32		/* avg length for hostnames ick!! */
X
Xstruct target {
X    char *host, *user;
X    struct found {		/* count of entries found in each state */
X	int nidle, idle, hungry;
X    } found[2];			/* now and before */
X    struct target *tnext;
X};
X# define TOTAL(tp,t) ((tp)->found[t].idle + (tp)->found[t].nidle)
X
Xstruct target *targets;		/* list of targets */
Xstruct target *lasttarget;	/* last target created */
Xint numt = 0;			/* number of targets */
Xint this = 0;			/* toggling index (0/1) into found array */
X
Xchar wildhost[] = "*";
X# define WILDHOST wildhost	/* empty host for targets */
X
Xint beep = DEF_BEEP;
Xint stamp = DEF_STAMP;
Xint machines = DEF_MACHINES;
Xint downtime = DEF_DOWNTIME * 60;
Xint idletime = DEF_IDLETIME * 60;
Xint sleeptime = DEF_SLEEPTIME;
X
Xchar nowstr[ 50 ];			/* for time stomping */
X
Xstruct machine {			/* hash table of machines */
X    char name[40];
X    enum { UP=1, DOWN=2 } state;
X    time_t latest;			/* last time seen up */
X    int boottime, sendtime;		/* from whod packet */
X    struct machine
X	*hnext,				/* next in hash chain */
X	*anext;				/* next in list of all hosts */
X};
X
X# define MHSIZE 101			/* small prime */
Xstruct machine *mhash[ MHSIZE ], *allmachines;
Xint firstpass = TRUE;
X
X# if DEBUG > 0
Xchar *state_name[] = { "??", "UP", "DOWN" };
X# endif /* DEBUG > 0 */
X
X# define WHODIR "/usr/spool/rwho"
X
X# define ISHUNGRY(we) \
X    ((we->we_utmp.out_line[sizeof(we->we_utmp.out_line)-1] & 0200) != 0 )
X
X# define CLEARHUNGRY(we) \
X    we->we_utmp.out_line[sizeof(we->we_utmp.out_line)-1] &= ~0200
X
Xmain() {
X    int i;
X
X    if( !readinit( INITFILE ) ) {	/* check current dir */
X	extern char *getenv();
X	char tname[ 1024 ], *hp;
X
X	if( (hp = getenv("HOME")) == NULL ) { /* check 'home' */
X	    /* getpwuid(getuid()) */
X	    fprintf( stderr, "No ./.fido and HOME not set\n" );
X	    exit( 1 );
X	} /* no HOME */
X	sprintf( tname, "%s/%s", hp, INITFILE );
X	if( !readinit( tname ) ) {
X	    fprintf( stderr, "No ./.fido or %s\n", tname );
X	    exit( 1 );
X	} /* readinit failed */
X    } /* no .fido in "." */
X
X# if DEBUG > 0
X    dumpinit();
X# endif /* DEBUG > 0 */
X
X    if( numt == 0 && !machines ) {
X	fprintf(stderr, "Nothing to do!!\n" );
X	exit( 1 );
X    } /* nothing to do!! */
X
X    if( chdir( WHODIR ) < 0 ) {
X	perror( WHODIR );
X	exit( 1 );
X    } /* chdir failed */
X
X    nice( 10 );				/* be nice to real users */
X
X    for( ; ; ) {
X	dowho();			/* prowl rwho spool dir */
X	if( machines )			/* watching machines? */
X	    scan();			/* yell about them */
X	this = !this;			/* toggle found index */
X	fflush( stdout );
X	sleep( sleeptime );		/* go to sleep */
X	if( getppid() == 1 )		/* child of init? */
X	    exit( 1 );			/* quitting time */
X    } /* for ever */
X} /* main */
X
Xdowho() {				/* poke thru rwho spool */
X    register int i;
X    register struct target *tp;
X
X    int now;
X    char **fp;
X    struct whod wd;
X    struct stat newstat;
X    char buf[MAXBUF], user[MAXUSR], host[MAXHST], tty[MAXTTY];
X
X    static int nfiles;
X    static struct stat oldstat;
X    static char *filenames[ NUMFILES ];	/* ACK!! hardwired limits! */
X    static char filenamestrings[ NUMFILES * BPHOST ]; /* avoid malloc/free */
X
X    if( stat( ".", &newstat ) < 0 )
X	return;
X
X    if( newstat.st_mtime != oldstat.st_mtime ) {
X	DIR *dirp;
X	struct direct *d;
X	register char *dp;
X
X	oldstat = newstat;
X# if DEBUG > 0
X	puts("(re)reading RWHODIR");
X# endif /* DEBUG > 0 */
X
X	if( (dirp = opendir( "." )) == NULL )
X	    return;
X
X	nfiles = 0;
X	fp = filenames;
X	dp = filenamestrings;
X	while( (d = readdir( dirp )) != NULL ) {
X	    register char *sp;
X
X	    if( strncmp( d->d_name, "whod.", 5 ) != 0 )
X		continue;
X
X# if DEBUG > 4
X	    printf("readdir: %s\n", d->d_name );
X# endif /* DEBUG > 4 */
X	    if( nfiles == NUMFILES ) {
X		fprintf( stderr, "Too many files!! Increase NUMFILES!!\n");
X		break;
X	    } /* too many files */
X
X	    nfiles++;
X	    *fp++ = dp;
X	    sp = d->d_name;
X	    while( *dp++ = *sp++ )
X		;
X	} /* while readdir */
X	closedir( dirp );
X    } /* rwhod mtime has changed */
X
X    for( tp = targets, i = 0; i < numt; tp = tp->tnext, i++ ) {
X	tp->found[this].hungry = 0;
X	tp->found[this].nidle  = 0;
X	tp->found[this].idle   = 0;
X    } /* for i */
X
X    time( &now );
X    if( stamp ) {
X	strcpy( nowstr, ctime( &now ) );
X	nowstr[ 16 ] = ' ';
X	nowstr[ 17 ] = EOS;		/* kill year and seconds */
X    } /* stamping */
X
X    for( fp = filenames, i = 0; i < nfiles; i++, fp++ ) {
X	register struct whoent *we;
X	int f, cc, down;
X	time_t recvtime;
X	int try;
X# ifdef USEFILETIME
X	struct stat ftime;
X# endif /* USEFILETIME defined */
X
X# if DEBUG > 19
X	printf("=== %s ===\n", *fp );
X# endif /* DEBUG > 19 */
X
X	for( try = 0; try < 5; try++, sleep(1) ) {
X	    cc = -1;
X	    if( (f = open( *fp, 0 )) < 0 )
X		continue;		/* try to read again */
X
X	    cc = read( f, &wd, sizeof( wd ));
X# ifdef USEFILETIME
X	    fstat( f, &ftime );
X# endif /* USEFILETIME defined */
X	    close( f );
X
X	    cc -= sizeof( wd ) - sizeof ( wd.wd_we );
X# if DEBUG > 19
X	    printf("%d chars\n", cc );
X# endif /* DEBUG > 19 */
X	    if( cc < 0 ) {
X# if DEBUG > 0 && DEBUG < 20
X		printf("%s: %d chars\n", wd.wd_hostname, cc );
X# endif /* DEBUG > 0 && DEBUG < 20 */
X		continue;		/* re-read file */
X	    } /* short file */
X	    else
X		break;
X	} /* for each try */
X	if( cc < 0 )
X	    continue;			/* re-read */
X
X# ifdef USEFILETIME
X	recvtime = ftime.st_mtime;
X# else  /* USEFILETIME not defined */
X	recvtime = wd.wd_recvtime;
X# endif /* USEFILETIME not defined */
X
X	down = now - recvtime;
X# if DEBUG > 19
X	printf( "down %d\n", down );
X# endif /* DEBUG > 19 */
X
X	if( wd.wd_hostname[0] == EOS ) {
X# if DEBUG > 0
X	    printf( "null hostname in file %s\n", *fp );
X# endif /* DEBUG > 0 */
X	    continue;			/* re-read!? */
X	} /* hostname empty */
X
X	if( machines )
X	    if( wd.wd_hostname[0] != EOS )
X		process( &wd, recvtime );
X
X	if( down > downtime )		/* host is down? */
X	    continue;
X
X	for( we = wd.wd_we; cc > 0 ; we++, cc -= sizeof( *we ) ) {
X	    int tcnt;
X	    int washungry;
X	    register char *cp, *dp;
X
X	    washungry = ISHUNGRY(we);	/* save hungry bit */
X	    CLEARHUNGRY(we);		/* clear to avoid bad comparisons */
X
X	    strncpy( user, we->we_utmp.out_name, 8 );
X	    strcpy( host, wd.wd_hostname );
X	    strcpy( tty, we->we_utmp.out_line );
X
X# if DEBUG > 10
X	    printf("%s@%s %s idle %d\n", user, host, tty, we->we_idle );
X# endif /* DEBUG > 10 */
X
X	    for( tp = targets, tcnt = 0; tcnt < numt; tcnt++, tp = tp->tnext ) {
X		if( strcmp(user, tp->user) == 0 ) { /* matching user */
X		    if( (dp = index( host, '.' )) != NULL )
X			*dp = EOS;	/* strip domains */
X
X# if DEBUG > 4
X# if DEBUG < 20
X		    printf("%s@%s %s idle %d\n",
X			   user, host, tty, we->we_idle );
X# endif /* DEBUG < 20 */
X		    printf("%s %d %d\n", tp->user,
X			   tp->found[this].nidle,
X			   tp->found[this].idle );
X# endif /* DEBUG > 4 */
X
X		    if( tp->host == WILDHOST || /* wild or */
X		       strcmp(host, tp->host) == 0 ) { /*matching host*/
X			if( we->we_idle > idletime )
X			    tp->found[this].idle++;
X			else {		/* not idle */
X			    if( tp->found[this].nidle++ == 0 && /*first*/
X			       tp->found[!this].nidle == 0 ) { /*none b4*/
X# if DEBUG > 0 && DEBUG < 5
X				tprint( tp );
X# endif /* DEBUG > 0 && DEBUG < 5 */
X				/* nothing before or, imbalance */
X				if( tp->found[!this].idle == 0 ||
X				   TOTAL(tp,this) > TOTAL(tp,!this) )
X				    now_on( user, host, tty );
X				else		/* return from idleness */
X				    now_active( user, host, tty );
X			    } /* first non idle where none before */
X			} /* not idle */
X			if( washungry )
X			    if( tp->found[this].hungry++ == 0 &&
X			       tp->found[!this].hungry == 0 )
X				now_hungry( user, host, tty );
X		    } /* matching host */
X		    goto nextwhoent;
X		} /* matching user */
X	    } /* for all targets */
X	nextwhoent: ;
X	} /* for we */
X    } /* for each file */
X   
X    for( tp = targets, i = 0; i < numt; i++, tp = tp->tnext ) {
X# if DEBUG > 49
X	tprint( tp );
X# endif /* DEBUG > 49 */
X	if( tp->found[!this].nidle > 0 ) { 	/* was here before */
X# if DEBUG > 4 && DEBUG < 50
X	    tprint( tp );
X# endif /* DEBUG > 4 && DEBUG < 50 */
X	    if( tp->found[this].nidle == 0 ) {	/* no non idle users */
X# if DEBUG > 0 && DEBUG < 5
X		tprint( tp );
X# endif /* DEBUG > 0 && DEBUG < 5 */
X		if( tp->found[this].idle > 0 &&	/* have idle users */
X		   TOTAL(tp,this) == TOTAL(tp,!this) ) /* no net change */
X		    now_idle( tp->user, tp->host );
X		else
X		    now_gone( tp->user, tp->host ); /* none idle or */
X						/* net change. */
X						/* must have logged out */
X	    } /* no non-idle */
X	} /* prev had non-idle */
X	else if( tp->found[!this].idle > 0 ) {	/* prev idle? */
X	    if( TOTAL(tp,this) == 0 ) {		/* no current */
X# if DEBUG > 0 && DEBUG < 5
X		tprint( tp );
X# endif /* DEBUG > 0 && DEBUG < 5 */
X		now_gone( tp->user, tp->host );
X	    } 
X	} /* here but idle before */
X    } /* for tp */
X} /* do who */
X
Xnow_on( user, host, tty )
X    char *user, *host, *tty;
X{
X    stomp();
X    printf("bow wow!! %s now on %s %s\n", user, host, tty );
X} /* now on */
X
Xnow_active( user, host, tty )
X    char *user, *host, *tty;
X{
X    stomp();
X    printf("arf! %s active on %s %s\n", user, host, tty );
X} /* now active */
X
Xnow_hungry( user, host, tty )
X    char *user, *host, *tty;
X{
X    stomp();
X    printf("woof! %s hungry on %s %s\n",  user, host, tty );
X} /* now hungry */
X
Xnow_idle( user, host )
X    char *user, *host;
X{
X    stomp();
X    /* doggie noise here? */
X    fputs( user, stdout );
X    if( host != WILDHOST ) {
X	putchar('@');
X	fputs( host, stdout );
X    } /* have host */
X    puts(" is idle");
X} /* now idle */
X
Xnow_gone( user, host )
X    char *user, *host;
X{
X    stomp();
X    /* doggie noise here? */
X    fputs( user, stdout );
X    if( host != WILDHOST ) {
X	putchar('@');
X	fputs( host, stdout );
X    } /* have host */
X    puts(" is gone");
X} /* now gone */
X
Xnow_sated( user, host )
X    char *user, *host;
X{
X    stomp();
X    /* doggie noise here? */
X    fputs( user, stdout );
X    if( host != WILDHOST ) {
X	putchar('@');
X	fputs( host, stdout );
X    } /* have host */
X    puts(" is sated!!");
X} /* now sated */
X
Xbool
Xcheckflag( buf, name, ptr )
X    char *name, *buf;
X    bool *ptr;
X{
X    int l;
X
X    l = strlen( name );
X    if( strncmp( name, buf, l ) == 0 )
X	*ptr = TRUE;
X    else if( buf[0] == 'n' && buf[1] == 'o' && strncmp( name, buf+2, l ) == 0 )
X	*ptr = FALSE;
X    else
X	return( FALSE );
X    return( TRUE );
X} /* checkflag */
X
Xbool
Xnumarg( buf, name, ptr, scale )
X    char *name, *buf;
X    int *ptr, scale;
X{
X    if( strncmp( name, buf, strlen( name ) ) == 0 ) {
X	int i;
X	if( sscanf( buf, "%*s %d", &i ) != 1 )
X	    fprintf( stderr, "fido: bad line: %s\n", buf );
X	else
X	    *ptr = i * scale;
X	return( TRUE );
X    } /* matches */
X    return( FALSE );
X} /* numarg */
X
Xbool
Xreadinit( file )
X    char *file;
X{
X    FILE *f;
X    char buf[ MAXBUF ];
X
X    if( (f = fopen(file, "r")) == NULL )
X	return( FALSE );
X
X    while( fgets(buf, sizeof buf, f) != NULL ) {
X	register char *cp;
X
X	if( (cp = index(buf,'\n')) != NULL )
X	    *cp = EOS;
X
X# if DEBUG > 9
X	printf("%s\n", buf );
X# endif /* DEBUG > 9 */
X
X	if( buf[0] == '#' )
X	    continue;
X
X	if( strncmp(buf, "user", 4) == 0 )
X	    r_user( buf );
X	else if( checkflag(buf, "beep", &beep) )
X	    continue;
X	else if( checkflag(buf, "machines", &machines) )
X	    continue;
X	else if( checkflag(buf, "stamp", &stamp) )
X	    continue;
X	else if( numarg(buf, "idletime", &idletime, 60)  )
X	    continue;
X	else if( numarg(buf, "downtime", &downtime, 60)  )
X	    continue;
X	else if( numarg(buf, "sleeptime", &sleeptime, 1) )
X	    continue;
X	else
X	    fprintf(stderr, "fido: bad line in %s: %s\n", file, buf );
X    } /* while */
X    if( sleeptime < MIN_SLEEPTIME )
X	sleeptime = MIN_SLEEPTIME;
X    fclose ( f );
X    return( TRUE );
X} /* readinit */
X
Xchar *savestr( s )
Xregister char *s;
X{
X    register char *t;
X
X    while( *s != EOS && isspace( *s ) )
X	s++;
X    if( *s == EOS )
X	return( "" );
X
X    t = s;
X    while( *t != EOS && !isspace( *t ) && *t != '\n' )
X	t++;
X    if( *t != EOS )
X	*t = EOS;
X
X# if DEBUG > 10
X    printf("savestr '%s'\n", s);
X# endif /* DEBUG > 10 */
X
X    return( strcpy( malloc( strlen( s ) + 1 ), s) );
X    
X} /* savestr */
X
Xr_user( l )
Xregister char *l;
X{
X    register char *cp;
X    register struct target *tp;
X
X    while( *l != EOS && !isspace( *l ) )
X	  l++;
X
X    while( *l != EOS && isspace( *l ) )
X	  l++;
X
X    if( *l == EOS )
X	return;
X
X    tp = (struct target *) malloc( sizeof( struct target ) );
X    tp->tnext = NULL;
X
X    if( targets == NULL )
X	targets = tp;
X    else
X	lasttarget->tnext = tp;
X
X    if( (cp = index(l, '@')) != NULL ) {
X	*cp++ = EOS;			/* blast @ to tie off user */
X	if( *cp != EOS )		/* have host? */
X	    tp->host = savestr( cp );	/* save it */
X	else				/* none? */
X	    tp->host = WILDHOST;	/* WTF -- save as wild */
X    } /* have host */
X    else
X	tp->host = WILDHOST;
X    tp->user = savestr( l );
X
X    tp->found[0].idle   = 0;
X    tp->found[0].nidle  = 0;
X    tp->found[0].hungry = 0;
X    tp->found[1].idle   = 0;
X    tp->found[1].nidle  = 0;
X    tp->found[1].hungry = 0;
X
X    lasttarget = tp;
X    numt++;
X} /* r_user */
X
Xdumpinit() {
X    register int i;
X    register struct target *tp;
X
X    for( tp = targets, i = 0; i < numt; tp = tp->tnext, i++ )
X	if( tp->host == WILDHOST )
X	    printf("%s\n", tp->user );
X	else
X	    printf("%s@%s\n", tp->user, tp->host );
X} /* dumpinit */
X
X/**************** machine stuff ****************/
X
X# define SH 7
X# define WS 32
Xunsigned
Xhash( n )
X    register char *n;
X{
X    register unsigned l, i;
X
X    i = l = 0;
X    while( *n != EOS ) {
X	i = ((i << SH) | (i >> (WS-SH))) ^ *n++;
X	l++;
X    } /* while not end of string */
X    return( (i + l) % MHSIZE );
X} /* hash */
X
Xstruct machine *
Xfindmachine( n )
X    char *n;
X{
X    int h;
X    register struct machine *mp;
X
X    h = hash( n );
X    for( mp = mhash[ h ]; mp != NULL; mp = mp->hnext )
X	if( strcmp(mp->name, n) == 0 )
X	    return( mp );
X    return( NULL );
X} /* findmachine */
X
Xstruct machine *
Xnewmachine( n )
X    char *n;
X{
X    int h;
X    register struct machine *mp;
X
X    mp = (struct machine *) malloc( sizeof( struct machine ) );
X    strcpy(mp->name, n);
X    mp->state = UP;
X
X    mp->anext = allmachines;		/* put in list of all machines */
X    allmachines = mp;
X
X    h = hash( n );			/* put in hash list */
X    mp->hnext = mhash[ h ];
X    mhash[ h ] = mp;
X
X    if( !firstpass )
X	saynew( mp );
X
X    return( mp );
X} /* newmachine */
X
Xprocess( wd, t )
X    struct whod *wd;
X    time_t t;
X{
X    struct machine *mp;
X
X    if( (mp = findmachine( wd->wd_hostname )) != NULL ) {
X# if DEBUG > 10
X	printf("found machine: %s\n", wd->wd_hostname);
X# endif /* DEBUG > 10 */
X	if( wd->wd_boottime - mp->boottime > 120 ) { /* filter out freaks */
X	    sayreboot( mp );
X# if DEBUG > 0	    
X	    printf("new %d old %d diff %d\n",
X		   wd->wd_boottime,
X		   mp->boottime,
X		   wd->wd_boottime - mp->boottime );
X# endif /* DEBUG > 0 */
X	} /* new boottime */
X    } /* machine found */
X    else
X	mp = newmachine( wd->wd_hostname );
X
X    mp->latest = t;			/* packet sendtime or file time */
X    mp->boottime = wd->wd_boottime;
X    mp->sendtime = wd->wd_sendtime;
X} /* process */
X
Xscan() {
X    register struct machine *mp;
X    int i;
X    time_t now;
X
X    time( &now );
X    for( mp = allmachines; mp != NULL; mp = mp->anext ) {
X# if DEBUG > 4
X	printf("scan: %s (%s)\n", mp->name, state_name[ mp->state+1 ] );
X# endif /* DEBUG > 4 */
X	/*
X	 * if /usr/spool/rwho is on another machine (via NFS) and our
X	 * clocks differ we get alot of up/down noise.  there must be
X	 * a good way to dampen this!!
X	 *
X	 * perhaps keep a new state "may be down" as a cushion
X	 *
X	 * perhaps keep threshold per host and increase each time the
X	 * host comes "up" without having been rebooted (see
X	 * wd_boottime)
X	 */
X	if( now - mp->latest > downtime ) { /* now down */
X	    if( mp->state != DOWN ) {
X		saydown( mp, now - mp->latest );
X		mp->state = DOWN;
X	    } /* new state: down */
X	} /* now down */
X	else {			/* now up */
X	    if( mp->state != UP ) {
X		sayup( mp );
X		mp->state = UP;
X	    } /* new state: up */
X	} /* now up */
X    } /* for all machines */
X    firstpass = FALSE;
X} /* scan */
X
Xstomp() {
X    if( beep )
X	putchar('\007');
X    if( stamp )
X	fputs( nowstr, stdout );
X} /* stomp */
X
Xsaydown( mp, t )
X    struct machine *mp;
X    int t;
X{
X    int d, h, m;
X
X    stomp();
X    printf("%s is down (", mp->name );
X    t /= 60;				/* toss seconds */
X    m = t % 60;				/* get mins */
X    t /= 60;				/* toss mins */
X    d = t % 24;				/* get days */
X    t /= 24;				/* cast out days */
X    t %= 7;				/* get weeks!! */
X    if( t > 0 ) printf("%dw", t );
X    if( d > 0 ) printf("%dd", d );
X    if( h > 0 ) printf("%dh", h );
X    if( m > 0 ) printf("%dm", m );
X    puts(")");
X} /* saydown */
X
Xsayup( mp )
X    struct machine *mp;
X{
X    stomp();
X    printf("%s is up\n", mp->name );
X} /* sayup */
X
Xsaynew( mp )
X    struct machine *mp;
X{
X    stomp();
X    printf("new machine: %s\n", mp->name );
X} /* saynew sayme */
X
Xsayreboot( mp )
X    struct machine *mp;
X{
X    stomp();
X    printf("%s rebooted\n", mp->name );
X} /* saynew sayme */
X
X# if DEBUG > 0
Xtprint( tp )
X    register struct target *tp;
X{
X    printf("%s (%d %d %d) (%d %d %d)",
X	   tp->user,
X
X	   tp->found[this].nidle,
X	   tp->found[this].idle,
X	   tp->found[this].hungry,
X
X	   tp->found[!this].nidle,
X	   tp->found[!this].idle,
X	   tp->found[!this].hungry
X	   );
X    if( TOTAL(tp,this) != TOTAL(tp,!this) )
X	printf(" ***** net imbalance!! *****");
X    puts("");
X} /* tprint */
X# endif /* DEBUG > 0 */
END_OF_fido.c
if test 19284 -ne `wc -c <fido.c`; then
    echo shar: \"fido.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f fido.1 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"fido.1\"
else
echo shar: Extracting \"fido.1\" \(2894 characters\)
sed "s/^X//" >fido.1 <<'END_OF_fido.1'
X.\"-*-nroff-*-
X.TH FIDO 1 "9 Aug 1988"
X.SH NAME
Xfido \- a watchdog for users and machines
X.SH SYNOPSIS
X.B fido &
X.SH FEATURES
X.I Fido
Xperiodically scans the files created by
X.IR rwhod (8c)
Xand reports the commings and goings of people and machines.
X.I Fido
Xreads a
X.I .fido
Xinit file to determine what to watch. 
X.I .fido
Xis first checked for in the current directory, and then
Xin the directory contained in the
X.B \s-1HOME\s0
Xenvironment variable.
X.PP
XThe
X.I .fido
Xfile consists of a list of commands from the list below.
XLines starting with a hash-mark (#) are discarded as comments.
X.HP
X.B user
X.IR user [@ host ]
X.br
XWatch for a specified user.  Normally only the first login and last
Xlogout of the specified user will be reported.  If a host is
Xspecified, then only instances of that user on that host are reported
X(useful when multiple users have the same name).  You can have
Xmultiple lines with the same username and different hosts (including
Xthe form with no host).
X.HP
X.RB [ no ] beep
X.br
XRing bell on output (default is
X.BR nobeep )
X.HP
X.RB [ no ] machines
X.br
XWatch machines go up and down (default is
X.BR machines )
X.HP
X.RB [ no ] stamp
X.br
XOutput timestamps. (default is
X.BR stamp )
X.HP
X.B idletime
X.I minutes
X.br
XSpecify the 
Xnumber of minutes until a user is declared idle (default is 15).
X.HP
X.B downtime
X.I minutes
X.br
XSpecify the number of minutes before a host is declared down (default is 8).
X.B Note!!
XIf the
X.IR rwho (1c)
Xspool directory is located on another (NFS) host, a skew between
Xtime of day clocks can cause major misery.
X.HP
X.B sleeptime
X.I seconds
X.br
XSpecify number of seconds to sleep between scans (default is 60,
Xminimum is 32).
X.PP
XYou must specify at least one
X.B user
Xline or
X.B machines
Xin your
X.I .fido
Xfile, otherwise
X.I fido
Xhas nothing to to!!
XEach time it wakes up
X.I fido
Xchecks to see if it's parent pid is now pid 1 (init), if this
Xis true, then
X.IR fido 's
Xorginal parent must have exited, and so
X.I fido
Xfollows its master into oblivion.
X.SH FILES
X.TP 20
X.I /usr/spool/rwho
Xrwho spool area
X.PD 0
X.TP
X.I ./.fido
Xfido init file first choice
X.TP
X.RI $\s-1HOME\s0 /.fido
Xfido init file
X.PD
X.SH SEE ALSO
X.IR who (1),
X.IR rwho (1c),
X.IR ruptime (1c),
X.IR environ (5v),
X.IR rwhod (8c),
X.IR hungry (l)
X.SH AUTHOR
XPhilip L. Budne, Boston University, Distributed Systems Group
X.SH BUGS
XSee features.
X.PP
XNo way to exclude a given machine, or user on a given machine.
X.PP
XNo way to specify a particular terminal line.
X.PP
XNo way to specify any user on a given host (ie; watch for idle
Xworkstations)
X.PP
X.I Fido
Xwas inspired by a program of the same name running on the MIT
XITS operating system for PDP-10s.
X.PP
X.I Fido
Xcan only read 100 files from
X.I /usr/spool/rwho
X(see
X.B \s-1NUMFILES\s0
Xin the source)
Xand the total number of bytes consumed by the filenames
Xcannot exceed
X.BR \s-1BPHOST\s0 "*" \s-1NUMFILES\s0 .
X.RB ( \s-1BPHOST\s0
Xis currently 32)
END_OF_fido.1
if test 2894 -ne `wc -c <fido.1`; then
    echo shar: \"fido.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.