home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / tutorial / eg / statmon < prev    next >
Encoding:
Text File  |  1990-03-10  |  9.4 KB  |  447 lines

  1. #!/usr/bin/perl
  2. #
  3. # statmon - check for hosts going up and down, or with bad clocks
  4. # tom christiansen <tchrist@convex.com> on 3/8/90
  5. #
  6.  
  7. RESTART:              # shouldn't really need this...
  8.  
  9. ($program = $0) =~ s%.*/%%;
  10. $version = 0.3;
  11.  
  12. $| = 1;
  13.  
  14. &bad_usage unless $#ARGV >= 0;
  15.  
  16. printf "%s v%3.1g; ", $program, $version;
  17.  
  18. if ($compiled) {
  19.     print "quick start.... ";
  20. } else {
  21.     print "initializing... ";
  22.     
  23.     # some useful constants
  24.     $sockaddr_t    = 'S n a4 x8';
  25.     $inetaddr_t = 'C4';
  26.     $sgttyb_t   = 'C4 S';            
  27.  
  28.     $SINCE_1970 = 2208988800;
  29.  
  30.     $def_timeout  = 5;      # how long we give a host to answer us
  31.     $def_timewarp = 10;     # how far time may vary until we complain
  32.     $def_retries  = 5;        # he gets this many tries to answer us
  33.     $def_sleep    = 5;      # between send loops
  34.  
  35.     $retries      = $def_retries;
  36.     $timeout      = $def_timeout;
  37.     $timewarp     = $def_timewarp;
  38.     $sleep        = $def_sleep;
  39.  
  40.     $OOPS = ", can't continue";
  41.  
  42.     $dashes = ('-' x 75) . "\n";
  43.  
  44.     %cmds = (
  45.     'q',    'quit',
  46.     'x',    'quit',
  47.     'h',    'help',
  48.     '?',    'help',
  49.     't',    'timers',
  50.     'd',    'downers',
  51.     'u',    'uppers' ,
  52.     'm',    'missing',
  53.     'U',    'usage' 
  54.     );
  55.  
  56.     &source('sys/errno.h');
  57.     &source('sys/socket.h');
  58.     &source('sizeof.h');
  59.     &source('sys/ioctl.h');
  60.     &source('ctime.pl');
  61.     &source('getopts.pl');
  62.  
  63.  
  64. &Getopts('udmt:r:c:s:') || &bad_usage;
  65.  
  66. $debug = $opt_d;
  67.  
  68.  
  69. $retries  = $opt_r if defined $opt_r;
  70. $timeout  = $opt_t if defined $opt_t;
  71. $timewarp = $opt_c if defined $opt_c;
  72. $sleep    = $opt_s if defined $opt_s;
  73.  
  74. DUMP: {
  75.     if ($opt_u) {  # dump this puppy
  76.     if ($compiled++) {
  77.         warn "already dumped, ignoring -u\n";
  78.         last DUMP;
  79.     } 
  80.     print "dumping\n";
  81.     reset 'o';        # so the opt_* vars (especially $opt_u!) go away
  82.     dump RESTART;
  83.     # not reached
  84.     } 
  85. }
  86.  
  87. $SIG{'INT'}  = $SIG{'HUP'} = $SIG{'TERM'} = $SIG{'QUIT'} = 'quit';
  88. $SIG{'CONT'} = 'continue';
  89.  
  90. # if they say -m, then they want to take stuff from /usr/adm/MACHINES
  91. #
  92. # which is of the general form:
  93. #
  94. #    NAME    features
  95. #
  96. #    spool   vax bsd
  97. #    coyote    sunos4 diskserver
  98. #    pokey    sunos4 diskless slow
  99. #    gort     convex bsd 
  100. #
  101. if ($opt_m) {
  102.     # try very hard to find a machines file
  103.     $MACHINES = $ENV{'GHOSTS'};
  104.     $MACHINES = $ENV{'MACHINES'}         unless $MACHINES;        
  105.     $MACHINES = $ENV{'HOME'} . '/.ghosts'   unless $MACHINES;
  106.     $MACHINES = $ENV{'HOME'} . '/.machines' unless -f $MACHINES;
  107.     $MACHINES = '/usr/adm/MACHINES'         unless -f $MACHINES;
  108.  
  109.     die "Can't find any MACHINES file"      unless -f $MACHINES;
  110.  
  111.     open MACHINES ||                        die "can't open $MACHINES: $!";
  112.  
  113.     print "opened $MACHINES\n"            if $debug;
  114.     @hosts = <MACHINES>;
  115.     close MACHINES;
  116.  
  117.     @hosts = grep(/^\w+\s/, @hosts);
  118.  
  119.     while ($criterion = shift) {
  120.     @hosts = grep(/\b$criterion\b/, @hosts);
  121.     } 
  122.  
  123.     for (@hosts) {
  124.     chop;
  125.     s/^(\w+).*/$1/;
  126.     } 
  127. } else {
  128.     @hosts = @ARGV;
  129.  
  130. if ($#hosts < 0) {
  131.     print "No hosts\n";
  132.     &bad_usage;
  133.  
  134. print "hosts are @hosts\n" if $debug;
  135.  
  136. #
  137. # ok, now create our socket we want everyone to talk to us at
  138. #
  139.  
  140. chop ($localhost = `hostname`);
  141.  
  142. (($name, $aliases, $type, $len, $thisaddr) = gethostbyname($localhost))
  143.     || die "no localhost \"$localhost\"$OOPS";
  144.  
  145. (($name, $aliases, $port, $proto) = getservbyname('time', 'udp'))
  146.     || die "no udp service for \"time\"$OOPS";
  147.  
  148. print "service is $name, port is $port\n" 
  149.     if $debug;
  150.  
  151.  
  152. (($name, $aliases, $proto) = getprotobyname('udp'))
  153.     || die "can't get udp proto$OOPS" ;
  154.  
  155.  
  156. socket(SOCKET, &AF_INET, &SOCK_DGRAM, $proto) 
  157.     || die "can't get socket$OOPS";
  158.  
  159. $this = &sockaddr(&AF_INET, 0, $thisaddr);
  160.  
  161. bind(SOCKET, $this) 
  162.     || die "can't bind socket: $!$OOPS";
  163.  
  164. #
  165. # now go find all of our hosts' addresses, storing
  166. # these in %hosts keyed on $name
  167. #
  168.  
  169.  
  170. print "fetching addrs... ";
  171.  
  172. for $host (@hosts) {
  173.     (($name, $aliases, $type, $len, @addrs) = gethostbyname($host))
  174.     || die "no remote \"$host\"\n";
  175.  
  176.     $name =~ s/\.convex\.com$//;
  177.  
  178.     $hosts{$name} = $addrs[0];
  179. }
  180.  
  181. print "done.\nType 'h' for help.\n";
  182.  
  183. $rin = $win = $ein = '';
  184. vec($rin,fileno(SOCKET),1) = 1;
  185. vec($ttyin,fileno(STDIN),1) = 1;
  186. $rin |= $ttyin;
  187.  
  188.  
  189.  
  190. # now keep interrogating forever
  191. for (;;) {
  192.     %sent = ();  # haven't sent anybody anything yet
  193.     $sent = 0;
  194.  
  195.     &cbreak;
  196.  
  197.     print $dashes, "entering send loop\n" if $debug;
  198.  
  199.     while (($name, $addr) = each %hosts) {
  200.     $that = &sockaddr(&AF_INET, $port, $addr);
  201.  
  202.     if (!send(SOCKET,0,0,$that)) {
  203.         printf STDERR "couldn't send to %-12s %-16s\n", $name, &fmtaddr($addr);
  204.         next;
  205.     }
  206.  
  207.     $sent{$name}++;
  208.     $sent++;
  209.  
  210.     #printf "sent to %-12s %s\n", $name, &fmtaddr($addr) if $debug;
  211.     }
  212.  
  213.     print $dashes, "entering recv loop\n" if $debug;
  214.  
  215.     $ntimeout = $timeout;
  216.  
  217.     while ($sent > 0) {
  218.         $then = time;
  219.         last unless $nfound = select($rout=$rin, $wout=$win, $eout=$ein, $ntimeout);
  220.         if ($nfound < 0) {
  221.         warn "select failed: $!\n" unless $! == &EINTR;
  222.         redo;
  223.         } 
  224.         $took = (time - $then);
  225.         $ntimeout -= $took; 
  226.  
  227.         &readsock if vec($rout,fileno(SOCKET),1); 
  228.         &readtty if vec($rout,fileno(STDIN),1); 
  229.     }
  230.  
  231.     for $name (sort keys %sent) {
  232.     $missed{$name}++;
  233.     printf "%-12s missed %d times\n", $name, $missed{$name} if $debug;
  234.     if (! $down{$name}) {
  235.         next unless $missed{$name} > $retries;
  236.         next if $down{$name};
  237.         $down{$name} = time;
  238.         printf "%-12s %-16s down at %s", 
  239.         $name, &fmtaddr($hosts{$name}), &ctime($down{$name});
  240.     }
  241.     } 
  242.  
  243.     print "sleeping $sleep -- hit any key to interrupt\n" if $debug;
  244.     select($ttyout = $ttyin, $wout=$win, $eout = $ein, $sleep);
  245.     &readtty if vec($ttyout,fileno(STDIN),1); 
  246. }
  247.  
  248. sub sockaddr {
  249.     if (wantarray) {
  250.         unpack($sockaddr_t, $_[0]);
  251.     } else {
  252.         pack($sockaddr_t, $_[0], $_[1], $_[2]);
  253.     } 
  254.  
  255. sub inetaddr {
  256.     if (wantarray) {
  257.         unpack($inetaddr_t, $_[0]);
  258.     } else {
  259.         pack($inetaddr_t, $_[0], $_[1], $_[2]);
  260.     }
  261.  
  262. sub source {
  263.     local($file) = @_;
  264.     local($return) = 0;
  265.  
  266.     $return = do $file;
  267.     die "couldn't do \"$file\": $!" unless defined $return;
  268.     die "couldn't parse \"$file\": $@" if $@;
  269.     die "couldn't run \"$file\"" unless $return;
  270. }
  271.  
  272. sub usage {
  273.     print STDERR <<EOM;
  274. usage: $program [switches] host ...
  275.    or: $program [switches] -m [criterion ...]
  276.  
  277. switches are:
  278.     -m  look in MACHINES file for hosts matching criteria
  279.  
  280.     -t    timeout for responses (default $def_timeout)
  281.     -r    retries until timed-out host considered down (default $def_retries)
  282.     -c  clock drift tolerance (default $def_timewarp)
  283.     -s  sleep interval between send loops (default $def_sleep)
  284.  
  285.     -d  print out debugging information
  286.     -u  dump state to disk for faster init
  287. EOM
  288.  
  289. sub bad_usage {
  290.     &usage;
  291.     exit(1);
  292.  
  293. sub fmtaddr {
  294.     sprintf("[%d.%d.%d.%d]", &inetaddr(@_[0]));
  295.  
  296.  
  297. sub readsock {
  298.     ($hisaddr = recv(SOCKET,$histime='',4,0))
  299.     || (warn "couldn't recv: $!$OOPS", return);
  300.  
  301.     $sent--;
  302.  
  303.     ($addrtype, $port, $iaddr) = &sockaddr($hisaddr);
  304.  
  305.     $histime = unpack('L',$histime);
  306.     $histime -= $SINCE_1970;
  307.  
  308.     unless (($name,$aliases,$addrtype,$length,@addrs) =
  309.         gethostbyaddr($iaddr,$addrtype)) 
  310.     {
  311.     printf STDERR "received reply from unknown address %sn",
  312.                 &fmtaddr($iaddr);
  313.     next;
  314.     } 
  315.     $name =~ s/\.convex\.com$//;
  316.  
  317.     printf "%-12s %-16s thinks it's %s", 
  318.         $name, &fmtaddr($iaddr), &ctime($histime) if $debug;
  319.  
  320.     $delta = ($histime - time);
  321.     $delta = -$delta if $delta < 0;
  322.     $delta{$name} = $delta;
  323.  
  324.     delete $missed{$name};
  325.  
  326.     if ($down{$name}) {
  327.     printf "%-12s %-16s back at %s",
  328.         $name, &fmtaddr($iaddr), &ctime(time);
  329.     delete $down{$name};
  330.     } 
  331.  
  332.     printf "funny, i didn't send $name anything\n" unless $hosts{$name};
  333.     delete $sent{$name};
  334. }
  335.  
  336. sub readtty {
  337.     local($cmd) = getc;
  338.     local($routine) = '';
  339.  
  340.     $cmd = sprintf ("%c", ord($cmd) & 0x7f);
  341.  
  342.     if (defined $cmds{$cmd}) {
  343.     $routine = $cmds{$cmd};
  344.     print "\n",$dashes unless $routine eq 'quit';
  345.     &$routine;
  346.     print $dashes;
  347.     } else {
  348.     printf " -- unknown command: `%s' (0x%02x)\n", $cmd, ord($cmd);
  349.     } 
  350.  
  351. sub quit {
  352.     $SIG{'TTOU'} = "IGNORE";
  353.     &cooked;
  354.     exit 0;
  355.  
  356. sub help {
  357.     local($cmd);
  358.     print "Key\tCommand\n";
  359.     for $cmd (sort keys %cmds) {
  360.     printf "%s\t%s\n", $cmd, $cmds{$cmd};
  361.     } 
  362.  
  363. sub timers {
  364.     local($name);
  365.     print "Bad Clocks exceeding $timewarp seconds\n";
  366.     for $name (sort keys %delta) {
  367.     next unless $delta{$name} > $timewarp;
  368.     printf "%-12s %-16s has a clock that's %4d seconds off\n", 
  369.         $name, &fmtaddr($hosts{$name}), $delta{$name};
  370.     }
  371. }
  372.  
  373.  
  374. sub missing {
  375.     local($name);
  376.     print "Missing Hosts\n";
  377.     for $name (sort keys %missed) {
  378.     printf "%-12s %-16s has missed %d timeout%s of %d seconds\n",
  379.         $name, &fmtaddr($hosts{$name}), $missed{$name},
  380.         ($missed{$name} == 1) ? " " : "s", $timeout;
  381.     }
  382.  
  383. sub downers {
  384.     local($name);
  385.     print "Down Hosts\n";
  386.     for $name (sort keys %down) {
  387.     printf "%-12s %-16s down since %s", 
  388.         $name, &fmtaddr($hosts{$name}), &ctime($down{$name});
  389.     } 
  390.  
  391. sub uppers {
  392.     local ($name);
  393.  
  394.     print "Up Hosts\n";
  395.  
  396.     for $name (sort keys %hosts) {
  397.     next if $down{$name};
  398.     printf "%-12s up\n", $name;
  399.     } 
  400.  
  401. sub continue { 
  402.     print "continuing...\n";
  403.     &cbreak; 
  404. }
  405.  
  406. sub cbreak {
  407.     &set_cbreak(1);
  408.  
  409. sub cooked {
  410.     &set_cbreak(0);
  411.  
  412. sub set_cbreak {
  413.     local($on) = @_;
  414.  
  415.     ioctl(STDIN,&TIOCGETP,$sgttyb) 
  416.     || die "Can't ioctl TIOCGETP: $!";
  417.  
  418.     @ary = unpack($sgttyb_t,$sgttyb);
  419.     if ($on) {
  420.     $ary[4] |= &CBREAK;
  421.     $ary[4] &= ~&ECHO;
  422.     } else {
  423.     $ary[4] &= ~&CBREAK;
  424.     $ary[4] |= &ECHO;
  425.     }
  426.     $sgttyb = pack($sgttyb_t,@ary);
  427.     ioctl(STDIN,&TIOCSETP,$sgttyb)
  428.         || die "Can't ioctl TIOCSETP: $!";
  429.  
  430. }
  431.