#!/usr/local/bin/perl -w

use strict 'refs';
require Net::Ping;
require Term::ReadKey;

#defaults
$mibident=".ERRORNAME";
$miberrflag=".ERRORFLAG";
$miberrmsg=".ERRORMSG";
$mibfix=".ERRORFIX";
$mibheadall=".EXTENSIBLEDOTMIB";
$mibclearcache="$mibheadall.VERSIONMIBNUM.VERCLEARCACHE";
$mibrestartagent="$mibheadall.VERSIONMIBNUM.VERRESTARTAGENT";
$mibupdateconfig="$mibheadall.VERSIONMIBNUM.VERUPDATECONFIG";
%miblist=(    '.PROCMIBNUM.1' => 'processes',
	      '.SHELLMIBNUM.1' => 'scripts',
	      '.MEMMIBNUM' => 'swap space',
	      '.DISKMIBNUM.1' => 'disks',
	      '.LOADAVEMIBNUM.1' => 'load-average',
	      '.ERRORMIBNUM' => 'snmp-agent-errors');
@fixitlist=('.PROCMIBNUM.1','.SHELLMIBNUM.1');
%mibchecklist = ('.PROCMIBNUM.1' => 1,
		 '.SHELLMIBNUM.1' => 1,
		 '.MEMMIBNUM' => 1,
		 '.DISKMIBNUM.1' => 1,
		 '.LOADAVEMIBNUM.1' => 1,
		 '.ERRORMIBNUM' => 1);
$errlog="/net/tyfon/1/OV/log/ece-log";
$default_get_args = "-v 1 %s private";
$default_set_args = "-v 1 %s private";
$andlog=0;
$snmppath="BINDIR";
$eraseline="                                                                           \r";
$fixit=0;  # this should be 0 not -1, but is necessary till getc(STDIN) works
$rescanWhen = 300;
$display = $ENV{'DISPLAY'};
$hidden = 0;
$pinghost = 0;
$loglevel = 1;
$logwindowatstart = 0;
$numloglevels = 5;
$dontstart = 0;
$raiseonnew = 1;

#
#  Mib Package:  Each mib has a mib number attached and can check/fix itself;
#

package Mib;

# @ISA = qw( Host );

sub new {
    my $tmp = shift;
    my $self = {};
    $self->{'HostId'} = shift;
    $self->{'Host'} = $self->{'HostId'}->{'Name'};
    $_ = shift;
    $self->{'Mib'} = $_;
    print "test: $_\n";
    ($self->{'MibSuffix'}) = /(\.[0-9]+)$/;
    if (!defined($mibchecklist{$self->{'MibSuffix'}})) {
	($self->{'MibSuffix'}) = /(\.[0-9]+\.1)$/;
    }
    print "suff: $self->{'MibSuffix'}\n";
    $self->{'MibDesc'} = shift;
    $self->{'Frame'} = shift;
    bless $self;
}

sub getmibnum {
    my $self = shift;
    return ($self->{'Mib'});
}

sub snmp_walk {
    my $self = shift;
    $self->{'Frame'}->toplevel->Busy() if ($::display);
    my $mib = shift;
    my $cmd = "$::snmppath/snmpwalk " . sprintf($::default_get_args, $self->{'Host'}) . " $mib|";
    ::addToLog("running:  $cmd",5);
    open(OUT,"$cmd");
    my $outcount = 0;
    my @result = [];
    while (<OUT>) {
	$result[$outcount] = $_;
	chop;
	::addToLog("snmpwalk:  $_",5);
	if ($::display) {
	    $self->{'Frame'}->toplevel->update;
	}
	$outcount++;
    }
    close(OUT);
    for($i=0; $i <= $#result; $i++) {
	$result[$i] =~ s/ Hex:.*$//g;
	$result[$i] =~ s/\"//g;
    }
    $self->{'Frame'}->toplevel->Unbusy() if ($::display);
    if ($result[0] =~ /No Response/) {
	$self->{'HostId'}->hostdown;
	splice(@result,0);
    }
    return @result;
}

sub check {
    my $self = shift;
    my $tmp = [];
    if ($::mibchecklist{$self->{'MibSuffix'}} != 1) {
	return @{$tmp};
    }
    if (! $::display) {
	printf "%sChecking %s: %s\r", $::eraseline,$self->{'Host'},
	$self->{'MibDesc'};
    }
    my @walkout = $self->snmp_walk("$self->{'Mib'}$::miberrflag");
    while ($#walkout > -1) {
	$_ = shift @walkout;
	($result) = /= ([0-9]+)/;
	if (defined($result) && $result > 0)
	{
	    ($mibloc) = /\.([0-9]+) /;
	    push(@{$tmp},FixProblem::new("",$self->{'HostId'}, $self->{'Mib'},
					 $self->{'MibDesc'},
					 $mibloc,$self->{'Frame'}));
	    if (! $::display) {
		printf("%s%-8.8s  %-12.12s  %2d -- %-37.37s",$::eraseline,
		       $self->{'Host'},$tmp->[0]->{'ErrName'},
		       $result,$tmp->[0]->{'ErrMsg'});
		if ($tmp->[0]->canfix() && $::fixit == 0) {
		    printf(" / Fix? ");
		    $ans = Term::ReadKey::ReadKey(0);
		    if ("$ans" eq "y" || "$ans" eq "Y") {
			printf("\b\b\b\b\b\b\b\b");
			$tmp->[0]->fix($mibloc);     # fix now if curses
		    } else {
			print $ans;
			printf("\nChecking %s: %s\r",$self->{'Host'}, 
			       $self->{'MibDesc'});
		    }
		} elsif ($::fixit > 0) {
		    $tmp->[0]->fix($mibloc);     # fix now if curses
		}
		shift @{$tmp};
	    }
	}
    }
    return(@{$tmp});
}

#
#  Problem Package: A problem comes into existence when found.  It may
#  or may not know how to fix itself (Problem/FixProblem).
#

package Problem;

@ISA = qw( Mib );

sub snmp_get {
    my $self = shift;
    $self->{'Frame'}->toplevel->Busy() if ($::display);
    my $mib = shift;
    my $args = sprintf($::default_get_args, $self->{'Host'});
    $_ = `$::snmppath/snmpget $args $mib`;
    my ($result) = /= (.*)$/;
    if (!defined($result) || $result =~ /No Response/) {
	$self->{'HostId'}->hostdown;
	$result = "";
    }
    $result =~ s/\"//g;
    $result =~ s/ Hex:.*$//g;
    ::addToLog("snmpget:  $_",5);
    $self->{'Frame'}->toplevel->Unbusy() if ($::display);
    return $result;
}

sub snmp_set {
    my $self = shift;
    $self->{'Frame'}->toplevel->Busy() if ($::display);
    my $mib = shift;
    my $args = sprint($::default_set_args, $self->{'Host'});
    $_ = `$::snmppath/snmpset $args $mib`;
    my ($result) = /= (.*)$/;
    $result = "" if (!defined($result));
    $result =~ s/\"//g;
    ::addToLog("snmpset:  $_",5);
    $self->{'Frame'}->toplevel->Unbusy() if ($::display);
    return $result;
}

sub new{
    my $tmp = shift;
    my $hostId = shift;
    my $mib = shift;
    my $mibname = shift;
    my $self = new Mib ($hostId,$mib,$mibname);
    $self->{'MibLocation'} = shift;
    $tmp = shift;
    if ($::display) {
	$self->{'Frame'} = $tmp->Frame();
    }
    bless $self;
    $self->{'ErrName'} = 
	$self->snmp_get("$self->{'Mib'}$::mibident.$self->{'MibLocation'}");
    $self->{'ErrMsg'} = 
	$self->snmp_get("$self->{'Mib'}$::miberrmsg.$self->{'MibLocation'}");
    if (exists $self->{'HostId'}->{'Down'}) {
	return $self;
    }
    if ($::display) {
	$self->{'Frame'}->pack();
	$self->{'Desc'} = 
	    $self->{'Frame'}->Button(-text => sprintf("%-12.12s %-42.42s",
						     $self->{'ErrName'},
						     $self->{'ErrMsg'}),
				     -font => "6x13",
				     -highlightcolor => "#ffffff",
				     -borderwidth => 0,
				     -relief => "flat",
				     -bd => 0, -padx => 0, -pady => 0,
				     -activeforeground => 'red',
				     -activebackground => '#C9C9C9',
				     -background => '#E0C9C9',
				     -command => [\&selectme,$self]);
	$self->{'Desc'}->pack(-fill => "x",-expand => 1,-side=>"left"); # 
	if ($::raiseonnew) {
	    $tmp->toplevel->deiconify();
	    $tmp->toplevel->raise();
	}
	::addToLog("problem found:  $self->{'Host'}\t$self->{'ErrName'}\t$self->{'ErrMsg'}",2);
    }
    bless $self;
    return $self;
}

sub haveseen {
    my $self = shift;
    $self->{'Desc'}->configure(-background => '#C9C9C9');
}

sub selectme {
    my $self = shift;
    if ($main::hidden) {
      main::makeappear();
	return;
    }
    if (exists $self->{'Selected'}) {
      main::deselectitem($self);
	delete $self->{'Selected'};
    } else {
      main::selectitem($self);
	$self->{'Desc'}->configure(-foreground => "red");
	$self->{'Selected'} = 1;
    }
    $self->haveseen();
}

sub deselectme {
    my $self = shift;
    $self->{'Desc'}->configure(-foreground => "black");
    delete $self->{'Selected'};
}

sub check {
    my $self = shift;
    if ($::display) {
      main::setstatus("Checking $self->{'Host'} -- $self->{'ErrName'}");
    }
    else {
	printf("Checking \b\b\b\b\b\b\b\b\b");
    }
    $result = $self->snmp_get("$self->{'Mib'}$::miberrflag.$self->{'MibLocation'}");
    if (exists $self->{'HostId'}->{'Down'}) {
	return 0;
    }
    if ($result == 0) {
	$self->deleteme();
    }
  main::setstatus("idle");
    return $result;
}

sub fix {
# Don't fix and/or unable to
    my $self = shift;
  main::setmsg("Don't know how to fix $self->{'ErrName'}");
}

sub rsh {
    my $self = shift;
    if ($::display) {
	system "xterm -e rsh $self->{'HostId'}->{'Name'} -l root &";
    }
}

sub deleteme {
    my $self = shift;
    my $host = $self->{'HostId'};
    $host->deleteProb($self);
}

sub deleteself {
    my $self = shift;
    if ($::display) {
	if ($self->{'Selected'}) {
	  main::deselectitem($self);
	}
	$self->{'Desc'}->destroy();
	$self->{'Frame'}->destroy();
    }
}

sub canfix {
    return 0;
}

package FixProblem;

@ISA = qw( Problem );

sub new {
    my $tmp = shift;
    my $hostId = shift;
    my $mib = shift;
    my $mibdesc = shift;
    my $mibloc = shift;
    my $frame = shift;
    my $self = new Problem ($hostId,$mib,$mibdesc,$mibloc,$frame);
    $_ = $mib;
    ($mymib) = /(\.[0-9]+)$/;
    if (grep(/$mymib/,@::fixitlist) && ($::fixit >= 0)) {
	bless $self;		# Make it a FixProblem if fixable
    }
    return $self;               # else just return a Problem
}

sub canfix {
    return 1;
}

sub fix {
    my $self = shift;
    my $mibloc = shift;
    if ($::display) {
      main::setstatus(sprintf("Fixing %s:  %s",
			      $self->{'Host'}, $self->{'ErrName'}));
    } 
    else {
	printf(" / Fixing...\b\b\b\b\b\b\b\b\b");
    }
    $self->snmp_set("$self->{'Mib'}$::mibfix.$self->{'MibLocation'} integer 1");
    $self->snmp_set("$::mibclearcache integer 1");
    if (exists $self->{'HostId'}->{'Down'}) {
	return;
    }
    if ($::display) {
	main::setstatus("Sleeping");
    }
    else {
	printf("Sleeping \b\b\b\b\b\b\b\b\b");
    }
    sleep(2);
    if ($::display) {
	main::setstatus("Checking");
    }
    else {
	printf("Checking\b\b\b\b\b\b\b\b");
    }
    if ($self->check() != 0) {
	if (! $::display) {
	    printf("*failed*  \n");
	} else {
	    main::setmsg("Failed to fix $self->{'ErrName'} on $self->{'Host'}");
	}
    }
    else {
	if ($::display) {
#	    $self->{'HostId'}->deleteProb($self);
	    main::setmsg("Fixed $self->{'ErrName'} on $self->{'Host'}");
	} 
	else {
	    printf("Fixed     \n");
	}
    }
  main::setstatus("Idle");
}

#
#  Host Package:  Each object is a host which can check itself and display
#                 the results
#
package Host;

sub mibsort {
    $_ = $a;
    ($av) = /\.([0-9]+)/;
    $_ = $b;
    ($bv) = /\.([0-9]+)/;
    return $av <=> $bv;
}

sub new {
    my $self = {};
    my $tmp = shift;
    $self->{'Name'} = shift;
    $self->{'Host'} = $self->{'Name'};
    $self->{'Mibs'} = [];
    $self->{'Problems'} = [];
    bless $self;
    if ($::display) {
	$self->{'MainFrame'} = $::HostFrame->Frame();
	if (!$::hidden) {
	    $self->{'MainFrame'}->configure(-relief =>"sunken",-borderwidth=>2);
	}
	$self->{'ProbFrame'} = $self->{'MainFrame'}->Frame();
	$self->{'hostlabel'} = 
	    $self->{'MainFrame'}->Button(-text => sprintf("%-9.9s",
							    $self->{'Name'}),
					 -bd => 0, -padx => 0, -pady => 0,
					 -command =>[\&selectme,$self],
					 -activeforeground => 'red',
					 -activebackground => '#C9C9C9',
					 -width => 9,
					 -anchor => "w",
					 -relief => "flat");
	$self->{'hostlabel'}->pack(-side=>"left",-ipadx=>1,
				   -padx=> 1,-pady =>1);
	$self->{'ProbFrame'}->pack(-side=>"left",-ipadx=>1,
				   -padx=> 1,-pady =>1);
	$self->{'MainFrame'}->pack( #-padx => 2,-pady =>2, 
				   -fill => "x", -expand => 1);
    } 
    foreach $mibx ( sort mibsort keys(%::miblist) ) {
	push(@{$self->{'Mibs'}},
	     new Mib ($self,"$::mibheadall$mibx",$::miblist{$mibx},
		      $self->{'ProbFrame'}));
    }
    return $self;
}

sub rsh {
    my $self = shift;
    if ($::display) {
	system "xterm -e rsh $self->{'Name'} -l root &";
    }
}

sub selectme {
    my $self = shift;
    if ($main::hidden) {
      main::makeappear();
	return;
    }
    if (exists $self->{'Selected'}) {
      main::deselectitem($self);
	delete $self->{'Selected'};
    } else {
      main::selectitem($self);
	$self->{'hostlabel'}->configure(-foreground => "red");
	$self->{'Selected'} = 1;
    }
}

sub deselectme {
    my $self = shift;
    $self->{'hostlabel'}->configure(-foreground => "black");
    delete $self->{'Selected'};
}

sub fix {
    my $self = shift;
    if (! exists $self->{'Down'}) {
	foreach $i (@{$self->{'Problems'}}) {
	    if ($i->canfix() && ref($i) ne Host) {
		$i->fix();
	    }
	}
    }
}

sub seenall {
    my $self = shift;
    foreach $i (@{$self->{'Problems'}}) {
	if (ref($i) ne Host) {
	    $i->haveseen();
	}
    }
}

sub canfix {
    return 1;
}

sub hostdown {
    my $self = shift;
    $self->deleteProbs();
    push(@{$self->{'Problems'}},$self);
    $self->{'Down'} = 1;
    if ($::display) {
	if (!exists $self->{'hostlabel'}) {
	    $self->{'hostlabel'} = 
		$self->{'MainFrame'}->Button(-text => sprintf("%-9.9s",
							      $self->{'Name'}),
					     -bd => 0, -padx => 0, -pady => 0,
					     -command =>[\&selectme,$self],
					     -activeforeground => 'red',
					     -activebackground => '#C9C9C9',
					     -width => 9,
					     -anchor => "w",
					     -relief => "flat");
	}
	::addToLog("$self->{'Name'} is down",2);
	$self->{'hostlabel'}->configure(-text => 
					sprintf("%-9.9s down",$self->{'Name'}),
					-width => 14);
    }
}

sub check {
    my $self = shift;
    $self->{'noDelete'} = 1;
    $self->deleteProbs();
    delete $self->{'noDelete'};
    if ($::display) {
	$self->{'hostlabel'}->configure(-text => $self->{'Name'},-width=>9);
    }
    delete $self->{'Down'};
  main::setstatus("pinging $self->{'Name'}");
    if (!($::pinghost) || Net::Ping::pingecho($self->{'Name'},2)) {
	foreach $i (@{$self->{'Mibs'}}) {
	    if (ref($i) ne Mib) {
		print "$i is a ref($i) not a Mib\n";
	    } else {
	      main::setstatus("Checking $self->{'Name'}:  " . $i->{'MibDesc'});
		push(@{$self->{'Problems'}},$i->check());
	    }
	    if (exists $self->{'Down'}) {
		last;
	    }
	}
    } else {
	$self->hostdown();
    }
  main::setstatus("Idle");
    if ($#{$self->{'Problems'}} == -1) {
	$self->deleteme();
    }
}

sub deleteme {
    my $self = shift;
    if ($self->{'Selected'}) {
      main::deselectitem($self);
    }
    $self->deleteProbs();
    if ($::display) {
	$self->{'hostlabel'}->destroy();
	$self->{'ProbFrame'}->destroy();
	my $top = $self->{'MainFrame'}->toplevel;
	$self->{'MainFrame'}->destroy();
	$top->update;
    }
  main::deletehost($self->{'Name'});
}

sub deleteProbs {
    my $self = shift;
    foreach $i (@{$self->{'Problems'}}) {
	if (ref($i) eq Host) {
	    delete $self->{'Problems'};
	    return;
	}
	if (ref($i) ne Problem && ref($i) ne FixProblem) {
	    print "i:  $i is a ", ref($i), "\n";
	    next;
	}
	$self->deleteProb($i);
    }
}

sub deleteProb {
    my $self = shift;
    my $child = shift;
    for ($k = 0; $k <= $#{$self->{'Problems'}}; $k++) {
	if (ref($self->{'Problems'}->[$k]) eq Problem ||
	    ref($self->{'Problems'}->[$k]) eq FixProblem ) {
	    if ($self->{'Problems'}->[$k]->{'Mib'} eq $child->{'Mib'} && 
	    $self->{'Problems'}->[$k]->{'MibLocation'} eq
	    $child->{'MibLocation'}) {
		splice(@{$self->{'Problems'}},$k,1);
		$child->deleteself();
		if ($#{$self->{'Problems'}} == -1 && 
		    !exists $self->{'noDelete'}) {
		    $self->deleteme();
		}
		last;
	    }
	} else {
	    print "    not: ",$self->{'Problems'}->[$k],"/",
	    ref($self->{'Problems'}->[$k]),"\n";
	}
    }
}

package main;

#
# Read arguments
#

if ($#ARGV != -1) {
    while ($#ARGV >= 0 && $ARGV[0] =~ /^-/) {
	$_ = shift;
	$andlog = 1 if (/^-a/);
	$dontstart = 1 if (/^-d/);
	$fixit = -1 if (/^-n/);
	$fixit = 1 if (/^-y/);
	$display = 0 if (/^-x/);
	$pinghost = 1 if (/^-p/);
	$hidden = 1 if (/^-H/);
	$loglevel = shift if (/^-V/);
	$logwindowatstart = 1 if (/^-L/);
	&display_help() if (/^-h/);
	&setmibchecklist(@fixitlist) if (/^-f/);
    }
}

#
# If necessary check the ece-log file for problems
#

if (($andlog || $#ARGV == -1) && !$dontstart) {
    open(LOG,$errlog);
    while (<LOG>) {
	@fields = split;
	@tmp = grep(/$fields[0]/,@ARGV);
	if ($#tmp == -1) { #  && $fields[1] ne "down") {
	    $ARGV[$#ARGV + 1] = $fields[0];
	}
    }
    close(LOG);
}

#
# Check all the found hosts 
#

if ($display) {
    use Tk;
#    $tk_strictMotif = 1;
    $top = MainWindow->new();
    $top->bind('all',"<Control-q>",[\&quit]);
    $top->bind('all',"<Control-h>",[\&makehidden]);
    $top->bind('all',"<Control-s>",[\&seenall]);
    $top->bind('all',"<Control-f>",[\&fixall]);
     $top->option('add','*highlightThickness','0');        #wish this worked
#     $top->option('add','*highlightbackground','#C9C9C9');
     $top->option('add','*background','#C9C9C9');
     $top->option('add','*font','6x13');
    $HostFrame = $top->Frame();
    $MenuFrame = $top->Frame(-relief => "raised",-borderwidth => 2);
    $MenuFrame->pack(-fill => "x",-expand => 1);
    $statusBar = $top->Frame(-relief => "raised",-borderwidth => 2);
    $status = $statusBar->Label(-text => "initializing",-anchor =>"e");
    $statusl = $statusBar->Label(-text => "Status:  ", -anchor => "w");
    $msgBar = $top->Frame(-relief => "raised",-borderwidth => 2);
    $msg = $msgBar->Label(-text => "",-anchor =>"e");
    $msgl = $msgBar->Label(-text => "Note:  ", -anchor => "w");
    
    $botFrame = $top->Frame();
    $butFrame = $top->Frame();
    $entryhost = "";
    $NewHost = $botFrame->Entry(-textvariable => \$entryhost,-width=>20,
				-relief => "sunken");
    $NewHost->bind("<Return>",sub {newHost("$entryhost");
			       $NewHost->delete(0,length($entryhost));});
    $BotLabel = $botFrame->Label(-text => "Check New Host:  ",
			    -anchor => "w");
    $CmdsMenuBut = $MenuFrame->Menubutton(-text => "Cmds");
    $CmdsMenu = $CmdsMenuBut->Menu(-tearoff => 1);
    $CmdsMenuBut->configure(-menu => $CmdsMenu);
    $CmdsMenuBut->pack(-side => "left");
    $CmdsMenuBut->command(-label => "Check Hosts", -command => [\&rescanhosts]);
    $CmdsMenuBut->command(-label => "Check Log", -command => [\&scanlog]);
    $CmdsMenuBut->command(-label => "Fix All", -command => [\&fixall],
			  -accelerator => "Ctrl-f");
    $CmdsMenuBut->command(-label => "Seen All", -command => [\&seenall],
			  -accelerator => "Ctrl-s");
    $CmdsMenuBut->separator();
    $CmdsMenuBut->command(-label => "Hide", -command => [\&makehidden],
			  -accelerator => "Ctrl-h");
    $CmdsMenuBut->command(-label => "Quit", -command => [\&quit],
			  -accelerator => "Ctrl-q");
    $PrefsMenuBut = $MenuFrame->Menubutton(-text => "Prefs");
    $PrefsMenu = $PrefsMenuBut->Menu(-tearoff => 1);
    $PrefsMenuBut->configure(-menu => $PrefsMenu);
    $PrefsMenuBut->pack(-side => "left");
    $PrefsMenuBut->cascade(-label => "Rescan");
    $RescanPrefsBut = $PrefsMenu->Menu();
    $PrefsMenuBut->entryconfigure("Rescan",-menu => $RescanPrefsBut);
    $AutoRescan = 1;
    if ($AutoRescan) {
      $afterId = Tk::after($rescanWhen*1000,[\&autorescan]);
    }
    $RescanPrefsBut->checkbutton(-label =>"Auto Rescan",
			       -variable =>\$AutoRescan,
			       -command => sub {if ($AutoRescan) {
				   $afterId = 
				     Tk::after($rescanWhen*1000,[\&autorescan])
				   } else {
				     Tk::after("cancel",$afterId);
				   }});
    $AutoCheckLog = 1;
    $RescanPrefsBut->checkbutton(-label =>"Checks Log",
			       -variable =>\$AutoCheckLog);
    $AutoCheckHosts = 0;
    $RescanPrefsBut->checkbutton(-label =>"Checks Hosts",
			       -variable =>\$AutoCheckHosts);
    $RescanWhenHidden = 1;
    $RescanPrefsBut->checkbutton(-label =>"Only When Hidden",
			       -variable =>\$RescanWhenHidden);

    $RescanPrefsBut->checkbutton(-label =>"Pop forward with new",
			       -variable =>\$raiseonnew);

    $PrefsMenuBut->cascade(-label => "Log Verbosity");
    $LogVerbBut = $PrefsMenu->Menu();
    $PrefsMenuBut->entryconfigure("Log Verbosity",
				  -menu => $LogVerbBut);
    for ($i=1; $i <= $numloglevels; $i++) {
	$LogVerbBut->radiobutton(-label => "$i", -variable => \$loglevel, 
				 -value => $i);
    }

    $PrefsMenuBut->cascade(-label => "Check For");
    $CheckForBut = $PrefsMenu->Menu();
    $PrefsMenuBut->entryconfigure("Check For",
				  -menu => $CheckForBut);
    $CheckForBut->command(-label => "Fixable Problems", 
			  -command => [\&setmibchecklist,@fixitlist]);
    $CheckForBut->command(-label => "Everything", 
			  -command => [\&setmibchecklist,keys(%miblist)]);
    $CheckForBut->separator();
    foreach $i ( sort mibsort keys(%::mibchecklist) ) {
	$CheckForBut->checkbutton(-label => $miblist{$i}, 
				  -variable => \$mibchecklist{$i});
    }

    $PrefsMenuBut->checkbutton(-label => "Ping Host First", 
			       -variable => \$pinghost);

    # Agent control

    $agentMenuBut = $MenuFrame->Menubutton(-text => "Agent-Control");
    $agentMenu = $agentMenuBut->Menu(-tearoff => 1);
    $agentMenuBut->configure(-menu => $agentMenu);
    $agentMenuBut->pack(-side => "left");
    $agentMenuBut->command(-label => "Re-read Configuration", 
			   -command => [sub {if ($selected) { $top->Busy(); 
							      my $args = sprint($::default_get_args, $selected->{'Host'});
$_ = `$::snmppath/snmpset $args $mibupdateconfig i 1`; $top->Unbusy();}}]);
    $agentMenuBut->command(-label => "Clear Exec Cache", 
			   -command => [sub {if ($selected) { $top->Busy();
							      my $args = sprint($::default_get_args, $selected->{'Host'});
$_ = `$::snmppath/snmpset $args $mibclearcache i 1`; $top->Unbusy();}}]);
    $agentMenuBut->separator();
    $agentMenuBut->command(-label => "Re-start Agent", 
			   -command => [sub {if ($selected) { $top->Busy(); 
							      my $args = sprint($::default_get_args, $selected->{'Host'});
$_ = `$::snmppath/snmpset $args $mibrestartagent i 1`; $top->Unbusy();} }]);

    # set up remote commands

    $remoteMenuBut = $MenuFrame->Menubutton(-text => "Remote-Info");
    $remoteMenu = $remoteMenuBut->Menu(-tearoff => 1);
    $remoteMenuBut->configure(-menu => $remoteMenu);
    $remoteMenuBut->pack(-side => "left");
    $remoteMenuBut->command(-label => "Load-Av", -command => [\&remote_load]);
    $remoteMenuBut->separator();
    $remoteMenuBut->command(-label => "top", -command => [\&remote_cmd,"top"]);
    $remoteMenuBut->command(-label => "mailq", -command => [\&remote_cmd,"mailq"]);
    $remoteMenuBut->command(-label => "ps", -command => [\&remote_cmd,"ps"]);
    $remoteMenuBut->command(-label => "conf", -command => [\&remote_cmd,"conf"]);

    # set up log file menu
    $logFileMenuBut = $MenuFrame->Menubutton(-text => "Log");
    $logFileMenu = $logFileMenuBut->Menu(-tearoff => 1);
    $logFileMenuBut->configure(-menu => $logFileMenu);
    $logFileMenuBut->pack(-side => "left");
    $logFileMenuBut->command(-label => "show log", -command => [\&displayLog]);
    $logFileMenuBut->command(-label => "clear log", -command => [\&clearLog]);
    $logFileMenuBut->separator();
    $logFileMenuBut->command(-label => "show Tyfon's log", -command => [\&displayTyfon]);


    # set up status bar

    $statusl->pack(-fill => "x", -expand => 1, -side =>"left");
    $status->pack(-fill => "x", -expand => 1, -side =>"left");
    $msgl->pack(-fill => "x", -expand => 1, -side => "left");
    $msg->pack(-fill => "x", -expand => 1, -side => "left");
    $statusBar->pack(-fill => "x", -expand => 1);
    $msgBar->pack(-fill => "x", -expand => 1);
    $HostFrame->pack(-fill => "x",-expand => 1);
    $butFrame->pack(-fill => "x",-expand => 1);
    $botFrame->pack(-fill => "x",-expand => 1);
    $FixBut = $butFrame->Button(-text => "Fix",-command=>[sub{print "hi\n"}],
				-state => "disabled");
    $FixBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2,
		   -ipady => 2);
    $RshBut = $butFrame->Button(-text => "Rsh",-command=>[sub{print "hi\n"}],
				-state => "disabled");
    $RshBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2,
		   -ipady => 2);
    $DelBut = $butFrame->Button(-text => "Del",
				 -state => "disabled");
    $DelBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2,
		   -ipady => 2);
    $ChkBut = $butFrame->Button(-text => "Chk",
				 -state => "disabled");
    $ChkBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2,
		   -ipady => 2);
    $BotLabel->pack(-fill => "x",-expand => 1,-side=>"left");
    $NewHost->pack(-side=>"left");
    &makehidden() if ($hidden);
    $top->update();

    # generate log window, but tell it not to create display 
    $logwindow = MainWindow->new;
    $logwindow->option('add','*highlightThickness','0');        #wish this worked
#     $logwindow->option('add','*highlightbackground','#C9C9C9');
    $logwindow->option('add','*background','#C9C9C9');
    $logwindow->option('add','*font','6x13');

    $logbuttons = $logwindow->Frame;
    $logbuttons->pack(-side => 'bottom', -expand => 1, -fill => 'x');
    $logclose = $logbuttons->Button(-text => 'Close', 
				 -command => ['withdraw',$logwindow]);
    $logclose->pack(-side => 'left', -expand => 1);

    $logtext = $logwindow->Text(-height => 40, -setgrid => 1);
    $logtext->pack(-side => 'left', -fill => 'both', -expand => 1);
    $logscroll = $logwindow->Scrollbar(-command => ['yview',$logtext]);
    $logscroll->pack(-side => 'right', -fill => 'y');
    $logtext->configure(-yscrollcommand => ['set', $logscroll]);
    $logwindow->title("snmpcheck Action Log file");
    $logwindow->iconname("snmpcheck-log");
    $logtext->delete('1.0','end');
    $logclear = $logbuttons->Button(-text => 'Clear Log', 
				 -command => [\&deleteLog]);
    $logclear->pack(-side => 'right', -expand => 1);
    if (! $logwindowatstart) {
	$logwindow->withdraw;
    }

    $status->configure(-text => "Idle");
    $selected = 0;
    # fill table with hosts
    if (!$dontstart) {
	loadAllHosts(@ARGV);
    }
    MainLoop;
}
else {
    select(STDOUT);
    $| = 1;
    if ($::fixit == 0) {
	Term::ReadKey::ReadMode(3);
    }
    loadAllHosts(@ARGV);
    printf("$eraseline");
}

sub loadAllHosts {
    my @hostlist = @_;
    foreach $host ( @hostlist ) {
	newHost($host);
    }
}    

sub newHost {
    my $name = shift;
    if (!exists $chost{"$name"}) {
	$chost{"$name"} = new Host ($name);
	if ($::display) { $top->update(); }
	$chost{"$name"}->check;
    } else {
	setmsg("$name all ready exists");
    }
}

sub deletehost {
    my $name = shift;
    delete $chost{"$name"};
}

sub setstatus {
    my $arg = shift;
    if ($display) {
	$status->configure(-text => $arg);
	$top->update();
	addToLog($arg,4);
    }
}

sub setmsg {
    my $arg = shift;
    if ($display) {
	$msg->configure(-text => $arg);
	$top->update();
	addToLog($arg);
    }
}

sub addToLog {
    if ($display) {
	my $logmsg = shift;
	my $logaddlevel = shift;
	if (! defined($logaddlevel)) {
	    $logaddlevel = 1;
	}
	if ($logaddlevel <= $loglevel) {
	    $logtext->insert('end'," " x ($logaddlevel-1) . "$logmsg\n");
	}
    }
}

sub displayTyfon {
    remote_cmd_generic("cat /net/tyfon/1/OV/log/ece-log","Tyfon -- ece-log"); 
}

sub displayLog {
    $logwindow->deiconify;
    $logwindow->raise;
}

sub deleteLog {
    $logtext->delete('1.0','end');
}

sub deselectitem {
    $obj = shift;
    $obj->deselectme();
    $FixBut->configure(-state => "disabled");
    $RshBut->configure(-state => "disabled");
    $DelBut->configure(-state => "disabled");
    $ChkBut->configure(-state => "disabled");
    $selected = 0;
}

sub selectitem {
    if ($selected) {
	$selected->deselectme();
    }
    $selected = shift;
    if (ref($selected) ne Host || !(exists $selected->{'Down'})) {
	$RshBut->configure(-state => "normal", -command => ['rsh',$selected]);
    } else {
	$RshBut->configure(-state => "disabled");
    }
    $DelBut->configure(-state => "normal", -command => ['deleteme',$selected]);
    $ChkBut->configure(-state => "normal", -command => ['check',$selected]);
    if ($selected->canfix() && !(exists $selected->{'Down'})) {
	$FixBut->configure(-state => "normal", 
			   -command => ['fix',$selected]);
    } else {
	$FixBut->configure(-state => "disabled");
    }
    if ($hidden == 1) {
	makeappear();
    }
}

sub makehidden {
    $MenuFrame->pack("forget");
    $statusBar->pack("forget");
    $msgBar->pack("forget");
    $butFrame->pack("forget");
    $botFrame->pack("forget");
    flatten();
    $hidden=1;
}

sub makeappear {
    $HostFrame->pack("forget");
    $MenuFrame->pack(-expand => 1, -fill => "x");
    $statusBar->pack(-expand => 1, -fill => "x");
    $msgBar->pack(-expand => 1, -fill => "x");
    $HostFrame->pack(-expand => 1, -fill => "x");
    $butFrame->pack(-expand => 1, -fill => "x"); 
    $botFrame->pack(-expand => 1, -fill => "x");
    reliefen();
    $hidden=0;
}

sub quit {
    $top->destroy();
    exit();
}

sub scanlog {
    my (@fields, @tmp);
    open(LOG,$::errlog);
    while (<LOG>) {
	@fields = split;
	@tmp = grep(/$fields[0]/,@ARGV);
	if ($#tmp == -1 && !exists $::chost->{$fields[0]}) {
	    newHost($fields[0]);
	}
    }
    close(LOG);
}

sub rescanhosts {
    foreach $i (keys(%chost)) {
	$chost{$i}->check();
    }
}

sub autorescan {
    $afterId = Tk::after($rescanWhen*1000,[\&autorescan]);
    if ($RescanWhenHidden && !$hidden) {return;}
    if ($AutoCheckHosts) {
	rescanhosts();
    }
    if ($AutoCheckLog) {
	scanlog();
    }
}

sub flatten {
    foreach $i (keys(%chost)) {
	$chost{$i}->{'MainFrame'}->configure(-relief => "flat",-borderwidth=>0);
    }
}

sub reliefen {
    foreach $i (keys(%chost)) {
	$chost{$i}->{'MainFrame'}->configure(-relief =>"sunken",-borderwidth=>2);
    }
}

sub fixall {
    foreach $i (keys(%chost)) {
	$chost{$i}->fix();
    }
}

sub seenall {
    foreach $i (keys(%chost)) {
	$chost{$i}->seenall();
    }
}

sub remote_cmd {
    my $type = shift;
    if ($selected) {
	remote_cmd_generic("$::snmppath/rsnmp -p $type $selected->{'Host'}",
			   "$selected->{'Host'} -- $type",1);
    } else {
	setmsg("Error:  Nothing selected");
    }
}

sub remote_load {
    if ($selected) {
	remote_cmd_generic("$::snmppath/snmpwalk " . sprintf($::default_get_args,$selected->{'Host'}) . " .EXTENSIBLEDOTMIB.LOADAVEMIBNUM.LOADAVE",
			   "$selected->{'Host'} -- LoadAve");
    } else {
	setmsg("Error:  Nothing selected");
    }
}

sub remote_cmd_generic {
    my $cmd = shift;
    my $title = shift;
    my $insert = shift;
    addToLog("running:  $cmd ... ");
    my $newwin = MainWindow->new;
    $newwin->Busy();

    $newwin->option('add','*highlightThickness','0');        #wish this worked
#     $newwin->option('add','*highlightbackground','#C9C9C9');
    $newwin->option('add','*background','#C9C9C9');
    $newwin->option('add','*font','6x13');

    my $buttons = $newwin->Frame;
    $buttons->pack(-side => 'bottom', -expand => 1, -fill => 'x');
    my $entries = $newwin->Frame;
    $entries->pack(-side => 'bottom', -expand => 1, -fill => 'x');

    my $text = $newwin->Text(-height => 40, -setgrid => 1);
    $text->pack(-side => 'left', -fill => 'both', -expand => 1);
    my $scroll = $newwin->Scrollbar(-command => ['yview',$text]);
    $scroll->pack(-side => 'left', -fill => 'y');
    $text->configure(-yscrollcommand => ['set', $scroll]);

    my $close = $buttons->Button(-text => 'Close', 
				 -command => ['destroy',$newwin]);
    $close->pack(-side => 'left', -expand => 1);
    my $rerun = $buttons->Button(-text => 'Re-Run', 
				 -command=>[\&fill_text,'',$text,
					    \$cmd,$insert]);
    $rerun->pack(-side => 'left', -expand => 1);

    my $cmdlabel = $entries->Label(-text => "Command:  ");
    my $cmdtexte = $entries->Entry(-textvariable => \$cmd, 
				      -relief => "sunken");
    $cmdtexte->bind('<Return>' => [\&fill_text,$text, \$cmd,$insert]);
    $cmdlabel->pack(-side => 'left');
    $cmdtexte->pack(-side => 'left');
    
    my $searchtext = '';
    my $searchlabel = $entries->Label(-text => "Search for:  ");
    my $searchtexte = $entries->Entry(-textvariable => \$searchtext, 
				      -relief => "sunken");

    $searchtexte->pack(-side => 'right');
    $searchlabel->pack(-side => 'right');
    $searchtexte->bind('<Return>' => [sub { $text->tag('remove','search','0.0','end');
					    my($current, $length) = ('1.0', 0);
					    while (1) {
						$current = $text->search(-count => \$length, $searchtext, $current, 'end');
						last if not $current;
						$text->tag('add', 'search', $current, "$current + $length char");
						$current = $text->index("$current + $length char");
						$text->tag('configure','search',
							   -background => 
							   'lightBlue');}}]);

    if (defined($title)) {
	$newwin->title($title);
	$newwin->iconname($title);
    }
    fill_text('',$text,\$cmd,$insert);
}

sub fill_text {
    my $dump = shift;
    my $textw = shift;
    my $cmd = shift;
    my $insert = shift;
    $textw->delete('1.0','end');
    if (defined($insert) && $insert) {
	$textw->insert('end',"running:  $$cmd\n\n");
    }
    $textw->toplevel->update();
    $textw->toplevel->Busy();
    open(OUT,"$$cmd|");
    while (<OUT>) {
	$textw->insert('end',$_);
	$textw->toplevel->update();
	$textw->toplevel->Busy();
    }
    close(OUT);
    if (defined ($insert) && $insert) {
	$textw->insert('end',"\ndone.\n");
    }
    $textw->toplevel->Unbusy();
    $textw->Unbusy();
    addToLog("done:  $$cmd");
}

sub display_help {
    print "
Usage:  snmpcheck [-x] [-n|y] [-h] [-H] [-V NUM] [-L] [-f] [[-a] HOSTS] 

  -h\tDisplay this message.
  -a\tcheck error log file AND hosts specified on command line.
  -p\tDon't try and ping-echo the host first
  -f\tOnly check for things I can fix
  HOSTS\tcheck these hosts for problems.

X Options:
  -x\tforces ascii base if \$DISPLAY set (instead of tk).
  -H\tstart in hidden mode.  (hides user interface)
  -V NUM\tsets the initial verbosity level of the command log (def: 1)
  -L\tShow the log window at startup
  -d\tDon't start by checking anything.  Just bring up the interface.

Ascii Options:
  -n\tDon't ever try and fix the problems found.  Just list.
  -y\tAlways fix problems found.

";
    exit(0);

}

sub option_get {
    my $resource = shift;
    return $top->option('get',$resource);
}

sub option_set {
    my $resource = shift;
    my $value = shift;
    $top->option('add',"*$resource",$value);
}

sub option_save {

}

sub mibsort {
    $_ = $a;
    ($av) = /\.([0-9]+)/;
    $_ = $b;
    ($bv) = /\.([0-9]+)/;
    return $av <=> $bv;
}

sub setmibchecklist {
    my $i;
    foreach $i (keys(%mibchecklist)) {
	$mibchecklist{$i} = 0;
    }
    foreach $i (@_) {
	$mibchecklist{$i} = 1;
    }
}
