#!/usr/bin/perl
;#
;#  MibTest hostname community [variables] [<mib.lex]
;#
;# This program uses the SNMP Walk command to extract the MIB from  the
;# given agent.  The file mibtbl is read to get the information on what
;# variables are available.  You may name a group, in  which  case  all
;# variables in the group will be queried.
;#
;# If there are no variables on the command line, we read from stdin to
;# get the list of variables. The list must be one variable per line. A
;# one-byte  token  in  the  first column is ignored, as are any tokens
;# after the variable name, and any lines  that  have  '#'  or  '|'  in
;# column 1.

$Ofile = 'MibTest.vals';
$Vfile = 'MibTest.vars'; 

#include "dbg.pl"; 

open(O,">>$Ofile") || die "Can't write \"$Ofile\" [$!]\n"; select O; $| = 1;
open(V,">>$Vfile") || die "Can't write \"$Vfile\" [$!]\n"; select V; $| = 1;
select STDOUT; $| = 1;
print STDOUT "\n";
print STDOUT "Writing names and values to $Vfile\n";
print STDOUT "Writing variable summary to $Ofile\n";

($sec,$min,$hour,$day,$month,$year) = gmtime;
printf(O "Started: %02d/%02d/%02d/ %02d:%02d:%02d UTC\n",
	$year, $month+1, $day, $hour, $min, $sec);
$ymd = sprintf("%02d%02d%02d", $year, $month+1, $day);
($sys = shift(ARGV) || $ENV{"SNMP_HOST"} || `hostname`) =~ s/\s*$//;
$com = shift(ARGV) || $ENV{"SNMP_COMM"} || '';	# Our agent can handle this. (Can yours?)
$com = "'$com'";			# Quote it to prevent any expansion by sh.
die "System name unknown\n" if (!$sys);
die "Community name unknown\n" if (!$com);

open(MIB,"<mibtbl")
	|| die "### Can't read mibtbl [$!]\n";
for $line (<MIB>) {
	chop $line;
	if ($line =~ /^([0-9.]+)\s+([-A-Za-z0-9_]+)$/) {
		$var{$1} = $2;
		$oid{$2} = $1;
		push(@mib, "$1 $2");
	}
}
close(MIB);

for $v (@ARGV) {	# Are there command-line variables?
	++$vars;
#	D "Var $vars: $v" D3;
	if ($o = $oid{$v}) {
		&var($v, $o);
	}
}
if (!$vars) {	# If no command-line vars, read stdin.
	print STDERR "$0 reading variable names from standard input ...\n";
	for $l (<STDIN>) {	# Input may be comments or variables.
		chop $l;
		next if ($l =~ /^#/);	# Ignore comments.
		next if ($l =~ /^\|/);	# Ignore variables flagged with '|'.
		next if ($l =~ /^[A-Z].*:/);	# Ignore email headers.
		$l =~ s/^(.)\s+//;		# Strip off initial 1-char token.
		if ($l =~ /^\s*([a-z][A-Za-z0-9_\-]+)/) {	# Find first word.
			$v = $1;
			++$vars;
#			D "Var $vars: $v\n" D4;
			if ($o = $oid{$v}) {
				&var($v, $o);
			}
		} else {
#			D "Ignore: $l" D3; 
		}
	}
}
print O "\nSummary:\n";
if ($rsps) {
	printf O "  %d responses.\n", $rsps;
} else {
	print O "  No responses.\n";
}
if ($bads) {
	printf O "%d bad responses:\n", $bads;
	if ($nulls) {
		$list = &vars(%null);
		printf O "  %d unimplemented: $list\n", $nulls;
	} else {
		print O "  No unimplementeds.\n";
	}
	if ($norsps) {
		$list = &vars(@norsp);
		printf O "  %d failed: $list\n", &vars($norsps);
	} else {
		print O "  No failures.\n";
	}
;#} else {
;#	print O "  No bad responses.\n";
}
($sec,$min,$hour,$day,$month,$year) = gmtime;
printf O "Finished: %02d/%02d/%02d/ %02d:%02d:%02d UTC\n",
	$year, $month+1, $day, $hour, $min, $sec;
exit 0;

;# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
;# Search the @mib table for sub-variables to the given variable $v. If
;# any are found, a recursive call is done to find the variables.  When
;# we get down to the actual variables, we call &test to  generate  the
;# SNMP requests.
sub var {
	local($v,$o) = @_;
	local($ent,$m,$subvars);
	for ($m=0; $ent = $mib[$m]; $m++) {
#		D "MIB[$m]=\"$ent\"" D5;
		if ($ent =~ /^$o\s/) {
#			D "found $o in \"$ent\"" D3;
			;
		}
		if ($ent =~ /.*\s$v$/) {
#			D "found $v in \"$ent\"" D3;
			;
		}
		if ($ent =~ /^($o\.\d+)\s(.*)/) {
			$oo = $1;
			$vv = $2;
#			D "Found $oo $vv in \"$ent\"" D3;
			$subvars++;
			&var($vv,$oo);
		}
	}
	if (!$subvars) {
#		D "Test variable v=$v o=$o" D3;
		&test($v,$o);
	}
}

;# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
;# Here's where we generate a query for a variable.  We use "Walk" so that  we #
;# can  get  all the instances of the variable.  We test for a list of special #
;# values from Walk that indicate problems, and update various  counters.   We #
;# also generate the summary data in the V file here.                          #
;# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
sub test {
	local($v,$o) = @_;
	local($cmd);	# Shell command to get variable's data.
	local($smry);	# Summary line for this variable.
	local($rsp);	# The response from the agent.
	local($r,@r);	# Split response up into lines.

	print O "$v:\n";
	$cmd = "Walk '%D1' '+o2' $sys $com $v";
#	print O "Cmd:\t\"$cmd\"\n";
	$rsp = `$cmd`;
	print O $rsp;	# Write the response to the "output" log.
	@r = split(/\n/,$rsp);
	for $r (@r) {
#		D "Cmd \"$r\"" D2;
		if ($r =~ 'No response') {
			push(@norsp,$v);
			++$norsps;
			++$bads;
			print O "$v: No response.\n";
			$smry = "- $v\t-- $ymd failed" if !$smry;
		} elsif ($r =~ /\.F\tNULL$/) {
			++$null{$v};
			++$nulls;
			++$bads;
			print O "$v: -\n";
			$smry = "- $v\t-- $ymd Unimplemented" if !$smry;
		} elsif ($r =~ /\.[\d.]+\tNULL$/) {
			++$null{$v};
			++$nulls;
			++$bads;
			print O "$v: -\n";
			$smry = "- $v\t-- $ymd NULL" if !$smry;
		} else {
			$rsp{$v} = $r;
			++$rsps;
			$r =~ s/\.\.\.+/\t/g;
			print $r, "\n";
			if ($r =~ /\t0+$/) {
				$smry = "+ $v\t-- $ymd zero" if !$smry;
			} elsif ($r =~ /\t[0. ]+$/) {
				$smry = "+ $v\t-- $ymd zeroes";
			} elsif ($r =~ /\t[" _]+$/) {
				$smry = "+ $v\t-- $ymd blank";
			} elsif ($r =~ /Error (\d+) /) {
				$smry = "+ $v\t-- $ymd Err$1"if !$smry;
			} elsif ($r =~ /$v\.[\d.]+\t/) {
				$smry = "+ $v\t-- $ymd works";
			} else {
				$smry = "+ $v\t-- $ymd ???" if !$smry;
			}
		}
	}
	print(V $smry, "\n") if $smry;	# Write summary to "variable" log.
	++$Walks;
}

;# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
;# Format a list of variable names for printing.  We break them up into
;# lines of about 70 to 80 chars, and put two tabs at the start of each
;# line.
sub vars {
	local($val);
	local($len) = 80;
	for (@_){
		next if (/^\d+$/);
		if (($len += length) > 70) {
			$val .= "\n\t";
			$len = 0;
		} else {
			$val .= ' ';
		}
		$val .= $_;
	}
	return $val;
}

;# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
