#!/usr/bin/perl
#
# monshow - concise, user view-based opstatus summary
#
# Jim Trocki, trockij@transmeta.com
#
# $Id: monshow,v 1.12.2.12 2000/02/06 22:39:30 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 strict "vars";

use Getopt::Long;
use English;
use CGI;

use Mon::Client;

#
# forward declarations
#
sub usage;
sub get_auth;
sub read_cf;
sub err_die;
sub secs_to_hms;
sub display_allok;
sub compose_detail;
sub compose_group;
sub compose_header;
sub compose_table;
sub compose_trailer;
sub get_client_status;
sub opstatus_color;
sub tdiff_string;

my %OPSTAT = %Mon::Client::OPSTAT;
my $WORD = '[a-zA-Z0-9_-]+';
my $OUT_BUF = "";
my $e;
$= = 1000;
$SIG{INT} = \&handle_sig;
$SIG{TERM} = \&handle_sig;

my ($DEP, $GROUP, $SERVICE, $STATUS, $TIME, $NEXT, $ALERTS, $SUMMARY, $DESC);

my %opt;
GetOptions (\%opt, "showall", "auth", "help", "full", "disabled",
	"rcfile=s", "login=s", "server=s", "port=i", "prot=s", "old");

my $CGI;
my %QUERY_ARGS;
if (defined $ENV{"REQUEST_METHOD"})
{
    $CGI = new CGI;
}

if (!$CGI && $opt{"help"})
{
    usage;
    exit 1;
}

my $CF = {
    full		=> 0,
    show-disabled	=> 0,
    bg_ok		=> "a0d0a0",
    bg_fail		=> "e088b7",
    bg_untested		=> "e0e0e0",
};

my ($e, $what) = read_cf ($CF);

if ($e ne "")
{
    err_die ("while reading config file, $e");
}

#
# cmdline args override config file
#
if (!$CGI)
{
    $CF->{"all"}		= 1 if ($opt{"showall"});
    $CF->{"show-disabled"}	= 1 if ($opt{"disabled"});
    $CF->{"full"}		= 1 if ($opt{"full"});
    $CF->{"host"}		= $opt{"server"} if ($opt{"server"});
    $CF->{"port"}		= $opt{"port"} if ($opt{"port"});
    $CF->{"prot"}		= $opt{"prot"} if ($opt{"prot"});
}

else
{
    foreach my $e (split (/\?/, $ENV{"QUERY_STRING"}))
    {
    	my ($var, $val) = split (/=/, $e);
	$QUERY_ARGS{$var} = $val;
    }
}

#
# retrieve client status
#
my ($e, $state, $opstatus, $disabled, $deps, $groups, $descr) = get_client_status;

if ($e ne "")
{
    err_die ($e);
}

my $rows = select_table ($what, $state, $opstatus, $disabled, $deps);

compose_header ($state);

if ($CGI && !$QUERY_ARGS{"detail"})
{
    compose_table ($rows, $opstatus, $disabled, $deps, $descr);
    compose_disabled ($disabled);
}

elsif ($CGI && $QUERY_ARGS{"detail"})
{
    compose_detail ($QUERY_ARGS{"detail"}, $opstatus, $disabled, $deps, $descr, $groups);
}

elsif ($CGI && $QUERY_ARGS{"group"})
{
    compose_group ($QUERY_ARGS{"group"}, $groups);
}

elsif (!$CGI)
{
    compose_table ($rows, $opstatus, $disabled, $deps, $descr);
    compose_disabled ($disabled);
}

compose_trailer;

if ($CGI)
{
    print <<EOF;
Content-type: text/html

$OUT_BUF
EOF
}

else
{
    print "\n";
}

exit;

#-----------------------------------------------------------------------------

format STDOUT_TOP =

  GROUP           SERVICE      STATUS      LAST       NEXT       ALERTS SUMMARY
.


#
# usage
#
sub usage
{
    print <<EOF;

usage: monshow [--auth] [--showall] [--full] [--login user] [--disabled]
               [--server host] [--port num] [--rcfile file]
    --showall      do not read rcfile and show opstatus of all services
    --full         show disabled, failures, successes, and untested
    --auth         authenticate to mon server
    --disabled     show disabled
    --prot ver     set protocol version, must match 1.2.3 format
    --old          use old protocol (0.37.0) and old port (32777)
    --login user   use "login" as username while authenticating
    --server host  use "host" as monhost, instead of MONHOST
    --port num     use "num" as port number instead of default
    --rcfile file  use "file" as config file instead of \$HOME/.monshowrc

EOF
}


#
# signal handler
#
sub handle_sig
{
    system "stty echo" unless ($CGI);
    exit;
}


#
# get auth info
#
# returns a list "error", "login", "password"
#
sub get_auth
{
    my ($login, $pass);

    if ($CGI)
    {
    	$login = $CGI->query ("login");
    	$pass  = $CGI->query ("password");
    }

    else
    {
	if ($opt{"login"})
	{
	    $login = $opt{"login"};
	}

	else
	{
	    return "could not determine username"
	    	if (!defined ($login = getpwuid($EUID)));
	}

	if (-t STDIN)
	{
	    system "stty -echo";
	    print "Password: ";
	    chop ($pass = <STDIN>);
	    print "\n";
	    system "stty echo";
	    return "invalid password" if ($pass =~ /^\s*$/);
	}

	else
	{
	    my $cmd;

	    while (defined ($cmd = <>))
	    {
		chomp $cmd;
		if ($cmd =~ /^user=(\S+)$/i)
		{
		    $login = $1;
		}

		elsif ($cmd =~ /^pass=(\S+)$/i)
		{
		    $pass = $1;
		}

		last if (defined ($login) && defined ($pass));
	    }
	}
    }

    return "inadequate authentication information supplied"
	if ($login eq "" || $pass eq "");
    
    return ("", $login, $pass);
}

#
# config file
#
sub read_cf
{
    my $CF = shift;

    my ($group, $service);
    my @RC;

    my $RC = "/etc/mon/monshowrc";

    if ($CGI)
    {
	if (-f ".monshowrc")
	{
	    $RC = ".monshowrc";
	}
    }

    else
    {
	if ($opt{"rcfile"})
	{
	    $RC = $opt{"rcfile"};
	}

	elsif (-f "$ENV{HOME}/.monshowrc")
	{
	    $RC = "$ENV{HOME}/.monshowrc";
	}
    }

    if ($opt{"old"})
    {
	$CF->{"prot"} = "0.37.0";
	$CF->{"port"} = 32777;
    }

    if (-f $RC)
    {
	open (IN, $RC) || return "could not read $RC: $!";

	while (<IN>)
	{
	    next if (/^\s*#/ || /^\s*$/);
	    chomp;
	    s/^\s*//;
	    s/\s*$//;
	    if (/^set \s+ (\S+) \s* (\S+)?/ix)
	    {
		my $cmd = $1;
		my $arg = $2;
		if ($cmd eq "show-disabled") { }
		elsif ($cmd eq "host") { }
		elsif ($cmd eq "prot") { }
		elsif ($cmd eq "port") { }
		elsif ($cmd eq "full") { }
		elsif ($cmd eq "bg_ok") { }
		elsif ($cmd eq "bg_fail") { }
		elsif ($cmd eq "bg_untested") { }
		else
		{
		    print STDERR "unknown set, line $.\n";
		    next;
		}
		if ($arg ne "")
		{
		    $CF->{$cmd} = $arg;
		}

		else
		{
		    $CF->{$cmd} = 1;
		}
	    }

	    else
	    {
		($group, $service) = split;
		if ($group eq "" || $service eq "")
		{
		    print STDERR "unknown group or service, line $.\n";
		    next;
		}
		push (@RC, [$group, $service]);
	    }
	}
	close (IN);
    }

    return ("", \@RC);
}


sub secs_to_hms
{
    my ($s) = @_;
    my ($dd, $hh, $mm, $ss);

    $dd = int ($s / 86400);
    $s -= $dd * 86400;

    $hh = int ($s / 3600);
    $s -= $hh * 3600;

    $mm = int ($s / 60);
    $s -= $mm * 60;

    $ss = $s;

    if ($dd == 0)
    {
        sprintf("%02d:%02d", $hh, $mm);
    }

    else
    {
        sprintf("%d days, %02d:%02d", $dd, $hh, $mm);
    }
}


#
# exit displaying error in appropriate output format
#
sub err_die
{
    my $msg = shift;

    if ($CGI)
    {
print <<EOF;
Content-type: text/html

<html>
<head>
<title> Error </title>
</head>
<body>
<h2> Error </h2>
<pre>
$msg
</pre>
</body>
</html>
EOF
    }

    else
    {
print <<EOF;
Error: $msg

EOF
    }

    exit 1;
}


#
# everything is cool
#
sub display_allok
{
    if ($CGI)
    {
    	$OUT_BUF .= <<EOF;
<h2> All systems OK </h2>
<p>

EOF
    }

    else
    {
	print "\nAll systems OK\n";
    }
}


#
# client status
#
# return ("", $state, \%opstatus, \%disabled, \%deps, \%groups, \%descriptions);
#
sub get_client_status {
    my $cl;
    if (!defined ($cl = Mon::Client->new))
    {
	return "could not create client object: $@";
    }

    my ($username, $pass);
    if ($opt{"auth"} && !$CGI)
    {
	my $e;
	($e, $username, $pass) = get_auth;
	if ($e eq "")
	{
	    return "$e";
	}
	$cl->username ($username);
	$cl->password ($pass);
    }

    $cl->host ($CF->{"host"}) if (defined $CF->{"host"});
    $cl->port ($CF->{"port"}) if (defined $CF->{"port"});
    $cl->port ($CF->{"prot"}) if (defined $CF->{"prot"});

    $cl->connect;
    if ($cl->error) {
	return "Could not connect to server: " . $cl->error;
    }

    #
    # authenticate self to the server if necessary
    #
    if ($opt{"auth"} && !defined ($cl->login)) {
	my $e = $cl->error;
	$cl->disconnect;
	return "Could not log in: $e";
    }

    #
    # get disabled things
    #
    my %disabled = $cl->list_disabled;
    if ($cl->error) {
	my $e = $cl->error;
	$cl->disconnect;
	return "could not get disabled: $e";
    }

    #
    # get stats
    #
    my ($running, $t) = $cl->list_state;
    if ($cl->error) {
	my $e = $cl->error;
	$cl->disconnect;
	return "could not get state: $e";
    }

    my $state;
    if ($running) {
	$state = $t;
    }

    else
    {
	$state = "scheduler stopped since " . localtime ($t);
    }

    #
    # get opstatus
    #
    my %opstatus = $cl->list_opstatus;
    if ($cl->error) {
	my $e = $cl->error;
	$cl->disconnect;
	return "could not get opstatus: $e";
    }

    #
    # dependencies
    #
    my %deps;
    if ($opt{"deps"}) {
	%deps = $cl->list_deps;
	if ($cl->error) {
	    my $e = $cl->error;
	    $cl->disconnect;
	    return "could not list deps: $e";
	}
    }

    #
    # descriptions
    #
    my %desc;
    %desc = $cl->list_descriptions;
    if ($cl->error) {
	my $e = $cl->error;
	$cl->disconnect;
	return "could not list descriptions: $e";
    }

    #
    # groups
    #
    my %groups;

    if ($QUERY_ARGS{"detail"})
    {
    	foreach my $g (keys %opstatus)
	{
	    my @g = $cl->list_group ($g);
	    if ($cl->error)
	    {
	    	my $e = $cl->error;
		$cl->disconnect;
		return "could not list group: $e";
	    }

	    $groups{$g} = [@g];
	}
    }

    #
    # log out
    #
    if (!defined $cl->disconnect) {
	return "error while disconnecting: " . $cl->error;
    }


    return ("", $state, \%opstatus, \%disabled, \%deps, \%groups, \%desc);
}


sub compose_header {
    my $state = shift;

    my $t = localtime;

    #
    # HTML stuff
    #
    if ($CGI)
    {
	$OUT_BUF = <<EOF;
<html>
<head>
<title> Mon Operational Status </title>
</head>
<h1> Operational Status </h1>
<body>

<table>
    <tr>
	<td>
	    <table>
		<tr>
		    <td align=right> Server: </td>
		    <td> $CF->{host} </td>
		</tr>
		<tr>
		    <td align=right> Time: </td>
		    <td> $t </td>
		</tr>
		<tr>
		    <td align=right> State: </td>
		    <td> $state </td>
		</tr>
	    </table>
	</td>

	<td>
	    <table>
		<tr>
		    <td> Color legend </td>
		</tr>
		<tr>
		    <td bgcolor="$CF->{bg_ok}"> all is OK </td>
		</tr>
		<tr>
		    <td bgcolor="$CF->{bg_fail}"> failure </td>
		</tr>
		<tr>
		    <td bgcolor="$CF->{bg_untested}"> untested </td>
		</tr>
	    </table>
	</td>
    </tr>
</table>
<p>
<hr>
EOF
    }

    else
    {
    	print <<EOF;

     server: $CF->{host}
       time: $t
      state: $state
EOF
    }
}


sub select_table {
    my ($what, $state, $opstatus, $disabled, $deps) = @_;

    my @rows;

    #
    # display everything real nice
    #
    if ($CF->{"all"} || @{$what} == 0)
    {
	foreach my $group (keys %{$opstatus})
	{
	    foreach my $service (keys %{$opstatus->{$group}})
	    {
		push (@rows, [$group, $service]);
	    }
	}
    }

    else
    {
	@rows = @{$what};
    }

    my (%DEP, %DEPROOT);

    foreach my $l (@rows)
    {
	my ($group, $service) = @{$l};

	my $sref = \%{$opstatus->{$group}->{$service}};

	next if (!defined $sref->{"opstatus"});

	#
	# disabled things
	#
	if (defined $disabled->{"watches"}->{$group})
	{
	    next;
	}

	elsif (defined $disabled->{"services"}->{$group}->{$service})
	{
	    next;
	}

	#
	# potential root dependencies
	#
	elsif ($sref->{"depend"} eq "")
	{
	    push (@{$DEPROOT{$sref->{"opstatus"}}}, ["R", $group, $service]);
	}

	#
	# things which have dependencies
	#
	else
	{
	    push (@{$DEP{$sref->{"opstatus"}}}, ["D", $group, $service]);
	}
    }

    if ($CF->{"full"})
    {
	[
	    @{$DEPROOT{$OPSTAT{"fail"}}},
	    @{$DEPROOT{$OPSTAT{"linkdown"}}},
	    @{$DEPROOT{$OPSTAT{"timeout"}}},
	    @{$DEPROOT{$OPSTAT{"coldstart"}}},
	    @{$DEPROOT{$OPSTAT{"warmstart"}}},
	    @{$DEPROOT{$OPSTAT{"untested"}}},
	    @{$DEPROOT{$OPSTAT{"unknown"}}},

	    @{$DEP{$OPSTAT{"fail"}}},
	    @{$DEP{$OPSTAT{"linkdown"}}},
	    @{$DEP{$OPSTAT{"timeout"}}},
	    @{$DEP{$OPSTAT{"coldstart"}}},
	    @{$DEP{$OPSTAT{"warmstart"}}},

	    @{$DEPROOT{$OPSTAT{"ok"}}},
	    @{$DEP{$OPSTAT{"ok"}}},
	    @{$DEP{$OPSTAT{"untested"}}},
	    @{$DEP{$OPSTAT{"unknown"}}},
	];
    }

    else
    {
	[
	    @{$DEPROOT{$OPSTAT{"fail"}}},
	    @{$DEPROOT{$OPSTAT{"linkdown"}}},
	    @{$DEPROOT{$OPSTAT{"timeout"}}},
	    @{$DEPROOT{$OPSTAT{"coldstart"}}},
	    @{$DEPROOT{$OPSTAT{"warmstart"}}},

	    @{$DEP{$OPSTAT{"fail"}}},
	    @{$DEP{$OPSTAT{"linkdown"}}},
	    @{$DEP{$OPSTAT{"timeout"}}},
	    @{$DEP{$OPSTAT{"coldstart"}}},
	    @{$DEP{$OPSTAT{"warmstart"}}},
	];
    }
}


#
# build the table
#
sub compose_table {
    my ($rows, $opstatus, $disabled, $deps, $desc) = @_;

    if (@{$rows} == 0)
    {
	display_allok;
	return;
    }

    #
    # display the failure table
    #
    if ($CGI)
    {
	$OUT_BUF .= <<EOF;

<table cellpadding=2 cellspacing=1 bgcolor="#CCCCCC">
<th> Dep <th>Group <th>Service <th> Desc. <th> Last check <th> Next check <th> Alerts <th> Status <th> Summary

EOF
    }

    foreach my $l (@{$rows})
    {
	my ($depstate, $group, $service) = @{$l};

	my $sref = \%{$opstatus->{$group}->{$service}};

	$STATUS = "unknown";
	$TIME = "";
	$DEP = $depstate;
	my $last = "";
	my $bgcolor = opstatus_color ($sref->{"opstatus"});

	if ($sref->{"opstatus"} == $OPSTAT{"untested"})
	{
	    $STATUS = "untested";
	    $TIME = "untested";
	}

	elsif ($sref->{"opstatus"} == $OPSTAT{"ok"})
	{
	    $STATUS = "-";
	}

	elsif ($sref->{"opstatus"} == $OPSTAT{"fail"})
	{
	    if ($sref->{"ack"})
	    {
		if ($CGI) {
	    	$STATUS = "<a href=\"$ENV{SCRIPT_NAME}?detail=$group,$service\">" .
			  "<b>ACK FAIL</b></a>";
		} else {
		    $STATUS = "ACK FAIL";
		}
	    }

	    else
	    {
		$STATUS = "FAIL";
	    }
	}

	if ($depstate eq "")
	{
	    $DEP = "-";
	}

	$GROUP = $group;
	$SERVICE = $service;
	$DESC = $desc->{$group}->{$service};


	if ($TIME eq "")
	{
	    $TIME = tdiff_string (time - $sref->{"last_check"}) . " ago";
	}

	if ($sref->{"timer"} < 60)
	{
	    $NEXT = "$sref->{timer} secs";
	}

	else
	{
	    $NEXT = secs_to_hms ($sref->{"timer"});
	}

	$SUMMARY = $sref->{"last_summary"};

	$ALERTS = $sref->{"alerts_sent"} || "none";

	my $fmt;
	if (!$CGI)
	{
	    $fmt = <<EOF;
format STDOUT =
@ @<<<<<<<<<<<<<< @<<<<<<<<<<< @<<<<<<<<<  @<<<<<<<   @<<<<<<<<< @<<<   @
EOF
	    chomp $fmt;
	    $fmt .= "<" x length($SUMMARY) . "\n";
	    $fmt .= <<'EOF';
$DEP, $GROUP, $SERVICE, $STATUS, $TIME, $NEXT, $ALERTS, $SUMMARY
.
EOF
	    eval $fmt;
	    write;
	}

	else
	{
	    if ($SUMMARY =~ /^\s*$/)
	    {
		$SUMMARY = "<pre> </pre>";
	    }
	    if ($bgcolor ne "")
	    {
	    	$bgcolor = "bgcolor='#$bgcolor'";
	    }
	    $OUT_BUF .= <<EOF;

<tr $bgcolor>
   <td> $DEP
   <td> $GROUP
   <td> <a href="$ENV{SCRIPT_NAME}?detail=$group,$service">$SERVICE</a>
   <td> $DESC
   <td> $TIME
   <td> $NEXT
   <td> $ALERTS
   <td> $STATUS
   <td> $SUMMARY
EOF

	}
    }

    if ($CGI)
    {
	$OUT_BUF .= <<EOF;
</table>

EOF
    }
}


sub compose_disabled {
    my $disabled = shift;

    return if (!$CF->{"show-disabled"});

    if (keys %{$disabled->{"watches"}})
    {
	if ($CGI)
	{
	    $OUT_BUF .= <<EOF;

<h2> Disabled Watches </h2>

EOF
	}

	else
	{
	    print "\nDISABLED WATCHES:\n";
	}

	foreach my $watch (keys %{$disabled->{"watches"}})
	{
	    if ($CGI)
	    {
	    	$OUT_BUF .= "$watch <br>\n";
	    }

	    else
	    {
		print "$watch\n";
	    }
	}
    }

    my @disabled_services;
    foreach my $watch (keys %{$disabled->{"services"}})
    {
	foreach my $service (keys %{$disabled->{"services"}{$watch}})
	{
	    push (@disabled_services, "$watch $service");;
	}
    }

    if (@disabled_services)
    {
	if ($CGI)
	{
	    $OUT_BUF .= <<EOF;

<h2> Disabled Services </h2>

EOF
	}

	else
	{
	    print "\nDISABLED SERVICES\n";
	}
	for (@disabled_services)
	{
	    if ($CGI)
	    {
	    	$OUT_BUF .= "$_ <br>\n";
	    }

	    else
	    {
		print "$_\n";
	    }
	}
    }

    if (keys %{$disabled->{"hosts"}})
    {
	if ($CGI)
	{
	    $OUT_BUF .= <<EOF;

<h2> Disabled Hosts </h2>

EOF
	}

	else
	{
	    print "\nDISABLED HOSTS:\n";
	}
	foreach my $group (keys %{$disabled->{"hosts"}})
	{
	    my @HOSTS = ();
	    foreach my $host (keys %{$disabled->{"hosts"}{$group}})
	    {
		push (@HOSTS, $host);
	    }
	    if ($CGI)
	    {
		$OUT_BUF .= sprintf ("%-15s %s <br>\n", $group, "@HOSTS");
	    }

	    else
	    {
		printf ("%-15s %s\n", $group, "@HOSTS");
	    }
	}
    }
}


sub compose_trailer {
    if ($CGI)
    {
    	$OUT_BUF .= <<EOF;
</body>
</html>
EOF
    }
}


sub compose_detail {
    my ($args, $opstatus, $disabled, $deps, $descr, $groups) = @_;

    my ($group, $service) = split (/,/, $args, 2);

    if (!defined ($opstatus->{$group}->{$service}))
    {
    	err_die ("$group/$service not a valid service");
    }

    my $sref = \%{$opstatus->{$group}->{$service}};

    my $d;

    foreach my $k (qw ( opstatus exitval depend monitor
    			last_check timer ack ackcomment))
    {
    	if ($sref->{$k} =~ /^\s*$/)
	{
	    $d->{$k} = "<pre> </pre>";
	}

	else
	{
	    $d->{$k} = $sref->{$k};
	}
    }

    my $t = time;

    $d->{"last_check"} = tdiff_string ($t - $d->{"last_check"}) . " ago";
    $d->{"timer"} = "in " . tdiff_string ($d->{"timer"});

    my $bgcolor = opstatus_color ($sref->{"opstatus"});

    $bgcolor = "bgcolor='#$bgcolor'" if ($bgcolor ne "");


    $OUT_BUF .= <<EOF;

<h2> Detail for group $group, service $service </h2>

<table>
    <tr $bgcolor>
    	<td> <b> Description: </b> <td> $descr->{$group}->{$service}
    <tr $bgcolor>
	<td> <b> Summary: </b> <td> $sref->{last_summary}
    <tr $bgcolor>
	<td> <b> Hosts: </b> <td> @{$groups->{$group}}
    <tr $bgcolor>
	<td valign=top> <b> Detail: </b>
	<td>
	<pre>
$sref->{last_detail}
	</pre>
</table>
<hr>
EOF

    if ($d->{"ack"}){
	$OUT_BUF .= <<EOF;
<table>
    <tr>
    	<td> <h2>Acknowledgment of failure</h2> </td>
    <tr>
	<td> $d->{ackcomment}
</table>

EOF
    }

    $OUT_BUF .= <<EOF;
<h2>Other Information</h2>
<table cellpadding=2 cellspacing=1 bgcolor="#CCCCCC">
    <tr>
    	<td> opstatus </td>
	<td> $d->{opstatus} </td>
    <tr>
    	<td> exitval </td>
	<td> $d->{exitval} </td>
    <tr>
    	<td> depend </td>
	<td> $d->{depend} </td>
    <tr>
    	<td> monitor </td>
	<td> $d->{monitor} </td>
    <tr>
    	<td> last_check </td>
	<td> $d->{last_check} </td>
    <tr>
    	<td> next_check </td>
	<td> $d->{timer} </td>
</table>

<p>
<a href="$ENV{SCRIPT_NAME}">Back to summary table</a>
<p>

EOF

}


sub compose_group {
    my $arg = shift;
}


sub opstatus_color {
    my $o = shift;

    my %color_hash = (
    	$OPSTAT{"untested"}	=> $CF->{"bg_untested"},
	$OPSTAT{"ok"}		=> $CF->{"bg_ok"},
	$OPSTAT{"fail"}		=> $CF->{"bg_fail"},
    );

    $color_hash{$o} || "";
}


sub tdiff_string {
    my $time = shift;

    my $s;

    if ($time <= 90)
    {
    	$s = "$time secs";
    }

    else
    {
    	$s = secs_to_hms ($time);
    }
}

