#!/store/bin/perl
##
# Tests the possibility to reach the hosts listed in config-file
# 
# #author Petter Reinholdtsen
# #date 1996-09-05
sub about {}

# List of hosts to test the connection to
$hostsfile  = "/home/pere/priv/hostlist";
$statusfile = "$hostsfile.status";
$logfile    = "/home/pere/priv/network.log";

# Send note if this is the first change in # seconds
$notelimit = 60*5; # five minutes

use Getopt::Std;

##
# return true if 2 or more ICMP ping packets are returnet from $host
# OS-depentendent!
sub reachable {
    ($host) = @_;
# HP/UX 9.05
#    open(PING, "/etc/ping $host 64 5 2>&1 |") || die "Unable to start ping";
# Irix 5.3
#    open(PING, "/usr/etc/ping -c 5 $host 2>&1 |") || die "Unable to start ping";
# OSF/1, NetBSD
    open(PING, "/sbin/ping -c 5 $host 2>&1 |") || die "Unable to start ping";
    while (<PING>) {
	if (/packets transmitted, (\d*) packets received/) {
	    $packets = $1;
	}
    }
    close(PING);
    return ($packets > 1);
}

##
# Print out help on usage.
sub usage {
    print <<"EOT";
netwatch.pl [-l] [-c <logfile>]
  -l  Print dates in local-time instead of GMT
  -c  Convert logfile to readable format
  -h  Show usage info
EOT
}

&getopts('c:lh');

if ($opt_h) {
    &usage();
    exit(0);
}

if ($opt_c) {
    &convert_print_logfile($opt_c);
    exit(0);
}

@hosts  = &load_hostfile($hostsfile);
%status = &load_status;

%mailinfo = ();

foreach $host (@hosts) {
    $status{$host} = "unknown 0 0" if (! $status{$host} );
    ($oldhoststat, $oldtimestamp, $oldnotifiedstamp) = 
	$status{$host} =~ m/(.*) (.*) (.*)/;
    $timestamp = time();
    if ( reachable($host) ) {
	$hoststat = "up";
    } else {
	$hoststat = "down";
    }

    # Only report if nothing has happend the last $notelimit seconds
    $notifiedstamp = $oldnotifiedstamp;
    if ($hoststat ne $oldhoststat) { # status changed, should we report?
	if ($timestamp - $oldtimestamp > $notelimit) { # its been a long time
	    $mailinfo{$host} = "$hoststat $timestamp";
	    $notifiedstamp = $timestamp;
	}
    } else { # same status, should we report?
	if ( ($timestamp - $oldnotifiedstamp > $notelimit) &&
	    ($oldtimestamp > $oldnotifiedstamp) ) {
	    $mailinfo{$host} = "$hoststat $oldtimestamp";
	    $notifiedstamp = $timestamp;
	}
    }

    if ($oldhoststat ne $hoststat) {
	$newstatus{$host} = "$hoststat $timestamp $notifiedstamp";
	&log_line("$host $hoststat");
    } else {
	$newstatus{$host} = "$oldhoststat $oldtimestamp $notifiedstamp";
    }
}

&save_status(%newstatus);

# Send mail if the link go down and this is the first change in more
# then $notelimit seconds.

if (%mailinfo) {
    print "Linkstatus endret:\n";
    foreach $host (sort keys %mailinfo) {
	($status, $oldtimestamp) = $mailinfo{$host} =~ m/([^\s]*) (.*)/;
	write_hostline($host, $status, $oldtimestamp);
    }
}

##
# record a line to the $logfile
# #params $info
sub log_line {
    local($line) = @_;
    open(LOG, ">>$logfile");

    print LOG "$timestamp $line\n";
    
    close(LOG);
}

##
# load list of hosts 
# XXX This should handle comments and blank lines
# XXX This should convert to lower-case
sub load_hostfile {
    local($hostsfile) = @_;
    open(HOSTS, "<$hostsfile") || die "unable to open $hostsfile";
    @hosts = <HOSTS>;
    close(HOSTS);
    chop(@hosts); # remove trailing newline
    return @hosts;
}

##
# #return %status
sub load_status {
    local(%status, $host, $status);
    if ( -r $statusfile ) {
	open(STATUS, "<$statusfile") || die "unable to open $statusfile";
	while (<STATUS>) {
	    ($host, $status) = m/^([^\s]*) (.*)$/;
	    $status{$host} = $status;
	}
	close(STATUS);
    } else {
	open(STATUS, ">$statusfile") || die "Unable to create $statusfile";
	close(STATUS);
    }
    return %status;
}

##
# #params %status
sub save_status {
    local(%status) = @_;
    open(STATUS, ">$statusfile") || die "Unable to open $statusfile";
    foreach $host (keys %status) {
	print STATUS "$host $status{$host}\n";
    }
    close(STATUS);
}

sub write_hostline {
    local($host, $status, $timestamp) = @_;
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
	($opt_l ? localtime($timestamp) : gmtime($timestamp));
    $mon++;
    $year += 1900;
    $~ = ($opt_l ? "HOSTLIST" : "GMTHOSTLIST");
    $mon  = "0".$mon  if ($mon  < 10);
    $mday = "0".$mday if ($mday < 10);
    $min  = "0".$min  if ($min  < 10);
    $sec  = "0".$sec  if ($sec  < 10);
    write;
}

format GMTHOSTLIST =
  @<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<< GMT @<<<-@<-@< @>:@>:@>
$host, $status, $year, $mon, $mday, $hour, $min, $sec
.

format HOSTLIST =
  @<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<     @<<<-@<-@< @>:@>:@>
$host, $status, $year, $mon, $mday, $hour, $min, $sec
.

##
# 
sub convert_print_logfile {
    ($filename) = @_;

    open(LOGFILE, "< $filename") || die "Unable to open logfile $filename";
    while (<LOGFILE>) {
	($timestamp, $host, $status) = m/^(\d*) ([^\s]*) ([^\s]*)$/;
	write_hostline($host, $status, $timestamp);
    }
}
