#!/usr/local/bin/perl -w
#
# check_remote_memory
#
# by ATonns Mon Sep  9 14:15:23 EDT 2002
#
# monitor the memory OID in net-snmpd for memory data
#

#
# $Id: check_remote_memory,v 1.3 2003/07/08 18:12:33 atonns Exp atonns $
#
# check_remote_memory
# Copyright (C) 2003 - iVillage.com, Anthony Tonns
#
# 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.
#

# perl setup
use strict;
use Getopt::Long;
use Net::SNMP ();
use CGI;
use IO::String;

use lib "/usr/local/nagios/libexec";
use utils qw($TIMEOUT %ERRORS &print_revision &support);
delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
use NWPE;

# static variables
my $PROGNAME = "check_remote_memory";
my $version = '$Revision: 1.3 $';

# OIDs for the net-snmpd memory data
#
# .iso.org.dod.internet.private.enterprises.ucdavis.memory

my %linkage = (
	"1.3.6.1.4.1.2021.4.3.0" => "TotalSwap",
	"1.3.6.1.4.1.2021.4.4.0" => "AvailSwap",
	"1.3.6.1.4.1.2021.4.5.0" => "TotalReal",
	"1.3.6.1.4.1.2021.4.6.0" => "AvailReal",
);
my @oids = keys %linkage;

# auth config stuff
my $username = "xxxxxxxx";
my $authpass = "xxxxxxxx";
my $privpass = "xxxxxxxx";

################################################################################
my $nwpe = NWPE->new($PROGNAME,$version);

@ARGV = $nwpe->get_args;
if ( ! exists $ARGV[0] ) {
	print "$PROGNAME: no args passed\n";
	$nwpe->quit($ERRORS{UNKNOWN});
}

# parse args
my ($opt_V,$opt_h,$opt_H,$opt_c,$opt_w,$opt_v);
Getopt::Long::Configure('bundling');
GetOptions(
	"V"   => \$opt_V, "version"    => \$opt_V,
	"h"   => \$opt_h, "help"       => \$opt_h,
	"v+"  => \$opt_v, "verbose+"   => \$opt_v,
	"H=s" => \$opt_H, "hostname=s" => \$opt_H,
	"c=s" => \$opt_c, "critical=s" => \$opt_c,
	"w=s" => \$opt_w, "warning=s"  => \$opt_w,
);

# check args
if ( $opt_h )
	{ print_usage($nwpe,""); }
if ( $opt_V )
	{ print_revision($PROGNAME,$version); $nwpe->quit($ERRORS{OK}); }
if ( ! $opt_H )
	{ print_usage($nwpe,"must specify hostname with -H option."); }
if ( ! $opt_c )
	{ print_usage($nwpe,"must specify critical values with -c option."); }
if ( ! $opt_w )
	{ print_usage($nwpe,"must specify warning values with -w option."); }

# validate/rename input to sane variable names
my $hostname = $opt_H;
#
my ($input_mw,$input_sw) = split(',',$opt_w,3);
my ($input_mc,$input_sc) = split(',',$opt_c,3);
#
my $memwarn = $1 if ($input_mw =~ /([0-9]{1,2}\%?|100\%?)/);
($memwarn) || print_usage($nwpe,"Invalid warning threshold: $input_mw");
my $swapwarn = $1 if ($input_sw =~ /([0-9]{1,2}\%?|100\%?)/);
($swapwarn) || print_usage($nwpe,"Invalid warning threshold: $input_sw");
my $memcrit = $1 if ($input_mc =~ /([0-9]{1,2}\%?|100\%?)/);
($memcrit) || print_usage($nwpe,"Invalid critical threshold: $input_mc");
my $swapcrit = $1 if ($input_sc =~ /([0-9]{1,2}\%?|100\%?)/);
($swapcrit) || print_usage($nwpe,"Invalid critical threshold: $input_sc");

# set a timeout w/error message
$SIG{'ALRM'} = sub {
        print ("$PROGNAME: ERROR: alarm timeout\n");
        $nwpe->quit($ERRORS{UNKNOWN});
};
alarm($TIMEOUT);

# establish a session
my ($session,$error) = Net::SNMP->session(
	-hostname =>		$hostname,
	-version =>		"3",
	-username =>		$username,
	-authprotocol =>	"md5",
	-authpassword =>	$authpass,
	-privpassword =>	$privpass,
	-maxmsgsize =>		1048576,
	-timeout =>		$TIMEOUT,
	-retries =>		3,
);
if ( $error ) {
	print "$PROGNAME: session error: $error\n";
	$nwpe->quit($ERRORS{UNKNOWN});
}

# retreive the relevant OIDs
my $result = $session->get_request( -varbindlist => \@oids, );
if ( $session->error ) {
	print "$PROGNAME: get_request error: ".$session->error."\n";
	$session->close;
	$nwpe->quit($ERRORS{UNKNOWN});
}
$session->close;

# reorganize the data for coherent use
my %data;
foreach (sort keys %{$result}) {
        my $key = $_;
	$data{$linkage{$key}} = ${$result}{$key};
#DEBUG
#	print "$linkage{$key} = ${$result}{$key}\n";
}

# since we've checked all the sanity beforehand,
# start off assuming all is well
my $state = $ERRORS{OK};

my $mempct  = int(($data{AvailReal} * 100) / $data{TotalReal});
my $swappct = int(($data{AvailSwap} * 100) / $data{TotalSwap});

#
# Real Memory Warning Check
#
# is it a percentage?
if ( substr($memwarn,-1,1) eq "%" ) {
        my $memwarnpcnt = substr($memwarn,0,length($memwarn)-1) + 0;
        if ( $memwarnpcnt > $mempct ) {
                $state = $ERRORS{WARNING} if ( $state < $ERRORS{WARNING} );
        }
}
# ah, it must be a total value
else {
        my $memwarnavail = $memwarn + 0;
        if ( $memwarnavail > $data{AvailReal} ) {
                $state = $ERRORS{WARNING} if ( $state < $ERRORS{WARNING} );
        }
}

#
# Swap Warning Check
#
# is it a percentage?
if ( substr($swapwarn,-1,1) eq "%" ) {
        my $swapwarnpcnt = substr($swapwarn,0,length($swapwarn)-1) + 0;
        if ( $swapwarnpcnt > $swappct ) {
                $state = $ERRORS{WARNING} if ( $state < $ERRORS{WARNING} );
        }
}
# ah, it must be a total value
else {
        my $swapwarnavail = $swapwarn + 0;
        if ( $swapwarnavail > $data{AvailSwap} ) {
                $state = $ERRORS{WARNING} if ( $state < $ERRORS{WARNING} );
        }
}

#
# Real Memory Critical Check
#
# is it a percentage?
if ( substr($memcrit,-1,1) eq "%" ) {
        my $memcritpcnt = substr($memcrit,0,length($memcrit)-1) + 0;
        if ( $memcritpcnt > $mempct ) {
                $state = $ERRORS{CRITICAL} if ( $state < $ERRORS{CRITICAL} );
        }
}
# ah, it must be a total value
else {
        my $memcritavail = $memcrit + 0;
        if ( $memcritavail > $data{AvailReal} ) {
                $state = $ERRORS{CRITICAL} if ( $state < $ERRORS{CRITICAL} );
        }
}

#
# Swap Critical Check
#
# is it a percentage?
if ( substr($swapcrit,-1,1) eq "%" ) {
        my $swapcritpcnt = substr($swapcrit,0,length($swapcrit)-1) + 0;
        if ( $swapcritpcnt > $swappct ) {
                $state = $ERRORS{CRITICAL} if ( $state < $ERRORS{CRITICAL} );
        }
}
# ah, it must be a total value
else {
        my $swapcritavail = $swapcrit + 0;
        if ( $swapcritavail > $data{AvailSwap} ) {
                $state = $ERRORS{CRITICAL} if ( $state < $ERRORS{CRITICAL} );
        }
}

# print text for the humans
my $statetxt;
foreach (keys(%ERRORS)) {
	my $key = $_;
	$statetxt=$key if ( $state == $ERRORS{$key} );
}

# the almighty output
print "Memory - $data{AvailReal}/$data{TotalReal} ($mempct\%), ";
print "Swap - $data{AvailSwap}/$data{TotalSwap} ($swappct\%)";
print " - $statetxt" unless $state == $ERRORS{OK};
print "\n";
$nwpe->quit($state);

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

# how does this work again?
sub print_usage {
	my ($nwpe,$msg) = @_;
	my $PROGNAME = $nwpe->PROGNAME;
	my $version = $nwpe->version;
	if ( $msg ) { print "$PROGNAME: $msg\n\n"; }
	print_revision($PROGNAME,$version);
	print "Usage: $PROGNAME -H hostname -w memlimit,swaplimit -c memlimit,swaplimit\n";
	print "Usage: $PROGNAME --hostname=hostname " .
				"--warning=memlimit,swaplimit --critical=memlimit,swaplimit\n";
	print "       ".' ' x length($PROGNAME) .
				     " [-v|--verbose -V|--version -h|--help]\n";
	$nwpe->quit($ERRORS{UNKNOWN});
}
