#! /local/bin/perl
#
## Copyright (C) 1994    Institut Pasteur
## Copyright (C) 1994    Pierre David & Universite de Versailles - St. Quentin
##                       en Yvelines
## Copyright (C) 1993-94 OLEANE
## All rights reserved.
## See the file COPYRIGHT for the terms and licence of use.
##---------------------------------------------------------------------------
##
## RCS ID: do not change, use this revision number to report bugs.
$Id = '$Id: netup,v 1.4 1994/11/08 14:34:07 wolf Exp $';

##----- Configuration section -----------------------------------------------
##

## Put here to name of the configuration file. This value can be
## overridden by the -f conffile option.

$conf = "/local/adm/conf/netup.conf";

## Netup does generate a file with the addresses of hosts to ping. The
## file is relative to the working directory. You may change it if you
## wish, but the default is usually acceptable.

$phosts = "hosts-to-ping";

##----- End of configuration section ---------------------------------------

require 'syslog.pl';
#require 'ioctl.pl';
require 'ctime.pl';

## Parse command line

while ($_ = $ARGV[0], /^-/) {
   shift;
   /^-f$/
      && ((@ARGV > 0) || die "Missing argument to \"-f\" option.\n")
          && ($conf = shift) && next;
   /^-f(.+)$/
      && ($conf = $1) && next;
   /^-v(ersion)?$/
      && ($version = 1) && next;
   /^-d(ebug)?$/
      && ($debug = 1) && next;
   /^-o(ld)?$/
      && ($old = 1) && next;
   die "Unknown option \"$_\".\n";
}

if ($version) {
   print "Netup - $Id: netup,v 1.4 1994/11/08 14:34:07 wolf Exp $\n";
   exit(0);
}

$< = $> = 1;
#do openlog("links", "cons,pid", "local6");
&readConf();

if (!defined($history)) {
   print STDERR "hist clause not found in $conf. Aborting.\n";
   exit(0);
}
if (!defined($interval)) {
   print STDERR "delay clause not found in $conf. Aborting.\n";
   exit(0);
}
if (!defined($sumdir)) {
   print STDERR "dir clause not found in $conf. Aborting.\n";
   exit(0);
}
if (!defined($global)) {
   print STDERR "global clause not found in $conf. Aborting.\n";
   exit(0);
}
if (!defined($fping)) {
   print STDERR "fping clause not found in $conf. Aborting.\n";
   exit(0);
}

chdir($sumdir) || die("Can't change directory to $sumdir");
&readChkpnt() if ($old && $chkpnt);
open(HOST, "> $phosts") || die("Can't write $phosts");
print HOST @toping;
close HOST;

foreach (@tomkdir) {
   next if (-d $_);
   print STDERR "Could not mkdir $_: $!\n" if (!mkdir($_, 0755));
}

foreach (@iactions) {
   eval("$_");
}

close(STDIN);
open(STDOUT, ">> /dev/null");
if (! $debug) {
   open(STDERR, ">> /dev/null");
   if (open(TTY, "/dev/tty")) {
      ioctl(TTY, $TIOCNOTTY, 0);
      close(TTY);
   } 
   setpgrp(0, 0);
   exit(0) if (fork != 0);
   $SIG{'HUP'} = 'doreload';
}

while (1) {
   $updates = 0;
   $t = time();
   %openActions = ();
   open(STDIN, "$fping -e $args < $phosts |") || die("Can't fping");
   while (<>) {
      next if (! /^(\S+) is (\S+)/);
      $host = $1; $state = $2; $old = $conf{"$host"};
      if ($old ne $state) {
         $updates = 1;
         print STDERR "$host from $old to $state\n" if ($debug);
         foreach $a (split(/,/, $action{$host})) {
            if ($actionList{$a}) {
               &sendAction($a, $actionList{$a}, 
                           "$host ($name{$host}) from $old to $state");
               next;
            }
         }
         $conf{"$host"} = $state;
         $when{"$host"} = $t;
         open(SUM, ">> $name{$host}") || die("Can't write $name{$host}");
         $date = &ctime($t);
         $date =~ s/^....//;
         chop($date);
         printf SUM "$date %11s %11s\n", $old, $state;
         open(HIS, ">> $history") || die("Can't open history");
         ($sec,$min,$hour,$mday,$mon,$year) = localtime($t);
         printf HIS "%04d%02d%02d%02d%02d $name{$host} $old $state\n",
                1900 + $year, $mon + 1, $mday, $hour, $min;
         close(HIS);
         close(SUM);
      }
   } 
   &closeActions();
   close(STDIN);

   if (1) {
      open(SUM, "> $global.new") || die("Can't write $global");
      foreach $i (sort byage keys(%conf)) {
         printf SUM "%-25s %-11s %s\n", $name{$i}, $conf{$i},
                    &duration($t - $when{$i});
      }
      close(SUM);
      rename("$global.new", "$global");
   }
   if ($updates) {
      open(SUM, "> $chkpnt.new") || die("Can't write $chkpnt");
      foreach $i (keys(%conf)) {
         print SUM "$i $conf{$i} $when{$i}\n";
      }
      close(SUM);
      rename("$chkpnt.new", "$chkpnt");
   }
   sleep($interval);
   &readConf() if ($reload);
}
exit(0);

sub byage {
   return($name{$a} cmp $name{$b}) if ($when{$b} == $when{$a});
   $when{$b} <=> $when{$a};
}

sub duration {
   local($t) = @_;
   local($r);

   $r = sprintf("%3d+%02d:%02d", $t / 86400, ($t % 86400) / 3600, 
               ($t % 3600) / 60);
   return($r);
}

sub sendAction {
   local($host, $action, $line) = @_;

   if ($action =~ /^\|\s*(.+)$/) {
      if (!$openActions{$host}) {
         open($host, $action) || die("Can't $action");
         $openActions{$host} = 1;
      }
   } else {
      eval("$action");
   }
   print $host "$line\n";
}

sub closeActions {
   local($a);

   foreach $a (keys %openActions) {
      close($a);
   }
}

sub readConf {
   local($ip, $nm, $act);

   $reload = 0;
   $args = "";
   %conf = %name = %action = %actionList = ();
   open(CONF, "$conf") || die("Can't open $conf");
   while (<CONF>) {
      s/\s*#.*$//g; s/^\s+//g; s/\s+$//g;
      next if (/^$/);
      if (/^host\s+(\S+)\s+(\S+)\s+(\S+)$/i) {;
         $ip = $1; $act = $2; $nm = $3;
         $name{"$ip"} = "$nm";
         $action{"$ip"} = $act;
         $conf{"$ip"} = "??";
         push (@toping, "$ip\n");
         push (@tomkdir, $1) if ($nm =~ /^(\S+)\/[^\/]+$/);
         next;
      }
      if (/^action\s+(\S+)\s+(.+)$/i) {
         $actionList{$1} = $2;
         next;
      }
      if (/^dir\s+(\S+)$/i) {
         $sumdir = $1;
         next;
      }
      if (/^hist\s+(\S+)$/i) {
         $history = $1;
         next;
      }
      if (/^delay\s+(\d+)$/i) {
         $interval = $1;
         next;
      }
      if (/^global\s+(\S+)$/i) {
         $global = $1;
         next;
      }
      if (/^chkpnt\s+(\S+)$/i) {
         $chkpnt = $1;
         next;
      }
      if (/^fping\s+(.+)$/i) {
         $fping = $1;
         next;
      }
      if (/^iaction\s+(.+)$/i) {
         push(@iactions, $1);
         next;
      }
      print STDERR "Unknown configuration file line: $_\n";
   }
   do syslog("notice", "Reloading $conf");
   close(CONF);
}

sub readChkpnt {
   return if (! -r $chkpnt);
   open(CONF, "$chkpnt") || die("Can't open $chkpnt");
   while (<CONF>) {
      next if (!/^(\S+)\s+(\S+)\s+(\d+)$/i);
      $when{$1} = $3; $conf{$1} = $2;
   }
   close(CONF);
}

sub doreload {
   $reload = 1;
}
