#!/usr/bin/perl

=head1 NAME

check_snmp_acs_pm - Check various status of iPDUs connected to Avocent ACS console server

=head1 AUTHOR

Steve Huff <shuff@hmdc.harvard.edu>

=head1 SYNOPSIS

Query an ACS console server via SNMP to derive firmware revision, current draw, and temperature.

=head1 REQUIRES

Perl5.004, strict, warnings, Data::Dumper, Nagios::Plugin, SNMP, Sort::Versions

=head1 EXPORTS

Nothing

=head1 DESCRIPTION

C<check_snmp_acs_pm> derives current, temperature, and firmware version from an iPDU attached to an ACS.  WARNING and CRITICAL thresholds may be set for current and temperature, and a minimum firmware version may be provided (firmware version mismatch returns WARNING).  Errors return UNKNOWN.  

Performance data (current and temperature per iPDU) is provided.

=cut

# these need to be outside the BEGIN
use strict;
use warnings;

# see this page for an explanation regarding the levels of warnings:
# http://search.cpan.org/~rgarcia/perl-5.6.2/pod/perllexwarn.pod
no warnings qw( redefine prototype );

BEGIN {

    # use Opsview libs
    use lib '/usr/local/nagios/perl/lib';
    use lib '/usr/local/nagios/lib';

    use Data::Dumper;
    use Nagios::Plugin;

    use Sort::Versions;

    use SNMP;

    # reregister interrupt
    $SIG{INT}      = \&DoInterrupt;

} ## end BEGIN

########################################################################
# IMPORTANT VARIABLES
#
# default values
my( $timeout, $verbose );

########################################################################
# Temperature modifications to add support for Fahrenheit/Celsius
# 

my %tempUnits		= (F => 'Fahrenheit', C => 'Celsius');

# Define in which unit you would like to have the output in. 
# Valid values are C or F and there are NO sanity checks on this!

my $tempUnit		= 'C';

my ($TEMPWARNING, $TEMPCRITICAL);

if($tempUnit =~ /F/) {
	$TEMPWARNING         = 110; # 43 C
	$TEMPCRITICAL        = 120; # 49 C
} else {
	$TEMPWARNING         = 43; # 110 F
	$TEMPCRITICAL        = 49; # 120 F
}

my $CURWARNING          = 10;
my $CURCRITICAL         = 12;
my $FIRMWARE            = '1.9.2';  # current as of 2009-05-13

my $SNMP_VERSION        = '2c';
my $SNMP_COMMUNITY      = 'public';

my $NAGIOS_MIBS         = '/usr/share/nagios/snmp';

#
########################################################################

#
# MAIN
#

# instantiate the Nagios::Plugin object
my( $usagemsg ) = <<USAGE;
Usage: %s -H <hostname> [-w <warning temp>] [-c <critical temp>] [--curwarning <warning current>] [--curcritical <critical current>] [-f <firmware version>] [-p <PDU>='<warning temp> <critical temp> <warning current> <critical current>'] [-t timeout] [-v|d] [-h]
USAGE
my( $blurb ) = <<BLURB;
check_snmp_acs_pm derives current, temperature, and firmware version from an iPDU attached to an ACS.  WARNING and CRITICAL thresholds may be set for current and temperature, and a minimum firmware version may be provided (firmware version mismatch returns WARNING).  Errors return UNKNOWN.  

Performance data (current and temperature per iPDU) are provided.
BLURB
my( $license ) = <<LICENSE;
This Nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. 
It may be used, redistributed and/or modified under the terms of the GNU 
General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).

This plugin was written at The Harvard-MIT Data Center
(http://www.hmdc.harvard.edu) by Steve Huff (<shuff\@hmdc.harvard.edu>).
LICENSE
my( $plugin ) = Nagios::Plugin->new( 
                                    shortname   => 'check_snmp_acs_pm',
                                    usage       => $usagemsg,
                                    version     => '0.2',
                                    blurb       => $blurb,
                                    license     => $license,
                                );

=head2 Plugin Metadata

Metadata is documented in L<Nagios::Plugin> and L<Nagios::Plugin::Getopt>.

Other available options of interest include C<url>, C<extra>, and C<timeout>.

=cut

# add the additional options
# example of using multiline usage specification
$plugin->add_arg( 
                 spec       => 'hostname|H=s', 
                 help       => [ 
                                "Hostname to query",
                                "IP address to query",
                               ],
                 label      => [ 'HOSTNAME', 'IP' ],
                 required   => 1,
             ); 
            
$plugin->add_arg( 
                 spec       => 'protocol|P=s', 
                 help       => "SNMP protocol to use (default \"$SNMP_VERSION\")",
                 default    => $SNMP_VERSION,
                 label      => [ '[1|2c]' ],
                 required   => 0,
             ); 
            
$plugin->add_arg( 
                 spec       => 'community|C=s', 
                 help       => "SNMP community string to use (default \"$SNMP_COMMUNITY\")",
                 default    => $SNMP_COMMUNITY,
                 required   => 0,
             ); 
            
$plugin->add_arg( 
                 spec       => 'tempwarning|w=i', 
                 help       => "Warning temperature threshold in degrees $tempUnits{$tempUnit} (default $TEMPWARNING)",
                 default    => $TEMPWARNING,
                 label      => [ "DEGREES $tempUnits{$tempUnit}" ],
                 required   => 0,
             ); 
            
$plugin->add_arg( 
                 spec       => 'tempcritical|c=i', 
                 help       => "Critical temperature threshold in degrees $tempUnits{$tempUnit} (default $TEMPCRITICAL)",
                 default    => $TEMPCRITICAL,
                 label      => [ "DEGREES $tempUnits{$tempUnit}" ],
             ); 

$plugin->add_arg( 
                 spec       => 'curwarning=s', 
                 help       => "Warning current threshold in Amperes (default $CURWARNING)",
                 default    => $CURWARNING,
                 label      => [ 'AMPERES' ],
                 required   => 0,
             ); 
            
$plugin->add_arg( 
                 spec       => 'curcritical=s', 
                 help       => "Critical current threshold in Amperes (default $CURCRITICAL)",
                 default    => $CURCRITICAL,
                 label      => [ 'AMPERES' ],
                 required   => 0,
             ); 
            
$plugin->add_arg( 
                 spec       => 'firmware|f=s', 
                 help       => "Firmware version string to match or exceed (default $FIRMWARE)",
                 default    => $FIRMWARE,
                 required   => 0,
             ); 

$plugin->add_arg( 
                 spec       => 'pdu|p=s%', 
                 help       => "Override thresholds for a particular PDU (may be specified multiple times).\nEnter 'PDU=0' to exclude a given PDU from testing.",
                 label      => [ '[PDU=\'TEMPWARNING <TEMPCRITICAL> <CURWARNING> <CURCRITICAL> <FIRMWARE>\']' ],
                 default    => undef,
                 required   => 0,
             ); 

=head2 Plugin Options

Refer to L<Nagios::Plugin::Getopt> for option specifications - they differ slightly from pure L<Getopt::Long>-style option specifications.

A number of standard options (C<--help>, C<--version>, C<--usage>, C<--timeout>, and C<--verbose>) are implemented by default, thanks to the arguments passed to C<Nagios::Plugin->new()>, and do not need to be specified.  Moreover, if you attempt to override them, you will simply create two such options, which produces confusing C<--help> output and unexpected behavior.
            
=cut

# parse options
$plugin->getopts;

my( $opts ) = $plugin->opts;

# declare variables
my( $server, $protocol, $community, $tempwarning, $tempcritical, $curwarning, $curcritical, $firmware, $pdus );

# every variable must have a value
$server = $opts->get( 'hostname' );
$protocol = $opts->get( 'protocol' );
$community = $opts->get( 'community' );
$timeout = $opts->get( 'timeout' );  # See above: option provided by default
$verbose = $opts->get( 'verbose' );  # See above: option provided by default
$tempwarning = $opts->get( 'tempwarning' );
$tempcritical = $opts->get( 'tempcritical' );
$curwarning = $opts->get( 'curwarning' );
$curcritical = $opts->get( 'curcritical' );
$firmware = $opts->get( 'firmware' );
$pdus = $opts->get( 'pdu' );

# sanity check
defined( $server )
    or $plugin->nagios_die( "No value provided for --hostname!" );

$protocol =~ /^(1|2c)$/
    or $plugin->nagios_die( "Protocol specification must be \"1\" or \"2c\"; you provided \"$protocol\"" );

( checkWarnCrit( $plugin, ( 'warn' => $tempwarning, crit => $tempcritical, suffix => " $tempUnit", 'die' => 1 ) ) &&
    checkWarnCrit( $plugin, ( 'warn' => $curwarning, crit => $curcritical, suffix => 'A', 'die' => 1 ) ) )
    or $plugin->nagios_die( "Unable to validate provided thresholds." );

foreach my $threshold ( $tempwarning, $tempcritical, $curwarning, $curcritical ) {

    unless ( $threshold =~ /^\d+(?:\.\d)?$/ ) {

        debug( "Valid threshold values are integers greater than 0 or floating point numbers greater than 0.0 and with no more than one digit to the right of the decimal point." );
        $plugin->nagios_die( "'$threshold' is not a valid threshold value." );
    }
}

=head2 Testing and Exit Status

The basic methodology of a Nagios plugin is as follows:

=over

=item 1.

Run some sort of test.

=item 2.

Validate the result against provided (or default) thresholds.

=item 3.

Exit with the appropriate error code, optionally outputting text.

=back

Running the test is up to you.  

The function to use for validating the result is C<Nagios::Plugin::check_threshold()> (see L<Nagios::Plugin::Threshold> and L<Nagios::Plugin::Range> for details and specifications), which returns a value appropriate for use as an exit code.

The functions to use for exiting are C<Nagios::Plugin::nagios_exit()> and C<Nagios::Plugin::nagios_die()>.  C<nagios_exit()> is the general-purpose exit function, while C<nagios_die()> should be used for any unexpected exits and indicates that something went wrong with the plugin's operation.

=head3 Return String

The plugin return string performs two functions:

=over

=item 1.

It communicates in human-readable format more details about what exactly has gone wrong (e.g. "Disk usage on '/var' is at 92% (threshold 90%)").

=item 2.

It (optionally) communicates in machine-readable format performance data which can be aggregated by tools such as Nagiosgraph (e.g. '| time=0.042745s;5.000000;10.000000;0.000000 size=2162B;;;0').  See L<Nagios::Plugin::Performance> for details and specifications; the function to use is C<Nagios::plugin::add_perfdata()>.

=back

The plugin string will automatically be populated with the plugin name and the exit status (e.g. "check_snmp_disk OK - "); everything after the "- " is what you provide with the message arguments to C<nagios_exit()> or C<nagios_die()>.  C<add_perfdata()> takes care of appending the "|" and properly formatting performance data.

=cut

# begin tracking status and message
my( $status, $message ) = ( OK, '' );

# do some test, with a timeout
alarm $timeout; # the heavy lifting is done in Nagios::Plugin::Getopt
my( $result ) = doTest( $plugin, $server, $protocol, $community, $verbose );
alarm 0;

# validate the result
my( %ok, %warning, %critical, %unknown );

my( @thresholds ) = ( $tempwarning, $tempcritical, $curwarning, $curcritical, $firmware );

foreach my $segment ( sort( keys( %{$result} ) ) ) {

    # process override if present
    debug( "Checking for override for '$segment'..." );

    if ( exists( $pdus->{$segment} ) && defined( $pdus->{$segment} ) ) {

        debug( "Processing override for '$segment'..." );
        debug( "Override: " . $pdus->{$segment}, 2 );

        # if we got a 0, continue to the next $segment
        next if ( $pdus->{$segment} eq 0 );

        # validate the provided override
        my( $tw, $tc, $cw, $cc, $fw ) = split( ' ', $pdus->{$segment}, 5 );

        $tempwarning = ( ( defined( $tw ) && ( $tw =~ /^\d+(?:\.\d)?$/ ) ) ? $tw : $tempwarning ); 
        $tempcritical = ( ( defined( $tc ) && ( $tc =~ /^\d+(?:\.\d)?$/ ) ) ? $tc : $tempcritical ); 
        $curwarning = ( ( defined( $cw ) && ( $cw =~ /^\d+(?:\.\d)?$/ ) ) ? $cw : $curwarning ); 
        $curcritical = ( ( defined( $cc ) && ( $cc =~ /^\d+(?:\.\d)?$/ ) ) ? $cc : $curcritical ); 
        $firmware = ( defined( $fw ) ? $fw : $firmware ); 

        # if anything's not kosher, revert to saved
        unless ( checkWarnCrit( $plugin, ( 'warn' => $tempwarning, crit => $tempcritical, suffix => " $tempUnit" ) )
                 && checkWarnCrit( $plugin, ( 'warn' => $curwarning, crit => $curcritical, suffix => 'A' ) ) ) {
            
            debug( "Problems with override values found, reverting to defaults." );
            ( $tempwarning, $tempcritical, $curwarning, $curcritical, $firmware ) = @thresholds;
        }
    }
    else {

        # no override, use the defaults
        ( $tempwarning, $tempcritical, $curwarning, $curcritical, $firmware ) = @thresholds;
    }

    # store the thresholds in $result
    $result->{$segment}->{thresholds} = {
                                         tempwarning    => $tempwarning,
                                         tempcritical   => $tempcritical,
                                         curwarning     => $curwarning,
                                         curcritical    => $curcritical,
                                         firmware       => $firmware,
                                        };

    # first check for segments CURRENTLY over temp
    my( $temp ) = $result->{$segment}->{temperature}->{cur};
    my( $tempstatus ) = $plugin->check_threshold( 
                                                 check      => $temp,
                                                 warning    => $tempwarning,
                                                 critical   => $tempcritical
                                                );

    if ( $tempstatus == CRITICAL ) {

        push( @{$critical{temperature}}, $segment );
        $critical{tempcount}++;
    }
    elsif ( $tempstatus == WARNING ) {

        push( @{$warning{temperature}}, $segment );
        $warning{tempcount}++;
    }
    elsif ( $tempstatus == OK ) {

        push( @{$ok{temperature}}, $segment );
        $ok{tempcount}++;
    }
    else {

        # huh?
        push( @{$unknown{temperature}}, $segment );
        $unknown{tempcount}++;
    }

    # them check for segments CURRENTLY over current
    my( $cur ) = $result->{$segment}->{current}->{curtotal};
    my( $curstatus ) = $plugin->check_threshold( 
                                                check      => $cur,
                                                warning    => $curwarning,
                                                critical   => $curcritical
                                               );

    if ( $curstatus == CRITICAL ) {

        push( @{$critical{current}}, $segment );
        $critical{curcount}++;
    }
    elsif ( $curstatus == WARNING ) {

        push( @{$warning{current}}, $segment );
        $warning{curcount}++;
    }
    elsif ( $curstatus == OK ) {

        push( @{$ok{current}}, $segment );
        $ok{curcount}++;
    }
    else {

        # huh?
        push( @{$unknown{current}}, $segment );
        $unknown{curcount}++;
    }

    # then check for segments with out-of-date firmware
    my( $firm ) = $result->{$segment}->{firmware};
    my( $firmstatus ) = ( ( versioncmp( $firm, $firmware ) == -1 ) ? WARNING : OK );
    if ( $firmstatus == WARNING ) {

        push( @{$warning{firmware}}, $segment );
        $warning{firmcount}++;
    }
    elsif ( $firmstatus == OK ) {

        push( @{$ok{firmware}}, $segment );
        $ok{firmcount}++;
    }

    # add performance data
    $plugin->add_perfdata(
                          label     => $segment . '_cur',
                          value     => $result->{$segment}->{current}->{curtotal},
                          uom       => undef,
                          warning   => $curwarning,
                          critical  => $curcritical,
                          min       => 0,
                          max       => $result->{$segment}->{current}->{maxtotal},
                         );

    $plugin->add_perfdata(
                          label     => $segment . '_temp',
                          value     => $result->{$segment}->{temperature}->{cur},
                          uom       => undef,
                          warning   => $tempwarning,
                          critical  => $tempcritical,
                          min       => 0,
                          max       => $result->{$segment}->{temperature}->{max},
                         );
}

# ok, what's our status?
if ( exists( $critical{tempcount} ) && ( $critical{tempcount} > 0 ) ) {

    # at least one temperature CRITICAL
    $status = CRITICAL;
    $message .= "Temperature threshold exceeded:";
    foreach my $segment ( sort( @{$critical{temperature}} ) ) {

        $message .= " $segment (" . $result->{$segment}->{temperature}->{cur} . " $tempUnit > " . $result->{$segment}->{thresholds}->{tempcritical} . " $tempUnit)";
    }
}
elsif ( exists( $critical{curcount} ) && ( $critical{curcount} > 0 ) ) {

    # at least one current CRITICAL
    $status = CRITICAL;
    $message .= "Current threshold exceeded:";
    foreach my $segment ( sort( @{$critical{current}} ) ) {

        $message .= " $segment (" . $result->{$segment}->{current}->{curtotal} . " A > " . $result->{$segment}->{thresholds}->{curcritical} . " A)";
    }
}
elsif ( exists( $warning{tempcount} ) && ( $warning{tempcount} > 0 ) ) {

    # at least one temperature WARNING
    $status = WARNING;
    $message .= "Temperature threshold exceeded:";
    foreach my $segment ( sort( @{$warning{temperature}} ) ) {

        $message .= " $segment (" . $result->{$segment}->{temperature}->{cur} . " $tempUnit > " . $result->{$segment}->{thresholds}->{tempwarning} . " $tempUnit)";
    }
}
elsif ( exists( $warning{curcount} ) && ( $warning{curcount} > 0 ) ) {

    # at least one current WARNING
    $status = WARNING;
    $message .= "Current threshold exceeded:";
    foreach my $segment ( sort( @{$warning{current}} ) ) {

        $message .= " $segment (" . $result->{$segment}->{current}->{curtotal} . " A > " . $result->{$segment}->{thresholds}->{curwarning} . " A)";
    }
}
elsif ( exists( $warning{firmcount} ) && ( $warning{firmcount} > 0 ) ) {

    # at least one firmware WARNING
    $status = WARNING;
    $message .= "Firmware ($firmware) out of date:";
    foreach my $segment ( sort( @{$warning{firmware}} ) ) {

        $message .= " $segment (" . $result->{$segment}->{firmware} . " < " . $result->{$segment}->{thresholds}->{firmware} . ")";
    }
}
elsif ( exists( $unknown{tempcount} ) && ( $unknown{tempcount} > 0 ) ) {

    # at least one temperature UNKNOWN
    $status = UNKNOWN;
    $message .= "Unable to determine temperature:";
    foreach my $segment ( sort( @{$unknown{temperature}} ) ) {

        $message .= " $segment";
    }
}
elsif ( exists( $unknown{curcount} ) && ( $unknown{curcount} > 0 ) ) {

    # at least one current UNKNOWN
    $status = UNKNOWN;
    $message .= "Unable to determine current:";
    foreach my $segment ( sort( @{$unknown{current}} ) ) {

        $message .= " $segment";
    }
}
else {

    # we made it!
    $message .= "All returned values within thresholds";
}

# exit with appropriate return values
$plugin->nagios_exit( $status, $message );

################################################################################
#                                                                              #
# doTest - do some kind of test
#                                                                              #
################################################################################
sub doTest( $$$$$ ) {

    my( $plugin, $server, $protocol, $community, $verbose ) = @_;

    # set up the SNMP session
    $SNMP::debugging = $verbose;
    SNMP::addMibDirs( $NAGIOS_MIBS );
    SNMP::loadModules( 'ALL' );

    my( $session ) = new SNMP::Session(
                                       DestHost     => $server,
                                       Version      => $protocol,
                                       Community    => $community,
                                       UseEnums     => 1,
                                      );

    no warnings 'once';     # stop yelling about $SNMP::Session::ErrorStr
    defined( $session )
        or $plugin->nagios_die( "SNMP error: " . $SNMP::Session::ErrorStr . "\n" );

    # collect information about attached PMs
    my( %PM );

    # poll for cyPMTable
    my( $cyPMTable );

    my( $VBcyNumberOfPM ) = new SNMP::Varbind( ['cyNumberOfPM'] );
    my( $cyNumberOfPM ) = $session->get( $VBcyNumberOfPM );

    ( $session->{ErrorNum} ) && $plugin->nagios_die( "SNMP error: " . $session->{ErrorStr} . "\n" );

    my( 
        $cyPMSerialPortNumber,
        $cyPMNumberOutlets,
        $cyPMNumberUnits,
        $cyPMCurrent,
        $cyPMVersion,
        $cyPMTemperature,
      ) = $session->bulkwalk( 0, $cyNumberOfPM + 1, [
                                                     ['cyPMSerialPortNumber'],
                                                     ['cyPMNumberOutlets'],
                                                     ['cyPMNumberUnits'],
                                                     ['cyPMCurrent'],
                                                     ['cyPMVersion'],
                                                     ['cyPMTemperature'],
                                                    ] );
    my( $i ) = 0;
    while ( $i < $cyNumberOfPM ) {

        $cyPMTable->{$cyPMSerialPortNumber->[$i]->val} = {
                                                          cyPMNumberOutlets => $cyPMNumberOutlets->[$i]->val,
                                                          cyPMNumberUnits => $cyPMNumberUnits->[$i]->val,
                                                          cyPMCurrent => $cyPMCurrent->[$i]->val,
                                                          cyPMVersion => $cyPMVersion->[$i]->val,
                                                          cyPMTemperature => $cyPMTemperature->[$i]->val,
                                                         };
        $i++;
    }

    debug( Data::Dumper->Dump( [$cyPMTable], [qw(*cyPMTable)] ) );

    # extract the data we want
    foreach my $port ( keys( %{$cyPMTable} ) ) {

        # get the PM IDs and firmware versions
        foreach my $line ( split( "\n", $cyPMTable->{$port}->{cyPMVersion} ) ) {

            if ( $line =~ /^([^:]*):.*?\bSW\s*\b([0-9]+(?:\.[0-9]+)*)\b/ ) {

                my( $pmID, $firmware ) = ( $1, $2 );

                $PM{$pmID}->{port} = $port;
                $PM{$pmID}->{firmware} = $firmware;
            }
        }

        # get the current and max current
        foreach my $line ( split( "\n", $cyPMTable->{$port}->{cyPMCurrent} ) ) {

            if ( $line =~ /^([^:]*):.*:\s+(\d+[\d.]*\d)A\..*:\s+(\d+[\d.]*\d)A/ ) {  

                # check for summary
                my( $pmID, $cur, $max ) = ( $1, $2, $3 );

                $PM{$pmID}->{current}->{curtotal} = $cur;
                $PM{$pmID}->{current}->{maxtotal} = $max;
            }
            elsif ( $line =~ /^([^:]*):.*(\w+):\s+(\d+[\d.]*\d)A\..*(\w+):\s+(\d+[\d.]*\d)A/ ) { 

                # check per segment
                my( $pmID, $segment, $cur, $max ) = ( $1, $2, $3, $4 );

                $PM{$pmID}->{current}->{cur$segment} = $cur;
                $PM{$pmID}->{current}->{max$segment} = $cur;
            }
        }

        # get the current and max temperature
	# CELSIUS HACK //Marcus
	# i48A: Temperature: 37.0 C (98.6 F). Maximum: 38.0 C (100.4 F)
        foreach my $line ( split( "\n", $cyPMTable->{$port}->{cyPMTemperature} ) ) {

	    my( $pmID, $cur, $max );

	    if($tempUnit =~ /F/) {
		if ( $line =~ /^([^:]*):[^(]*\(([\d]+\.\d)\s+F\)[^(]*\(([\d]+\.\d)+\s+F\)/ ) {  

		    # save temperature
		    ( $pmID, $cur, $max ) = ( $1, $2, $3 );

		    $PM{$pmID}->{temperature}->{cur} = $cur;
		    $PM{$pmID}->{temperature}->{max} = $max;

		}
	    } else {
		if ( $line =~ /^(.*?):.*?(\d+\.\d).*?:\s(\d+\.\d).*$/ ) {  

		    # save temperature
		    ( $pmID, $cur, $max ) = ( $1, $2, $3 );

		    $PM{$pmID}->{temperature}->{cur} = $cur;
		    $PM{$pmID}->{temperature}->{max} = $max;
		}
	    }
        }
    }

    debug( Data::Dumper->Dump( [\%PM], [qw(*PM)] ) );
    return( \%PM );
}

################################################################################
#                                                                              #
# debug - print debug info
#                                                                              #
################################################################################
sub debug( $;$ ) {

    my( $message, $level ) = @_;

    defined( $level ) or $level = 1;

    if ( $verbose >= $level ) { 
        
        chomp $message;
        print "$message\n";
    }
}

################################################################################
#                                                                              #
# checkWarnCrit - check relative values of warning and critical
#                                                                              #
################################################################################
sub checkWarnCrit( $% ) {

    my( $plugin, %params ) = @_;

    unless ( ref( $plugin ) eq 'Nagios::Plugin' ) {

        debug( "You must provide a Nagios::Plugin object as the first argument to checkWarnCrit()." );
        return( undef );
    }

    my( $warn ) = ( defined( $params{'warn'} ) ? $params{'warn'} : undef );
    my( $crit ) = ( defined( $params{crit} ) ? $params{crit} : undef );
    my( $suffix ) = ( defined( $params{suffix} ) ? $params{suffix} : '' );
    my( $die ) = ( defined( $params{'die'} ) ? $params{'die'} : 0 );

    unless ( defined( $warn ) && defined( $crit ) ) {

        debug( "You must provide values for both the 'warn' and 'crit' parameters." );
        return( undef );
    }

    if ( $warn >= $crit ) {

        debug( "Warning threshold ($warn$suffix) must be less than critical threshold ($crit$suffix)." );
        $die and
            $plugin->nagios_die( "Warning threshold ($warn$suffix) must be less than critical threshold ($crit$suffix)." );
        return( undef );
    }
    else {

        return( 1 );
    }
}

################################################################################
#                                                                              #
# DoInterrupt - handle SIGINT and clean up
#                                                                              #
################################################################################
sub DoInterrupt {

    $plugin->nagios_die( "Interrupt received" );
}

