#!/usr/bin/perl
#
# monstatus - print the basic status of mon in a readable format.
#
# -d		debug (sets server and port to localhost, 32768)
# -s server	use server 
# -p port	user port
#
# Written by David Eckelkamp
# multiple commands per session by Jim Trocki
#
# $Id: monstatus,v 1.5 1999/02/16 12:27:40 trockij Exp $
#
#    Copyright (C) 1998, David Eckelkamp and 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 Getopt::Std;
use Socket;
use POSIX qw(strftime);

getopts ("s:p:d");

$MonHost = "monhost";

$MonHost = $ENV{"MONHOST"}
    if (defined ($ENV{"MONHOST"}));

$MonHost = $opt_s if ($opt_s);

$MonPort   = $opt_p || getservbyname ("mon", "tcp") || 2583;

if ($opt_d) {
    $MonHost = "localhost";
    $MonPort = 32768;
}

if (!defined ($MonHost)) {
    die "No host specified or found in MONHOST\n";
}

@OPSTATS = qw(fail ok coldstart warmstart linkdown unknown
              timeout untested dependency);

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

format STATUS_TOP =
                                      Checked
Group           Service           Last        Next      Status
---------------------------------------------------------------------
.
format STATUS = 
@<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<<<<
$group,		$service,	$last,		$next,		$status
.


#
# set up TCP socket to mon server
#
$iaddr = inet_aton ($MonHost);
if ($MonPort =~ /\D/) { $MonPort = getservbyname ($MonPort, 'tcp') }
$paddr = sockaddr_in ($MonPort, $iaddr);
$proto = getprotobyname ('tcp');

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

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

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


#
# get opstatus
#
($ret, @op) = do_cmd(MON,"list opstatus");
if (!defined($ret)) {
    die "oops--unexpected error: $!\n";
} elsif ($ret !~ /^220/) {
    die "oops--list opstatus returned:\n    $ret\n";
}
@op = sort @op;


#
# get groups
#
foreach $i (@op) {
    ($group)   = $i =~ /group=(\S+)/;
    ($service) = $i =~ /service=(\S+)/;
    
    next if (defined($groups{$group}));

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

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

#
# write output
#
print "Operating Status of mon server $MonHost as of $localtime\n";

$~ = "STATUS";
$^ = "STATUS_TOP";
foreach $l (@op) {
    ($group)   = $l =~ /group=(\S+)/;
    ($service) = $l =~ /service=(\S+)/;
    ($t)       = $l =~ /last_success=(\S+)/;
    ($next)    = $l =~ /timer=(\S+)/;
    ($stat)    = $l =~ /opstatus=(\S+)/;

    $status = $OPSTATS[$stat];
    ($lh,$lm,$ls) = (localtime ($t))[2,1,0];
    ($nh,$nm,$ns) = (localtime ($time + $next))[2,1,0];
    $last = sprintf("%02d:%02d:%02d", $lh,$lm,$ls);
    $next = sprintf("%02d:%02d:%02d", $nh,$nm,$ns);
    write;
}

format GROUP_TOP = 

    Group definitions

Name            Members
-----------------------------------------------------------------------------
.
format GROUP = 
@<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$group,         $groups{$group}
.

$yuck = "yes";
$- = 0;
$^ = "GROUP_TOP";
$~ = "GROUP";
foreach $group (sort keys %groups) {
    write;
}

#
# get the failures
#
($ret, @fail) = do_cmd(MON, "list failurehist");
if (!defined($ret)) {
    die "oops--unexpected error: $!\n";
} elsif ($ret !~ /^220/) {
    die "oops--list failurehist returned:\n    $ret\n";
}

format FAIL_TOP = 

                 Recent Failures

Group           Service  Failed Time             Failed Members
--------------------------------------------------------------------------
.
format FAIL = 
@<<<<<<<<<<<<<< @<<<<<<< @<<<<<<<<<<<<<<<<<<<<   @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$group,		$service, $time,	     $which
.

if ($#fail >= 0) {
    @fail = reverse @fail;		# make it reverse chronological order
    $- = 0;
    $^ = "FAIL_TOP";
    $~ = "FAIL";
    foreach $g (@fail) {
	($group, $service, $t, $which) = split(' ', $g, 4);
	populate_group($group);
	$time = strftime("%a %D %H:%M:%S", localtime($t));
	write
#	print("$group\t$service\t$time\t$which\n");
    }
}

do_cmd("quit");
close(MON);

exit;


#
# populate_group
#
sub populate_group {
    my $group = shift;
    my(@l, $x, $hosts);

    return if (defined($groups{$group}));

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

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


#
# 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+)/) {
            last;
        }
        push (@out, $l);
    }

    ($l, @out);
}
