#!/usr/bin/perl # # statmon - check for hosts going up and down, or with bad clocks # tom christiansen on 3/8/90 # RESTART: # shouldn't really need this... ($program = $0) =~ s%.*/%%; $version = 0.3; $| = 1; &bad_usage unless $#ARGV >= 0; printf "%s v%3.1g; ", $program, $version; if ($compiled) { print "quick start.... "; } else { print "initializing... "; # some useful constants $sockaddr_t = 'S n a4 x8'; $inetaddr_t = 'C4'; $sgttyb_t = 'C4 S'; $SINCE_1970 = 2208988800; $def_timeout = 5; # how long we give a host to answer us $def_timewarp = 10; # how far time may vary until we complain $def_retries = 5; # he gets this many tries to answer us $def_sleep = 5; # between send loops $retries = $def_retries; $timeout = $def_timeout; $timewarp = $def_timewarp; $sleep = $def_sleep; $OOPS = ", can't continue"; $dashes = ('-' x 75) . "\n"; %cmds = ( 'q', 'quit', 'x', 'quit', 'h', 'help', '?', 'help', 't', 'timers', 'd', 'downers', 'u', 'uppers' , 'm', 'missing', 'U', 'usage' ); require 'sys/errno.ph'; require 'sys/socket.ph'; require 'sys/ioctl.ph'; require 'ctime.pl'; require 'getopts.pl'; } &Getopts('udmt:r:c:s:') || &bad_usage; $debug = $opt_d; $retries = $opt_r if defined $opt_r; $timeout = $opt_t if defined $opt_t; $timewarp = $opt_c if defined $opt_c; $sleep = $opt_s if defined $opt_s; DUMP: { if ($opt_u) { # dump this puppy if ($compiled++) { warn "already dumped, ignoring -u\n"; last DUMP; } print "dumping\n"; reset 'o'; # so the opt_* vars (especially $opt_u!) go away dump RESTART; # not reached } } $SIG{'INT'} = $SIG{'HUP'} = $SIG{'TERM'} = $SIG{'QUIT'} = 'quit'; $SIG{'CONT'} = 'continue'; # if they say -m, then they want to take stuff from /usr/adm/MACHINES # # which is of the general form: # # NAME features # # spool vax bsd # coyote sunos4 diskserver # pokey sunos4 diskless slow # gort convex bsd # if ($opt_m) { # try very hard to find a machines file $MACHINES = $ENV{'GHOSTS'}; $MACHINES = $ENV{'MACHINES'} unless $MACHINES; $MACHINES = $ENV{'HOME'} . '/.ghosts' unless $MACHINES; $MACHINES = $ENV{'HOME'} . '/.machines' unless -f $MACHINES; $MACHINES = '/usr/adm/MACHINES' unless -f $MACHINES; die "Can't find any MACHINES file" unless -f $MACHINES; open(MACHINES) || die "can't open $MACHINES: $!"; print "opened $MACHINES\n" if $debug; @hosts = ; close MACHINES; @hosts = grep(/^\w+\s/, @hosts); while ($criterion = shift) { @hosts = grep(/\b$criterion\b/, @hosts); } for (@hosts) { chop; s/^(\w+).*/$1/; } } else { @hosts = @ARGV; } if ($#hosts < 0) { print "No hosts\n"; &bad_usage; } print "hosts are @hosts\n" if $debug; # # ok, now create our socket we want everyone to talk to us at # chop ($localhost = `hostname`); (($name, $aliases, $type, $len, $thisaddr) = gethostbyname($localhost)) || die "no localhost \"$localhost\"$OOPS"; (($name, $aliases, $port, $proto) = getservbyname('time', 'udp')) || die "no udp service for \"time\"$OOPS"; print "service is $name, port is $port\n" if $debug; (($name, $aliases, $proto) = getprotobyname('udp')) || die "can't get udp proto$OOPS" ; socket(SOCKET, &AF_INET, &SOCK_DGRAM, $proto) || die "can't get socket$OOPS"; $this = &sockaddr(&AF_INET, 0, $thisaddr); bind(SOCKET, $this) || die "can't bind socket: $!$OOPS"; # # now go find all of our hosts' addresses, storing # these in %hosts keyed on $name # print "fetching addrs... "; for $host (@hosts) { (($name, $aliases, $type, $len, @addrs) = gethostbyname($host)) || die "no remote \"$host\"\n"; $name =~ s/\.convex\.com$//; $hosts{$name} = $addrs[0]; } print "done.\nType 'h' for help.\n"; $rin = $win = $ein = ''; vec($rin,fileno(SOCKET),1) = 1; vec($ttyin,fileno(STDIN),1) = 1; $rin |= $ttyin; # now keep interrogating forever for (;;) { %sent = (); # haven't sent anybody anything yet $sent = 0; &cbreak; print $dashes, "entering send loop\n" if $debug; while (($name, $addr) = each %hosts) { $that = &sockaddr(&AF_INET, $port, $addr); if (!send(SOCKET,0,0,$that)) { printf STDERR "couldn't send to %-12s %-16s\n", $name, &fmtaddr($addr); next; } $sent{$name}++; $sent++; #printf "sent to %-12s %s\n", $name, &fmtaddr($addr) if $debug; } print $dashes, "entering recv loop\n" if $debug; $ntimeout = $timeout; while ($sent > 0) { $then = time; last unless $nfound = select($rout=$rin, $wout=$win, $eout=$ein, $ntimeout); if ($nfound < 0) { warn "select failed: $!\n" unless $! == &EINTR; redo; } $took = (time - $then); $ntimeout -= $took; &readsock if vec($rout,fileno(SOCKET),1); &readtty if vec($rout,fileno(STDIN),1); } for $name (sort keys %sent) { $missed{$name}++; printf "%-12s missed %d times\n", $name, $missed{$name} if $debug; if (! $down{$name}) { next unless $missed{$name} > $retries; next if $down{$name}; $down{$name} = time; printf "%-12s %-16s down at %s", $name, &fmtaddr($hosts{$name}), &ctime($down{$name}); } } print "sleeping $sleep -- hit any key to interrupt\n" if $debug; select($ttyout = $ttyin, $wout=$win, $eout = $ein, $sleep); &readtty if vec($ttyout,fileno(STDIN),1); } sub sockaddr { if (wantarray) { unpack($sockaddr_t, $_[0]); } else { pack($sockaddr_t, $_[0], $_[1], $_[2]); } } sub inetaddr { if (wantarray) { unpack($inetaddr_t, $_[0]); } else { pack($inetaddr_t, $_[0], $_[1], $_[2]); } } sub source { local($file) = @_; local($return) = 0; $return = do $file; die "couldn't do \"$file\": $!" unless defined $return; die "couldn't parse \"$file\": $@" if $@; die "couldn't run \"$file\"" unless $return; } sub usage { print STDERR < $timewarp; printf "%-12s %-16s has a clock that's %4d seconds off\n", $name, &fmtaddr($hosts{$name}), $delta{$name}; } } sub missing { local($name); print "Missing Hosts\n"; for $name (sort keys %missed) { printf "%-12s %-16s has missed %d timeout%s of %d seconds\n", $name, &fmtaddr($hosts{$name}), $missed{$name}, ($missed{$name} == 1) ? " " : "s", $timeout; } } sub downers { local($name); print "Down Hosts\n"; for $name (sort keys %down) { printf "%-12s %-16s down since %s", $name, &fmtaddr($hosts{$name}), &ctime($down{$name}); } } sub uppers { local ($name); print "Up Hosts\n"; for $name (sort keys %hosts) { next if $down{$name}; printf "%-12s up\n", $name; } } sub continue { print "continuing...\n"; &cbreak; } sub cbreak { &set_cbreak(1); } sub cooked { &set_cbreak(0); } sub set_cbreak { local($on) = @_; ioctl(STDIN,&TIOCGETP,$sgttyb) || die "Can't ioctl TIOCGETP: $!"; @ary = unpack($sgttyb_t,$sgttyb); if ($on) { $ary[4] |= &CBREAK; $ary[4] &= ~&ECHO; } else { $ary[4] &= ~&CBREAK; $ary[4] |= &ECHO; } $sgttyb = pack($sgttyb_t,@ary); ioctl(STDIN,&TIOCSETP,$sgttyb) || die "Can't ioctl TIOCSETP: $!"; }