#!/usr/pkg/bin/perl
#
# $Header: /home/vikas/netmgt/nocol/src/perlnocol/RCS/snmpmon,v 1.3 1994/12/19 04:49:03 vikas Exp $
#
# Server module for 'snmpmon' - monitoring SNMP data for nocol. Derived
# from the 'hostmon' server module.
#
# AUTHOR:  Vikas Aggarwal, vikas@navya.com
#
#	Copyright 1994 Vikas Aggarwal, vikas@navya.com
#
# No warranty is expressed or implied. Permission to copy and use is
# extended to all. Permission to redistribute is granted under the
# following conditions: it is not sold for profit; this copyright
# notice remains intact; the same permissions extend to the recipient;
# and if any changes are made, a notice is added so stating.

#####################
#
# Command Format:
#
#  snmpmon
#
#    Automatically kills old process and forks a new one, reading
#    the configuration file in the process. Expects all data files
#    to be in the /tmp/snmpmon_data directory (does not attempt to
#    retrieve the files from remote distributed clients like hostmon).
#
# What it does:
#
#    snmpmon is the 'master' server that processes the SNMP data collected
#    by the snmpmon-client and processes it for NOCOL.
#
#    It can monitor practically any variable that has a numeric value.
#    Thresholds can be increasing or decreasing (accordingly the event
#    is flagged if the value exceeds or drops past the thresholds).
#
#
##
#
#######

############################
## Variables customization #  overrides values in the nocollib.pl library
############################
local ($progvar) = "snmpmonData" ;	# Indicator if hostmon-data is good.
local ($TMPDATADIR) = "/tmp/snmpmon_data"; # All host data files under here.

#########################################
$debug = 0;
$libdebug = 0;			# toggles on getting SIGUSR1 signal

# $nocolroot = "/tmp";		# for testing/debugging
require  "nocollib.pl";

    # The sleeptime needs to be larger than the snmpmon.client's so that
    # it gets updated fresh data each pass.
$sleeptime=(60*5);		# default sleeptime of 15minutes

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

-d $TMPDATADIR || mkdir($TMPDATADIR, 0700) || die("Cannot create $TMPDATADIR");

$prognm = $0 ;			# save program name
select (STDERR); $| = 1; select(STDOUT); $| = 1 ; # make unbuffered


## read configuration file
##
#   POLLINTERVAL & STARTHOSTS are keywords.
#   <variable>  <host regex>  <warn-thres> <err-thres> <crit-thres> <comment>
#
sub readconf {
    local ($starthosts, $i) = (0, 0);
    $numknownvars = 0 ;

    $debug && print STDERR "Config file= $cfile\n" ;
    open (CONFIG, "< $cfile") || 
	die ("Couldn't open config $cfile, exiting");
    while (<CONFIG>)
    {
	chop;
	if( /^\s*#/ || /^\s*$/ ) {next;} # skip comments & blank lines

	if ( /^POLLINTERVAL\s(\d+)/i )  { 
	    if ($1 < 60) {$sleeptime = $1 * 60; } # assume minutes
	    else {$sleeptime = $1; }
	    next;
	}

	# here if reading a variable line:
	#	 VAR host-regex  wthres ethres cthres   [reg exp]
	if ( /^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s*(.*)\s*$/ )
	{
	    local ($re) = $2 ;	# temp variable
	    if ($2 eq '*' || $2 eq '+') { $re = '.+' } ; # convert '*' => '.+'
	    push (@thress, "$1\t$re\t$3\t$4\t$5\t$6") ;	# save the thresholds
	    $thresindex{$1} .= "$i:" ; ++$i ;  # save location in @thress array
	    ++$isknownvar{$1};
	    next ;
	}

	# here if bad line
	print STDERR "Bad config line, ignoring- $_\n" ;
    }		# end: while (CONFIG)
    close (CONFIG);


    foreach ( keys %thresindex ) {
	chop ($thresindex{$_}) ; # delete trailing ':'
	++$numknownvars;
	push(@knownvars, "$_");
    }

    $knownvars = sort keys %thresindex ;

    if ($debug > 1) {	# extended debuggin
	foreach (keys %thresindex) {
	    print STDERR "(dbg) thresindex{$_}  = $thresindex{$_}\n";
	}
    }
    if ($debug) {
	print STDERR "(dbg) Total config variables= $numknownvars\n" ;
	print STDERR "(dbg) Poll/Sleep time= $sleeptime secs\n";
	print STDERR "(dbg) Threshold table is:\n";
	foreach (0..$#thress) { 
	    print STDERR "\t thress[$_] = $thress[$_]\n" ;}
    }

}	# readconf ()

## Foreach data line, see which config line it relates to and compare
## the thresholds.
##
sub do_datafile {
    local ($hdfile) = @_ ;

    $curtime = 0;		# reset for each datafile.

    $debug && print STDERR "Doing datafile $hdfile\n";
    if (!open (DFILE, "< $hdfile")) {	# open for reading
	print STDERR "ERROR: Could not open $hdfile\n";
    }
    while (<DFILE>)
    {
	chop ;
	if (/^\s*-+\s*$/ || /^\s*$/ ) {next; } # separators, blank lines
	if (/^TIME\s+(\d+)/)   { $curtime = $1 ; next; }
	if (/^DEVICE\s+(\S+)/) { $curdev = $1 ;  next; }

	if ($curtime == 0) { last ;} # need a valid time stamp line

	if (/^VARIABLE\s+(\S+)\s+(\S+)\s*$/) {
	    $curvar = $1 ; $curunit = $2 ; next;
	}
	if (! $isknownvar{$curvar}) {
	    if (!$isunknownvar{$dvname}) {
		print STDERR "Unknown variable '$curvar' in file $hdfile\n";
		++$isunknownvar{$dvname} ; # print out error msg only once
	    }
	    next;
	}

	# rest should be data lines
	($debug > 1) && 
	    print STDERR "(dbg) current var= $curvar unit= $curunit time= $curtime device = $curdev\n" ;

	if ( /^\s*(\d+)(.*\s+COMMENT\s+(\S.*)?)?$/ )  # Value & Comment
	{
	    ($debug > 1) && print STDERR "(dbg) DataLine = $_\n" ;
	    &do_line($1, $3) ;	# value and comment
	}
	elsif ($debug) { print STDERR "Error: unknown input data line syntax- $_\n"; }

    }		# end: while(DFILE)
    close (DFILE);

}	# end: do_datafile()

## Parse each data line. This routine is called with the various fields of
## the input dataline.
## Stores the variable value, thres, max-severity etc. in %curstat
##
sub do_line {
    local ($dvalue, $dcomment) = @_ ;
    local ($isok, $varthres, $maxsev);
    local ($foundmatch) = 0;
    local ($i, $j, $v);

    # now cycle thru the indexes for the variable, and try and match the
    # dataline's comment with the regular expressions for that variable
    # as well as the regular expression for the host (read from config file).
    # No regular expression in the comment field serves as a 'default'.
    # Stop after the first match.
    
#   $debug && print STDERR "(dbg)thresindex{$curvar}= $thresindex{$curvar}\n";
    foreach $i ( split(/:\n/, $thresindex{$curvar}) ) {
	local ($junk, $host_regex, $t1,$t2,$t3, $regex) = 
	    split (/\t/, $thress[$i]);
#	$debug && print STDERR "(dbg) check if thress[$i] = $thress[$i]\n";
	if (($curdev =~ /$host_regex/)  && 
	    ($regex eq '' || $dcomment =~ m/$regex/i) )
	{
	    ($debug > 1) && 
		print STDERR "(dbg) Matched- thress[$i], $thress[$i]\n";
	    ($isok, $varthres, $maxsev)= &calc_status ($dvalue, $t1, $t2, $t3);
	    ++$foundmatch ;
	    last ;	# dont search anymore patterns of foreach()
	}
    }
    if (! $foundmatch)	# print a warning
    {
	if (!$nodefaultvar{$curvar}) { # warning first time only
	    print STDERR "do_line: ERROR- no 'default' config for $curvar\n";
	    ++$nodefaultvar{$curvar};
	}
	return ;
    }			# end foreach (list of values)

    ($debug && !$isok) && print STDERR "(dbg) Dev= $curdev $curvar $dcomment, status= $isok\n";
    ## Keep track of variables only if they are down or have just changed
    #  state to back up. Use device + variable + comment as the index
    #  (since the comment is typically the interface anyway).
    local ($idx) = "$curdev" . "\t" . "$curvar" . "\t" . "$dcomment" ;
    if ( !$isok && !$isknown{$idx} ) # init the nocol structure
    {
	$varname = $curvar;
	$varunits = $curunits;
	$eventtime = $curtime;
	&init_event($curdev, $dcomment, $idx); # sitename and siteaddr
	$isknown{$idx} = 1;
    }
    if ($isknown{$idx}) {	# update the NOCOL structure
	$varthres{$idx} = $varthres; # insert new threshold level
	&update_event ($idx, $isok, $dvalue, $maxsev);
    }
    if ($isok == 0) {push (@downsites, "$idx");}   # for printing out later
    
}	# end: do_line()
	    

## 
## main
##

$cfile = shift || $cfile;
&nocol_startup ;
&readconf ;

## Little bit of initializing for notification about this program's status
$varname = "Restart" ; $varunits = "Status" ;
&init_event("snmpmon", "-", "snmpmonidx");
&update_event("snmpmonidx", 1, 1, $E_CRITICAL);	# forces logging
$varname{"snmpmonidx"} = "Running" ; # change to indicate running.

# Loop forever. Assume all datafile are in the data directory.
#
local ($stime, $deltatime);	# outside while() to prevent memory leaks ?
while (1)			# forever...
{
    local ($f, $i);

    $stime = time;          # time starting tests
    
    opendir (DATADIR, "$TMPDATADIR");
    foreach $f ( grep(/\.snmpmon$/, readdir(DATADIR)) ) {
	&do_datafile("$TMPDATADIR" . "/" . "$f");
    }
    closedir(DATADIR);
    $eventtime = time;
    &update_event("snmpmonidx", 1, 1, $E_CRITICAL);
    open(OEVENTS,">$datafile");	# overwrite the events each time
    &writeevent(OEVENTS, "snmpmonidx");
    while ($i = shift (@downsites)) {&writeevent(OEVENTS, $i); }
    close(OEVENTS);

    $deltatime = time - $stime;		# time to do tests
    $debug && print STDERR "(dbg) sleep for= $sleeptime - $deltatime\n";
    if ($sleeptime > $deltatime) { sleep(($sleeptime - $deltatime)) };

}	# end: while(forever) 

