#!/s/std/bin/perl -w
# -*- perl -*-
#
# $Revision: 1.20 $
package procs;

require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = qw(&print_usage &print_version &service_test);
use lib $main::runtimedir;
use csl_utils qw($TIMEOUT %ERRORS);
use strict;

use Socket;
use POSIX;

sub print_usage ();
sub print_version ();
sub service_test (%);

sub error () {
    print "CRITICAL: ", shift;
    return $ERRORS{'CRITICAL'};
}

sub usage () {
    print shift;
    print_usage();
}

sub print_usage () {
    print "
Usage:  Check local processes.
Options:
    -H <hostname> [-v -P <port> -A <afs_version> -X -p <proc> --vmware
           ( -m <mul_warn> ) ( -M <mul_crit> ) ]
    --help
    --version
    --usage
";
    return $ERRORS{'OK'};
}

sub print_version () {
    my $version = '$Revision: 1.20 $';
    $version =~ s/^\$Revision: //;
    $version =~ s/ \$\s*$//;

    print "csl_procs.pm - $version\n";
    return $ERRORS{'OK'};
}


sub service_test (%) {
    # grab options
    my %options = @_;

    # Start processing those options...
    if ( $options{verbose} ) {
        foreach my $opt ( sort keys(%options) ) {
            if ( $options{$opt} ) {
                print "$opt - $options{$opt}\n";
            }
        }
    }

    # Set the default variables
    my %proc = ( "warn" => 200, "crit" => 300 );

    # get the variables ready to use
    my %mulproc;
    my $args = "";
    my $output = "";
    my $state = "OK";
    my $msg = "";

    my $host = $options{opt_H};
    if ( !$host || ( $host eq "" ) ) {
        return &error("\nHost name not specified.\n");
    }

    my $port = $options{opt_P} ? $options{opt_P} : 49186;

    # NUMBER OF PROCESSES INFO
    ( ($proc{warn}, $proc{crit}) =
                        split(",", $options{opt_p}) ) if ($options{opt_p});
    return &error("Bad Proc\n") if ((!defined($proc{warn})) ||
                                (!defined($proc{crit})) ||
                                             ($proc{warn} > $proc{crit}));


    # PROCESS INFO
    my $TYPE = "WARNING";
    PROC: foreach (@{$options{opt_m}}, "switch", @{$options{opt_M}}) {
        if ($_ eq "switch") {
            $TYPE = "CRITICAL";
            next PROC;
        }
        my ($min, $max, $name, $owner) = split(",");
        return &error("Bad mul_proc\n") if ( (!$name) || ($min > $max) );
        $owner = "" unless $owner;

        $mulproc{"$name-$owner"}{name} = $name;
        $mulproc{"$name-$owner"}{min} = $min;
        $mulproc{"$name-$owner"}{max} = $max;
        $mulproc{"$name-$owner"}{owner} = $owner;
        $mulproc{"$name-$owner"}{type} = $TYPE;

        $args .= "/" if $args;
        $args .= "$name";
        $args .= ":$owner" if ($owner ne "");
    }

# Tell the people what we've done if they really want to know.

    if ($options{verbose}) {
        print "Hostname: $host
Port: $port
Proc: $proc{warn} - $proc{crit}
args: $args
Processes:\n";

        for my $name (keys %mulproc) {
            print "$name: ";
            for my $key (keys %{$mulproc{$name}}) {
                print "$key=$mulproc{$name}{$key} ";
            }
            print "\n";
        }
    }

    print "\nSetting up network socket and sending args:\n$args\n\n" if ($options{verbose});

    # magic socket stuff
    my $remoteaddr = inet_aton("$host");
    my $paddr = sockaddr_in($port, $remoteaddr) ||
                        return &error("Can't create info for connection: $!\n");
    my $proto = getprotobyname('tcp');
    socket(Server, PF_INET, SOCK_STREAM, $proto) ||
                                    return &error("Can't create socket: $!\n");
    setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, 1);
    connect(Server, $paddr) || return &error("Can't connect to server: $!\n");
    select(Server);
    $| = 1;

    # tell the host our info
    print Server "$args\n";

    # and get the response in serveranswer
    my ($s_afs, $s_X, $s_xfs, $s_vmware, $s_logrotate, $s_proc, @s_mulproc) = <Server>;

    # more socket magic
    close(Server);
    select(STDOUT);

    print "Received: $s_afs$s_X$s_xfs$s_vmware$s_logrotate$s_proc@s_mulproc" if $options{verbose};

    # If we don't get all of the absolutely default info, go ahead and die
    unless ($s_proc) {
        print "CRITICAL:Wrong format # Didn't get correct response\n";
        exit $ERRORS{CRITICAL};
    }

#
# AFS
#

    if ( $options{opt_A} ) {
        chomp $s_afs;
        my $version = ( split (": ", $s_afs) ) [1];
        if ( $version =~ /$options{opt_A}/ ) {
            $output = "$s_afs, ";
        } else {
            $state = "WARNING";
            $msg = "<u>Wrong AFS Client: $version instead of $options{opt_A}</u>, ";
        }
    }

#
# X Check
#

    if ( $options{opt_X} ) {
        chomp $s_X;
        if ( $s_X =~ /XStatus: OK/ ) {
            $output .= "$s_X, ";
        } else {
            $state = "CRITICAL";
            my $text = ( split (": ", $s_X) ) [1];
            $msg .= "<u>X down? $text</u>, ";
        }
    }
#
# Xfs Check
#

    if ( $options{opt_x} ) {
        chomp $s_xfs;
        if ( $s_xfs =~ /XfsStatus: OK/ ) {
            $output .= "$s_xfs, ";
        } else {
            $state = "CRITICAL";
            my $text = ( split (": ", $s_xfs) ) [1];
            $msg .= "<u>Xfs down? $text</u>, ";
        }
    }

#
# VMWare Check
#

    if ( $options{opt_vmware} ) {
        chomp $s_vmware;
        if ( $s_vmware =~ /VMwareStatus: OK/ ) {
            $output .= "$s_vmware, ";
        } else {
            $state = "CRITICAL";
            my $text = ( split (": ", $s_vmware) ) [1];
            $msg .= "<u>VMware down? $text</u>, ";
        }
    }

#
# Logrotate Check
#

    if ( $options{opt_L} ) {
        chomp $s_logrotate;
        if ($s_logrotate =~ /Logrotate: Ran (\d+)/) {
            if ($1 > $options{opt_L}) {
                $state = "CRITICAL";
                $msg .= "<u>Logrotate ran $1 hours ago (>$options{opt_L})</u>, ";
            } else {
                $output .= "$s_logrotate, ";
            }
        } elsif ($s_logrotate =~ /Logrotate: .*not a file/) {
            $output .= "$s_logrotate, ";
        } elsif ($s_logrotate eq "Logrotate: never ran") {
            $output .= "$s_logrotate, ";
        } else {
            $state = "CRITICAL";
            $msg .= "<u>$s_logrotate</u>, ";
        }
    }

#
# Proc
#

    if ($s_proc =~ /NumProc: (\d+)/) {
        my $procs = $1;
        $output .= "$procs processes running: ";
        if ($procs >= $proc{crit}) {
            $state = "CRITICAL";
            $msg .= "<u>$procs procs higher than critical of $proc{crit}</u>, ";
        } elsif ($procs >= $proc{warn}) {
            $state = "WARNING" if ($state eq "OK");
            $msg .= "<u>$procs procs higher than warning of $proc{warn}</u>, ";
        }
    } else {
        $state = "UNKNOWN";
        $msg .= "<u>Unknown number of processes running.</u>, ";
    }

#
# MulProc
#

    foreach (@s_mulproc) {
        my ($arg, $snum) = /^(.*): (\d+)$/;
        my $user="";
        if ($arg =~ /^(.*)\((.*)\)$/) {
            $arg = $1;
            $user = $2;
        }

        my $combined = "$arg-$user";

        if ( ($snum < $mulproc{$combined}{min}) || ($snum > $mulproc{$combined}{max}) ){
            if ($mulproc{$combined}{type} eq "WARNING") {
                $state = $mulproc{$combined}{type} if ($state eq "OK");
            } else {
                $state = $mulproc{$combined}{type};
            }
            $msg .= "<u>$snum $arg processes";
            $msg .= " as $mulproc{$combined}{owner}" if ($mulproc{$combined}{owner});
            $msg .= " instead of $mulproc{$combined}{min}->$mulproc{$combined}{max}</u>, ";

        } else {
            $output .= "$snum $arg";
            $output .= "($user)" if ($user);
            $output .= ", ";
        }

    }

    print "$state: ";

    $output =~ s/\|/-OR-/g;
    ($output) =~ s/, $//;
    ($output) =~ s/: $//;
    ($msg) =~ s/, $//;

    print "$msg<hr>" if $msg;
    print "$output\n";

    exit $ERRORS{$state};

}

