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.