#! /usr/bin/perl -w

# ------------------------------------------------------------------------------
# check_snmp.pl - check preconfigured SNMP Query's
# Copyright (C) 2004 NETWAYS GmbH, Marius Hein <mhein (at) netways.de>
#
# 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.
# ------------------------------------------------------------------------------

# Main:
use strict;
use Net::SNMP;
use File::Basename;
use Getopt::Long;
use vars qw (
    $version
    $snmp_sess
    $snmp_err
    $snmp_res
    $snmp_val
    $progname
    $opt_help
    $opt_host
    $opt_port
    $opt_community
    $opt_query
    $opt_critical
    $opt_warning
    %states
    $state
    %queries
    $tmp
    $val
    $out
);

# pre-declare sub's
sub print_help;

# script name && version
$progname = basename($0);
$version = "1.0";

# nagios exit states
%states = (
    UNKNOWN     =>  -1,
    OK          =>  0,
    WARNING     =>  1,
    CRITICAL    =>  2,
);

# SNMP Query Cofiguration:
# KEYS:
#   - OID
#       The OID to query in dotted notation.
#   - CHECK
#       The input pattern (checking numbers, string) warning and critical (pcre).
#   - SPLIT
#       The splitregex to seperate the right value from unmeant stuff.
#
#   ----------------------------------------------------------------------------
#   Possible vars: %VAL(snmp return value after splitting), %CRITICAL(the critical value), %WARNING(the warning value)
#   Usage   [...] { WARNING => '%VAL ne %CRITICAL && %CRITICAL ne %WARNING' }, [...]
#
#   - WARNING
#       The warning if pattern.
#   - CRITICAL
#       The critical if pattern.
#

%queries = (
    SNMP_UPTIME     =>  {
                            OID         =>  '1.3.6.1.2.1.1.3.0',
                        },
    
    SNMP_HOSTNAME   =>  {
                            OID         =>  '1.3.6.1.2.1.1.5.0',
                            CHECK       =>  '[a-z-_]+',
                            WARNING     =>  '%VAL ne %WARNING',
                            CRITICAL    =>  '%VAL ne %CRITICAL',
                        },
    
    SNMP_HOSTUP     =>  {
                            OID         =>  '1.3.6.1.2.1.25.1.1.0',
                            CHECK       =>  '\d+',
                            SPLIT       =>  ' days.*?$',
                            WARNING     =>  '%VAL > %WARNING',
                            CRITICAL    =>  '%VAL > %CRITICAL',
                        },
    
    # Testing...
    SNMP_ERROR      =>  {
                            OID         =>  'interfaces.ifTable.ifDescription.[...]',
                        },
);

# Setting up default values
$opt_host = 'localhost';
$opt_port = 161;
$opt_community = 'public';

# Getting the ARGV's
Getopt::Long::Configure('bundling');
GetOptions(
    # HELP ARG
    "help"          =>  \$opt_help,         "h"     =>  \$opt_help,
    # Host ARG
    "host=s"        =>  \$opt_host,         "H=s"   =>  \$opt_host,
    # Community ARG
    "community=s"   =>  \$opt_community,    "C=s"   =>  \$opt_community,
    # Port ARG
    "port=s"        =>  \$opt_port,         "p=s"   =>  \$opt_port,
    # Query ARG
    "query=s"       =>  \$opt_query,        "q=s"   =>  \$opt_query,
    # Critical ARG
    "critical=s"    =>  \$opt_critical,     "c=s"   =>  \$opt_critical,
    # Warning ARG
    "warning=s"     =>  \$opt_warning,      "w=s"   =>  \$opt_warning,
) || die "Too few arguments. Try '$progname --help' for more information!\n";

# Print out the help
if ($opt_help) {
    print_help;
    exit ($states{UNKNOWN});
}

# checking necessary options
unless (defined($opt_query) && defined($opt_port) && defined($opt_host) && defined($opt_community) && defined($opt_warning) && defined($opt_critical)) {
    print "Too few arguments. Try '$progname --help' for more information!\n";
}
# if all there getting started...
else {
    # checking if the query key exists...
    unless (exists($queries{$opt_query})) {
        print "The query '$opt_query' doesn't exist!\n";
        exit ($states{UNKNOWN});
    } else {
        
        # draw comparisons between c/w values and checkpattern
        unless($opt_critical =~ /$queries{$opt_query}->{CHECK}/i && $opt_warning =~ /$queries{$opt_query}->{CHECK}/i) {
            print "ERROR: critical and/or warning values not match the input pattern!\n";
            exit ($states{UNKNOWN});
        }
        
        # open a Net::SNMP session
        ($snmp_sess, $snmp_err) = Net::SNMP->session(
            -hostname   =>  $opt_host,
            -community  =>  $opt_community,
            -port       =>  $opt_port,
        );
        
        # if an error occured
        if (!defined($snmp_sess)) {
            printf("SNMP Error: %s.\n", $snmp_err);
            $snmp_sess->close();
            exit ($states{UNKNOWN});
        } else {
            # Query the SNMP Agent
            $snmp_res = $snmp_sess->get_request($queries{$opt_query}->{OID});
            # Checking if something went wrong...
            if (!defined($snmp_res)) {
                printf("SNMP Error: %s.\n", $snmp_sess->error());
                $snmp_sess->close();
                exit ($states{UNKNOWN});
            } else {
                # getting the raw value from snmpquery
                $snmp_val = $snmp_res->{$queries{$opt_query}->{OID}};
                
                # replacing HASH Syntax with real variables
                $queries{$opt_query}->{WARNING} =~ s/(\%VAL)/\$snmp_val/i;
                $queries{$opt_query}->{WARNING} =~ s/(\%CRITICAL)/\$opt_critical/i;
                $queries{$opt_query}->{WARNING} =~ s/(\%WARNING)/\$opt_warning/i;
                $queries{$opt_query}->{CRITICAL} =~ s/(\%VAL)/\$snmp_val/i;
                $queries{$opt_query}->{CRITICAL} =~ s/(\%CRITICAL)/\$opt_critical/i;
                $queries{$opt_query}->{CRITICAL} =~ s/(\%WARNING)/\$opt_warning/i;
                
                # If some splitkey is given, use it!
                if (defined($queries{$opt_query}->{SPLIT})) {
                    ($val,$tmp) = split(/$queries{$opt_query}->{SPLIT}/i, $snmp_val);
                    $snmp_val = $val;
                }
                
                # preparing plugin output
                $out = "$progname: ";
                
                # eval CRITICAL syntax
                if (eval($queries{$opt_query}->{CRITICAL})) {
                    $state = $states{CRITICAL};
                    $out .= "CRITICAL ";
                }
                # eval WARNING syntax
                elsif(eval($queries{$opt_query}->{WARNING})) {
                    $state = $states{WARNING};
                    $out .= "WARNING ";
                }
                # nothing passed above, then... it's OK!
                else {
                    $state = $states{OK};
                    $out .= "OK ";
                }
                
                # Closing SNMP Session
                $snmp_sess->close();
                
                # make the ouput and run out with the given state
                if (defined($state) && defined($out)) {
                    $out .= "(Query: '$opt_query', Value returned: '$snmp_val')\n";
                    print $out;
                    exit ($state);
                }
            }
        }
        
    }
}

# generic exit.
exit ($states{UNKNOWN});

# ------------------------------------------------------------------------------------------------------

# +------------------------+
# |       The Subs         |
# +------------------------+

# print's the helpscreen
sub print_help {
    print "\n";
    print "$progname Version $version, Copyright (C) 2004 NETWAYS GmbH\n\n";
    print " [long]          [short]     [description]\n";
    print "------------------------------------------\n\n";
    print " --help          -h          Prints this screen.\n\n";
    print " [--host]        [-H]        The Host.\n";
    print " [--port]        [-p]        The Port.\n";
    print " [--community]   [-C]        The Community.\n\n";
    print " --query         -q          The OID.\n";
    print " --warning       -w          Warning Value.\n";
    print " --critical      -c          Critical Value.\n\n";
    print "Usage: ./$progname -H 192.168.0.25 -C MY_COMMUNITY -q SNMP_HOSTUP -w 39 -c 49\n";
    print "\n";
}

# generic script return true
1;