#! /usr/bin/perl -w
#
# check_hr_process - nagios plugin 
#
# Copyright (C) 2008 Guenther Mair,
# Derived from check_ifoperstatus by Christoph Kron.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#
# Report bugs to:  guenther.mair@hoslo.ch
#
# $Id: check_hr_process 25 2009-11-15 13:19:58Z gunny $
#

use POSIX;
use strict;
use lib "/usr/local/nagios/libexec"  ;
use utils qw($TIMEOUT %ERRORS &print_revision &support);

use Net::SNMP;
use Getopt::Long;
&Getopt::Long::config('bundling');

my $PROGNAME = 'check_hr_process';
my $REVISION = '$Rev: 25 $';
sub list ();
sub print_help ();
sub usage ();
sub process_arguments ();

my $DEBUG = 0;

my $timeout;
my $hostname;
my $session;
my $error;
my $response;
my $sub_response;
my $opt_h;
my $opt_V;
my $key;
my $lastc;
my $name;
my $status;
my $state = 'UNKNOWN';
my $answer = "";
my $snmpkey = 0;
my $community = "public";
my $snmp_version = 1 ;
my $maxmsgsize = 1472 ; # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $auth, $priv, $context);
my $port = 161;
my $processName;
my $warning;
my $critical;
my $count = 0;
my $currentOID = '';
my $list;

my $hrSWRunName = '1.3.6.1.2.1.25.4.2.1.2';
my $hrSWRunStatus = '1.3.6.1.2.1.25.4.2.1.7';

## Validate Arguments

process_arguments();
list() if (defined $list);

## Just in case of problems, let's not hang Nagios

$SIG{'ALRM'} = sub {
     print ("ERROR: No snmp response from $hostname (alarm)\n");
     exit $ERRORS{"UNKNOWN"};
};

alarm($timeout);



## Main function

if (!defined ($response = $session->get_table($hrSWRunName))) {
	$answer=$session->error;
	$session->close;
	$state = 'CRITICAL';
	printf ("$state: SNMP error with snmp version $snmp_version ($answer)\n");
	$session->close;
	exit $ERRORS{$state};
}

$processName =~ s/\//\\\//g;

foreach $key ( keys %{$response}) {
	if ($response->{$key} =~ /^$processName$/) {
		$key =~ /.*\.(\d+)$/;
		$snmpkey = $1;
		print "$processName = $key / $snmpkey \n" if $DEBUG;
		$currentOID = $hrSWRunStatus . "." . $snmpkey;
		if (!defined ($sub_response = $session->get_request($currentOID))) {
			$answer=$session->error;
			$session->close;
			$state = 'CRITICAL';
			printf ("$state: SNMP error with snmp version $snmp_version ($answer)\n");
			$session->close;
			exit $ERRORS{$state};
		}
		# hrSWRunStatus: 1 = running = OK
		# hrSWRunStatus: 2 = runnable = OK
		# hrSWRunStatus: 3 = notRunnable / waiting for event = OK
		# hrSWRunStatus: 4 = invalid = K.O.
		if ( $sub_response->{$currentOID} < 4 ) {
			$count++;
		}
	}
}

if ( $count < $critical ) {
	$state = 'CRITICAL';
} elsif ( $count < $warning ) {
	$state = 'WARNING';
} else {
	$state = 'OK';
}

printf("$state: %d process(es) running\n", $count);
exit $ERRORS{$state};


### subroutines

sub usage() {
  printf "\nMissing arguments!\n";
  printf "\n";
  printf "usage: \n";
  printf "check_hr_process -d <PROCESSNAME> -w <MIN> -c <MIN> -H <HOSTNAME> [-C <community>]\n";
  printf "Copyright (C) 2008 Guenther Mair\n";
  printf "\n\n";
  exit $ERRORS{"UNKNOWN"};
}

sub list() {
  if ( ! defined ($response = $session->get_table($hrSWRunName)) ) {
    $answer = $session->error;
    $session->close;
    $state = 'CRITICAL';
    printf("$state: SNMP error with snmp version $snmp_version ($answer)\n");
    $session->close;
    exit $ERRORS{$state};
  }
 
  print "Currently listed processes are:\n";
  foreach $key ( keys %{$response} ) {
    $key =~ /.*\.(\d+)$/;
    print " - '".$response->{$key}."' (SNMP-ID: ".$1.")\n";
  }
  exit $ERRORS{'OK'};
}

sub print_help() {
  printf "check_hr_process plugin for Nagios\n";
  printf "\nUsage:\n";
  printf "   -H (--hostname)   Hostname to query - (required)\n";
  printf "   -C (--community)  SNMP read community (defaults to public,\n";
  printf "                     used with SNMP v1 and v2c\n";
  printf "   -v (--snmp_version)  1 for SNMP v1 (default)\n";
  printf "                        2 for SNMP v2c\n";
  printf "                        SNMP v2c will use get_bulk for less overhead\n";
  printf "                        if monitoring with -d\n";
  printf "   -L (--seclevel)   choice of \"noAuthNoPriv\", \"authNoPriv\", or	\"authPriv\"\n";
  printf "   -U (--secname)    username for SNMPv3 context\n";
  printf "\n";
  printf "   -d (--process)    SNMP hrSWRunName process name\n";
  printf "\n";
  printf "   -l (--list)       list available SNMP hrSWRunName volume names\n";
  printf "\n";
  printf "   -w (--warning)    number of processes that should run\n";
  printf "   -c (--critical)   number of processes that need to run\n";
  printf "\n";
  printf "   -A (--authpass)   authentication password (cleartext ascii or localized key\n";
  printf "                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n"; 
  printf "                     auth password and authEngineID\n";
  printf "   -a (--authproto)  Authentication protocol ( MD5 or SHA1)\n";
  printf "   -X (--privpass)   privacy password (cleartext ascii or localized key\n";
  printf "                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n"; 
  printf "                     privacy password and authEngineID\n";
  printf "   -p (--port)       SNMP port (default 161)\n";
  printf "   -M (--maxmsgsize) Max message size - usefull only for v1 or v2c\n";
  printf "   -t (--timeout)    seconds before the plugin times out (default=$TIMEOUT)\n";
  printf "   -V (--version)    Plugin version\n";
  printf "   -h (--help)       usage help \n\n";
  printf " -d, -c and -w must be specified\n\n";
  print_revision($PROGNAME, '$Revision: 25 $');
}

sub process_arguments() {
  $status = GetOptions(
    "V"   => \$opt_V,        "version"         => \$opt_V,
    "h"   => \$opt_h,        "help"            => \$opt_h,
    "v=i" => \$snmp_version, "snmp_version=i"  => \$snmp_version,
    "C=s" => \$community,    "community=s"     => \$community,
    "L=s" => \$seclevel,     "seclevel=s"      => \$seclevel,
    "l"   => \$list,         "list"            => \$list,
    "a=s" => \$authproto,    "authproto=s"     => \$authproto,
    "U=s" => \$secname,      "secname=s"       => \$secname,
    "A=s" => \$authpass,     "authpass=s"      => \$authpass,
    "X=s" => \$privpass,     "privpass=s"      => \$privpass,
    "c=i" => \$critical,     "critical=i"      => \$critical,
    "w=i" => \$warning,      "warning=i"       => \$warning,
    "d=s" => \$processName,  "process=s"       => \$processName,
    "p=i" => \$port,         "port=i"          => \$port,
    "H=s" => \$hostname,     "hostname=s"      => \$hostname,
    "M=i" => \$maxmsgsize,   "maxmsgsize=i"    => \$maxmsgsize,
    "t=i" => \$timeout,      "timeout=i"       => \$timeout,
  );

  if ($status == 0) {
    print_help();
    exit $ERRORS{'OK'};
  }

  if ($opt_V) {
    print_revision($PROGNAME,'$Revision: 25 $');
    exit $ERRORS{'OK'};
  }

  if ($opt_h) {
    print_help();
    exit $ERRORS{'OK'};
  }

  if ( ! utils::is_hostname($hostname) ) {
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

  unless (defined $list) {
    unless (defined $critical) {
      printf "A minimum number of process before a critical status (-c) is issued must be specified\n";
      usage();
      exit $ERRORS{"UNKNOWN"};
    }

    unless (defined $warning) {
      printf "A minimum number of process before a warning status (-w) is issued must be specified\n";
      usage();
      exit $ERRORS{"UNKNOWN"};
    }

    unless (defined $processName) {
      printf "A valid process name (-d) must be provided\n";
      usage();
      exit $ERRORS{"UNKNOWN"};
    }
  }

  unless (defined $timeout) {
    $timeout = $TIMEOUT;
  }

  if  ($snmp_version =~ /3/ ) {
    # Must define a security level even though default is noAuthNoPriv
    # v3 requires a security username
    if (defined $seclevel  && defined $secname) {

      # Must define a security level even though defualt is noAuthNoPriv
      unless ( grep /^$seclevel$/, qw(noAuthNoPriv authNoPriv authPriv) ) {
        usage();
        exit $ERRORS{"UNKNOWN"};
      }

      # Authentication wanted
      if ( $seclevel eq 'authNoPriv' || $seclevel eq 'authPriv' ) {

        unless ( $authproto eq 'MD5' || $authproto eq 'SHA1' ) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        }

        if ( ! defined $authpass ) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        } else {
          if ( $authpass =~ /^0x/ ) {
            $auth = "-authkey => $authpass" ;
          } else {
            $auth = "-authpassword => $authpass";
          }
        }

      }
                      
      # Privacy (DES encryption) wanted
      if ( $seclevel eq  'authPriv' ) {
        if ( ! defined $privpass ) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        } else {
          if ( $privpass =~ /^0x/ ) {
            $priv = "-privkey => $privpass";
          } else {
            $priv = "-privpassword => $privpass";
          }
        }
      }

      # Context name defined or default
      unless ( defined $context) {
        $context = "";
      }

    } else {
      usage();
      exit $ERRORS{'UNKNOWN'}; ;
    }
  } # end snmpv3

  if ( $snmp_version =~ /[12]/ ) {
    ($session, $error) = Net::SNMP->session(
      -hostname   => $hostname,
      -community  => $community,
      -port       => $port,
      -version    => $snmp_version,
      -maxmsgsize => $maxmsgsize
    );

    if ( ! defined($session) ) {
      $state = 'UNKNOWN';
      $answer = $error;
      print ("$state: $answer");
      exit $ERRORS{$state};
    }

  } elsif ( $snmp_version =~ /3/ ) {

    if ( $seclevel eq 'noAuthNoPriv' ) {
      ($session, $error) = Net::SNMP->session(
        -hostname => $hostname,
        -port     => $port,
        -version  => $snmp_version,
        -username => $secname,
      );
    } elsif ( $seclevel eq 'authNoPriv' ) {
      ($session, $error) = Net::SNMP->session(
        -hostname     => $hostname,
        -port         => $port,
        -version      => $snmp_version,
        -username     => $secname,
        $auth,
        -authprotocol => $authproto,
      );	
    } elsif ( $seclevel eq 'authPriv' ) {
      ($session, $error) = Net::SNMP->session(
        -hostname     => $hostname,
        -port         => $port,
        -version      => $snmp_version,
        -username     => $secname,
        $auth,
        -authprotocol => $authproto,
        $priv
      );
    }
                                      
    if ( ! defined($session) ) {
      $state = 'UNKNOWN';
      $answer = $error;
      print ("$state: $answer");
      exit $ERRORS{$state};
    }

  } else {
    $state='UNKNOWN';
    print ("$state: No support for SNMP v$snmp_version yet.\n");
    exit $ERRORS{$state};
  }
}
## End validation
