#!/usr/bin/perl -w
############################## check_snmp_process ##############
my $VERSION = "2.1.0";

# Date : Oct 12 2007
# Author  : Patrick Proy (patrick at proy dot org)
# Help : http://nagios.manubulon.com
# License : GPL - http://www.fsf.org/licenses/gpl.txt
# Contrib : Makina Corpus, adam At greekattic d0t com
# TODO : put $o_delta as an option
# 	 If testing on localhost, selects itself....
###############################################################
#
# help : ./check_snmp_process -h

use strict;
use Net::SNMP;
use Getopt::Long;

############### BASE DIRECTORY FOR TEMP FILE ########
my $o_base_dir                    = "/tmp/tmp_Icinga_proc.";
my $file_history                  = 200;                       # number of data to keep in files.
my $delta_of_time_to_make_average = 300;                       # 5minutes by default

# Icinga specific
my $TIMEOUT = 15;
my %ERRORS = ('OK' => 0, 'WARNING' => 1, 'CRITICAL' => 2, 'UNKNOWN' => 3, 'DEPENDENT' => 4);

# SNMP Datas
my $process_table   = '1.3.6.1.2.1.25.4.2.1';
my $index_table     = '1.3.6.1.2.1.25.4.2.1.1';
my $run_name_table  = '1.3.6.1.2.1.25.4.2.1.2';
my $run_path_table  = '1.3.6.1.2.1.25.4.2.1.4';
my $run_param_table = '1.3.6.1.2.1.25.4.2.1.5';
my $proc_mem_table  = '1.3.6.1.2.1.25.5.1.1.2';                # Kbytes
my $proc_cpu_table  = '1.3.6.1.2.1.25.5.1.1.1';                # Centi sec of CPU
my $proc_run_state  = '1.3.6.1.2.1.25.4.2.1.7';

# Globals

my $o_host      = undef;                                       # hostname
my $o_community = undef;                                       # community
my $o_port      = 161;                                         # port
my $o_domain    = 'udp/ipv4';                                  # Default to UDP over IPv4
my $o_version2  = undef;                                       #use snmp v2c
my $o_descr     = undef;                                       # description filter
my $o_warn      = 0;                                           # warning limit
my @o_warnL     = undef;                                       # warning limits (min,max)
my $o_crit      = 0;                                           # critical limit
my @o_critL     = undef;                                       # critical limits (min,max)
my $o_help      = undef;                                       # wan't some help ?
my $o_verb      = undef;                                       # verbose mode
my $o_version   = undef;                                       # print version
my $o_noreg     = undef;                                       # Do not use Regexp for name
my $o_path      = undef;                                       # check path instead of name
my $o_inverse   = undef;                                       # checks max instead of min number of process
my $o_get_all   = undef;                                       # get all tables at once
my $o_param     = undef;                                       # Add process parameters for selection
my $o_perf      = undef;                                       # Add performance output
my $o_timeout   = 5;                                           # Default 5s Timeout

# SNMP V3 specific
my $o_login     = undef;                                       # snmp v3 login
my $o_passwd    = undef;                                       # snmp v3 passwd
my $v3protocols = undef;                                       # V3 protocol list.
my $o_authproto = 'md5';                                       # Auth protocol
my $o_privproto = 'des';                                       # Priv protocol
my $o_privpass  = undef;                                       # priv password

# SNMP Message size parameter (Makina Corpus contrib)
my $o_octetlength = undef;

# Memory & CPU
my $o_mem     = undef;                                         # checks memory (max)
my @o_memL    = undef;                                         # warn and crit level for mem
my $o_mem_avg = undef;                                         # cheks memory average
my $o_cpu     = undef;                                         # checks CPU usage
my @o_cpuL    = undef;                                         # warn and crit level for cpu
my $o_delta   = $delta_of_time_to_make_average;                # delta time for CPU check

# functions

sub p_version { print "check_snmp_process version : $VERSION\n"; }

sub print_usage {
    print
"Usage: $0 [-v] -H <host> -C <snmp_community> [-2] | (-l login -x passwd) [-p <port>] [-P <IP Protocol>] -n <name> [-w <min_proc>[,<max_proc>] -c <min_proc>[,max_proc] ] [-m<warn Mb>,<crit Mb> -a -u<warn %>,<crit%> -d<delta> ] [-t <timeout>] [-o <octet_length>] [-f -A -F ] [-r] [-V] [-g]\n";
}

sub isnotnum {                                                 # Return true if arg is not a number
    my $num = shift;
    if ($num =~ /^-?(\d+\.?\d*)|(^\.\d+)$/) { return 0; }
    return 1;
}

sub read_file {

    # Input : File, items_number
    # Returns : array of value : [line][item]
    my ($traffic_file, $items_number) = @_;
    my ($ligne, $n_rows) = (undef, 0);
    my (@last_values, @file_values, $i);
    open(FILE, "<" . $traffic_file) || return (1, 0, 0);

    while ($ligne = <FILE>) {
        chomp($ligne);
        @file_values = split(":", $ligne);

        #verb("@file_values");
        if ($#file_values >= ($items_number - 1)) {

            # check if there is enough data, else ignore line
            for ($i = 0; $i < $items_number; $i++) { $last_values[$n_rows][$i] = $file_values[$i]; }
            $n_rows++;
        }
    }
    close FILE;
    if ($n_rows != 0) {
        return (0, $n_rows, @last_values);
    } else {
        return (1, 0, 0);
    }
}

sub write_file {

    # Input : file , rows, items, array of value : [line][item]
    # Returns : 0 / OK, 1 / error
    my ($file_out, $rows, $item, @file_values) = @_;
    my $start_line = ($rows > $file_history) ? $rows - $file_history : 0;
    if (open(FILE2, ">" . $file_out)) {
        for (my $i = $start_line; $i < $rows; $i++) {
            for (my $j = 0; $j < $item; $j++) {
                print FILE2 $file_values[$i][$j];
                if ($j != ($item - 1)) { print FILE2 ":" }
            }
            print FILE2 "\n";
        }
        close FILE2;
        return 0;
    } else {
        return 1;
    }
}

sub help {
    print "\nSNMP Process Monitor for Icinga/Nagios/Naemon/Shinken, Version ", $VERSION, "\n";
    print "GPL license, (c)2004-2006 Patrick Proy\n\n";
    print_usage();
    print <<EOT;
-v, --verbose
   print extra debugging information (and lists all storages)
-h, --help
   print this help message
-H, --hostname=HOST
   name or IP address of host to check
-C, --community=COMMUNITY NAME
   community name for the host's SNMP agent (implies SNMP v1 or v2c with option)
-l, --login=LOGIN ; -x, --passwd=PASSWD, -2, --v2c
   Login and auth password for snmpv3 authentication 
   If no priv password exists, implies AuthNoPriv 
   -2 : use snmp v2c
-X, --privpass=PASSWD
   Priv password for snmpv3 (AuthPriv protocol)
-L, --protocols=<authproto>,<privproto>
   <authproto> : Authentication protocol (md5|sha : default md5)
   <privproto> : Priv protocole (des|aes : default des) 
-p, --port=PORT
   SNMP port (Default 161)
-P, --protocol=PROTOCOL
   Network protocol to be used
   ['udp/ipv4'] : UDP over IPv4
    'udp/ipv6'  : UDP over IPv6
    'tcp/ipv4'  : TCP over IPv4
    'tcp/ipv6'  : TCP over IPv6
-n, --name=NAME
   Name of the process (regexp)
   No trailing slash !
-r, --noregexp
   Do not use regexp to match NAME in description OID
-f, --fullpath
   Use full path name instead of process name 
   (Windows doesn't provide full path name)
-A, --param
   Add parameters to select processes.
   ex : "named.*-t /var/named/chroot" will only select named process with this parameter 
-F, --perfout
   Add performance output
   outputs : memory_usage, num_process, cpu_usage
-w, --warn=MIN[,MAX]
   Number of process that will cause a warning 
   -1 for no warning, MAX must be >0. Ex : -w-1,50
-c, --critical=MIN[,MAX]
   number of process that will cause an error (
   -1 for no critical, MAX must be >0. Ex : -c-1,50
Notes on warning and critical : 
   with the following options : -w m1,x1 -c m2,x2
   you must have : m2 <= m1 < x1 <= x2
   you can omit x1 or x2 or both
-m, --memory=WARN,CRIT
   checks memory usage (default max of all process)
   values are warning and critical values in Mb
-a, --average
   makes an average of memory used by process instead of max
-u, --cpu=WARN,CRIT
   checks cpu usage of all process
   values are warning and critical values in % of CPU usage
   if more than one CPU, value can be > 100% : 100%=1 CPU
-d, --delta=seconds
   make an average of <delta> seconds for CPU (default 300=5min)   
-g, --getall
  In some cases, it is necessary to get all data at once because
  process die very frequently.
  This option eats bandwidth an cpu (for remote host) at breakfast.
-o, --octetlength=INTEGER
  max-size of the SNMP message, usefull in case of Too Long responses.
  Be carefull with network filters. Range 484 - 65535, default are
  usually 1472,1452,1460 or 1440.  
-t, --timeout=INTEGER
   timeout for SNMP in seconds (Default: 5)
-V, --version
   prints version number
Note :   
  CPU usage is in % of one cpu, so maximum can be 100% * number of CPU 
  example : 
  Browse process list : <script> -C <community> -H <host> -n <anything> -v 
  the -n option allows regexp in perl format : 
  All process of /opt/soft/bin  	: -n /opt/soft/bin/ -f
  All 'named' process			: -n named

EOT
}

sub verb { my $t = shift; print $t, "\n" if defined($o_verb); }

sub check_options {
    Getopt::Long::Configure("bundling");
    GetOptions(
        'v'             => \$o_verb,
        'verbose'       => \$o_verb,
        'h'             => \$o_help,
        'help'          => \$o_help,
        'H:s'           => \$o_host,
        'hostname:s'    => \$o_host,
        'p:i'           => \$o_port,
        'port:i'        => \$o_port,
        'P:s'           => \$o_domain,
        'protocol:s'    => \$o_domain,
        'C:s'           => \$o_community,
        'community:s'   => \$o_community,
        'l:s'           => \$o_login,
        'login:s'       => \$o_login,
        'x:s'           => \$o_passwd,
        'passwd:s'      => \$o_passwd,
        'X:s'           => \$o_privpass,
        'privpass:s'    => \$o_privpass,
        'L:s'           => \$v3protocols,
        'protocols:s'   => \$v3protocols,
        'c:s'           => \$o_crit,
        'critical:s'    => \$o_crit,
        'w:s'           => \$o_warn,
        'warn:s'        => \$o_warn,
        't:i'           => \$o_timeout,
        'timeout:i'     => \$o_timeout,
        'n:s'           => \$o_descr,
        'name:s'        => \$o_descr,
        'r'             => \$o_noreg,
        'noregexp'      => \$o_noreg,
        'f'             => \$o_path,
        'fullpath'      => \$o_path,
        'm:s'           => \$o_mem,
        'memory:s'      => \$o_mem,
        'a'             => \$o_mem_avg,
        'average'       => \$o_mem_avg,
        'u:s'           => \$o_cpu,
        'cpu'           => \$o_cpu,
        '2'             => \$o_version2,
        'v2c'           => \$o_version2,
        'o:i'           => \$o_octetlength,
        'octetlength:i' => \$o_octetlength,
        'g'             => \$o_get_all,
        'getall'        => \$o_get_all,
        'A'             => \$o_param,
        'param'         => \$o_param,
        'F'             => \$o_perf,
        'perfout'       => \$o_perf,
        'd:i'           => \$o_delta,
        'delta:i'       => \$o_delta,
        'V'             => \$o_version,
        'version'       => \$o_version
    );
    if (defined($o_help))    { help();      exit $ERRORS{"UNKNOWN"} }
    if (defined($o_version)) { p_version(); exit $ERRORS{"UNKNOWN"} }

    # check snmp information
    if (!defined($o_community) && (!defined($o_login) || !defined($o_passwd))) {
        print "Put snmp login info!\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    if ((defined($o_login) || defined($o_passwd)) && (defined($o_community) || defined($o_version2))) {
        print "Can't mix snmp v1,2c,3 protocols!\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    if (defined($v3protocols)) {
        if (!defined($o_login)) {
            print "Put snmp V3 login info with protocols!\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
        my @v3proto = split(/,/, $v3protocols);
        if ((defined($v3proto[0])) && ($v3proto[0] ne "")) { $o_authproto = $v3proto[0]; }    # Auth protocol
        if (defined($v3proto[1])) { $o_privproto = $v3proto[1]; }                             # Priv  protocol
        if ((defined($v3proto[1])) && (!defined($o_privpass))) {
            print "Put snmp V3 priv login info with priv protocols!\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
    }
    if (defined($o_timeout) && (isnotnum($o_timeout) || ($o_timeout < 2) || ($o_timeout > 60))) {
        print "Timeout must be >1 and <60 !\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    if (!defined($o_timeout)) { $o_timeout = 5; }

    # Check compulsory attributes
    if (!defined($o_descr) || !defined($o_host)) { print_usage(); exit $ERRORS{"UNKNOWN"} }
    @o_warnL = split(/,/, $o_warn);
    @o_critL = split(/,/, $o_crit);
    verb("$o_warn $o_crit $#o_warnL $#o_critL");
    if (isnotnum($o_warnL[0]) || isnotnum($o_critL[0])) {
        print "Numerical values for warning and critical\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    if ((defined($o_warnL[1]) && isnotnum($o_warnL[1])) || (defined($o_critL[1]) && isnotnum($o_critL[1]))) {
        print "Numerical values for warning and critical\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }

    # Check for positive numbers on maximum number of processes
    if ((defined($o_warnL[1]) && ($o_warnL[1] < 0)) || (defined($o_critL[1]) && ($o_critL[1] < 0))) {
        print " Maximum process warn and critical > 0 \n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }

    # Check min_crit < min warn < max warn < crit warn
    if ($o_warnL[0] < $o_critL[0]) {
        print " warn minimum must be >= crit minimum\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    if (defined($o_warnL[1])) {
        if ($o_warnL[1] <= $o_warnL[0]) {
            print "warn minimum must be < warn maximum\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
    } elsif (defined($o_critL[1]) && ($o_critL[1] <= $o_warnL[0])) {
        print "warn minimum must be < crit maximum when no crit warning defined\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    if (defined($o_critL[1]) && defined($o_warnL[1]) && ($o_critL[1] < $o_warnL[1])) {
        print "warn max must be <= crit maximum\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
    #### Memory checks
    if (defined($o_mem)) {
        @o_memL = split(/,/, $o_mem);
        if ($#o_memL != 1) { print "2 values (warning,critical) for memory\n"; print_usage(); exit $ERRORS{"UNKNOWN"} }
        if (isnotnum($o_memL[0]) || isnotnum($o_memL[1])) {
            print "Numeric values for memory!\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
        if ($o_memL[0] > $o_memL[1]) {
            print "Warning must be <= Critical for memory!\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
    }
    #### CPU checks
    if (defined($o_cpu)) {
        @o_cpuL = split(/,/, $o_cpu);
        if ($#o_cpuL != 1) { print "2 values (warning,critical) for cpu\n"; print_usage(); exit $ERRORS{"UNKNOWN"} }
        if (isnotnum($o_cpuL[0]) || isnotnum($o_cpuL[1])) {
            print "Numeric values for cpu!\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
        if ($o_cpuL[0] > $o_cpuL[1]) {
            print "Warning must be <= Critical for cpu!\n";
            print_usage();
            exit $ERRORS{"UNKNOWN"};
        }
    }
    #### octet length checks
    if (defined($o_octetlength) && (isnotnum($o_octetlength) || $o_octetlength > 65535 || $o_octetlength < 484)) {
        print "octet lenght must be < 65535 and > 484\n";
        print_usage();
        exit $ERRORS{"UNKNOWN"};
    }
}

########## MAIN #######

check_options();

# Check timeout if snmp screws up
if (defined($o_timeout)) {
    verb("Alarm in $o_timeout seconds");
    alarm($o_timeout);
}

$SIG{'ALRM'} = sub {
    print "No answer from host $o_host:$o_port\n";
    exit $ERRORS{"UNKNOWN"};
};

# Connect to host
my ($session, $error);
if (defined($o_login) && defined($o_passwd)) {

    # SNMPv3 login
    if (!defined($o_privpass)) {
        verb("SNMPv3 AuthNoPriv login : $o_login, $o_authproto");
        ($session, $error) = Net::SNMP->session(
            -hostname     => $o_host,
            -version      => '3',
            -port         => $o_port,
            -domain       => $o_domain,
            -username     => $o_login,
            -authpassword => $o_passwd,
            -authprotocol => $o_authproto,
            -timeout      => $o_timeout
        );
    } else {
        verb("SNMPv3 AuthPriv login : $o_login, $o_authproto, $o_privproto");
        ($session, $error) = Net::SNMP->session(
            -hostname     => $o_host,
            -version      => '3',
            -username     => $o_login,
            -port         => $o_port,
            -domain       => $o_domain,
            -authpassword => $o_passwd,
            -authprotocol => $o_authproto,
            -privpassword => $o_privpass,
            -privprotocol => $o_privproto,
            -timeout      => $o_timeout
        );
    }
} else {
    if (defined($o_version2)) {

        # SNMPv2 Login
        ($session, $error) = Net::SNMP->session(
            -hostname  => $o_host,
            -version   => 2,
            -community => $o_community,
            -port      => $o_port,
            -domain    => $o_domain,
            -timeout   => $o_timeout
        );
    } else {

        # SNMPV1 login
        ($session, $error) = Net::SNMP->session(
            -hostname  => $o_host,
            -community => $o_community,
            -port      => $o_port,
            -domain    => $o_domain,
            -timeout   => $o_timeout
        );
    }
}

if (!defined($session)) {
    printf("ERROR: %s.\n", $error);
    exit $ERRORS{"UNKNOWN"};
}

if (defined($o_octetlength)) {
    my $oct_resultat = undef;
    my $oct_test     = $session->max_msg_size();
    verb(" actual max octets:: $oct_test");
    $oct_resultat = $session->max_msg_size($o_octetlength);
    if (!defined($oct_resultat)) {
        printf("ERROR: Session settings : %s.\n", $session->error);
        $session->close;
        exit $ERRORS{"UNKNOWN"};
    }
    $oct_test = $session->max_msg_size();
    verb(" new max octets:: $oct_test");
}

# Look for process in name or path name table
my $resultat    = undef;
my %result_cons = ();
my ($getall_run, $getall_cpu, $getall_mem) = (undef, undef, undef);
if (!defined($o_path)) {
    $resultat
        = (version->parse(Net::SNMP->VERSION) < 4)
        ? $session->get_table($run_name_table)
        : $session->get_table(Baseoid => $run_name_table);
} else {
    $resultat
        = (version->parse(Net::SNMP->VERSION) < 4)
        ? $session->get_table($run_path_table)
        : $session->get_table(Baseoid => $run_path_table);
}

if (!defined($resultat)) {
    printf("ERROR: Process name table : %s.\n", $session->error);
    $session->close;
    exit $ERRORS{"UNKNOWN"};
}

my $resultat_param = undef;
if (defined($o_param)) {    # Get parameter table too
    $resultat_param
        = (version->parse(Net::SNMP->VERSION) < 4)
        ? $session->get_table($run_param_table)
        : $session->get_table(Baseoid => $run_param_table);
    if (!defined($resultat_param)) {
        printf("ERROR: Process param table : %s.\n", $session->error);
        $session->close;
        exit $ERRORS{"UNKNOWN"};
    }

}

if (defined($o_get_all)) {
    $getall_run
        = (version->parse(Net::SNMP->VERSION) < 4)
        ? $session->get_table($proc_run_state)
        : $session->get_table(Baseoid => $proc_run_state);
    if (!defined($getall_run)) {
        printf("ERROR: Process run table : %s.\n", $session->error);
        $session->close;
        exit $ERRORS{"UNKNOWN"};
    }
    foreach my $key (keys %$getall_run) {
        $result_cons{$key} = $$getall_run{$key};
    }
    $getall_cpu
        = (version->parse(Net::SNMP->VERSION) < 4)
        ? $session->get_table($proc_cpu_table)
        : $session->get_table(Baseoid => $proc_cpu_table);
    if (!defined($getall_cpu)) {
        printf("ERROR: Process cpu table : %s.\n", $session->error);
        $session->close;
        exit $ERRORS{"UNKNOWN"};
    }
    foreach my $key (keys %$getall_cpu) {
        $result_cons{$key} = $$getall_cpu{$key};
    }
    $getall_mem
        = (version->parse(Net::SNMP->VERSION) < 4)
        ? $session->get_table($proc_mem_table)
        : $session->get_table(Baseoid => $proc_mem_table);
    if (!defined($getall_mem)) {
        printf("ERROR: Process memory table : %s.\n", $session->error);
        $session->close;
        exit $ERRORS{"UNKNOWN"};
    }
    foreach my $key (keys %$getall_mem) {
        $result_cons{$key} = $$getall_mem{$key};
    }
}

my @tindex    = undef;
my @oids      = undef;
my @descr     = undef;
my $num_int   = 0;
my $count_oid = 0;

# Select storage by regexp of exact match
# and put the oid to query in an array

verb("Filter : $o_descr");

foreach my $key (keys %$resultat) {

    # test by regexp or exact match
    # First add param if necessary
    if (defined($o_param)) {
        my $pid = (split /\./, $key)[-1];
        $pid = $run_param_table . "." . $pid;
        $$resultat{$key} .= " " . $$resultat_param{$pid};
    }
    verb("OID : $key, Desc : $$resultat{$key}");
    my $test
        = defined($o_noreg)
        ? $$resultat{$key} eq $o_descr
        : $$resultat{$key} =~ /$o_descr/;
    if ($test) {

        # get the index number of the interface
        my @oid_list = split(/\./, $key);
        $tindex[$num_int] = pop(@oid_list);

        # get the full description
        $descr[$num_int] = $$resultat{$key};

        # put the oid of running and mem (check this maybe ?) in an array.
        $oids[$count_oid++] = $proc_mem_table . "." . $tindex[$num_int];
        $oids[$count_oid++] = $proc_cpu_table . "." . $tindex[$num_int];
        $oids[$count_oid++] = $proc_run_state . "." . $tindex[$num_int];

        #verb("Name : $descr[$num_int], Index : $tindex[$num_int]");
        verb($oids[$count_oid - 1]);
        $num_int++;
    }
}

if ($num_int == 0) {
    print "No process ", (defined($o_noreg)) ? "named " : "matching ", $o_descr, " found : ";
    if ($o_critL[0] >= 0) {
        print "CRITICAL\n";
        exit $ERRORS{"CRITICAL"};
    } elsif ($o_warnL[0] >= 0) {
        print "WARNING\n";
        exit $ERRORS{"WARNING"};
    }
    print "YOU told me it was : OK\n";
    exit $ERRORS{"OK"};
}

my $result     = undef;
my $num_int_ok = 0;

# Splitting snmp request because can't use get_bulk_request with v1 protocol
if (!defined($o_get_all)) {
    if ($count_oid >= 50) {
        my @toid       = undef;
        my $tmp_num    = 0;
        my $tmp_index  = 0;
        my $tmp_count  = $count_oid;
        my $tmp_result = undef;
        verb("More than 50 oid, splitting");
        while ($tmp_count != 0) {
            $tmp_num = ($tmp_count >= 50) ? 50 : $tmp_count;
            for (my $i = 0; $i < $tmp_num; $i++) {
                $toid[$i] = $oids[$i + $tmp_index];

                #verb("$i :  $toid[$i] : $oids[$i+$tmp_index]");
            }
            $tmp_result
                = (version->parse(Net::SNMP->VERSION) < 4)
                ? $session->get_request(@toid)
                : $session->get_request(Varbindlist => \@toid);
            if (!defined($tmp_result)) {
                printf("ERROR: running table : %s.\n", $session->error);
                $session->close;
                exit $ERRORS{"UNKNOWN"};
            }
            foreach (@toid) { $result_cons{$_} = $$tmp_result{$_}; }
            $tmp_count -= $tmp_num;
            $tmp_index += $tmp_num;
        }

    } else {
        $result
            = (version->parse(Net::SNMP->VERSION) < 4)
            ? $session->get_request(@oids)
            : $session->get_request(Varbindlist => \@oids);
        if (!defined($result)) {
            printf("ERROR: running table : %s.\n", $session->error);
            $session->close;
            exit $ERRORS{"UNKNOWN"};
        }
        foreach (@oids) { $result_cons{$_} = $$result{$_}; }
    }
}

$session->close;

#Check if process are in running or runnable state
for (my $i = 0; $i < $num_int; $i++) {
    my $state  = $result_cons{ $proc_run_state . "." . $tindex[$i] };
    my $tmpmem = $result_cons{ $proc_mem_table . "." . $tindex[$i] };
    my $tmpcpu = $result_cons{ $proc_cpu_table . "." . $tindex[$i] };
    verb("Process $tindex[$i] in state $state using $tmpmem, and $tmpcpu CPU");
    if (!isnotnum($state)) {    # check argument is numeric (can be NoSuchInstance)
        $num_int_ok++ if (($state == 1) || ($state == 2));
    }
}

my $final_status = 0;
my $perf_output;
my ($res_memory, $res_cpu) = (0, 0);
my $memory_print = "";
my $cpu_print    = "";
###### Checks memory usage

if (defined($o_mem)) {
    if (defined($o_mem_avg)) {
        for (my $i = 0; $i < $num_int; $i++) { $res_memory += $result_cons{ $proc_mem_table . "." . $tindex[$i] }; }
        $res_memory /= ($num_int_ok * 1024);
        verb("Memory average : $res_memory");
    } else {
        for (my $i = 0; $i < $num_int; $i++) {
            $res_memory
                = ($result_cons{ $proc_mem_table . "." . $tindex[$i] } > $res_memory)
                ? $result_cons{ $proc_mem_table . "." . $tindex[$i] }
                : $res_memory;
        }
        $res_memory /= 1024;
        verb("Memory max : $res_memory");
    }
    if ($res_memory > $o_memL[1]) {
        $final_status = 2;
        $memory_print = ", Mem : " . sprintf("%.1f", $res_memory) . "Mb > " . $o_memL[1] . " CRITICAL";
    } elsif ($res_memory > $o_memL[0]) {
        $final_status = 1;
        $memory_print = ", Mem : " . sprintf("%.1f", $res_memory) . "Mb > " . $o_memL[0] . " WARNING";
    } else {
        $memory_print = ", Mem : " . sprintf("%.1f", $res_memory) . "Mb OK";
    }
    if (defined($o_perf)) {
        $perf_output = "'memory_usage'=" . sprintf("%.1f", $res_memory) . "MB;" . $o_memL[0] . ";" . $o_memL[1];
    }
}

######## Checks CPU usage

if (defined($o_cpu)) {
    my $timenow = time;
    my $temp_file_name;
    my ($return, @file_values) = (undef, undef);
    my $n_rows        = 0;
    my $n_items_check = 2;
    my $trigger       = $timenow - ($o_delta - ($o_delta / 10));
    my $trigger_low   = $timenow - 3 * $o_delta;
    my ($old_value, $old_time) = undef;
    my $found_value = undef;

    #### Get the current values
    for (my $i = 0; $i < $num_int; $i++) { $res_cpu += $result_cons{ $proc_cpu_table . "." . $tindex[$i] }; }

    verb("Time: $timenow , cpu (centiseconds) : $res_cpu");

    #### Read file
    $temp_file_name = $o_descr;
    $temp_file_name =~ s/ /_/g;
    $temp_file_name =~ s/\//_/g;
    $temp_file_name =~ s/-//g;
    $temp_file_name =~ s/=//g;
    $temp_file_name = substr($temp_file_name, 0, 40);
    $temp_file_name = $o_base_dir . $o_host . "." . $temp_file_name;

    # First, read entire file
    my @ret_array = read_file($temp_file_name, $n_items_check);
    $return = shift(@ret_array);
    $n_rows = shift(@ret_array);
    if ($n_rows != 0) { @file_values = @ret_array }
    verb("File read returns : $return with $n_rows rows");

    #make the checks if the file is OK
    if ($return == 0) {
        my $j = $n_rows - 1;
        do {
            if ($file_values[$j][0] < $trigger) {
                if ($file_values[$j][0] > $trigger_low) {

                    # found value = centiseconds / seconds = %cpu
                    $found_value = ($res_cpu - $file_values[$j][1]) / ($timenow - $file_values[$j][0]);
                    if ($found_value < 0) {    # in case of program restart
                        $j           = 0;
                        $found_value = undef;    # don't look for more values
                        $n_rows      = 0;        # reset file
                    }
                }
            }
            $j--;
        } while (($j >= 0) && (!defined($found_value)));
    }
    ###### Write file
    $file_values[$n_rows][0] = $timenow;
    $file_values[$n_rows][1] = $res_cpu;
    $n_rows++;
    $return = write_file($temp_file_name, $n_rows, $n_items_check, @file_values);
    if ($return != 0) { $cpu_print .= "! ERROR writing file $temp_file_name !"; $final_status = 3; }
    ##### Check values (if something to check...)
    if (defined($found_value)) {
        if ($found_value > $o_cpuL[1]) {
            $final_status = 2;
            $cpu_print .= ", Cpu : " . sprintf("%.0f", $found_value) . "% > " . $o_cpuL[1] . " CRITICAL";
        } elsif ($found_value > $o_cpuL[0]) {
            $final_status = ($final_status == 2) ? 2 : 1;
            $cpu_print .= ", Cpu : " . sprintf("%.0f", $found_value) . "% > " . $o_cpuL[0] . " WARNING";
        } else {
            $cpu_print .= ", Cpu : " . sprintf("%.0f", $found_value) . "% OK";
        }
        if (defined($o_perf)) {
            if (!defined($perf_output)) { $perf_output = ""; }
            else                        { $perf_output .= " "; }
            $perf_output .= "'cpu_usage'=" . sprintf("%.0f", $found_value) . "%;" . $o_cpuL[0] . ";" . $o_cpuL[1];
        }
    } else {
        if ($final_status == 0) { $final_status = 3 }
        $cpu_print .= ", No data for CPU (" . $n_rows . " line(s)):UNKNOWN";
    }
}

print $num_int_ok, " process ", (defined($o_noreg)) ? "named " : "matching ", $o_descr, " ";

#### Check for min and max number of process
if ($num_int_ok <= $o_critL[0]) {
    print "(<= ", $o_critL[0], " : CRITICAL)";
    $final_status = 2;
} elsif ($num_int_ok <= $o_warnL[0]) {
    print "(<= ", $o_warnL[0], " : WARNING)";
    $final_status = ($final_status == 2) ? 2 : 1;
} else {
    print "(> ", $o_warnL[0], ")";
}
if (defined($o_critL[1]) && ($num_int_ok > $o_critL[1])) {
    print " (> ", $o_critL[1], " : CRITICAL)";
    $final_status = 2;
} elsif (defined($o_warnL[1]) && ($num_int_ok > $o_warnL[1])) {
    print " (> ", $o_warnL[1], " : WARNING)";
    $final_status = ($final_status == 2) ? 2 : 1;
} elsif (defined($o_warnL[1])) {
    print " (<= ", $o_warnL[1], "):OK";
}

print $memory_print, $cpu_print;

if (defined($o_perf)) {
    if (!defined($perf_output)) { $perf_output = ""; }
    else                        { $perf_output .= " "; }
    $perf_output .= "'num_process'=" . $num_int_ok . ";" . $o_warnL[0] . ";" . $o_critL[0];
    print " | ", $perf_output;
}
print "\n";

if ($final_status == 2) { exit $ERRORS{"CRITICAL"}; }
if ($final_status == 1) { exit $ERRORS{"WARNING"}; }
if ($final_status == 3) { exit $ERRORS{"UNKNOWN"}; }
exit $ERRORS{"OK"};

