sysmon


#!/usr/bin/perl -w
#
# sysmon -- low tech monitoring of multiple systems,
#  plus mailbox/news checking
#
use strict;
use File::stat;
use Term::ReadKey;
use Term::ANSIScreen qw/:color :constants :cursor :screen/;

#
# signal handlers
#
$SIG{INT}  = \&sighndl;
$SIG{TERM} = \&sighndl;
$SIG{CHLD} = 'IGNORE';

my ( $logid, $home ) = ( getpwuid($<) )[ 0, 7 ];

my $key;          # key pressed
my $n;            # priority queue size
my $sigtermed;    # signal terminated
my $i;            # loop index
my ( $j, $k );    # priority queue indices
my @hostlist;     # systems to monitor
my @pq;           # priority queue (array of hashes)
my %row;          # screen coordinates for each host
my %col;
my $t;            # temp array element

open( LL, "<$home/.sysmonlist" );
chomp( @hostlist = <LL> );
close(LL);

$| = 1;
screeninit();

#
# set up priority queue, initially with everything equal priority (ASAP)
# number of elements in pq = 1 + number of hosts; elements are pq[1..n]
#
$n = 1 + scalar(@hostlist);
$pq[1] = { sched => 0, cmd => "checkmail" };
for ( $i = 0 ; $i <= $#hostlist ; $i++ ) {
    $pq[ $i + 2 ] = { sched => 0, cmd => $hostlist[$i] };
}

#
# also set up screen position for each host
#
my $nrows = int( ( 1 + scalar(@hostlist) ) / 2 );    # assume 2 columns
for ( $i = 0 ; $i <= $#hostlist ; $i++ ) {
    $row{ $hostlist[$i] } = 5 + ( $i % $nrows );
    $col{ $hostlist[$i] } = 1 + 40 * int( $i / $nrows );
}

ReadMode 4;                                          # raw

while ( !$sigtermed ) {
    locate( 1, 55 );
    print BOLD scalar localtime;
    if ( time() >= $pq[1]{sched} ) {
        $t = $pq[1];
        if ( $t->{cmd} eq "checkmail" ) {
            checkmail( $logid, $home );
            $t->{sched} = time() + 60;
        }
        else {
            checkhost( $pq[1]{cmd} );
            $t->{sched} = time() + 240 + rand(120);
        }

        #
        # requeue command with new scheduling
        #
        $k = 1;
        while ( $k <= int( $n / 2 ) ) {
            $j = $k + $k;
            if ( $j < $n && $pq[$j]{sched} > $pq[ $j + 1 ]{sched} ) {
                $j++;
            }
            last if ( $t->{sched} <= $pq[$j]{sched} );
            $pq[$k] = $pq[$j];
            $k = $j;
        }
        $pq[$k] = $t;
    }
    if ( defined( $key = ReadKey(1) ) ) {    # keypress!
        last if ( $key eq "Q" || $key eq "q" );
        screeninit();
    }
    sleep 1;
}
locate( 1, 1 );
cls;
ReadMode 0;
exit(0);

sub sighndl {
    my ($sig) = @_;
    $sigtermed = $sig;
}

sub screeninit {
    $Term::ANSIScreen::AUTORESET = 1;
    cls;
    locate( 1, 1 );
    print BOLD "CIS Unix System Monitor";
    locate( 4, 9 );
    print BOLD UNDERLINE "Users  Up(d)  Load  Resp(s)";
    locate( 4, 49 );
    print BOLD UNDERLINE "Users  Up(d)  Load  Resp(s)";
}

sub checkmail {
    my ( $logid, $home ) = @_;

    my @infiles;
    my $infile;
    my $col;
    my $sb;
    my $isnews;

    locate( 2, 1 );
    clline;    # clear two lines
    locate( 3, 1 );
    clline;    # clear two lines
    $col = 1;
    if ( -s "$home/.mailspool/$logid" ) {
        locate( 2, $col );
        print RED "[MAIL]";
        $col += 6;
    }

    $sb = stat("$home/.jnewsrc");
    if ( time() - $sb->mtime > 4 * 3600 ) {
        locate( 2, $col );
        print MAGENTA "[NEWS]";
        $col += 6;
    }
    opendir( M, "$home/Mail" ) || die "Can't open $home/Mail: $!\n";
    @infiles = grep { /^IN\./ } readdir(M);
    closedir(M);
    foreach $infile (@infiles) {
        $infile =~ s/^IN\.//;
        if ( -s "$home/Mail/IN.$infile" ) {
            locate( 2, $col );
            print YELLOW "[$infile]";
            $col += ( length($infile) + 2 );
        }
    }
    $sb = stat("$home/Mail/Postmaster");
    if ( $sb->size > 0 && time() - $sb->atime > 7200 ) {
        locate( 2, $col );
        print GREEN "[Postmaster]";
        $col += 12;
    }
    $sb = stat("$home/Mail/Spam");
    if ( $sb->size > 0 && time() - $sb->atime > 14400 ) {
        locate( 2, $col );
        print GREEN "[Spam]";
        $col += 6;
    }
}

#
# check host, display results
#
sub checkhost {
    my ($host) = @_;    # host to check
    my @psout;          # output from ps
    my $up;             # calculated uptime
    my ( $t1, $t2 );    # time before and after uptime query
    my $rt;             # elapsed time for query
    my ( $r, $c );      #row and column

    $r = $row{$host};
    $c = $col{$host};
    locate( $r, $c );
    print BOLD $host;
    locate( $r, $c + 8 );
    print ' ' x 32;
    eval {
        local $SIG{ALRM} = sub { die "alarm\n" };
        alarm(60);
        $t1 = time();
        open( F, "ssh $host uptime 2>&1|" );
        my $users  = "-";
        my $load   = "----";
        my $uptime = "----";
        while (<F>) {
            next unless / up /;
            / (\d+) user/ && ( $users = $1 );
            /average: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)/
              && ( $load = $3 );
            $up = 0;
            /(\d+) days?/  && ( $up += $1 );
            /(\d+) hrs?/   && ( $up += $1 / 24 );
            /(\d+) mins?/  && ( $up += $1 / 1440 );
            /(\d+) secs?/  && ( $up += $1 / 86400 );
            /(\d+):(\d+),/ && ( $up += ( $1 / 24 + $2 / 1440 ) );
            $uptime = sprintf( "%8.2f", $up );
        }
        close(F);
        $t2 = time();
        $rt = $t2 - $t1;
        if ( $users eq "-" ) {
            locate( $r, $c + 12 );
            print BLACK ON_RED "NO RESPONSE";
        }
        else {
            locate( $r, $c + 8 );
            printf( "%4s", $users );
            locate( $r, $c + 12 );
            printf( "%8s", $uptime );
            locate( $r, $c + 20 );
            if ( $load >= 2.0 ) {
                print RED sprintf( "%6s", $load );
            }
            elsif ( $load >= 1.0 ) {
                print YELLOW sprintf( "%6s", $load );
            }
            else {
                print GREEN sprintf( "%6s", $load );
            }
            locate( $r, $c + 29 );
            if ( $rt > 30 ) {
                print RED sprintf( "%5d", $rt );
            }
            elsif ( $rt > 15 ) {
                print YELLOW sprintf( "%5d", $rt );
            }
            else {
                print GREEN sprintf( "%5d", $rt );
            }
        }
        alarm(0);
    };
    if ($@) {
        die "Something went wrong!\n" unless $@ eq "alarm\n";
        locate( $r, $c + 9 );
        print BLACK ON_RED "NO RESPONSE";
        open( PS, "/bin/ps T|" );
        while (<PS>) {
            if (/ssh/) {
                @psout = split;
                kill 1, $psout[0];
            }
        }
        close(PS);
        close(F);
    }
}