#!/usr/bin/perl -wT
#
# check_rpc plugin for nagios
#
# usage:
#    check_rpc host service
#
# Check if an rpc serice is registered and running
# using rpcinfo - $proto $host $prognum 2>&1 |";
#
# Use these hosts.cfg entries as examples
#
# command[check_nfs]=/some/path/libexec/check_rpc $HOSTADDRESS$ nfs
# service[check_nfs]=NFS;24x7;3;5;5;unix-admin;60;24x7;1;1;1;;check_rpc
#
# initial version: 3 May 2000 by Truongchinh Nguyen and Karl DeBisschop
# Modified May 2002 Subhendu Ghosh - support for ePN and patches
# current status: $Revision: 1.8 $
#
# Copyright Notice: GPL
# $Id: check_rpc.pl,v 1.8 2003/08/10 12:11:49 kdebisschop Exp $
#

require 5.004;
use strict;
use lib utils.pm;
use Plugin;
use Plugin::Parameter qw(:DEFAULT :standard $p_opt);

use vars qw($PROGNAME $opt_H $opt_C $opt_p $opt_c $opt_U $opt_T $opt_t $verbose);

use utils qw(%ERRORS);

my $C_opt = new Plugin::Parameter(-name => "command", -flags => [ 'C', 'command' ],
				  -optional => "no", -valueoptional => "no", -type => "RPCCOMMAND",
				  -description => "The program name (or number)");
my $c_opt = new Plugin::Parameter(-name => "progver", -flags => [ 'c', 'progver' ],
				  -optional => "yes", -valueoptional => "no", -type => "STRING",
				  -description => "The version you want to check for, one or more. Should prevent checks of unknown versions being syslogged e.g. 2,3,6 to check v2, v3, and v6");
my $U_opt = new Plugin::Parameter(-name => "UDP", -flags => [ 'U', 'udp' ],
				  -optional => "yes", -valueoptional => "yes", -type => "NONE",
				  -description => "Test UDP");
my $T_opt = new Plugin::Parameter(-name => "TCP", -flags => [ 'T', 'tcp' ],
				  -optional => "yes", -valueoptional => "yes", -type => "NONE",
				  -description => "Test TCP");
$v_opt->binding(\$verbose);
my $plugin = new Plugin(-revision => '$Revision: 1.8 $',
			-copyright => "2002 Karl DeBisschop/Truongchinh Nguyen/Subhendu Ghosh, 2004 Howard Wilkinson <howard\@cohtech.com>",
			-shortcomment => "Check if a rpc service is registered and running using  'rpcinfo -H host -C rpc_command'",
			-checker => sub { my ($plugin) = @_;
					  if ($opt_T && $opt_U) {
					    $plugin->usage();
					    utils::usage("$PROGNAME UNKNOWN: Cannot defined both tcp AND udp options");
					  }
					},
			-parameterlists => [ [ $H_opt, $C_opt, $c_opt, $U_opt, $T_opt, $p_opt, $t_opt, $v_opt ], $h_opts, $V_opts ]);

$plugin->init();

my (@proto,%prognum,$response,$prognum,$port,$cmd,$progver,$state);
my ($array_ref,$test,$element,@progkeys,$proto,$a,$b);

my ($line, @progvers, $response2,$response3);

$state = 'UNKNOWN';
$progver = $response=$response2= $response3 ='';

sub in ($$);

$ENV{'BASH_ENV'}=''; 
$ENV{'ENV'}='';
$ENV{'PATH'}='';
$ENV{'LC_ALL'}='C';

#Initialise protocol for each progname number
# 'u' for UDP, 't' for TCP 
$proto[10003]='u';
$proto[10004]='u';
$proto[10007]='u';

# Hash containing all RPC program names and numbers
# Add to the hash if support for new RPC program is required

%prognum  = (
	"portmapper"  => 100000 ,
	"portmap"  => 100000 ,
	"sunrpc"  => 100000 ,
	"rpcbind"  => 100000 ,
	"rstatd"  => 100001 ,
	"rstat"  => 100001 ,
	"rup"  => 100001 ,
	"perfmeter"  => 100001 ,
	"rstat_svc"  => 100001 ,
	"rusersd"  => 100002 ,
	"rusers"  => 100002 ,
	"nfs"  => 100003 ,
	"nfsprog"  => 100003 ,
	"ypserv"  => 100004 ,
	"ypprog"  => 100004 ,
	"mountd"  => 100005 ,
	"mount"  => 100005 ,
	"showmount"  => 100005 ,
	"ypbind"  => 100007 ,
	"walld"  => 100008 ,
	"rwall"  => 100008 ,
	"shutdown"  => 100008 ,
	"yppasswdd"  => 100009 ,
	"yppasswd"  => 100009 ,
	"etherstatd"  => 100010 ,
	"etherstat"  => 100010 ,
	"rquotad"  => 100011 ,
	"rquotaprog"  => 100011 ,
	"quota"  => 100011 ,
	"rquota"  => 100011 ,
	"sprayd"  => 100012 ,
	"spray"  => 100012 ,
	"3270_mapper"  => 100013 ,
	"rje_mapper"  => 100014 ,
	"selection_svc"  => 100015 ,
	"selnsvc"  => 100015 ,
	"database_svc"  => 100016 ,
	"rexd"  => 100017 ,
	"rex"  => 100017 ,
	"alis"  => 100018 ,
	"sched"  => 100019 ,
	"llockmgr"  => 100020 ,
	"nlockmgr"  => 100021 ,
	"x25_inr"  => 100022 ,
	"statmon"  => 100023 ,
	"status"  => 100024 ,
	"bootparam"  => 100026 ,
	"ypupdated"  => 100028 ,
	"ypupdate"  => 100028 ,
	"keyserv"  => 100029 ,
	"keyserver"  => 100029 ,
	"sunlink_mapper"  => 100033 ,
	"tfsd"  => 100037 ,
	"nsed"  => 100038 ,
	"nsemntd"  => 100039 ,
	"showfhd"  => 100043 ,
	"showfh"  => 100043 ,
	"ioadmd"  => 100055 ,
	"rpc.ioadmd"  => 100055 ,
	"NETlicense"  => 100062 ,
	"sunisamd"  => 100065 ,
	"debug_svc"  => 100066 ,
	"dbsrv"  => 100066 ,
	"ypxfrd"  => 100069 ,
	"rpc.ypxfrd"  => 100069 ,
	"bugtraqd"  => 100071 ,
	"kerbd"  => 100078 ,
	"event"  => 100101 ,
	"na.event"  => 100101 ,
	"logger"  => 100102 ,
	"na.logger"  => 100102 ,
	"sync"  => 100104 ,
	"na.sync"  => 100104 ,
	"hostperf"  => 100107 ,
	"na.hostperf"  => 100107 ,
	"activity"  => 100109 ,
	"na.activity"  => 100109 ,
	"hostmem"  => 100112 ,
	"na.hostmem"  => 100112 ,
	"sample"  => 100113 ,
	"na.sample"  => 100113 ,
	"x25"  => 100114 ,
	"na.x25"  => 100114 ,
	"ping"  => 100115 ,
	"na.ping"  => 100115 ,
	"rpcnfs"  => 100116 ,
	"na.rpcnfs"  => 100116 ,
	"hostif"  => 100117 ,
	"na.hostif"  => 100117 ,
	"etherif"  => 100118 ,
	"na.etherif"  => 100118 ,
	"iproutes"  => 100120 ,
	"na.iproutes"  => 100120 ,
	"layers"  => 100121 ,
	"na.layers"  => 100121 ,
	"snmp"  => 100122 ,
	"na.snmp"  => 100122 ,
	"snmp-cmc"  => 100122 ,
	"snmp-synoptics"  => 100122 ,
	"snmp-unisys"  => 100122 ,
	"snmp-utk"  => 100122 ,
	"traffic"  => 100123 ,
	"na.traffic"  => 100123 ,
	"nfs_acl"  => 100227 ,
	"sadmind"  => 100232 ,
	"nisd"  => 100300 ,
	"rpc.nisd"  => 100300 ,
	"nispasswd"  => 100303 ,
	"rpc.nispasswdd"  => 100303 ,
	"ufsd"  => 100233 ,
	"ufsd"  => 100233 ,
	"pcnfsd"  => 150001 ,
	"pcnfs"  => 150001 ,
	"amd"  => 300019 ,
	"amq"  => 300019 ,
	"bwnfsd"  => 545580417 ,
	"fypxfrd"  => 600100069 ,
	"freebsd-ypxfrd"  => 600100069 ,
);

# -v means verbose, -v-v means verbose twice = print above hash
if (defined $verbose && ($verbose > 1) ){
	my $key;
	print "Supported programs:\n";
	print "    name\t=>\tnumber\n";
	print " ===============================\n";
	foreach $key (sort keys %prognum) {
		print "   $key \t=>\t$prognum{$key} \n";
	}
	print "\n\n";
	$plugin->usage();
	exit $ERRORS{'OK'};
}

@progkeys = keys %prognum;
if ($opt_C =~ m/^([0-9]+)$/){
    $prognum = $1;
} elsif ( in( \@progkeys, $opt_C)) {
    $prognum = $prognum{$opt_C};
} else {
    $plugin->usage();
    utils::usage("$PROGNAME UNKNOWN: Program $opt_C is not defined\n");
}

# -p means port number
if(defined $opt_p && $opt_p =~ /^([0-9]+)$/){
    $port = "-n $1";
} else {
    $port = "";
}

$proto = 'u';
$proto = $proto[$prognum] if ($proto[$prognum]);
$proto = 't' if ($opt_T);
$proto = 'u' if ($opt_U);

$plugin->start_timeout($opt_t, "No response from RPC server (alarm)");

# -c is progver  - if we need to check multiple specified versions.
if (defined $opt_c ) {
	my $vers;
	@progvers = split(/,/ ,$opt_c );
	foreach $vers (sort @progvers) {
		if($vers =~ /^([0-9]+)$/){
			$progver = "$1";
			print "Checking $opt_C version $progver proto $proto\n" if $verbose;
			get_rpcinfo();
		}else{
			print "Version $vers is not an integer\n" if $verbose;
		}
	
	}
}else{
	get_rpcinfo();
}


## translate proto for output
if ($proto eq "u" ){
	$proto = "udp";
}else{
	$proto = "tcp";
}

if ($state eq 'OK') {
	print "$PROGNAME $state: RPC program $opt_C".$response." $proto running\n";
}else{
	if($response){
		print "$PROGNAME $state: RPC program $opt_C".$response2." $proto is not running,".$response." $proto is running\n";
	}else{
		print "$PROGNAME $state: RPC program $opt_C $response2 $proto is not running\n";
	}
}
exit $ERRORS{$state};


########  Subroutines ==========================

sub get_rpcinfo {
	$cmd = "$utils::PATH_TO_RPCINFO $port -" . "$proto $opt_H $prognum $progver 2>&1 |";
	print "$cmd\n" if ($verbose);
	open CMD, $cmd or die "Can't fork for rpcinfo: $!\n" ;

	while ( $line = <CMD> ) {
		printf "$line " if $verbose;
		chomp $line;
	
    	if ( $line =~ /program $prognum version ([0-9]*) ready and waiting/ ) {
			$response .= " version $1";
			$state = 'OK' unless $state ne 'UNKNOWN';
			print "1:$response \n" if $verbose;
    	}

		if ( $line =~ /program $prognum version ([0-9]*) is not available/ ) {
			$response2 .= " version $1";
			$state = 'CRITICAL';
			print "2:$response2 \n" if $verbose;
		}
		if ( $line =~ /program $prognum is not available/ ) {
			$response3 = "";
			$response3 = "tcp" if $opt_T;
			$response3 = "udp" if $opt_U;
			$state = 'CRITICAL';
			print "3:$response3 \n" if $verbose;
		}
	}
	close CMD;
}

sub print_usage () {
	print "Usage: \n";
	print " $PROGNAME -H host -C rpc_command [-p port] [-c program_version] [-u|-t] [-v]\n";
	print " $PROGNAME [-h | --help]\n";
	print " $PROGNAME [-V | --version]\n";
}

sub in ($$) {
	$array_ref = shift;
	$test = shift;

	while ( $element = shift @{$array_ref} ) {
		if ($test eq $element) {
	    return 1;
		}
	}
	return 0;
}

