#!/usr/bin/perl

# VERSION 1.00
#
# Added ability to check CPU Idle
#
# Fixed a bug in where the DF filehandle was opened.
#
# VERSION 0.91
#
# All server/display-side changes.
# 
# VERSION 0.90
#
# Fixed some minor bugs in the client, but mostly code changes to the server
# and display code.
#
# VERSION 0.89
# 
# Added the "mailtotal" fuction, similar to mailspl, which reports total
# mail spool usage, and the largest mail spool file. (Thanks to
# Michael Riedel for this suggestion)
#
# Fixed a possible security bug. 
#
# VERSION 0.88
# 
# Finalized support for IMAP service (it was already in the server, the
# configuration file just didn't know about it). Also added FTP service
# checking. (Thanks to Chris Liljenstolpe for pointing out that it was
# missing!)
#
# Fixed bug where "finger @hostname" would return pong3 data and not
# pass through to the finger routine to finger 'everyone'.
#
# Fixed bug in pong3display.pl where dead/excluded/non-configured
# items would appear in graph or otherwise affect background color.
#
# Added the ability to check for the Telnet service on a remote machine.
# (by request).
#
# VERSION 0.87
#
# Fixed the "df" on linux to ignore iso9660 filesystems (which would
# always report 100% full), thanks to Daniel Lafraia for pointing it
# out. 
#
# Fixed a problem in the server where it wasn't waiting the sleep
# time because I was using two different variables. (Duh!) Thanks again
# to Daniel.
#
# Removed the need for a library proprietary to my employer in order for
# pong3detail.pl to work. Now uses the CGI package that is part of the 
# standard Perl5 distribution (CGI.pm)
#
# Pong3display.pl is now able to be fed a list of exclusions and 
# "only" params, e.g.
#   http://hostname/pongiii/pong3display.pl?exclude=Routers
#  would exclude routers, while
#   http://hostname/pongiii/pong3display.pl?exclude=Routers+Servers
#  would exclude routers and servers. Conversely
#   http://hostname/pongiii/pong3display.pl?only=Routers
#  would ONLY display the routers omitted from the previous graph.
#
# VERSION 0.86
#
# Fixed a bug in the mailbytes section where it wouldn't figure out dates
# properly
#
# Removed the need for a separate finger client by using the pong3client.pl
# script as the finger client itself.
#
# VERSION 0.85
#
# Added the "namedreq" function. This will parse a named.stats file, and 
# return the total of all inquiries received. This is a "running" total, and
# thus should NOT be graphed using "gauge" or "absolute".
#
# VERSION 0.84
#
# Removed the "uname" OS detection in favor of generic, faster, and leaner
# configuration options in the header. Downside is that the client no longer
# auto-detects where it is and behaves accordingly. Maybe in the future. :)
#
# Added APACHE_CONF so that the Web Stats system can read an apache conf
# file and determine what access logs need to be referenced. Caviats - 
# server root MUST appear in the config before the first TransferLog
# statement, and appear in the same config file.
#
# 0.83 -> 0.84
# 
# Added 'httphits' keyword which allows the script to generate an MRTG file
# containing (in) good filled requests and (out) requests that failed with 400
# or 500 series errors.
#
# Added 'clientv' which allows someone to hit the client and have it
# report its version. Useful for figuring out what versions of the software
# you have running on different clients as you upgrade certain machines to
# take advantage of new features, but DON'T upgrade others as they don't 
# need new features. :)
#
# Fixed bug in log analysis code that dorked on year end.

#$DEBUG = 0;

$FINGER_ALLOWED = 0;
$BOUND_CHECKING = 16;

$VERSION = "1.00";

($param) = @ARGV;

if (!$param)
{
    $param = <>;
    $param =~ s/[\r\n]//gi;
}

if ($param eq 'clientv')
{
    print "$VERSION\n";
    exit 0;
}

#if ($param eq 'pong3')
#{
#    undef $param;
#}

if (length($param) > $BOUND_CHECKING)
{
    $param = substr $param,0,$BOUND_CHECKING;
}

$desc_line = "This system";

# CONFIGURATION NEEDED FOR NAMED 

$NAMED_STAT_FILE = "/var/named/named.stats";
$NAMED_NDC = "/usr/sbin/ndc stats";

# CONFIGURATION NEEDED FOR FTP TESTING

$TEST_FTP_HOST = "phxdns01.phoenix.speedchoice.com";
$FTP_COMMAND = "/bin/ftp -v";

# CONFIGURATION NEEDED FOR WEB LOG GRAPHING

$APACHE_CONF = "/web/conf/httpd.conf";
$WEB_CHECK_INTERVAL = 5;

# CONFIGURATION NEEDED FOR MAIL LOG REPORTING

$MAIL_LOG_FILE = "/var/log/maillog";
$MAIL_SPOOL_DIR = "/var/spool/mail";
$MAIL_CHECK_INTERVAL = 5;

# CONFIGURATION NEEDED FOR PONG3 REPORTING

@DAYS=('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
@MONS=('Jan','Feb','Mar','Apr','May','Jun',
       'Jul','Aug','Sep','Oct','Nov','Dec');


################################## Linux Defaults
$free = "/usr/bin/vmstat";
$df = "/bin/df -x iso9660";
$uptime = "/usr/bin/uptime";
$top = "/usr/bin/top | head -5 | grep idle";
$bsddf = 1;

################################## FreeBSD Defaults
# $df = "/bin/df -k";
# $uptime = "/usr/bin/uptime";
# $free = "/usr/bin/vmstat";
# $top = "/usr/local/bin/top -d 2 | grep idle";
# $bsddf = 1;

################################## Solaris 2.5.1 defaults
# $df = "/usr/bin/df -k";
# $uptime = "/usr/bin/uptime";
# $free = "/usr/bin/vmstat";
# $top = "/usr/bin/top | head -5 | grep idle";

################################## SunOS 5.5.1 defaults
# $df = "/bin/df -k";
# $uptime = "/bin/uptime";
# $free = "/bin/vmstat";
# $bsddf = 1;
# $top = "/usr/bin/top | head -5 | grep idle";

################################## Cybermanager defaults
# $df = "/bin/df -a";
# $uptime = "/usr/ucb/uptime";
# $free = "/usr/ucb/vmstat";
# $bsddf = 1; 
# $top = "/usr/bin/top | head -5 | grep idle";


if ( ($param eq 'pong3') or ($param eq 'df') )
{
    open DF, "$df |";

    if ($bsddf)
    {
	print STDERR "Checking BSD DF...\n" if $DEBUG;
	while (<DF>)
	{
	    chomp;
	    next if /^Filesystem/;
	    ($dev,$total,$used,$avail,$cap,$mountpoint) = 
		/([\w\d]+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\%\s+(.*?)/;
	    $total_used = $total_used + $used;
	    $total_space = $total_space + $total;
	    if ($cap >= $highest_cap)
	    {
		$highest_cap = $cap;
	    }
	}
    }
    else
    {
	# STANDARD SUNOS DF
	print STDERR "Checking SunOS DF...\n" if $DEBUG;
	while (<DF>)
	{
	    next if !(/\/dsk/i);
	    ($total,$used,$avail,$cap) = /[\w\d\/]+\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\%/;
	    next if (!$total);
	    $total_space = $total_space + $total;
	    $total_used = $total_used + $used;
	    if ($cap >= $highest_cap)
	    {
		$highest_cap = $cap;
	    }
	}
    }
    close DF;
    
    $total_cap = sprintf "%02d", (($total_used / $total_space) * 100);
}


if ( ($param eq 'pong3') or ($param eq 'free') )
{

    print STDERR "Checking VMSTAT...\n" if $DEBUG;
    
    open FREE, "$free |";
    while (<FREE>)
    {
	next if /procs/;
	next if /r b w/;
	($swap,$free_mem) = /^\s?\d+\s+\d+\s+\d+\s+(\d+)\s+(\d+)\s+/;
    }
    close FREE;
}

print STDERR "Checking LOAD (and UPTIME)...\n" if $DEBUG;

open UPTIME, "$uptime |";
while (<UPTIME>)
{
    chomp;
    $line = $_;
#  4:38pm  up  5:15,  0 users,  load average: 0.01, 0.05, 0.01

    ($total_uptime) = $line =~
	/up\s+(.*?)\,\s+\d+ user/;
    ($one,$five,$ten) = $line =~
	/averages?\:\s+([\d\.]+)\,\s+([\d\.]+)\,\s+([\d\.]+)/i;
}
close UPTIME;
$high_load = max($one,$five,$ten);

if ( ($param eq 'idle') or ($param eq 'pong3') )
{
    open TOP, "$top |";
    $line = <TOP>;
    close TOP;
    chomp $line;
    ($idle) = $line =~ /\,\s*([\d\.]+)\%\s*idle/;
}			 

######################################

if ($param eq 'pong3')
{
    print "$highest_cap\n";
    print "$free_mem\n";
    print "$high_load\n";
    print "$idle\n";
}
else
{
    # WE AREN'T BEING CALLED BY PONG3, BUT BY MRTG, SO WE NEED TO 
    # REPORT STATISTICAL ANALYSIS DATA, AND NOT "good/bad" DATA.

    # FREE MEMORY ANALYSIS

    if ($param eq 'free')
    {
	print "$free_mem\n";
	print "0\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    }

    # SYSTEM LOAD ANALYSIS

    elsif ($param eq 'load')
    {
	$high_load = $high_load * 100;
	print "$high_load\n";
	print "0\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    }

    # DRIVE SPACE IN USE ON MOST-FULL DRIVE

    elsif ($param eq 'df')
    {
	print "$highest_cap\n";
	print "0\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    }

    # CPU IDLE PERCENTAGE

    elsif ($param eq 'idle')
    {
	print "$idle\n";
	print "0\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    }

    # FTP THROUGHPUT TO SITE $TEST_FTP_HOST GETS DUMPED TO A FILE

    elsif ($param eq 'ftp')
    {
#	open FTP, "$FTP_COMMAND $TEST_FTP_HOST |" || (die "Cannot execute command! : $!\n");
	open FTP, "/tmp/ftp-output";

	while (<FTP>)
	{
	    $line = $_;
	    last if $line =~ /bytes received/;
	}
	($rbytes,$rseconds) = $line =~ /^([\d\.e\+]+) bytes received in ([\d\.e\+]+) sec/;

	if ($rbytes =~ /e/)
	{
	    ($left,$exp) = $rbytes =~ /^([\d\.]+)e\+(\d+)$/;
	    $rbytes = ( $left * (10 ** $exp) );
	}
	
	if ($rseconds =~ /e/)
	{
	    ($left,$exp) = $rseconds =~ /^([\d\.]+)e\+(\d+)$/;
	    $rseconds = ( $left * (10 ** $exp) );
	}

	while (<FTP>)
	{
	    $line = $_;
	    last if $line =~ /bytes sent/;
	}
	($sbytes,$sseconds) = $line =~ /([\d\.e\+]+) bytes sent in ([\d\.e\+]+) sec/;

	if ($sbytes =~ /e/)
	{
	    ($left,$exp) = $sbytes =~ /^([\d\.]+)e\+(\d+)$/;
	    $sbytes = ( $left * (10 ** $exp) );
	}

	if ($sseconds =~ /e/)
	{
	    ($left,$exp) = $sseconds =~ /^([\d\.]+)e\+(\d+)$/;
	    $sseconds = ( $left * (10 ** $exp) );
	}

	close FTP;

        if (($sseconds == 0) or ($rseconds == 0))
        {
            print "0\n0\n$total_uptime\nThis System\n";
            exit 1;
        }
  
	$rrate = int ($rbytes / $rseconds);
	$srate = int ($sbytes / $sseconds);

	print "$rrate\n";
	print "$srate\n";
	print "$total_uptime\n";
	print "This System\n";
    }
    elsif ( ($param eq 'httpkps') or ($param eq 'http5min') or
	    ($param eq 'httphits') )
    {
	open APACHE, "$APACHE_CONF";
	while (<APACHE>)
	{
	    chomp;
	    if (/serverroot/i)
	    {
		if ( ! /.*?\#+.*?serverroot/i )
		{
		    ($server_root) = /serverroot\s+(.*?)\s*$/i ;
		}
	    }
	    next if ! /transferlog/i;
	    next if /.*?\#+.*?transferlog/i;
	    ($logname) = /transferlog\s+(.*?)\s*$/i;
	    push @WEB_LOGS, "$server_root/$logname";
	}
	close APACHE;

	require "timelocal.pl";
	foreach $log (@WEB_LOGS)
	{
	    $scantime = time;
	    $searchtime = $scantime - ($WEB_CHECK_INTERVAL*60);
	    
	    open WEBLOG, "tac $log |";
	    while (<WEBLOG>)
	    {
		undef $mon;
		chomp;
		$line = $_;

		print STDERR "$line\n" if $DEBUG;
		($mday,$wordmon,$year,$hour,$min,$sec) =
		    $line =~ /\[(\d+)\/(\w{3})\/(\d+)\:(\d+)\:(\d+)\:(\d+)\s/;
		$year = $year - 1900;
		print STDERR "Looking for Month $wordmon\n" if $DEBUG;
		for ($i=0;$i<12;$i++) 
		{ 
		    print STDERR "comparing $wordmon to $MONS[$i]\n" if $DEBUG;
		    $mon = $i if $MONS[$i] eq $wordmon; 
		    last if $MONS[$i] eq $wordmon;
		}
		print STDERR "This month is $mon \n" if $DEBUG;
		$thistime = timelocal($sec,$min,$hour,$mday,$mon,$year);
		print STDERR "Thistime: $thistime   Searchtime: $searchtime\n" if $DEBUG;
		print STDERR "Thistime converted is: ", scalar(localtime($thistime)), "\n" if $DEBUG;
		last if ($thistime < $searchtime);

		($code,$size) = $line =~ /\".*?\"\s([\d]+)\s([\d\-]+)/;
		if ( ($code >= 200) and ($code < 300) )
		{
		    $good_hits++;
		}
		elsif ( ($code >= 400) and ($code < 600) )
		{
		    $bad_hits++;
		}

		$total_bytes = $total_bytes + $size;
	    }
	    close WEBLOG;
	}

	if ($param eq 'httpkps')
	{
	    $total_bytes = $total_bytes || "0"; 
	    print int($total_bytes / ($WEB_CHECK_INTERVAL*60)), "\n";
	    print "0\n";
	}
	elsif ($param eq 'http5min')
	{
	    print "$total_bytes\n";
	    print "0\n";
	}
	elsif ($param eq 'httphits')
	{
	    printf "%d\n", $good_hits;
	    printf "%d\n", $bad_hits;
	}

	print "$total_uptime\n";
	print "$desc_line\n";
    }
    elsif (($param eq 'mailmsgs') or ($param eq 'mailbytes'))
    {
	require "timelocal.pl";
	$scantime = time;
	$year = (localtime)[5];
	$searchtime = $scantime - ($MAIL_CHECK_INTERVAL*60);
	
	open MAILLOG, "tac $MAIL_LOG_FILE |" || die ('Could not open file\n');
	while (<MAILLOG>)
	{
	    print STDERR if $DEBUG;
	    undef $mon;
	    chomp;
	    $line = $_;
	    next if ($line !~ /sendmail\[\d+\]/);
	    ($wordmon,$mday,$hour,$min,$sec) = $line =~
		/^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/;

	    for ($i=0;$i<12;$i++) 
	    {
		print STDERR "Looking for $MONS[$i] in $wordmon\n" if $DEBUG;
		if ($MONS[$i] eq $wordmon)
		{
		    $mon = $i;
		    last;
		}
	    }
	    $thistime = timelocal($sec,$min,$hour,$mday,$mon,$year);
	    last if ($thistime < $searchtime);

	    ($msgid,$direction) = $line =~ /\]:\s(\w+):\s(\w+)\=/;
	    if ($direction eq 'from')
	    {
		($msg_size) = $line =~ /size\=(\d+)\D/;
		$msg_sizes{$msgid} = $msg_size;
	    }
	    elsif ($direction eq 'to')
	    {
		($recipient) = $line =~ /to\=\<?(.*?)\>?\,/;
		if ($recipients{$msgid})
		{
		    $recipients{$msgid} .= " $recipient";
		}
		else
		{
		    $recipients{$msgid} = $recipient;
		}
	    }

	}
	close MAILLOG;

	$topline = 0;
	foreach $msgid (sort(keys(%recipients)))
	{
	    $sent = 0;
	    $msg_size = $msg_sizes{$msgid};
	    foreach $recipient (split / /,$recipients{$msgid})
	    {
		$sent++;
	    }
	    $total_bytes_sent = ($sent * $msg_size);
	    
	    if ($param eq 'mailmsgs')
	    {
		$topline = $topline + $sent;
	    }
	    elsif ($param eq 'mailbytes')
	    {
		$topline = $topline + $total_bytes_sent;
	    }
	}

	print "$topline\n";
	print "0\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    
    }
    elsif ($param eq 'namedreq')
    {
	system "$NAMED_NDC > /dev/null";
 	open NAMESTAT, "$NAMED_STAT_FILE" ;
	while (<NAMESTAT>)
        {
           $line = $_;
	   if ($line =~ /\+\+\+ Statistics Dump/)
           {
              $reqs = 0;
           }
           if ($line =~ /^(\d+)\s+\w+ queries/)
           {
	      $this_req = $1;
	      $reqs = $reqs + $this_req;
           }
        }
	close NAMESTAT;
	unlink $NAMED_STAT_FILE;
	print "$reqs\n";
	print "0\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    }
    elsif ($param eq 'mailspl')
    {
	$max = 0;
	$total = 0;
	open SPOOLDIR, "ls -l $MAIL_SPOOL_DIR |";
	while (<SPOOLDIR>)
	{
	    chomp;
	    ($size) = /\s(\d+) \w{3}\s+\d+\s\d{2}\:/;
	    if ($max < $size) { $max = $size; }
	    $total = $total + $size;
	    $samples++;
	}
	close SPOOLDIR;
	$average = $total/$samples;
	print "$max\n";
	print "$average\n";
	print "$total_uptime\n";
	print "$desc_line\n";
    }
    elsif ($param eq 'mailtotal')
    {
	$max = 0;
	$total = 0;
	open SPOOLDIR, "ls -l $MAIL_SPOOL_DIR |";
        while (<SPOOLDIR>)
        {
            chomp;
            ($size) = /\s(\d+) \w{3}\s+\d+\s\d{2}\:/;
            if ($max < $size) { $max = $size; }
            $total = $total + $size;
        }
        close SPOOLDIR;
        print "$total\n";       #(total bytes of spooldir)
        print "$max\n";         #(max bytes of one spoolfile)
        print "$total_uptime\n";
        print "$desc_line\n";
    }
    else
    {
	if ($FINGER_ALLOWED)
	{
	    print `finger "$param"`;
	}
    }
}

sub max
{
    @ary = @_;
    foreach $element (@ary)
    {
	if ($max)
	{
	    $max = $element if $element > $max;
	}
	else
	{
	    $max = $element;
	}
    }
    return $max;
}
