home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume25 / fido / fido.c < prev    next >
C/C++ Source or Header  |  1992-04-03  |  24KB  |  989 lines

  1. /*
  2.  *    Copyright 1988, 1989 Philip L. Budne.
  3.  *    All rights reserved.
  4.  *
  5.  *    This program may be freely distributed as long as this notice
  6.  *    and the above Copyright remain intact.
  7.  *
  8.  *    The author makes no claims of suitability for use.
  9.  */
  10.  
  11. /*
  12.  *    Phil Budne @ BU/DSG
  13.  *
  14.  *    prehistoric    users list compiled into struct. machine
  15.  *            watching was a seperate program. ran on my 3b2.
  16.  *    8/1/86        read .fido file
  17.  *    1/87        check ppid
  18.  *    8/14/87        read rwho files directly (used to popen rwho!)
  19.  *    8/20/87        add beep again, add idletime
  20.  *            rework -- put found structure in target
  21.  *            report found users once -- now way to
  22.  *            track where someone is gone from. report idle.
  23.  *    8/21/87        add machines, hungry
  24.  *    4/19/88        look in HOME for .fido, quit if not found
  25.  *            stat WHODIR, keep list of files to avoid
  26.  *            re-reading spooldir constantly!
  27.  *    8/6/88        convert to open mhash. malloc targets.
  28.  *            add stomp. remove BEEP, STOMP, MT, HOST, AT
  29.  *            use static filenamestrings.
  30.  *    8/8/88!        keep boottime and sendtime in struct machine.
  31.  *            report reboots.
  32.  *    8/9/88        All dressed up. Ready to go!
  33.  *    3/7/89        Calculate hours in saydown!!
  34.  *            Show idle machines!! (watch)
  35.  *    5/10/89        Watch by terminal line (line). add output decorations.
  36.  *    10/24/90    Add tick
  37.  */
  38.  
  39. /* do reverse video for machines? */
  40.  
  41. # include <sys/types.h>
  42. # include <sys/stat.h>
  43. # ifdef NDIR
  44. # include "ndir.h"
  45. # else  /* NDIR not defined */
  46. # include <sys/dir.h>
  47. # endif /* NDIR not defined */
  48. # include <strings.h>
  49. # include <stdio.h>
  50. # include <ctype.h>
  51. # include <protocols/rwhod.h>        /* But not on 4.2 systems!! */
  52. # include <time.h>
  53.  
  54. # define EOS '\0'
  55. extern char *malloc();            /* from libc */
  56.  
  57. # define bool int
  58. # define TRUE 1
  59. # define FALSE 0
  60.  
  61. # define INITFILE ".fido"
  62.  
  63. # define DEF_BEEP    FALSE
  64. # define DEF_STAMP    TRUE
  65. # define DEF_MACHINES    TRUE
  66. # define DEF_DOWNTIME    8        /* minutes (less than 4 is noisy) */
  67. # define DEF_IDLETIME    15        /* minutes */
  68.  
  69. # define DEF_SLEEPTIME    60        /* seconds */
  70. # define MIN_SLEEPTIME    32
  71.  
  72. # define DEF_TICK    0        /* default timestamp (minutes) */
  73. # define MAX_TICK    60        /* never allow tick over 1 hr */
  74.  
  75. # define MAXBUF 132            /* various buffers */
  76.  
  77. /* static lengths in target struct (fields from utmp) */
  78. # define MAXHST 33
  79. # define MAXUSR 9
  80. # define MAXTTY 9
  81.  
  82. # define NUMFILES 100        /* grrr -- a hardwired limit!! */
  83. # define BPHOST 32        /* avg length for hostnames ick!! */
  84.  
  85. struct target {
  86.     enum { USER, LINE } type;
  87.     char *host, *user;
  88.     struct found {        /* count of entries found in each state */
  89.     int nidle, idle, hungry;
  90.     } found[2];            /* now and before */
  91.     struct target *tnext;
  92. };
  93. # define TOTAL(tp,t) ((tp)->found[t].idle + (tp)->found[t].nidle)
  94.  
  95. struct target *targets;        /* list of targets */
  96. struct target *lasttarget;    /* last target created */
  97. int numt = 0;            /* number of targets */
  98. int this = 0;            /* toggling index (0/1) into found array */
  99.  
  100. char wildhost[] = "*";
  101. # define WILDHOST wildhost    /* empty host for targets */
  102.  
  103. int beep = DEF_BEEP;
  104. int stamp = DEF_STAMP;
  105. int machines = DEF_MACHINES;
  106. int downtime = DEF_DOWNTIME * 60;
  107. int idletime = DEF_IDLETIME * 60;
  108. int sleeptime = DEF_SLEEPTIME;
  109. int watchall = FALSE;
  110. int tick     = DEF_TICK;
  111.  
  112. char nowstr[ 50 ];            /* for time stomping */
  113. time_t now;                /* current time(3) */
  114.  
  115. struct machine {            /* hash table of machines */
  116.     char name[40];
  117.     enum { UP=1, DOWN=2 } state;
  118.     time_t latest;            /* last time seen up */
  119.     int boottime, sendtime;        /* from whod packet */
  120.     struct machine
  121.     *hnext,                /* next in hash chain */
  122.     *anext;                /* next in list of all hosts */
  123.     int busy[2];            /* has non-idle users (now and then) */
  124.     char watch;                /* watched flag */
  125. };
  126. struct machine *findmachine(), *newmachine(); /* forward */
  127.  
  128. # define MHSIZE 101            /* small prime */
  129. struct machine *mhash[ MHSIZE ], *allmachines;
  130. int firstpass = TRUE;
  131.  
  132. # if DEBUG > 0
  133. char *state_name[] = { "??", "UP", "DOWN" };
  134. # endif /* DEBUG > 0 */
  135.  
  136. # define WHODIR "/usr/spool/rwho"
  137.  
  138. # define ISHUNGRY(we) \
  139.     ((we->we_utmp.out_line[sizeof(we->we_utmp.out_line)-1] & 0200) != 0 )
  140.  
  141. # define CLEARHUNGRY(we) \
  142.     we->we_utmp.out_line[sizeof(we->we_utmp.out_line)-1] &= ~0200
  143.  
  144. time_t
  145. getnexttick( t, i )
  146.     time_t t;
  147.     int i;                /* interval in minutes <= 60 */
  148. {
  149.     struct tm *tm;
  150.     int min;
  151.  
  152.     tm = localtime(&t);
  153.     min = i * ((tm->tm_min / i) + 1);    /* get next minute for tick */
  154.     return( t + (min - tm->tm_min) * 60 - tm->tm_sec );
  155. }
  156.  
  157. main() {
  158.     int i;
  159.     time_t nextscan, nexttick;
  160.  
  161.     if( !readinit( INITFILE ) ) {    /* check current dir */
  162.     extern char *getenv();
  163.     char tname[ 1024 ], *hp;
  164.  
  165.     if( (hp = getenv("HOME")) == NULL ) { /* check 'home' */
  166.         /* getpwuid(getuid()) */
  167.         fprintf( stderr, "No ./.fido and HOME not set\n" );
  168.         exit( 1 );
  169.     } /* no HOME */
  170.     sprintf( tname, "%s/%s", hp, INITFILE );
  171.     if( !readinit( tname ) ) {
  172.         fprintf( stderr, "No ./.fido or %s\n", tname );
  173.         exit( 1 );
  174.     } /* readinit failed */
  175.     } /* no .fido in "." */
  176.  
  177. # if DEBUG > 0
  178.     dumpinit();
  179. # endif /* DEBUG > 0 */
  180.  
  181.     if( numt == 0 && !machines && tick == 0 ) {
  182.     fprintf(stderr, "Nothing to do!!\n" );
  183.     exit( 1 );
  184.     } /* nothing to do!! */
  185.  
  186.     if( chdir( WHODIR ) < 0 ) {
  187.     perror( WHODIR );
  188.     exit( 1 );
  189.     } /* chdir failed */
  190.  
  191.     nice( 10 );                /* be nice to real users */
  192.  
  193.     sleep( 1 );                /* wait for shell to prompt */
  194.     putchar('\n');            /* get fresh line */
  195.  
  196.     time( &now );            /* get initial time */
  197.     if( tick > 0 ) {
  198.     nexttick = getnexttick( now, tick );
  199.     }
  200.     if( numt > 0 || machines )
  201.     nextscan = now;            /* FIX? */
  202.  
  203.     for( ; ; ) {
  204.     int zzztime;
  205.  
  206.     if( tick > 0 && now >= nexttick ) { /* time to tick?? */
  207.         if( beep )
  208.         putchar('\007');
  209.         fputs( ctime( &now ), stdout );
  210.         nexttick = getnexttick( now, tick );
  211.     }
  212.  
  213.     if( (numt > 0 || machines) && now >= nextscan ) {
  214.         dowho();            /* prowl rwho spool dir */
  215.         if( machines )        /* watching machines? */
  216.         scan();            /* yell about them */
  217.         this = !this;        /* toggle found index */
  218.         nextscan = now + sleeptime;
  219.     }
  220.  
  221.     fflush( stdout );
  222.  
  223.     if( tick == 0 )            /* no ticks */
  224.         sleeptime = nextscan - now;    /* then only have scan to worry about */
  225.     else {                /* ticking.. */
  226.         if( numt > 0 || machines )    /* doing both */
  227.         sleeptime = (nextscan < nexttick) ?
  228.             nextscan - now : nexttick - now;
  229.         else
  230.         sleeptime = nexttick - now;
  231.     } /* ticking */
  232.  
  233.     sleep( sleeptime );        /* go to sleep */
  234.     if( getppid() == 1 )        /* child of init? */
  235.         exit( 1 );            /* quitting time */
  236.     time( &now );
  237.     } /* for ever */
  238. } /* main */
  239.  
  240. dowho() {                /* poke thru rwho spool */
  241.     register int i;
  242.     register struct target *tp;
  243.  
  244.     char **fp;
  245.     struct whod wd;
  246.     struct stat newstat;
  247.     char buf[MAXBUF], user[MAXUSR], host[MAXHST], tty[MAXTTY];
  248.  
  249.     static int nfiles;
  250.     static struct stat oldstat;
  251.     static char *filenames[ NUMFILES ];    /* ACK!! hardwired limits! */
  252.     static char filenamestrings[ NUMFILES * BPHOST ]; /* avoid malloc/free */
  253.  
  254.     if( stat( ".", &newstat ) < 0 )
  255.     return;
  256.  
  257.     if( newstat.st_mtime != oldstat.st_mtime ) {
  258.     DIR *dirp;
  259.     struct direct *d;
  260.     register char *dp;
  261.  
  262.     oldstat = newstat;
  263. # if DEBUG > 0
  264.     puts("(re)reading RWHODIR");
  265. # endif /* DEBUG > 0 */
  266.  
  267.     if( (dirp = opendir( "." )) == NULL )
  268.         return;
  269.  
  270.     nfiles = 0;
  271.     fp = filenames;
  272.     dp = filenamestrings;
  273.     while( (d = readdir( dirp )) != NULL ) {
  274.         register char *sp;
  275.  
  276.         if( strncmp( d->d_name, "whod.", 5 ) != 0 )
  277.         continue;
  278.  
  279. # if DEBUG > 4
  280.         printf("readdir: %s\n", d->d_name );
  281. # endif /* DEBUG > 4 */
  282.         if( nfiles == NUMFILES ) {
  283.         fprintf( stderr, "Too many files!! Increase NUMFILES!!\n");
  284.         break;
  285.         } /* too many files */
  286.  
  287.         nfiles++;
  288.         *fp++ = dp;
  289.         sp = d->d_name;
  290.         while( *dp++ = *sp++ )
  291.         ;
  292.     } /* while readdir */
  293.     closedir( dirp );
  294.     } /* rwhod mtime has changed */
  295.  
  296.     for( tp = targets, i = 0; i < numt; tp = tp->tnext, i++ ) {
  297.     tp->found[this].hungry = 0;
  298.     tp->found[this].nidle  = 0;
  299.     tp->found[this].idle   = 0;
  300.     } /* for i */
  301.  
  302.     if( stamp ) {
  303.     strcpy( nowstr, ctime( &now ) );
  304.     nowstr[ 16 ] = ' ';
  305.     nowstr[ 17 ] = EOS;        /* kill year and seconds */
  306.     } /* stamping */
  307.  
  308.     for( fp = filenames, i = 0; i < nfiles; i++, fp++ ) {
  309.     register struct whoent *we;
  310.     struct machine *mp;
  311.     int f, cc, down;
  312.     time_t recvtime;
  313.     int try;
  314.     
  315. # ifdef USEFILETIME
  316.     struct stat ftime;
  317. # endif /* USEFILETIME defined */
  318.  
  319. # if DEBUG > 19
  320.     printf("=== %s ===\n", *fp );
  321. # endif /* DEBUG > 19 */
  322.  
  323.     for( try = 0; try < 5; try++, sleep(1) ) {
  324.         cc = -1;
  325.         if( (f = open( *fp, 0 )) < 0 )
  326.         continue;        /* try to read again */
  327.  
  328.         cc = read( f, &wd, sizeof( wd ));
  329. # ifdef USEFILETIME
  330.         fstat( f, &ftime );
  331. # endif /* USEFILETIME defined */
  332.         close( f );
  333.  
  334.         cc -= sizeof( wd ) - sizeof ( wd.wd_we );
  335. # if DEBUG > 19
  336.         printf("%d chars\n", cc );
  337. # endif /* DEBUG > 19 */
  338.         if( cc < 0 ) {
  339. # if DEBUG > 0 && DEBUG < 20
  340.         printf("%s: %d chars\n", wd.wd_hostname, cc );
  341. # endif /* DEBUG > 0 && DEBUG < 20 */
  342.         continue;        /* re-read file */
  343.         } /* short file */
  344.         else
  345.         break;
  346.     } /* for each try */
  347.     if( cc < 0 )
  348.         continue;            /* re-read */
  349.  
  350. # ifdef USEFILETIME
  351.     recvtime = ftime.st_mtime;
  352. # else  /* USEFILETIME not defined */
  353.     recvtime = wd.wd_recvtime;
  354. # endif /* USEFILETIME not defined */
  355.  
  356.     down = now - recvtime;
  357. # if DEBUG > 19
  358.     printf( "down %d\n", down );
  359. # endif /* DEBUG > 19 */
  360.  
  361.     if( wd.wd_hostname[0] == EOS ) {
  362. # if DEBUG > 0
  363.         printf( "null hostname in file %s\n", *fp );
  364. # endif /* DEBUG > 0 */
  365.         continue;            /* re-read!? */
  366.     } /* hostname empty */
  367.  
  368.     if( machines ) {
  369.         if( (mp = findmachine( wd.wd_hostname )) != NULL ) {
  370. # if DEBUG > 10
  371.         printf("found machine: %s\n", wd.wd_hostname);
  372. # endif /* DEBUG > 10 */
  373.  
  374.         /* filter out freaks */
  375.         if( mp->boottime != 0 &&
  376.            wd.wd_boottime - mp->boottime > 120 ) { 
  377.             sayreboot( mp );
  378. # if DEBUG > 0        
  379.             printf("new %d old %d diff %d\n",
  380.                wd.wd_boottime,
  381.                mp->boottime,
  382.                wd.wd_boottime - mp->boottime );
  383. # endif /* DEBUG > 0         */
  384.         } /* new boottime */
  385.         } /* machine found */
  386.         else
  387.         mp = newmachine( wd.wd_hostname );
  388.  
  389.         mp->latest = recvtime;    /* packet sendtime or file time */
  390.         mp->boottime = wd.wd_boottime;
  391.         mp->sendtime = wd.wd_sendtime;
  392.         mp->busy[this] = FALSE;    /* clear for this pass */
  393.     } /* machines */
  394.  
  395.     if( down > downtime )        /* host is down? */
  396.         continue;
  397.  
  398.     for( we = wd.wd_we; cc > 0 ; we++, cc -= sizeof( *we ) ) {
  399.         int tcnt;
  400.         int washungry;
  401.         register char *cp, *dp;
  402.  
  403.         washungry = ISHUNGRY(we);    /* save hungry bit */
  404.         CLEARHUNGRY(we);        /* clear to avoid bad comparisons */
  405.  
  406.         strncpy( user, we->we_utmp.out_name, 8 );
  407.         strcpy( host, wd.wd_hostname );
  408.         strcpy( tty, we->we_utmp.out_line );
  409.  
  410.         if( machines && we->we_idle <= idletime ) /* @@ Have widletime? */
  411.         mp->busy[this] = TRUE;    /* just flag as busy */
  412.  
  413. # if DEBUG > 10
  414.         printf("%s@%s %s idle %d\n", user, host, tty, we->we_idle );
  415. # endif /* DEBUG > 10 */
  416.  
  417.         for( tp = targets, tcnt = 0; tcnt < numt; tcnt++, tp = tp->tnext ) {
  418.         if( ((tp->type == USER && strcmp( user, tp->user ) == 0) ||
  419.              (tp->type == LINE && strcmp( tty, tp->user ) == 0)) ) {
  420.  
  421.             if( (dp = index( host, '.' )) != NULL )
  422.             *dp = EOS;    /* strip domains */
  423.  
  424. # if DEBUG > 4
  425. # if DEBUG < 20
  426.             printf("%s@%s %s idle %d\n",
  427.                user, host, tty, we->we_idle );
  428. # endif /* DEBUG < 20 */
  429.             printf("%s %d %d\n", tp->user,
  430.                tp->found[this].nidle,
  431.                tp->found[this].idle );
  432. # endif /* DEBUG > 4 */
  433.  
  434.             if( tp->host == WILDHOST || /* wild or */
  435.                strcmp(host, tp->host) == 0 ) { /*matching host*/
  436.             if( we->we_idle > idletime ) {
  437.                 tp->found[this].idle++;
  438.             }
  439.             else {        /* not idle */
  440.                 if( tp->found[this].nidle++ == 0 && /*first*/
  441.                    tp->found[!this].nidle == 0 ) { /*none b4*/
  442. # if DEBUG > 0 && DEBUG < 5
  443.                 tprint( tp );
  444. # endif /* DEBUG > 0 && DEBUG < 5 */
  445.                 /* nothing before or, imbalance */
  446.                 if( tp->found[!this].idle == 0 ||
  447.                    TOTAL(tp,this) > TOTAL(tp,!this) )
  448.                     now_on( user, host, tty );
  449.                 else        /* return from idleness */
  450.                     now_active( user, host, tty );
  451.                 } /* first non idle where none before */
  452.             } /* not idle */
  453.             if( washungry )
  454.                 if( tp->found[this].hungry++ == 0 &&
  455.                    tp->found[!this].hungry == 0 )
  456.                 now_hungry( user, host, tty );
  457.             } /* matching host */
  458.             goto nextwhoent;
  459.         } /* matching user */
  460.         } /* for all targets */
  461.     nextwhoent: ;
  462.     } /* for we */
  463.  
  464.     } /* for each file */
  465.    
  466.     for( tp = targets, i = 0; i < numt; i++, tp = tp->tnext ) {
  467. # if DEBUG > 49
  468.     tprint( tp );
  469. # endif /* DEBUG > 49 */
  470.     if( tp->found[!this].nidle > 0 ) {     /* was here before */
  471. # if DEBUG > 4 && DEBUG < 50
  472.         tprint( tp );
  473. # endif /* DEBUG > 4 && DEBUG < 50 */
  474.         if( tp->found[this].nidle == 0 ) {    /* no non idle users */
  475. # if DEBUG > 0 && DEBUG < 5
  476.         tprint( tp );
  477. # endif /* DEBUG > 0 && DEBUG < 5 */
  478.         if( tp->found[this].idle > 0 &&    /* have idle users */
  479.            TOTAL(tp,this) == TOTAL(tp,!this) ) /* no net change */
  480.             now_idle( tp );
  481.         else
  482.             now_gone( tp );    /* none idle or net change. */
  483.                     /* must have logged out */
  484.         } /* no non-idle */
  485.     } /* prev had non-idle */
  486.     else if( tp->found[!this].idle > 0 ) {    /* prev idle? */
  487.         if( TOTAL(tp,this) == 0 ) {        /* no current */
  488. # if DEBUG > 0 && DEBUG < 5
  489.         tprint( tp );
  490. # endif /* DEBUG > 0 && DEBUG < 5 */
  491.         now_gone( tp );
  492.         } 
  493.     } /* here but idle before */
  494.     } /* for tp */
  495. } /* do who */
  496.  
  497. now_on( user, host, tty )
  498.     char *user, *host, *tty;
  499. {
  500.     stomp();
  501.     printf("bow wow!! %s now on %s %s\n", user, host, tty );
  502. } /* now on */
  503.  
  504.  
  505. now_active( user, host, tty )
  506.     char *user, *host, *tty;
  507. {
  508.     stomp();
  509.     printf("arf!!     %s active on %s %s\n", user, host, tty );
  510. } /* now active */
  511.  
  512. now_hungry( user, host, tty )
  513.     char *user, *host, *tty;
  514. {
  515.     stomp();
  516.     printf("woof!     %s hungry on %s %s\n",  user, host, tty );
  517. } /* now hungry */
  518.  
  519. now_idle( tp )
  520.     struct target *tp;
  521. {
  522.     stomp();
  523.     printf("Zzzz!     %s", tp->user );
  524.     if( tp->host != WILDHOST ) {
  525.     putchar('@');
  526.     fputs( tp->host, stdout );
  527.     } /* have host */
  528.     puts(" is idle");
  529. } /* now idle */
  530.  
  531. now_gone( tp )
  532.     struct target *tp;
  533. {
  534.     stomp();
  535.     printf("Aroooo!   %s", tp->user );
  536.     if( tp->host != WILDHOST ) {
  537.     putchar('@');
  538.     fputs( tp->host, stdout );
  539.     } /* have host */
  540.     if( tp->type == USER )
  541.     puts(" is gone");
  542.     else
  543.     puts(" is free");
  544. } /* now gone */
  545.  
  546. now_sated( user, host )
  547.     char *user, *host;
  548. {
  549.     stomp();
  550.     printf("Burp!!    %s", user );
  551.     fputs( user, stdout );
  552.     if( host != WILDHOST ) {
  553.     putchar('@');
  554.     fputs( host, stdout );
  555.     } /* have host */
  556.     puts(" is sated!!");
  557. } /* now sated */
  558.  
  559. bool
  560. checkflag( buf, name, ptr )
  561.     char *name, *buf;
  562.     bool *ptr;
  563. {
  564.     int l;
  565.  
  566.     l = strlen( name );
  567.     if( strncmp( name, buf, l ) == 0 )
  568.     *ptr = TRUE;
  569.     else if( buf[0] == 'n' && buf[1] == 'o' && strncmp( name, buf+2, l ) == 0 )
  570.     *ptr = FALSE;
  571.     else
  572.     return( FALSE );
  573.     return( TRUE );
  574. } /* checkflag */
  575.  
  576. bool
  577. numarg( buf, name, ptr, scale )
  578.     char *name, *buf;
  579.     int *ptr, scale;
  580. {
  581.     if( strncmp( name, buf, strlen( name ) ) == 0 ) {
  582.     int i;
  583.     if( sscanf( buf, "%*s %d", &i ) != 1 )
  584.         fprintf( stderr, "fido: bad line: %s\n", buf );
  585.     else
  586.         *ptr = i * scale;
  587.     return( TRUE );
  588.     } /* matches */
  589.     return( FALSE );
  590. } /* numarg */
  591.  
  592. bool
  593. readinit( file )
  594.     char *file;
  595. {
  596.     FILE *f;
  597.     char buf[ MAXBUF ];
  598.  
  599.     if( (f = fopen(file, "r")) == NULL )
  600.     return( FALSE );
  601.  
  602.     while( fgets(buf, sizeof buf, f) != NULL ) {
  603.     register char *cp;
  604.  
  605.     if( (cp = index(buf,'\n')) != NULL )
  606.         *cp = EOS;
  607.  
  608. # if DEBUG > 9
  609.     printf("%s\n", buf );
  610. # endif /* DEBUG > 9 */
  611.  
  612.     if( buf[0] == '#' )
  613.         continue;
  614.  
  615.     if( strncmp(buf, "user", 4) == 0 )
  616.         r_user( buf );
  617.     else if( strncmp(buf, "line", 4) == 0 )
  618.         r_line( buf );
  619.     else if( strncmp( buf, "watch", 5 ) == 0 )
  620.         r_watch( buf );
  621.     else if( checkflag(buf, "beep", &beep) )
  622.         continue;
  623.     else if( checkflag(buf, "machines", &machines) )
  624.         continue;
  625.     else if( checkflag(buf, "stamp", &stamp) )
  626.         continue;
  627.     else if( numarg(buf, "idletime", &idletime, 60)  )
  628.         continue;
  629.     else if( numarg(buf, "downtime", &downtime, 60)  )
  630.         continue;
  631.     else if( numarg(buf, "sleeptime", &sleeptime, 1) )
  632.         continue;
  633.     else if( numarg(buf, "tick", &tick, 1) )
  634.         continue;
  635.     else
  636.         fprintf(stderr, "fido: bad line in %s: %s\n", file, buf );
  637.     } /* while */
  638.     if( sleeptime < MIN_SLEEPTIME )
  639.     sleeptime = MIN_SLEEPTIME;
  640.     if( tick > MAX_TICK )
  641.     tick = MAX_TICK;
  642.     fclose ( f );
  643.     return( TRUE );
  644. } /* readinit */
  645.  
  646. char *savestr( s )
  647. register char *s;
  648. {
  649.     register char *t;
  650.  
  651.     while( *s != EOS && isspace( *s ) )
  652.     s++;
  653.     if( *s == EOS )
  654.     return( "" );
  655.  
  656.     t = s;
  657.     while( *t != EOS && !isspace( *t ) && *t != '\n' )
  658.     t++;
  659.     if( *t != EOS )
  660.     *t = EOS;
  661.  
  662. # if DEBUG > 10
  663.     printf("savestr '%s'\n", s);
  664. # endif /* DEBUG > 10 */
  665.  
  666.     return( strcpy( malloc( strlen( s ) + 1 ), s) );
  667.     
  668. } /* savestr */
  669.  
  670. r_user( l )
  671. register char *l;
  672. {
  673.     register char *cp;
  674.     register struct target *tp;
  675.  
  676.     while( *l != EOS && !isspace( *l ) )
  677.       l++;
  678.  
  679.     while( *l != EOS && isspace( *l ) )
  680.       l++;
  681.  
  682.     if( *l == EOS )
  683.     return;
  684.  
  685.     tp = (struct target *) malloc( sizeof( struct target ) );
  686.     tp->type = USER;
  687.     tp->tnext = NULL;
  688.  
  689.     if( targets == NULL )
  690.     targets = tp;
  691.     else
  692.     lasttarget->tnext = tp;
  693.  
  694.     if( (cp = index(l, '@')) != NULL ) {
  695.     *cp++ = EOS;            /* blast @ to tie off user */
  696.     if( *cp != EOS )        /* have host? */
  697.         tp->host = savestr( cp );    /* save it */
  698.     else                /* none? */
  699.         tp->host = WILDHOST;    /* WTF -- save as wild */
  700.     } /* have host */
  701.     else
  702.     tp->host = WILDHOST;
  703.     tp->user = savestr( l );
  704.  
  705.     tp->found[0].idle   = 0;
  706.     tp->found[0].nidle  = 0;
  707.     tp->found[0].hungry = 0;
  708.     tp->found[1].idle   = 0;
  709.     tp->found[1].nidle  = 0;
  710.     tp->found[1].hungry = 0;
  711.  
  712.     lasttarget = tp;
  713.     numt++;
  714. } /* r_user */
  715.  
  716. r_line( l )
  717. register char *l;
  718. {
  719.     register char *cp;
  720.     register struct target *tp;
  721.  
  722.     while( *l != EOS && !isspace( *l ) )
  723.       l++;
  724.  
  725.     while( *l != EOS && isspace( *l ) )
  726.       l++;
  727.  
  728.     if( *l == EOS )
  729.     return;
  730.  
  731.     tp = (struct target *) malloc( sizeof( struct target ) );
  732.     tp->type = LINE;
  733.     tp->tnext = NULL;
  734.  
  735.     if( targets == NULL )
  736.     targets = tp;
  737.     else
  738.     lasttarget->tnext = tp;
  739.  
  740.     if( (cp = index(l, '@')) != NULL ) {
  741.     *cp++ = EOS;            /* blast @ to tie off user */
  742.     if( *cp != EOS )        /* have host? */
  743.         tp->host = savestr( cp );    /* save it */
  744.     else                /* none? */
  745.         tp->host = WILDHOST;    /* WTF -- save as wild */
  746.     } /* have host */
  747.     else
  748.     tp->host = WILDHOST;
  749.     tp->user = savestr( l );
  750.  
  751.     tp->found[0].idle   = 0;
  752.     tp->found[0].nidle  = 0;
  753.     tp->found[0].hungry = 0;
  754.     tp->found[1].idle   = 0;
  755.     tp->found[1].nidle  = 0;
  756.     tp->found[1].hungry = 0;
  757.  
  758.     lasttarget = tp;
  759.     numt++;
  760. } /* r_line */
  761.  
  762. r_watch( l )
  763. register char *l;
  764. {
  765.     register struct machine *mp;
  766.  
  767.     while( *l != EOS && !isspace( *l ) )
  768.       l++;
  769.  
  770.     while( *l != EOS && isspace( *l ) )
  771.       l++;
  772.  
  773.     if( *l == EOS ) {
  774.     watchall = TRUE;
  775.     return;
  776.     }
  777.     if( (mp = findmachine( l )) == NULL )
  778.     mp = newmachine( l );
  779.     mp->watch = TRUE;
  780. } /* r_watch */
  781.  
  782. dumpinit() {
  783.     register int i;
  784.     register struct target *tp;
  785.  
  786.     for( tp = targets, i = 0; i < numt; tp = tp->tnext, i++ )
  787.     if( tp->host == WILDHOST )
  788.         printf("%s\n", tp->user );
  789.     else
  790.         printf("%s@%s\n", tp->user, tp->host );
  791. } /* dumpinit */
  792.  
  793. /**************** machine stuff ****************/
  794.  
  795. # define SH 7
  796. # define WS 32
  797. unsigned
  798. hash( n )
  799.     register char *n;
  800. {
  801.     register unsigned l, i;
  802.  
  803.     i = l = 0;
  804.     while( *n != EOS ) {
  805.     i = ((i << SH) | (i >> (WS-SH))) ^ *n++;
  806.     l++;
  807.     } /* while not end of string */
  808.     return( (i + l) % MHSIZE );
  809. } /* hash */
  810.  
  811. struct machine *
  812. findmachine( n )
  813.     char *n;
  814. {
  815.     int h;
  816.     register struct machine *mp;
  817.  
  818.     h = hash( n );
  819.     for( mp = mhash[ h ]; mp != NULL; mp = mp->hnext )
  820.     if( strcmp(mp->name, n) == 0 )
  821.         return( mp );
  822.     return( NULL );
  823. } /* findmachine */
  824.  
  825. struct machine *
  826. newmachine( n )
  827.     char *n;
  828. {
  829.     int h;
  830.     register struct machine *mp;
  831.  
  832.     mp = (struct machine *) malloc( sizeof( struct machine ) );
  833.     strcpy(mp->name, n);
  834.     mp->state = UP;
  835.  
  836.     mp->watch = watchall;        /* get default */
  837.     mp->busy[0] = mp->busy[1] = TRUE;    /* assume is/was busy */
  838.  
  839.     mp->anext = allmachines;        /* put in list of all machines */
  840.     allmachines = mp;
  841.  
  842.     h = hash( n );            /* put in hash list */
  843.     mp->hnext = mhash[ h ];
  844.     mhash[ h ] = mp;
  845.  
  846.     if( !firstpass )
  847.     saynew( mp );
  848.  
  849.     return( mp );
  850. } /* newmachine */
  851.  
  852.  
  853. scan() {
  854.     register struct machine *mp;
  855.     int i;
  856.     static char *TF = "FT";
  857.  
  858.     for( mp = allmachines; mp != NULL; mp = mp->anext ) {
  859. # if DEBUG > 4
  860.     printf("scan: %s (%s) busy: %c (%c)\n",
  861.            mp->name, state_name[ (int)mp->state ],
  862.            TF[mp->busy[this]], TF[mp->busy[!this]] );
  863. # endif /* DEBUG > 4 */
  864.     /*
  865.      * if /usr/spool/rwho is on another machine (via NFS) and our
  866.      * clocks differ we get alot of up/down noise.  there must be
  867.      * a good way to dampen this!!
  868.      *
  869.      * perhaps keep a new state "may be down" as a cushion
  870.      *
  871.      * perhaps keep threshold per host and increase each time the
  872.      * host comes "up" without having been rebooted (see
  873.      * wd_boottime)
  874.      */
  875.     if( now - mp->latest > downtime ) { /* is down */
  876.         if( mp->state != DOWN ) {
  877.         saydown( mp, now - mp->latest );
  878.         mp->state = DOWN;
  879.         } /* new state: down */
  880.     } /* is down */
  881.     else {            /* is up */
  882.         if( mp->state != UP ) {
  883.         sayup( mp );
  884.         mp->state = UP;
  885.         } /* new state: up */
  886.  
  887.         if(
  888. # if DEBUG <= 5
  889.            !firstpass &&
  890. # endif /* DEBUG <= 5 */
  891.            mp->watch && (mp->busy[this] ^ mp->busy[!this]) )
  892.         if( mp->busy[this] )
  893.             mach_busy( mp );
  894.         else
  895.             mach_free( mp );
  896.     } /* is up */
  897.     } /* for all machines */
  898.     firstpass = FALSE;
  899. } /* scan */
  900.  
  901. stomp() {
  902.     if( beep )
  903.     putchar('\007');
  904.     if( stamp )
  905.     fputs( nowstr, stdout );
  906. } /* stomp */
  907.  
  908. saydown( mp, t )
  909.     struct machine *mp;
  910.     int t;
  911. {
  912.     int d, h, m;
  913.  
  914.     stomp();
  915.     printf("<<<<<<<<< %s is down (", mp->name );
  916.     t /= 60;                /* toss seconds */
  917.  
  918.     m = t % 60;                /* get mins */
  919.     t /= 60;                /* toss mins */
  920.  
  921.     h = t % 24;                /* get hours */
  922.     t /= 24;                /* toss hours */
  923.  
  924.     d = t % 7;                /* get days */
  925.     t /= 7;                /* cast out days (leave weeks) */
  926.  
  927.     if( t > 0 ) printf("%dw", t );
  928.     if( d > 0 ) printf("%dd", d );
  929.     if( h > 0 ) printf("%dh", h );
  930.     if( m > 0 ) printf("%dm", m );
  931.     puts(")");
  932. } /* saydown */
  933.  
  934. sayup( mp )
  935.     struct machine *mp;
  936. {
  937.     stomp();
  938.     printf(">>>>>>>>> %s is up\n", mp->name );
  939. } /* sayup */
  940.  
  941. saynew( mp )
  942.     struct machine *mp;
  943. {
  944.     stomp();
  945.     printf("!!!!!!!!! new machine %s\n", mp->name );
  946. } /* saynew sayme */
  947.  
  948. sayreboot( mp )
  949.     struct machine *mp;
  950. {
  951.     stomp();
  952.     printf("~~~~~~~~~ %s rebooted\n", mp->name );
  953. } /* sayreboot */
  954.  
  955. mach_free( mp )
  956.     struct machine *mp;
  957. {
  958.     stomp();
  959.     printf("+++++++++ %s is free\n", mp->name );
  960. } /* machine idle */
  961.  
  962. mach_busy( mp )
  963.     struct machine *mp;
  964. {
  965.     stomp();
  966.     printf("--------- %s is busy\n", mp->name );
  967. } /* machine busy */
  968.  
  969. # if DEBUG > 0
  970. tprint( tp )
  971.     register struct target *tp;
  972. {
  973.     printf("%s (%d %d %d) (%d %d %d)",
  974.        tp->user,
  975.  
  976.        tp->found[this].nidle,
  977.        tp->found[this].idle,
  978.        tp->found[this].hungry,
  979.  
  980.        tp->found[!this].nidle,
  981.        tp->found[!this].idle,
  982.        tp->found[!this].hungry
  983.        );
  984.     if( TOTAL(tp,this) != TOTAL(tp,!this) )
  985.     printf(" ***** net imbalance!! *****");
  986.     puts("");
  987. } /* tprint */
  988. # endif /* DEBUG > 0 */
  989.