#!/usr/bin/perl

use Getopt::Long;
use IO::Socket;
use IO::Socket::INET;

use constant Undetermined => -1;
use constant Down => 0;
use constant Up => 1;


my $sock;
my $laststatus = Undetermined;
my $pos = 0;

sub WriteHtml {
    my ($status) = @_;

    my $msg = scalar localtime(time());
    $msg .= " - Status for $options{host} is " . htmlstatustostring($status) . "<br>\n";

    open(OF, ">$options{htmlfile}");
    print OF << "EOF";
<HTML>
<HEAD>
<TITLE>Simulator status of $options{host}</TITLE>
<META HTTP-EQUIV="Refresh" CONTENT="300">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" content="no-cache">
</HEAD>
<BODY>
$msg
</BODY>
</HTML>
EOF
    close (OF);
}

sub OpenConn {
    if (!($sock = IO::Socket::INET->new(Proto => 'udp',
					Type => SOCK_DGRAM,
					PeerPort => $options{port},
					PeerAddr => $options{host},
					Timeout => 1))){
	WriteHtml(0);
	die "Couldn't connect to $options{host} at port $options{port}";
    } else {
	print "Connected to $options{host} at port $options{port}\n" if $options{verbose};
    }
}

END {
    if ($sock){
	$sock->close();
    }
}

sub spin_it {

    print " \|\r" if ($pos == 0);
    print " /\r" if ($pos == 1);
    print " -\r" if ($pos == 2);
    print " \\\r" if ($pos == 3);
    if ($options{spindirection} == 1){
	$pos--;
    } else {
	$pos++;
    }
    $pos = $pos % 4;
}

sub isalive {
    my ($packet);

    $timestamp = time();
    $timestamphi = $timestamp;
    $timestamplo = $timestamp;
    if (!defined($etheridhi)){
	open(IF, "/sbin/ifconfig eth0 |");
	$line = <IF>;
	close(IF);
	$enet = (split /\s+/, $line)[4];
	if ($enet eq ""){
	    $etheridhi = hex("0x5370696e");
	    $etheridlo = hex("0x65746830");
	} else {
	    $enet =~ s/://g;
	    $etheridhi = hex("00" . substr($enet, 0, 6));
	    $etheridlo = hex(substr($enet, 6));
	}
    }

    $sequence = hex("0x12345678");
    
    $packet = pack("c2sL2l5L4", 3, 0, 4, 0, 0, $sequence, $sequence, 0, 0, 0, $timestamphi, $timestamplo, $etheridhi, $etheridlo);

    $numwrote = syswrite($sock, $packet, 48);
    print "Sent $numwrote bytes to $options{host}:$options{port}\n" if $options{verbose};

    $numread = eval {
	local $SIG{ALRM} = sub {
	    die "alarm clock restart";
	};

	alarm $options{timeout} if $options{timeout};
	$retval = eval {
	    sysread($sock, $data, 74);
	};
	alarm 0 if $options{timeout};
	if ($@ && ($@ =~ /alarm clock restart/)){
	    $retval = -1;
	} elsif ($@){
	    $retval = -2;
	}
	$retval;
    };
    alarm 0;

    print "Received $numread bytes from $options{host}:$options{port}\n" if $options{verbose};
    if ($numread > 0){
	@data = unpack("c2sL2l5", $data);
    } else {
	print "$options{host}:$options{port} is not alive at 0\n" unless $options{quiet};
	return 0;
    }
    print scalar localtime(time()) . ": " unless $options{quiet};
    if ($data[0] == 4){
	print "$options{host}:$options{port} is alive\n" unless $options{quiet};
	return 1;
    } else {
	print "Received invalid response type (", ($data[0] + '0'), ") from $options{host}:$options{port}\n" if $options{verbose};
    }
    print "$options{host}:$options{port} is not alive at 1\n" unless $options{quiet};
    return 0;
}


sub statustostring {
    my $status = shift;

    if ($status == Undetermined){
	return "Undetermined";
    } elsif ($status == Down){
	return "DOWN";
    } elsif ($status == Up){
	return "Up";
    } else {
	return "Unknown";
    }
}
    
sub htmlstatustostring {
    my $status = shift;

    if ($status == Undetermined){
	return "<font color=yellow>Undetermined</font>";
    } elsif ($status == Down){
	return "<font color=red>DOWN</font>";
    } elsif ($status == Up){
	return "<font color=green>Up</font>";
    } else {
	return "<font color=yellow>Unknown</font>";
    }
}
    
$| = 1;

%options = (host => "",
	    port => 7700,
	    timeout => 2,
	    sleep => undef,
	    spindirection => 0,
	    htmlfile => "/home/systest/html/status/%SERVER.html",
	    );

GetOptions(\%options, 'host=s', 'port=i', 'sleep=f', 'timeout=i', 'verbose', 'quiet', 'changeonly', 'spindirection', 'nospin', 'beep=s', 'dohtml', 'htmlfile=s', 'name=s');

($options{host} = shift) unless ($options{host} ne "");
die "Usage: $0 host" if ($options{host} eq "");

if (!defined($options{name})){
    $options{name} = $options{host};
}

if ($options{dohtml}){
    $options{htmlfile} =~ s/%SERVER/$options{host}/;
}

if ($options{verbose}){
    print "Options are:\n";
    foreach $key (keys %options) {
	print "\t$key = $options{$key}\n";
    }
    print "\n";
}

if ($options{changeonly}){
    $options{quiet} = 1;
    print "Verbosity being set to 0\n" if $options{verbose};
    $options{verbose} = 0;
}

OpenConn();

my $laststatus = Undetermined;

my $downcounter = 0;
while (1){
    $status = isalive();
    if ($options{changeonly}){
	if (($status != $laststatus) || $downcounter){
	    if ($status == Down){
		goto checkdone if ((++$downcounter < 2) || ($downcounter > 2));
	    } elsif ($status == Up){
		$downcounter = 0;
		goto checkdone if ($laststatus == Up);
		my $loopcount = 5;
	    } else { # Only other state is Undetermined
		$downcounter = 0;
		goto checkdone if ($laststatus == Undetermined);
	    }

	    my $msg = scalar localtime(time());
	    $msg .=  ": Status for $options{host} changed from " . statustostring($laststatus) . " to " . statustostring($status) . "\n";
	    print $msg;
	    if ($options{beep}){
		system("/home/systest/bin/send_mail $options{beep} \"Status Change\" $msg");
	    }
	    $laststatus = $status;
	} else {
	    spin_it() unless $options{nospin};
	}
    }
	
    WriteHtml($status) if $options{dohtml};

  checkdone:
    {
	if (!defined($options{sleep})){
	    if ($status){
		exit 0;
	    } else {
		exit 1;
	    }
	}
	select(undef,undef,undef,$options{sleep});
    }
}



