#!/s/std/bin/perl -w
# -*- perl -*-
#
# $Revision: 1.10 $
package unix;

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 service.
Options:
    -H <hostname> [-v -P <port> -o <os> -d <disk> -l <load> -u <user>
           -p <proc> ( -a <partition> ) ( -m <mul_warn> ) ( -M <mul_crit> ) ]
    --help
    --version
    --usage
";
    return $ERRORS{'OK'};
}

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

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


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

    # Set all the default variables

    my %alldisks = ( "warn" => 90, "crit" => 95 );
    my %ignoredisks = ( ".*vice/cache.*" => 1,
                        "/local.*" => 1,
                        "/mnt.*" => 1,
                        "/scratch.*" => 1,
                        ".*cdrom.*" => 1,
                        ".*/s/sushi.*/mountpoints.*" => 1, );

    my %load = ( "warn" => 6, "crit" => 12 );
    my %user = ( "warn" => 20, "crit" => 40 );
    my %proc = ( "warn" => 200, "crit" => 300 );
    my %uptime = ( "min" => 0, "max" => 10000 );

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

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

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

    my $port = $options{"opt_P"} ? $options{"opt_P"} : 49171;
    my $os = $options{"opt_o"} ? $options{"opt_o"} : "Unknown";

    # DISK INFO
    if ( $options{"opt_d"} ) {
        my @newignoredisks;
        $options{"opt_d"} =~ s/\%//;
        ( $alldisks{"warn"}, $alldisks{"crit"}, @newignoredisks ) =
                                                split(",", $options{"opt_d"});
        foreach ( @newignoredisks ) {
            $ignoredisks{$_} = 1;
        }
    }
    &usage("Bad Disks\n") if ((!$alldisks{"warn"}) ||
                                (!$alldisks{"crit"}) ||
                                     ($alldisks{"warn"} > $alldisks{"crit"}));

    if ( $options{"opt_i"} ) {
        foreach ( split(",", $options{"opt_i"}) ) {
            $ignoredisks{$_} = 1;
        }
    }

    if ( $options{"opt_w"} ) {
        foreach my $test ( split( ",", $options{"opt_w"} ) ) {
            foreach my $ignore (keys %ignoredisks) {
                if ( $test =~ /$ignore/ ) {
                    delete $ignoredisks{$ignore};
                }
            }
        }
    }

    # INDIVIDUAL PARTITION SETTING
    foreach (@{$options{"opt_a"}}) {
        my ($warn, $crit, $name) = split(",");
        &usage("Bad partition info\n") if ( (!$name) || ($warn > $crit) );

        $diskpartition{$name}->{"warn"} = $warn;
        $diskpartition{$name}->{"crit"} = $crit;
    }

    # LOAD INFO
    ( ($load{"warn"}, $load{"crit"}) =
                    split(",", $options{"opt_l"}) ) if ( $options{"opt_l"} );
    &usage("Bad Load\n") if ((!defined($load{"warn"})) ||
                                (!defined($load{"crit"})) ||
                                             ($load{"warn"} > $load{"crit"}));

    # NUMBER OF USER INFO
    ( ($user{"warn"}, $user{"crit"}) =
                    split(",", $options{"opt_u"}) ) if ( $options{"opt_u"} );
    &usage("Bad User\n") if ((!defined($user{"warn"})) ||
                                (!defined($user{"crit"})) ||
                                     ($user{"warn"} > $user{"crit"}));

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

    # UPTIME INFO
    ( ($uptime{"min"}, $uptime{"max"}) =
                        split(",", $options{"opt_t"})) if ($options{"opt_t"});
    &usage("Bad Uptime\n") if ((!defined($uptime{"min"})) ||
                                (!defined($uptime{"max"})) ||
                                         ($uptime{"min"} > $uptime{"max"}));

    # 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(",");
        &usage("Bad mul_proc\n") if ( (!$name) || ($min > $max) );

        #$mulproc{$name} = [$min, $max, $owner];
        $mulproc{$name}{min} = $min;
        $mulproc{$name}{max} = $max;
        $mulproc{$name}{owner} = $owner;
        $mulproc{$name}{type} = $TYPE;

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

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

    if ($options{"verbose"}) {
        print "Hostname: $host
Port: $port
Disk: $alldisks{warn} - $alldisks{crit}
Load: $load{warn} - $load{crit}
User: $user{warn} - $user{crit}
Proc: $proc{warn} - $proc{crit}
args: $args
Ignored disks: 
";

        foreach (keys %ignoredisks) {
            print "\t$_\n";
        }

        print "\nSpecial Disk Partition Values:\n";
        foreach (keys %diskpartition) {
            print "\t$_:\t$diskpartition{$_}->{warn} warn, $diskpartition{$_}->{crit} crit\n";
        }
        print "\nProcesses:\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 $serveranswer = <Server>;
    $serveranswer = "" if (!(defined($serveranswer)));
    print "Received: $serveranswer" if ($options{"verbose"});
    chomp($serveranswer);

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

    my ($s_disk, $s_load, $s_user, $s_os, $s_hostname, $s_kernel, $s_proc, @s_mulproc) = split("#", $serveranswer);

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

#
# DISK
#
    
#
# Removing the mount points that we were told to ignore...
#
    $s_disk =~ s/\(//g;
    my @s_disk = split(/\)/,$s_disk);


    PARTITION: foreach my $partition ( @s_disk ) {
        my ($name, $full, $remaining, $error) = split(',', $partition);

        # if there's an error, warn even if we ignore the partition
        if ($error) {
            $state = "CRITICAL";
            $msg .= ":$name=$error";
        }

        foreach my $ignorepartition (keys %ignoredisks) {
            next PARTITION if ($name =~ /$ignorepartition/);
        }

        # formating error check
        if (!defined($full)) {
            $state = "CRITICAL";
            $msg .= ":disks not defined";
        }

        # default partition info
        my $warn = $diskpartition{$name}->{"warn"} ?
                         $diskpartition{$name}->{"warn"} : $alldisks{"warn"};
        my $crit = $diskpartition{$name}->{"crit"} ?
                         $diskpartition{$name}->{"crit"} : $alldisks{"crit"};

        if ( $full && ($full >= $crit) ) {
            if ( $remaining =~ /G/ ) {
                $state = "WARNING" if ( $state eq "OK" );
                $msg .= ":$name=$full,$remaining";
            } else {
                $state = "CRITICAL";
                $msg .= ":$name=$full,$remaining";
            }
        } elsif ( ($full >= $warn ) && ( $remaining !~ /G/ ) ){
            $state = "WARNING" if ($state eq "OK");
            $msg .= ":$name=$full,$remaining";
        }
    }

#
# Uptime and Load
#
    if ($s_load =~ /^.* - (.*) /) {
        my $load = $1;

        # get unix uptime:
        my $day = 0;
        $day = $1 if ($s_load =~ /(\d+) day/);

        my $hour = 0;
        $hour = $1 if ($s_load =~ /(\d+):\d+/);

        my $uptime = ($day*24) + $hour;

        if (($uptime < $uptime{"min"}) || ($uptime > $uptime{"max"})) {
            $state = "WARNING" if ($state eq "OK");
            $msg .= ":uptime=$uptime hours";
        }

        if ($load >= $load{"crit"}) {
            $state = "CRITICAL";
            $msg .= ":load=$load";
        } elsif ($load >= $load{"warn"}) {
            $state = "WARNING" if ($state eq "OK");
            $msg .= ":load=$load";
        }

    } else {
        $state = "UNKNOWN";
        $msg .= ":uptime,load=???";
    }
#
# Users
#
    if ($s_user =~ /^(\d*)/) {
        my $users = $1;
        if ($users >= $user{"crit"}) {
            $state = "CRITICAL";
            $msg .= ":users=$users";
        } elsif ($users >= $user{"warn"}) {
            $state = "WARNING" if ($state eq "OK");
            $msg .= ":users=$users";
        } 
    } else {
        $state = "UNKNOWN";
        $msg .= ":users=???";
    }

#
# OS
#

    if ( (defined $options{"opt_o"}) && ( $s_os !~ /^$os\s*$/ ) ){
        $state = "WARNING" if ($state eq "OK");
        $msg .= ":Wrong OS $s_os ne $os";
    }

#
# Hostname
#

    if ($s_hostname eq "localhost.localdomain") {
        $state = "WARNING" if ($state eq "OK");
        $msg .= ":Wrong Hostname - localhost";
    }

#
# Kernel
#

    if ( (defined $options{"opt_k"}) && ( $s_kernel !~ /$options{opt_k}/ ) ){
        $state = "WARNING" if ($state eq "OK");
        $msg .= ":Wrong Kernel - $s_kernel instead of " . $options{"opt_k"};
    }

##
## Proc
##
#
#    if ($s_proc =~ /^(\d*) \w*/) {
#        my $procs = $1;
#        if ($procs >= $proc{"crit"}) {
#            $state = "CRITICAL";
#            $msg .= ":procs=$procs";
#        } elsif ($procs >= $proc{"warn"}) {
#            $state = "WARNING" if ($state eq "OK");
#            $msg .= ":procs=$procs";
#        }
#    } else {
#        $state = "UNKNOWN";
#        $msg .= ":procs=???";
#    }

#
# MulProc
#

    foreach (@s_mulproc) {
        my ($snum, $arg) = /^(\d+) (.*) $/;

        if ( ($snum < $mulproc{$arg}{min}) || ($snum > $mulproc{$arg}{max}) ){
            if ($mulproc{$arg}{type} eq "WARNING") {
                $state = $mulproc{$arg}{type} if ($state eq "OK");
            } else {
                $state = $mulproc{$arg}{type};
            }
            $msg .= ":$arg";
            $msg .= "(as $mulproc{$arg}{owner})" if ($mulproc{$arg}{owner});
            $msg .= "=$snum";
        }
    }

    $msg =~ s/\|/-OR-/g;
    print "$state$msg #$serveranswer\n";

    exit $ERRORS{$state};

}

