#!/usr/bin/perl -T
#
# all-in-one, no frills, just the information you need, web page for mon
#
# If you want an interactive version with buttons and stuff, you want
# to use Art Chan's package!
#
# To configure, adjust $MONSERVER, $MONPORT, $IMG, and $REFRESH below.
# 
# Jim Trocki, trockij@transmeta.com
#
# $Id: opstatus.cgi,v 1.13 1999/02/12 18:45:45 trockij Exp $
#
#    Copyright (C) 1998, Jim Trocki
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
use Socket;
use Text::ParseWords;

#
# CONFIGURE THESE:
#
$MONSERVER="localhost";
$MONPORT=2583;
$IMG="company-logo.gif";
$REFRESH=480;

$WORD = '[a-zA-Z0-9_-]+';
$SIG{'PIPE'} = 'IGNORE';
#
# operational statuses
#
($STAT_FAIL, $STAT_OK, $STAT_COLDSTART, $STAT_WARMSTART,
    $STAT_LINKDOWN, $STAT_UNKNOWN, $STAT_TIMEOUT, $STAT_UNTESTED,
    $STAT_DEPEND, $STAT_WARN) = (0..9);


$time = time;
$localtime = localtime(time);

$| = 1;

#
# set up TCP socket
#
$iaddr = inet_aton ($MONSERVER);
if ($MONPORT =~ /\D/) { $MONPORT = getservbyname ($MONPORT, 'tcp') }
$paddr = sockaddr_in ($MONPORT, $iaddr);
$proto = getprotobyname ('tcp');

if (!defined(socket (MON, PF_INET, SOCK_STREAM, $proto))) {
    &html_die ("could not create socket: $!\n");
}

if (!defined(connect (MON, $paddr))) {
    &html_die ("could not connect: $!\n");
}

select (MON); $| = 1; select (STDOUT);


#
# scheduler state
#
($ret, @state) = &do_cmd(MON, "list state");
if (!defined($ret)) {
    &html_die ("oops--unexpected error: $!\n");
} elsif ($ret !~ /^220/) {
    &html_die ("oops--list disabled returned:\n    $ret\n");
}

if ($state[0] =~ /stopped since (\d+)/) {
    $STATE_LINE = "Scheduler status: <font color=#ff4040> scheduler stopped since " .
	localtime ($1) . "</font>";
} else {
    $STATE_LINE = "Scheduler status: running";
}

#
# get what is disabled
#
($ret, @disabled) = &do_cmd(MON, "list disabled");
if (!defined($ret)) {
    &html_die ("oops--unexpected error: $!\n");
} elsif ($ret !~ /^220/) {
    &html_die ("oops--list disabled returned:\n    $ret\n");
}

foreach $l (@disabled) {
    if ($l =~ /^group ($WORD): (.*)$/) {
    } elsif ($l =~ /^watch ($WORD)$/) {
    	$watch{$1} = 1;
	next;
    } elsif ($l =~ /^watch ($WORD) service ($WORD)$/) {
    	$service{$1}{$2} = 1;
	next;
    }
}


#
# get the current operational status
#
($ret, @opstatus) = &do_cmd(MON, "list opstatus");
if (!defined($ret)) {
    &html_die ("oops--unexpected error: $!\n");
} elsif ($ret !~ /^220/) {
    &html_die ("oops--list opstatus returned:\n    $ret\n");
}

foreach $l (@opstatus) {
    my ($var, $val, $w, %vars);
# group=crap service=serv opstatus=7 exitval=undef timer=5 last_success=0 last_failure=0 last_trap=0 last_summary=

    foreach $w (quotewords ('\s+', 0, $l)) {
	($var, $val) = split (/=/, $w);
	$vars{$var} = $val;
    }

    if ($watch{$vars{"group"}} == 1) {
    	push (@dis_watch, {%vars});
	next;
    } elsif ($service{$vars{"group"}}{$vars{"service"}} == 1) {
    	push (@dis_service, {%vars});
	next;
    } elsif ($vars{"opstatus"} == $STAT_OK) {
    	push (@op, {%vars});
	next;
    } elsif ($vars{"opstatus"} == $STAT_UNTESTED) {
    	push (@untested, {%vars});
	next;
    } else {
    	push (@failures, {%vars});
	next;
    }
}


#
# get the group members
#
foreach $i (@failures, @dis_watch, @dis_service, @untested, @op) {
    
    next if (defined($groups{$$i{"group"}}));

    ($ret, @l) = &do_cmd(MON, "list group $$i{group}");
    if (!defined($ret)) {
	&html_die ("oops--unexpected error: $!\n");
    } elsif ($ret !~ /^220/) {
	&html_die ("oops--list group returned:\n    $ret\n");
    }

    ($x, $group, $hosts) = split (/\s+/, $l[0], 3);
    $groups{$group} = $hosts;
}

#
# all done
#
&do_cmd(MON, "quit");
if (!defined($ret)) {
    &html_die ("oops--unexpected error: $!\n");
} elsif ($ret !~ /^220/) {
    &html_die ("oops--quit returned:\n    $ret\n");
}
close (MON);


#
# display the table
#
my %displayed = ();

print <<EOF;
Content-type: text/html

<HTML>

<meta http-equiv="Refresh" content=480>

<head>
<title> Mon Operational Status </title>
</head>

<body bgcolor="#e1e1e1">
<center>
<img src="$IMG" alt="Company Logo">
</center>

<em>This page will refresh in 8 minutes.</em>
<p>

Current operating status for mon, running on $MONSERVER, as of $localtime
<p>

$STATE_LINE
<p>

<font color=#0000ff>blue</font> means disabled watches, services, or hosts<br>
<font color=#ff4040>red</font> means a failure for a particular service
<p>

<table cellpadding=2 cellspacing=1 bgcolor="#CCCCCC">
<th>Group <th>Service <th>Member Hosts <th> Last check <th> Next check <th> Status

EOF

#
# show list of hosts
#
foreach $l (sort (@failures), sort (@dis_watch), sort (@dis_service),
					sort (@untested), sort (@op)) {
    &display ($l);
}

print <<EOF;
</table>
</HTML>
EOF
exit;


#
# display a row in HTML
#
sub display {
    my ($l) = @_;
    my ($g, $t);

    $$l{"last_summary"} = "(untested)" if ($$l{"opstatus"} == $STAT_UNTESTED);
    $$l{"last_summary"} = "(succeeded)" if ($$l{"opstatus"} == $STAT_OK);

    $g = "";
    if ($displayed{$$l{"group"}} == 1) {
    	$g = "<pre> </pre>";
    } else {
	$displayed{$$l{"group"}} = 1;
	foreach $h (split (/\s+/, $groups{$$l{"group"}})) {
	    if ($h =~ /^\*(.*)$/) {
		$g .= "<font color=#0000ff>$1</font> ";
	    } else {
		$g .= "$h ";
	    }
	}
    }

    if ($$l{"opstatus"} == $STAT_OK || $$l{"opstatus"} == $STAT_DEPEND) {
    	$t = $$l{"last_success"};
    } else {
    	$t = 0;
    }

    ($lh,$lm,$ls) = (localtime ($t))[2,1,0];
    ($nh,$nm,$ns) = (localtime ($time + $$l{"timer"}))[2,1,0];

    if ($watch{$$l{"group"}} == 1) {
    	$wcolor = "bgcolor=#a0a0ff";
    	$gcolor = "bgcolor=#a0a0ff";
	$scolor = "bgcolor=#a0a0ff";
    	$t1color = "bgcolor=#a0a0ff";
    	$t2color = "bgcolor=#a0a0ff";
    	$ocolor = "bgcolor=#a0a0ff";
    } else {
    	$wcolor = "";
	if ($service{$l{"group"}}{$l{"service"}} == 1) {
	    $wcolor = "";
	    $gcolor = "";
	    $scolor = "bgcolor=#a0a0ff";
	    $t1color = "bgcolor=#a0a0ff";
	    $t2color = "bgcolor=#a0a0ff";
	    $ocolor = "bgcolor=#a0a0ff";
	} elsif ($$l{"opstatus"} != $STAT_OK &&
		 $$l{"opstatus"} != $STAT_UNTESTED &&
		 $$l{"opstatus"} != $STAT_DEPEND) {
	    $wcolor = "bgcolor=#ff4040";
	    $gcolor = "bgcolor=#ff4040";
	    $scolor = "bgcolor=#ff4040";
	    $t1color = "bgcolor=#ff4040";
	    $t2color = "bgcolor=#ff4040";
	    $ocolor = "bgcolor=#ff4040";
	} else {
	    $wcolor = "";
	    $scolor = "";
	    $gcolor = "";
	    $t1color = "";
	    $t2color = "";
	    $ocolor = "";
	}
    }

    print "<TR>";
    print "<TD align=center $wcolor> $$l{group}";
    print "<TD align=center $scolor> $$l{service}";
    print "<TD align=center $gcolor> $g";

    if ($t == 0) {
    	print "<td align=center $t1color> undefined";
    } else {
	printf ("<td align=center $t1color> %02d:%02d", $lh, $lm);
    }

    printf ("<td align=center $t2color> %02d:%02d", $nh,$nm);

    print "<td align=center $ocolor>$$l{last_summary}\n";
}



#
# do a command, returning an array full of the output,
# where the first element is the status of the command,
# or undef on error
#
sub do_cmd {
    my ($fd, $cmd) = @_;
    my ($l, @out);

    @out = ();
    print $fd "$cmd\n";
    while (defined($l = <$fd>)) {
	chomp $l;
    	if ($l =~ /^(\d{3}\s)/) {
	    last;
	}
	push (@out, $l);
    }

    ($l, @out);
}


#
# die with some HTML output
#
sub html_die {
    my ($str) = @_;
    print <<EOF;
Content-Type: text/html

<html>
<head>
<title> opstatus failure </title>
</head>
<pre>

opstatus failed with the following diagnostics:

$str

</pre>
</body>
</html>
EOF
    exit(1);
}
