#!/bin/perl

# setoolkit.pl
# AUTHOR: Brent Parish
# DATE WRITTEN: 11/29/01
# VERSION: 1.0

# This script normally resides in ~BIGSISTER/uxmon/Monitor as "setoolkit.pl"
#
# Please note that where you see "~BIGSISTER" in these comments, you will need
# to replace it with the ABSOLUTE PATH to your big sister implementation!

# The format for the ~BIGSISTER/adm/uxmon-net file is as follows:
#
#	localhost	exec="~BIGSISTER/uxmon/Monitor/setoolkit.pl maxlog=100 ythreshold=1\
#	rthreshold=2" time=20 item=se results="0:green:;1:yellow:;2:red:;3:red:Unable\
#	to determine error category;*:red:Unable to determine output\
#	of Big Sister agent" command 
 
# The "maxlog=" can be set to truncate your log file (on BSDISPLAY, not on the client!)
# for display/bandwidth reasons.  This numeric equates to issuing a "tail -XXX" on the logfile
# If the log is truncated, it will display "PARTIAL LOG" instead of "LOG" as a header on the
# BSDISPLAY screen
#
# The "ythreshold=" is a numeric value representing at what point to issue a "yellow alert".
# The "rthreshold=" is the same thing for a "red alert".  The formula for alerting is 
# a scaled approach, since the SE Toolkit logs thresholds crossed in three colors (amber, red 
# and black).  The formula can be edited below, but currently looks like this:
# 	amber alerts are worth 1 point each
#	red alerts are worth 3 points each
#	black alerts are worth 5 points each
# Add the points together for all SE Toolkit alarms tripped in the past 5 minutes.
# The result is compared against the ythreshold and rthreshold set in uxmon-net. 
# The default is 5 for yellow and 10 for red.  This means one SE Toolkit alarm would trigger
# a "yellow condition" in Big Sister.  So would 5 amber alerts or 3 red alerts.   
# The other flags set are standard Big Sister flags (i.e. time, item, results,exec, and command)
# and can be read about in the Big Sister documentation.

# This script polls the virtual_adrian.se logfile and reports errors back to BSDISPLAY
# It keeps a line count value in a file called .pointer 
# Please note that the BBUSER who runs uxmon must have write permissions to this pointer file!
# Likewise, there is a "total errors for the day" file (called .total), which also must be 
# written to a directory that BBUSER has permissions to.
# LOGFILE is the output file from the SE Toolkit virtual_adrian.se program

# NOTE: If you roll the SE Toolkit log, you will need to also reset the ".pointer" file (or just delete it)
# ENJOY!


$LOGFILE = "/var/adm/sa/monitor.log";
$POINTERFILE = "/var/adm/sa/.pointer";
$DAYTOTALFILE = "/var/adm/sa/.total";

# THIS NEXT VALUE ALLOWS YOU TO RESET THE "DAY'S TOTAL" ERROR COUNT TO ZERO AT MIDNIGHT EACH DAY.
# IF YOU CHOOSE TO MAKE THIS A "WEEKLY TOTAL" OR A CONTINUOUSLY RUNNING TOTAL, SET THIS
# TO "weekly" OR "running".
$ROLLTOTAL = "daily";

foreach (@ARGV) {
   ($KEY,$VALUE) = split('=',$_);
   $TRUNCATE = $VALUE if ($KEY eq 'maxlog');
   $YTHRESH = $VALUE if ($KEY eq 'ythreshold');
   $RTHRESH = $VALUE if ($KEY eq 'rthreshold');
}

$CURRLINECOUNT = `wc -l $LOGFILE`;
$CURRLINECOUNT =~ s/$LOGFILE//;
$CURRLINECOUNT =~ s/\s*//;
chomp($CURRLINECOUNT);
$TOTALERR = 0;
$AMBER = 0;
$RED = 0;
$BLACK = 0;
$COLOR = 0;
$COLORSTR = "GREEN";
$DAYTOTAL = 0;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime;
$TOTALDISK = 0;
$TOTALNET = 0;
$TOTALSWAP = 0;
$TOTALRAM  = 0;
$TOTALCPU = 0;
$TOTALPROC = 0;

# SET DEFAULTS FOR LOG TRUNCATING AND THRESHOLDS, IF THEY WERE NOT SET IN uxmon-net
if (! defined $TRUNCATE) {
   $TRUNCATE = 100;
}
if (! defined $YTHRESH) {
   $YTHRESH = 5;
}
if (! defined $RTHRESH) {
   $RTHRESH = 10;
}

if (-e $POINTERFILE && !-z $POINTERFILE) {
   # File is not null
   open(POINTERFH, "<$POINTERFILE") || die "Cannot open pointer file for reading\n";
    $LINECOUNT = <POINTERFH>;
   close(POINTERFH);
} else {
   # No File or is null
   $LINECOUNT = 1;
}

open(POINTERFH, ">$POINTERFILE") || die "Cannot open pointer file for writing\n";
 print POINTERFH $CURRLINECOUNT;
close(POINTERFH);

if (-e $DAYTOTALFILE && !-z $DAYTOTALFILE) {
   # File is not null
   open(DAYTOTALFH, "<$DAYTOTALFILE") || die "Cannot open total_errors_for_day file for reading\n";
    $TRASH = <DAYTOTALFH>;
   close(DAYTOTALFH);
   ($ERRFORDAY,$OLDYRDAY) = split(' ',$TRASH);
} else {
   # No File or is null
   $ERRFORDAY = 0;
}

if ($yday > $OLDYRDAY && $ROLLTOTAL eq "daily") {
   # LAST WRITE NOT TODAY AND ROLLING DAILY
   $ERRFORDAY = 0;
}

if ($yday > ($OLDYRDAY+6) && $ROLLTOTAL eq "weekly") {
   # LAST WRITE OVER A WEEK AGO AND ROLLING WEEKLY
   $ERRFORDAY = 0;
}

# PULL UP THE LAST XXX LINES WRITTEN TO THE SE TOOLKIT LOG FILE
# (SINCE THE LAST RUN OF THIS SCRIPT)
@TAIL = `tail +${LINECOUNT} $LOGFILE`;
# STORE EACH ERROR BLOCK OF TEXT IN @BLOCK
@BLOCK = "";

foreach (@TAIL) {
   if ( $_ !~ m/^$/ ) {
	# GRAB ANY LINE THAT IS NOT BLANK
	push(@BLOCK, $_);
   } else {
	# NULL LINES
	if (@BLOCK > 1) {
	   # DONE AVOIDING NULL LINES, WE HAVE SOME TEXT TO PROCESS
	   error_breakdown();
	   @BLOCK = "";
	   # RESET @BLOCK AND LOOP AGAIN
	}
   }
}

# DONE GRABBING LOG TEXT
# RUN TOTALS AND REPORT

open(DAYTOTALFH, ">$DAYTOTALFILE") || die "Cannot open total_errors_for_day file for writing\n";
 $DAYTOTAL = $ERRFORDAY + $TOTALERR;
 print DAYTOTALFH "$DAYTOTAL $yday";
 # UPDATE THE TOTALS FOR THE DAY
close(DAYTOTALFH);

######   HERE IS WHERE WE SET THE POINT VALUE FOR EACH SE TOOLKIT ALARM   #########
#
#

$COLOR = $COLOR + $AMBER;
$COLOR = $COLOR + $RED*3;
$COLOR = $COLOR + $BLACK*5;

#
#
###### YOU CAN CHANGE THIS TO WHATEVER MAKES MOST SENSE FOR YOUR IMPLEMENTATION ###

$COLORSTR = "YELLOW" if ($COLOR >= $YTHRESH);
$COLORSTR = "RED" if ($COLOR >= $RTHRESH);

# I RUN ALL MY BIG SISTER RESULTS IN HTML
# YOU CAN STRIP ALL OF THE TAGS OUT AND REMOVE THIS NEXT "&html" LINE
# TO REVERT TO *BORING* PLAIN TEXT
# PLEASE NOTE THIS WILL TAKE SOME WORK, AS THERE ARE HTML TAGS SCATTERED 
# THROUGHOUT THIS SCRIPT...

print "&html <HR WIDTH='50%' ALIGN='CENTER'>\n";
print "<CENTER><H3> ERRORS:</H3></CENTER>\n";
print "<HR WIDTH='50%' ALIGN='CENTER'>\n";

print "<TABLE BORDER=0><TR><TD>\n";
print "Total Today: <B>$DAYTOTAL\n";
print "</B><BR>\n";
print "Last 5 Minutes: <B>$TOTALERR\n";
print "</B>\n";
print "<UL><LI> Amber: <FONT COLOR='YELLOW'>$AMBER</FONT></LI>\n";
print "<LI> Red: <FONT COLOR='RED'>$RED</FONT></LI>\n";
print "<LI> Black: <FONT COLOR='BLACK'>$BLACK</FONT></LI></UL>\n";
print "</TD><TD>\n";
print "<FONT COLOR='#444444'>PLACEHOLDER</FONT>\n";
print "</TD><TD>\n";
print "DISK ERRORS: $TOTALDISK\n";
print "<BR>\n";
print "NET ERRORS: $TOTALNET\n";
print "<BR>\n";
print "SWAP ERRORS: $TOTALSWAP\n";
print "<BR>\n";
print "RAM ERRORS: $TOTALRAM\n";
print "<BR>\n";
print "CPU ERRORS: $TOTALCPU\n";
print "<BR>\n";
print "PROCESS ERRORS: $TOTALPROC\n";
print "<BR>\n";
print "</TD></TR></TABLE>\n";

print "<HR WIDTH='50%' ALIGN='CENTER'>\n";

$TRASH = @BUCKET;
# HOW MANY LINES DO WE HAVE?  DO WE NEED TO TRUNCATE BEFORE
# REPORTING BACK TO BSDISPLAY?

if ($TRASH > $TRUNCATE) {
   print "<CENTER><H3> PARTIAL LOG:</H3></CENTER>\n";
   print "<HR WIDTH='50%' ALIGN='CENTER'>\n";
   for ( $i = $TRASH - $TRUNCATE; $i <= $TRASH; $i++ ) {
      print "$BUCKET[$i]<BR>\n";
   } 
} else {
   print "<CENTER><H3> LOG:</H3></CENTER>\n";
   print "<HR WIDTH='50%' ALIGN='CENTER'>\n";
   foreach (@BUCKET) {
      print "$_<BR>\n";
   }
}

exit(0) if ($COLORSTR eq "GREEN");
exit(1) if ($COLORSTR eq "YELLOW");
exit(2) if ($COLORSTR eq "RED");
exit(3);


sub error_breakdown {
   $ERRCAT = "";
   $ERRLVL = "";
   $FIRSTLINE = "";
   $HTMLCOLOR = "WHITE";
   $DATETIME = "";

   if ($BLOCK[2] =~ m/^Satan.*attack/) {
	return;
   }

   if ($BLOCK[1] =~ m/^bbuser detected (.*) \((\w+)\): (.*)/) {
 	$ERRCAT = $1;	
	$ERRLVL = $2;	
	$DATETIME = $3;
	color_chooser();
	$FIRSTLINE = "bbuser detected <B>$ERRCAT</B> <B><FONT COLOR='$HTMLCOLOR'>$ERRLVL</FONT></B>: $DATETIME\n";
   } elsif ($BLOCK[1] =~ m/^bbuser detected (.*) (\w+): (.*)/) {
 	$ERRCAT = $1;	
	$ERRLVL = $2;	
	$DATETIME = $3;
	color_chooser();
	$FIRSTLINE = "bbuser detected <B>$ERRCAT</B> <B><FONT COLOR='$HTMLCOLOR'>$ERRLVL</FONT></B>: $DATETIME\n";
   } elsif ($BLOCK[1] =~ m/^bbuser detected (.*): (.*)/) {
 	$ERRCAT = $1;	
	$DATETIME = $2;
	$TRASH = @BLOCK - 1;
	if ($BLOCK[$TRASH] =~ m/(^black|^red|^amber) /i) {
	   $ERRLVL = $1;
	   color_chooser();
	   $FIRSTLINE = "bbuser detected <B>$ERRCAT</B> <B><FONT COLOR='$HTMLCOLOR'>$ERRLVL</FONT></B>: $DATETIME\n";
 	} else {
	   $FIRSTLINE = "bbuser detected <B>$ERRCAT</B>: $DATETIME\n";
	}
   } else {
 	return;
   }

# UPDATE STATISTICS
   $TOTALERR ++;
   $AMBER ++ if ($ERRLVL =~ m/amber/i);
   $RED ++ if ($ERRLVL =~ m/red/i);
   $BLACK ++ if ($ERRLVL =~ m/black/i);
   $TOTALDISK ++ if ($ERRCAT =~ m/disk|Directory|Inode/i);
   $TOTALNET ++ if ($ERRCAT =~ m/net|TCP|NFS/i);
   $TOTALSWAP ++ if ($ERRCAT =~ m/swap/i);
   $TOTALRAM ++ if ($ERRCAT =~ m/ram|memory/i);
   $TOTALCPU ++ if ($ERRCAT =~ m/CPU/i);
   $TOTALPROC ++ if ($ERRCAT =~ m/process|mutex/i);

   foreach $TRASH (@BLOCK) {
      if ($TRASH =~ m/^bbuser detected /) {
         push(@BUCKET,$FIRSTLINE);
         push(@BUCKET,"<PRE>");
      } else {
         push(@BUCKET,$TRASH);
      }
   }
   push(@BUCKET,"</PRE>");
}


sub color_chooser {
   if ($ERRLVL =~ m/amber/i) {
      # SE TOOLKIT REPORTS IN AMBER, NOT YELLOW!
      $HTMLCOLOR = "YELLOW";	
   } else {
      $HTMLCOLOR = $ERRLVL;
   }
}

