#!/usr/bin/perl
#
# This program is used to display information collected by the spong server to
# simple character based terminals.  It provides the same type of interface
# to the data as the web based (and perhaps some day Java based) interfaces do.
#
# History:
# (1) Created (Ed Hill, 05-05-1997)
# (2) Turned into client that makes queries to the spong server (Ed 07-27-1997)
#
# $Id: spong.pl,v 1.6 2000/10/16 15:34:32 sljohnson Exp $

use POSIX;
use Socket;
use Getopt::Long;
use Sys::Hostname;

# Load our configuration variables, including the user specified configuration
# information (spong.conf, spong.hosts, and spong.groups files).

$|++;
$conf_file   = "/etc/spong/spong.conf";
$hosts_file  = "/etc/spong/spong.hosts";
$groups_file = "/etc/spong/spong.groups";
($HOST)      = gethostbyname(&Sys::Hostname::hostname());
$HOST        =~ tr/A-Z/a-z/;
$view        = "standard";

&load_config_files();  # Loads the user specified configuration information

# Get the user options and spit back a usage message if they do something silly
# Go through each possible option and call the appropriate function based on 
# the input they provide.

%opt;
@options = ( "help", "summary:s", "grpsummary", "problems:s", "history:s",
             "host=s", "server=s",
	     "acks:s", "services=s", "stats=s", "config=s", "info=s",
	     "service=s", "brief", "standard", "full" );

if( ! GetOptions( \%opt, @options ) ) { warn "Incorrect usage:\n\n"; &help(); }

&help if defined $opt{'help'};

if( defined $opt{'server'} )     { $SPONGSERVER = $opt{'server'}; }

if( defined $opt{'brief'} )      { $view = "brief"; }
if( defined $opt{'standard'} )   { $view = "standard"; }
if( defined $opt{'full'} )       { $view = "full"; }

if( defined $opt{'problems'} )   { &problems( $opt{'problems'} );   $opt = 1; }
if( defined $opt{'summary'} )    { &summary( $opt{'summary'} );     $opt = 1; }
if( defined $opt{'grpsummary'} ) { &grpsummary($opt{'grpsummary'}); $opt = 1; }
if( defined $opt{'history'} )    { &history( $opt{'history'} );     $opt = 1; }

if( defined $opt{'host'} )       { &host( $opt{'host'} );           $opt = 1; }
if( defined $opt{'services'} )   { &services( $opt{'services'} );   $opt = 1; }
if( defined $opt{'acks'} )       { &acks( $opt{'acks'} );           $opt = 1; }
if( defined $opt{'stats'} )      { &stats( $opt{'stats'} );         $opt = 1; }
if( defined $opt{'config'} )     { &config( $opt{'config'} );       $opt = 1; }
if( defined $opt{'info'} )       { &info( $opt{'info'} );           $opt = 1; }

if( defined $opt{'service'} )  {
   my( $host, $service ) = split( ':', $opt{'service'} );
   &service( $host, $service );
   $opt = 1;
}

if( ! $opt ) { &summary( "all" ); }

exit(0);

# ---------------------------------------------------------------------------
# Functions that correspond to command line arguments, these all just send
# messages to the spong-server telling it that we are looking for text output
# ---------------------------------------------------------------------------


sub problems {
   print &query( $SPONGSERVER, "problems", $_[0], "text", $view ); }

sub summary {
   print &query( $SPONGSERVER, "summary", $_[0], "text", $view ); }

sub grpsummary {
   print &query( $SPONGSERVER, "grpsummary", $_[0], "text", $view ); }

sub history {
   print &query( $SPONGSERVER, "history", $_[0], "text", $view ); }

sub acks {
   print &query( $SPONGSERVER, "acks", $_[0], "text", $view ); }


sub host {
   print &query( $SPONGSERVER, "host", $_[0], "text", $view ); }

sub services {
   print &query( $SPONGSERVER, "services", $_[0], "text", $view ); }

sub stats {
   print &query( $SPONGSERVER, "stats", $_[0], "text", $view ); }

sub config {
   print &query( $SPONGSERVER, "config", $_[0], "text", $view ); }

sub info {
   print &query( $SPONGSERVER, "info", $_[0], "text", $view ); }


sub service {
   print &query( $SPONGSERVER, "service", $_[0], "text", $view, $_[1] ); }


# Just print a little message to stdout showing what valid options are to 
# the command line interface to spong, and then exit the program.

sub help {
   print <<'_EOF_';
Usage: spong [options]

Where "options" are one of the arguments listed below.  If no arguments are
supplied, then a table showing the status of all hosts is shown.

   --summary  [hostlist]    Summarizes the status of services on the host(s)
   --problems [hostlist]    Shows a summary of problems on the host(s)
   --history  [hostlist]    Show history information for the host(s)
   --acks     [hostlist]    Shows acknowledgment info for the given host(s)

   --grpsummary             Summarizes the status of host(s) in all group(s)

   --host     host          Shows all information available for the given host
   --services host          Shows detailed service info for the given host
   --stats    host          Shows statistical information for the given host
   --config   host          Shows configuration information for the given host
   --info     host          Shows admin supplied text for the given host

   --service  host:service  Shows detailed info for the given service/host

   --brief                  Display output in a brief format
   --standard               Display output in standard format (the default)
   --full                   Display more information then you probably want

All host names used as options must be fully qualified domain names. For the
options above that take an optional hostlist, the hosts listed should be
either a group name, or a list of individual hosts seperated by commas.  If
the host list is omitted, then information about all hosts monitored by spong
is returned.

_EOF_
   exit(0);
}



# ---------------------------------------------------------------------------
# Private/Internal functions
# ---------------------------------------------------------------------------

# This function sends a query to the spong server.  It takes the results it
# gets back based on the user's query and returns the string back to the 
# code that called this function.

sub query {
   my( $addr, $query, $hosts, $display, $view, $other ) = @_;
   my( $iaddr, $paddr, $proto, $line, $ip, $ok, $msg );

   $addr =~ s/^\s*//;
   $addr =~ s/\s+.*$//;

   if( $addr =~ /^\s*((\d+\.){3}\d+)\s*$/ ) {
      $ip = $addr;
   } else {
      my( @addrs ) = (gethostbyname($addr))[4];
      my( $a, $b, $c, $d ) = unpack( 'C4', $addrs[0] );
      $ip = "$a.$b.$c.$d";
   }

   $iaddr = inet_aton( $ip ) || die "no host: $host\n";
   $paddr = sockaddr_in( $SPONG_QUERY_PORT, $iaddr );
   $proto = getprotobyname( 'tcp' );
   
   # Set an alarm so that if we can't connect "immediately" it times out.

   $SIG{'ALRM'} = sub { die };
   alarm(30);

   eval <<'_EOM_';
   socket( SOCK, PF_INET, SOCK_STREAM, $proto ) || die "socket: $!";
   connect( SOCK, $paddr )                      || die "connect: $!";
   select((select(SOCK), $| = 1)[0]);
   print SOCK "$query [$hosts] $display $view $other\n";
   while( <SOCK> ) { $msg .= $_; }
   close( SOCK )                                || die "close: $!";
   $ok = 1;
_EOM_

   alarm(0);
   
   return $msg if $ok;
   return "Can't connect to spong server!\n" if ! $ok;
}

# This function just loads in all the configuration information from the 
# spong.conf.  The spong.hosts and spong.group files are not needed - all the
# hosts and groups smarts take place on the spong-server.

sub load_config_files {
   my( $evalme, $inhosts );

   require $conf_file || die "Can't load $conf_file: $!";
   if( -f "$conf_file.$HOST" ) {
      require "$conf_file.$HOST" || die "Can't load $conf_file.$HOST: $!";
   } else {
      my $tmp = (split( /\./, $HOST ))[0];
      if( -f "$conf_file.$tmp" ) { # for lazy typist
	 require "$conf_file.$tmp" || die "Can't load $conf_file.$tmp: $!";
      }
   }
}

