#!@client_perl@ # nagios: -epn # icinga: -epn # # check_multi - nagios plugin # # Copyright (c) 2007-2011 Matthias Flacke (matthias.flacke at gmx.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # use strict; use warnings; use Getopt::Long qw(:config no_ignore_case bundling); use vars qw( $MYSELF @cmds %opt $returncode %def %rc $no $VERSION $tmp_stdout $tmp_stderr $xml $check_multi $NAGIOS $nagios $Nagios $status_dat $livestatus_service $livestatus_host $timezero $OK $WARNING $CRITICAL $UNKNOWN $DETAIL_LIST $DETAIL_LIST_FULL $DETAIL_HTML $DETAIL_STDERR $DETAIL_PERFORMANCE $DETAIL_PERFORMANCE_CLASSIC $DETAIL_STATUS $DETAIL_PERFORMANCE_LINK $DETAIL_XML $DETAIL_NAGIOS2 $DETAIL_NOTES_LINK $DETAIL_SERVICE_DEFINITION $DETAIL_SEND_NSCA $DETAIL_FEED_PASSIVE $DETAIL_ENCODE_COMMANDS $DETAIL_HIDEIFOK $DETAIL_SEND_GEARMAN *DEBUG0 *DEBUG1 *DEBUG2 *DEBUG3 *DEBUG4 ); BEGIN { #--- OMD environment? Then we have to be sure that vars are defined #--- otherwise somebody copied check_multi from OMD to remote hosts if (@omd_environment@ && (!$ENV{OMD_SITE} || !$ENV{OMD_ROOT})) { print "Error: OMD_SITE or OMD_ROOT variables not found.\n". "If you're running check_multi outside of OMD environment,\n". "add OMD_ROOT and OMD_SITE to the environment\n"; exit 3; #--- add OMD_ROOT and OMD_SITE vars to check_multi internal environment #--- then $OMD_ROOT$ and $OMD_SITE$ can be used in command files } elsif (@omd_environment@) { $ENV{MULTI_OMD_ROOT}=$ENV{OMD_ROOT}; $ENV{MULTI_OMD_SITE}=$ENV{OMD_SITE}; } #--- if hires timer available, use it eval("use Time::HiRes qw(time sleep)"); if (! $@) { $opt{set}{use_time_hires}=1; } #--- if FindBin module available, use it for finding plugins if (@findbin@) { eval("use FindBin"); if (! $@) { $opt{set}{libexec}="$FindBin::Bin"; unshift @INC, "$FindBin::Bin"; } } } use lib "@libexecdir@"; #------------------------------------------------------------------------------- #--- vars ---------------------------------------------------------------------- #------------------------------------------------------------------------------- $timezero=time; $MYSELF="check_multi"; $nagios=lc("@nagios_name@"); $NAGIOS=uc("@nagios_name@"); $Nagios=ucfirst(lc("@nagios_name@")); $VERSION='@CM_VERSION@'. "\nconfigure @configure_args@"; # #--- RC defines $OK=0; $WARNING=1; $CRITICAL=2; $UNKNOWN=3; # #--- report defines $DETAIL_LIST=1; $DETAIL_HTML=2; $DETAIL_STDERR=4; $DETAIL_PERFORMANCE=8; $DETAIL_LIST_FULL=16; $DETAIL_PERFORMANCE_CLASSIC=32; $DETAIL_STATUS=64; $DETAIL_PERFORMANCE_LINK=128; $DETAIL_XML=256; $DETAIL_NAGIOS2=512; $DETAIL_NOTES_LINK=1024; $DETAIL_SERVICE_DEFINITION=2048; $DETAIL_SEND_NSCA=4096; $DETAIL_FEED_PASSIVE=8192; $DETAIL_ENCODE_COMMANDS=16384; $DETAIL_HIDEIFOK=32768; $DETAIL_SEND_GEARMAN=65536; # #--- vars $no=0; $returncode=0; $status_dat=undef; $livestatus_host=undef; $livestatus_service=undef; $tmp_stdout=""; $tmp_stderr=""; %def=( label => { $OK => "OK", $WARNING => "WARNING", $CRITICAL => "CRITICAL", $UNKNOWN => "UNKNOWN", }, llabel => { $OK => "ok", $WARNING => "warning", $CRITICAL => "critical", $UNKNOWN => "unknown", }, code => { "OK" => $OK, "WARNING" => $WARNING, "CRITICAL" => $CRITICAL, "UNKNOWN" => $UNKNOWN, "ok" => $OK, "warning" => $WARNING, "critical" => $CRITICAL, "unknown" => $UNKNOWN, }, s2r => { 0 => $OK, 2 => $WARNING, 3 => $CRITICAL, 1 => $UNKNOWN, }, r2s => { $OK => 0, $WARNING => 2, $CRITICAL => 3, $UNKNOWN => 1, }, color => { $OK => "#33FF00", $WARNING => "#FFFF00", $CRITICAL => "#F83838", $UNKNOWN => "#FF9900", }, bgcolor => { $OK => "#33FF00", $WARNING => "#FEFFC1", $CRITICAL => "#FFBBBB", $UNKNOWN => "#FFDA9F", }, ); my %opt=( "commands" => { "attribute" => 1, "command" => 1, "cumulate" => 1, "eval" => 1, "eeval" => 1, "livestatus" => 1, "snmp" => 1, "statusdat" => 1, "state" => 1, "output" => 1, }, "execute" => [], "filename" => [], "help" => 0, "set" => { # mouse_over & action_url: shows PNP chart popup triggered by mouse action_mouseover => @action_mouseover@, # place tags at the beginning of list entries (e.g. tag_host_servicedesc) add_tag_to_list_entries => @add_tag_to_list_entries@, # cancel child check before it can reach global timeout cancel_before_global_timeout => @cancel_before_global_timeout@, # checkresults_dir checkresults_dir => "@checkresults_dir@", # child_interval child_interval => "@child_interval@", # update config file (if HTTP/FTP/etc) once a day (in seconds) cmdfile_update_interval => @cmdfile_update_interval@, # should check_multi collapse child check_multi checks per default? collapse => @collapse@, # complain about undefined macros instead of only silently removing complain_unknown_macros => @complain_unknown_macros@, # path to check_multi config directory config_dir => "@config_dir@", # how many rows should be displayed for cumulate statement? cumulate_max_rows => 5, # should cumulate ignore zero values? cumulate_ignore_zero => @cumulate_ignore_zero@, # don't complain about being root etc. dont_be_paranoid => 0, # should empty output be flagged as UNKNOWN? empty_output_is_unknown => 1, # use open3 method to exec child checks exec_open3 => @exec_open3@, # show child checks also in status view (only in HTML mode) extinfo_in_status => "@extinfo_in_status@", # print extended perfdata, count of states and overall state extended_perfdata => "@extended_perfdata@", # flag which determines if feed_passive services should be autocreated # based on multi-child tags feed_passive_autocreate => "@feed_passive_autocreate@", # directory to contain automatically created service definitions # for passive feeded check_multi services feed_passive_dir_permissions => "@feed_passive_dir_permissions@", # directory to contain automatically created service definitions # for passive feeded check_multi services feed_passive_dir => "@feed_passive_dir@", # standard extension of check_multi command files, # to be searched in directories file_extension => "@file_extension@", # if set, return UNKNOWN if command file is not found ignore_missing_cmd_file => @ignore_missing_cmd_file@, # characters to cleanup from command files illegal_chars => "@illegal_chars@", # path to Nagios images image_path => "@image_path@", # indentation character(s) indent => "@indent@", # child checks indented? indent_label => @indent_label@, # plugin directory to be added to check_multi search path libexec => "@libexecdir@", # livestatus socket livestatus => "@livestatus@", # loose (German) performance data (replace commata by decimal points) loose_perfdata => "@loose_perfdata@", # pnp_version needed for mouseover: 0.6,0,4 perfdata_pass_through => "@perfdata_pass_through@", # pnp_version needed for mouseover: 0.6,0,4 pnp_version => "@pnp_version@", # label to be shown in output: OK, ... name => "@name@", # what RC should be returned if no checks are defined no_checks_rc => @no_checks_rc@, # see tag_notes_link: URL to be added notes_url => "@notes_url@", # objects.cache path objects_cache => "@objects_cache@", # objects.cache delimiter character objects_cache_delimiter => "@objects_cache_delimiter@", # omd environment omd_environment => @omd_environment@, # create persistent data persistent => @persistent@, # path to additional plugins plugin_path => "@plugin_path@", # PNP URL addon pnp_add2url => "@pnp_add2url@", # PNP URL pnp_url => "@pnp_url@", # report option, binary coded as sum of detail options report => @report@, # report option, binary coded as sum of detail options report_inherit_mask => @report_inherit_mask@, #--- send_gearman: path to binary send_gearman => "/usr/local/bin/send_gearman", #--- send_gearman: encryption (0|1) send_gearman_encryption => 1, #--- send_gearman key: either string or file send_gearman_key => "should_be_changed", #--- send_gearman result queue name, default:empty send_gearman_resultqueue => "", #--- send_gearman_srv: worker server send_gearman_srv => "localhost", #--- send_nsca: path to binary send_nsca => "@sbindir@/send_nsca", #--- send_nsca_cfg: path to config file send_nsca_cfg => "@sysconfdir@/send_nsca.cfg", #--- send_nsca_srv: nsca server name send_nsca_srv => "localhost", #--- send_nsca_port: nsca server port send_nsca_port => 5667, #--- send_nsca_timeout send_nsca_timeout => @child_timeout@, #--- send_nsca_opts: options to provide to send_nsca send_nsca_delim => ";", # name of the file which contains template for '-r 2048' service_definition_template => "@service_definition_template@", # service definition template default service_definition_template_default => "# service \'\$THIS_NAME\$\' for host \'\$HOSTNAME\$\'\n". "define service {\n". " service_description \$THIS_NAME\$\n". " host_name \$HOSTNAME\$\n". " passive_checks_enabled 1\n". " active_checks_enabled 0\n". " check_command check_dummy!3 'Error: passive check has been called actively - check your config'\n". " use local-service\n". "}\n\n", # signals to cover signals => ["DUMMY","INT","TERM","QUIT","HUP","__WARN__"], # which RC if caught signal signal_rc => @signal_rc@, # snmp_community snmp_community => "@snmp_community@", # snmp_community snmp_port => "@snmp_port@", # path to status.dat status_dat => "@status_dat@", # style of plus/minus char style_plus_minus => "style='color:#4444FF;line-height:0.3em;font-size:1.5em;cursor:crosshair'", # documentation URL to be added to child check tags tag_notes_link => "@tag_notes_link@", # frame target for action_url and notes_url target => "@target@", # internal development and test mode test => 0, # child check timeout (small t) timeout => @child_timeout@, # global check_multi timeout (BIG T) TIMEOUT => @parent_timeout@, # directory where check_multi stores its temporary output files tmp_dir => "@tmp_dir@", # temporary etc dir for local copies of configuration files tmp_etc => "@tmp_etc@", # octal permissions of tmp_dir tmp_dir_permissions => "@tmp_dir_permissions@", # characters which are allowed in macros valid_macro_chars => 'A-Za-z0-9\.\@\-\_\:', # characters which are allowed in tags valid_tag_chars => 'A-Za-z0-9\-\.\@\_\:\$', # verbosity, from 1 (less verbose) to 3 (much verbose) verbose => @verbose@, # elements to be added to XML structure eml_elements => "name,rc,output,error,plugin,command,performance,starttime,endtime,runtime,type", }, "variable" => { }, ); #--- Array of commands my @cmds = ( #--- 0: parent check { command => "none", # no command for parent critical => "", # state definition: CRITICAL endtime => 0.0, # end timestamp error => [ ], # stderr and other errors state_default => [ # state definitions "1", # OK "COUNT(WARNING) > 0", # WARNING "COUNT(CRITICAL) > 0", # CRITICAL "COUNT(UNKNOWN) > 0" # UNKNOWN ], state => [ "1", # OK "COUNT(WARNING) > 0", # WARNING "COUNT(CRITICAL) > 0", # CRITICAL "COUNT(UNKNOWN) > 0" # UNKNOWN ], hash => "", # hash for all child checks nallchecks => 0, # number of all child checks (with non-displayed checks) name => "", # no predefined name for parent (-n option) nchecks => 0, # number of child checks (displayed) number => 0, # current number of check (0 for head) ok => "", # state definition: OK output => "", # output rc => $OK, # return code runtime => 0.0, # runtime in seconds sleeped => 0.0, # time sleeped between child checks starttime => 0.0, # start timestamp timeouttime => 0.0, # timeout timestamp type => "head", # this is the master of disaster unknown => "", # state definition: UNKNOWN warning => "", # state definition: WARNING } #--- elements 1..x will be added by parse_lines / parse_header ); my %rc = ( count => [ 0, 0, 0, 0, ], # count displayed RCs count_all => [ 0, 0, 0, 0, ], # cound all RCs list => [ [],[],[],[], ], # list of displayed child checks list_all => [ [],[],[],[], ], # list of all child checks match => [ 0, 0, 0, 0, ], # ); my $check_multi = { cmds => \@cmds, rc => \%rc, opt => \%opt }; #--- DEBUG typeglobs: 1. verbose 2. errors 3. detailed 4. programmers debugging *DEBUG1=($opt{set}{verbose}>=1) ? \&debug_message : sub {}; *DEBUG2=($opt{set}{verbose}>=2) ? \&debug_message : sub {}; *DEBUG3=($opt{set}{verbose}>=3) ? \&debug_message : sub {}; *DEBUG4=($opt{set}{verbose}>=3) ? \&debug_message : sub {}; #------------------------------------------------------------------------------- #--- subs ---------------------------------------------------------------------- #------------------------------------------------------------------------------- #--- #--- process command line parameters and STDIN (if any) #--- sub process_input { my @SAVEARGV=@ARGV; my $stdin=""; #--- check version of modules if ($Getopt::Long::VERSION < 2.27) { print "Error: module Getopt::Long version $Getopt::Long::VERSION is too old, minimum version is 2.27\n"; return $UNKNOWN; } if (! GetOptions( "f|filename=s" => \@{$opt{filename}}, "h|help:+" => \$opt{help}, "i|instant=s" => \$opt{set}{instant}, "l|libexec=s" => \$opt{set}{libexec}, "n|name=s" => \$opt{set}{name}, "r|report=s" => \$opt{set}{report}, "s|set=s" => \%{$opt{variable}}, "t|timeout=i" => \$opt{set}{timeout}, "T|TIMEOUT=i" => \$opt{set}{TIMEOUT}, "v|verbose:+" => \$opt{set}{verbose}, "V|version" => \$opt{version}, "x|execute=s" => \@{$opt{execute}}, "y|inventory:+" => \$opt{set}{inventory}, "o|O|ok=s" => \$cmds[0]{state}[0], "w|W|warning=s" => \$cmds[0]{state}[1], "c|C|critical=s"=> \$cmds[0]{state}[2], "u|U|unknown=s" => \$cmds[0]{state}[3],) ) { short_usage(); return $UNKNOWN; } #--- redefine debug typeglobs, if verbose flag has been changed on cmdline { no warnings 'redefine'; *DEBUG1=($opt{set}{verbose}>=1) ? \&debug_message : sub {}; *DEBUG2=($opt{set}{verbose}>=2) ? \&debug_message : sub {}; *DEBUG3=($opt{set}{verbose}>=3) ? \&debug_message : sub {}; *DEBUG4=($opt{set}{verbose}>=3) ? \&debug_message : sub {}; } #--- -V(ersion) option if ($opt{version}) { print "Version: $VERSION\n"; return $UNKNOWN; } #--- -h/--help shows long usage, -hh extended usage DEBUG3("opt{help}=$opt{help}"); if ($opt{help} == 1) { short_usage(); long_usage(); return $UNKNOWN; } elsif ($opt{help} == 2) { short_usage(); long_usage(); extended_usage(); return $UNKNOWN; } elsif ($opt{help} > 2) { short_usage(); long_usage(); extended_usage(); detailed_usage(); return $UNKNOWN; } #--- check command line vars (--set KEY=VAL) and transfer it to $opt{set} foreach my $variable (sort keys(%{$opt{variable}})) { #--- export MULTI_$variable $ENV{"MULTI_".$variable}="$opt{variable}{$variable}"; #--- if existing, overwrite, otherwise set $opt{set} variable if (defined($opt{set}{$variable})) { DEBUG3("overwriting \$opt{set}{$variable}:$opt{set}{$variable} with $opt{variable}{$variable}"); } else { DEBUG3("setting \$opt{set}{$variable} with $opt{variable}{$variable}"); } $opt{set}{"$variable"}="$opt{variable}{$variable}"; } #--- determine some settings (user,hostname) $opt{set}{uid}=$<; if ($^O=~/Win32/) { #--- Win32? then use Win32 module if (module("Win32")) { DEBUG3("Win32 available"); $opt{set}{user}=&Win32::LoginName; if (!$opt{set}{HOSTNAME}) { $opt{set}{HOSTNAME}=&Win32::NodeName; } } else { DEBUG2("Win32 not available"); $opt{set}{user}="unknown"; if (!$opt{set}{HOSTNAME}) { $opt{set}{HOSTNAME}=$ENV{COMPUTERNAME}; } } } else { $opt{set}{user}=getpwuid($<); if (!$opt{set}{HOSTNAME}) { $opt{set}{HOSTNAME}=get_hostname("HOSTNAME"); } if (!$opt{set}{HOSTADDRESS}) { $opt{set}{HOSTADDRESS}=get_hostname("HOSTADDRESS"); } } #--- check_multi uses a temporary directory, per default /tmp/check_multi #--- 1. create if not yet done if (! my_mkdir("$opt{set}{tmp_dir}", $opt{set}{tmp_dir_permissions})) { print "Error: could not create tmp directory $opt{set}{tmp_dir} as user $opt{set}{user}\n"; return $UNKNOWN; } #--- 2. check if tmp_dir has correct permissions if ((stat("$opt{set}{tmp_dir}"))[2] != oct("$opt{set}{tmp_dir_permissions}") && ! chmod(oct("$opt{set}{tmp_dir_permissions}"), "$opt{set}{tmp_dir}") ) { print "Error: could not set tmp directory $opt{set}{tmp_dir} permissions to $opt{set}{tmp_dir_permissions} as user $opt{set}{user}\n"; return $UNKNOWN; #--- 3. and at last: it has to be writeable } elsif (! -w "$opt{set}{tmp_dir}") { print "Error: cannot write to tmp directory $opt{set}{tmp_dir} as user $opt{set}{user}\n"; return $UNKNOWN; } #--- #--- getting commands - either per command file or per command parameter #--- first generate regex which contains all allowed commands (we need it later) $opt{cmdregex}=join('|',keys(%{$opt{commands}})); #--- allowed characters: 'a-zA-Z0-9_-' if ($opt{set}{name}=~/[\[\]]+/) { print "$MYSELF error: name \'$opt{set}{name}\' invalid - [ brackets ] are not allowed\n"; return $UNKNOWN; } #--- loop over filename / URL / directory array for (my $i=0;$i<@{$opt{filename}}; $i++) { #--- only read STDIN if feeded by a PIPE, otherwise it would block when empty if ($opt{filename}[$i]=~/^-$/) { while () { $stdin.=$_; } DEBUG3("found >$stdin<"); #--- do we have input from check_multi XML report mode? (check_multi as filter) if ($stdin=~/check_multi_xml/) { #--- try to load module XML::Simple and bail out if not successful module("XML::Simple",1); #--- read in XML data and feed cmds structure my $in=XML::Simple::XMLin( $stdin, KeyAttr=>["CHILD"=>"no"], # index: child no ForceArray=>["CHILD"], # force array also for one child SuppressEmpty=>"", # if empty, add "" instead of {} empty hash ); if (module("Data::Dumper")) { DEBUG3("Data::Dumper available"); DEBUG3("XML input after XMLin:\n" . Dumper($in)); } else { $opt{set}{test}=0; DEBUG2("Data::Dumper not available"); } if (exists($in->{PARENT})) { #--- restore some values from parent $cmds[0]{nchecks}=$in->{PARENT}->{nchecks}; $cmds[0]{nallchecks}=$in->{PARENT}->{nallchecks}; #--- child values for my $i (keys %{$in->{PARENT}->{CHILD}}) { $cmds[$i]{no}=$i; # index itself as 'no' $cmds[$i]{feeded}=1; # mark this cmd as feeded foreach my $att (keys %{$in->{PARENT}->{CHILD}->{$i}}) { $cmds[$i]{$att}=$in->{PARENT}->{CHILD}->{$i}->{$att}; } } } else { #--- child values for my $no (keys %{$in->{$MYSELF}->{CHILD}}) { $cmds[$no]{no}=$no; # index itself as 'no' $cmds[$no]{feeded}=1; # mark this cmd as feeded foreach my $att (keys %{$in->{$MYSELF}->{CHILD}->{$no}}) { $cmds[$no]{$att}=$in->{$MYSELF}->{CHILD}->{$no}->{$att}; } $ENV{"MULTI_${no}_NAME"}="$cmds[$no]{name}"; $ENV{"MULTI_${no}_STATE"}="$cmds[$no]{rc}"; $ENV{"MULTI_${no}_LABEL"}="$def{label}{$cmds[$no]{rc}}"; } } DEBUG3("\@cmds after filling from XML input\n" . Dumper(\@cmds)); } elsif ($stdin=~/($opt{cmdregex})\s*\[.*\]\s*=/) { DEBUG3("command file in STDIN detected"); push @{$opt{execute}},split(/\n/,$stdin); } #--- 2. filename URL found } elsif ($opt{filename}[$i]=~/\/\//) { DEBUG3("http/ftp URL specified: $opt{filename}[$i]"); #--- LWP module NOT loaded? if (!module("LWP::Simple")) { add_error(0,"LWP::Simple module not available, could not get command file $opt{filename}[$i]"); next; } #--- split path components of URL my ($host,$path,$file)=("","",""); if ($opt{filename}[$i]=~/.*\/\/([^\/]+)([\/]*.*)/) { $host=$1; $path=$2; if ($path=~/(.*)\/(.*$opt{set}{file_extension})/) { $path=$1; $file=$2; } chop($path) if ($path && $path=~/\S+\/$/); $path=$1 if ($path && $path=~/\/(\S+)$/); if ($path eq "" || $file eq "") { print "$MYSELF error: empty path or filename specified in URL $opt{filename}[$i]\n"; return $UNKNOWN; } DEBUG4("URL:$opt{filename}[$i] hostname:$host path:$path file:$file"); } #--- create directory to store saved config files my $cmdfile_path="$opt{set}{tmp_dir}/$opt{set}{tmp_etc}/$host/$path"; if (! my_mkdir("$cmdfile_path", $opt{set}{tmp_dir_permissions})) { add_error(0,"Cannot create config directory path \'$cmdfile_path\':$!"); return $UNKNOWN; } #--- use ctime to determine if config file should be updated after #--- having reached cmdfile_update_interval my $cmdfile_age=time-(stat("$cmdfile_path/$file"))[10] if (-f "$cmdfile_path/$file"); if (!-f "$cmdfile_path/$file" || $cmdfile_age>$opt{set}{cmdfile_update_interval}) { DEBUG3("$cmdfile_path/$file age is $cmdfile_age, greater than allowed interval $opt{set}{cmdfile_update_interval}"); my $RC=LWP::Simple::mirror($opt{filename}[$i],"$cmdfile_path/$file"); if (LWP::Simple::is_success($RC) || $RC == 304) { DEBUG3("Mirroring $opt{filename}[$i] to $cmdfile_path/$file OK: RC $RC"); $opt{filename}[$i]="$cmdfile_path/$file"; `touch $opt{filename}[$i]`; } else { DEBUG2("Error: mirroring $opt{filename}[$i] to $cmdfile_path/$file failed: $RC"); } } else { DEBUG3("$opt{filename}[$i] is already downloaded $cmdfile_path/$file"); $opt{filename}[$i]="$cmdfile_path/$file"; } #--- 2. directory found? replace directory with '*cmd' files from these directories } elsif (-d $opt{filename}[$i]) { splice(@{$opt{filename}},$i,1,glob "$opt{filename}[$i]/*$opt{set}->{file_extension}"); #--- 3. and at last: the simple filename } else { if (! -f $opt{filename}[$i] || ! -r $opt{filename}[$i]) { DEBUG2("Error: filename $opt{filename}[$i] not existing or not readable"); } } } #--- none of them? return UNKNOWN if (! $opt{filename}[0] && ! $opt{execute}[0] && !$stdin && (!$opt{set}{persistent})) { print "$MYSELF error: no config file(s) or command parameters specified\n"; short_usage(); return $UNKNOWN; } #--- inherit report settings from parent check_multi if ($opt{set}{report_inherit_mask} && defined($ENV{MULTI_set_report}) && "$ENV{MULTI_set_report}") { $opt{set}{report}=$ENV{MULTI_set_report} & $opt{set}{report_inherit_mask}; DEBUG3("inherited reportstring $opt{set}{report} from parent check_multi, derived from $ENV{MULTI_set_report} with mask $opt{set}{report_inherit_mask}"); } #--- report option candy - allow speaking strings '1+2+4+8' instead of bare '15' if ($opt{set}{report}=~/[0-9+]/) { my $sum=0; for (split(/\+/,$opt{set}{report})) { $sum+=$_; } DEBUG3("reportstring $opt{set}{report} converted into numerical value $sum"); $opt{set}{report}=$sum; } elsif ($opt{set}{report}=~/[0-9]/) { # normal numeric value - do nothing } else { print "$MYSELF error: report option \'$opt{set}{report}\' contains invalid characters, allowed are numbers or 0-9+\n"; return $UNKNOWN; } #--- report options - name if ($opt{set}{report} & $DETAIL_PERFORMANCE_LINK && !$opt{set}{name}) { $opt{set}{name}=$MYSELF; DEBUG3("performance report option set and no name defined: taking $MYSELF as name"); } #--- report option - notes_url if ($opt{set}{report} & $DETAIL_NOTES_LINK && !$opt{set}{notes_url}) { if ($ENV{"MULTI_SERVICENOTESURL"}) { $opt{set}{notes_url}=$ENV{"MULTI_SERVICENOTESURL"}; DEBUG3("notes report option set - taking SERVICENOTESURL ".$ENV{"MULTI_SERVICENOTESURL"}); } elsif ($ENV{"${NAGIOS}_SERVICENOTESURL"}) { $opt{set}{notes_url}=$ENV{"${NAGIOS}_SERVICENOTESURL"}; DEBUG3("notes report option set - taking $Nagios SERVICENOTESURL ".$ENV{"${NAGIOS}_SERVICENOTESURL"}); } else { add_error(0,"process_input: Notes link report option chosen, but no environment variable SERVICENOTESURL found and no parameter notes_url specified"); } } #--- Initialize empty PATH if none defined $ENV{PATH}="" unless ($ENV{PATH}); my $path_sep=($^O=~/Win32/)?';':':'; #--- set plugin_path at 2nd position of PATH variable $ENV{PATH}="$opt{set}{plugin_path}${path_sep}$ENV{PATH}" if ($opt{set}{plugin_path}); #--- set libexec directory at the beginning of PATH variable $ENV{PATH}="$opt{set}{libexec}${path_sep}$ENV{PATH}" if ($opt{set}{libexec}); #--- be sure that there is no newline at state definitions end foreach my $state (sort numerically keys %{$def{s2r}}) { chomp($cmds[0]{state}[$state]); } #--- timeout checking: overall TIMEOUT has to be greater than child check timeout if ($opt{set}{timeout} && $opt{set}{TIMEOUT} && $opt{set}{timeout} > $opt{set}{TIMEOUT}) { print "$MYSELF: error - child timeout $opt{set}{timeout}s must not be greater than parent timeout $opt{set}{TIMEOUT}s\n"; return $UNKNOWN; } #--- add _the_ parent pid as indicator of the absolute first instance in a check_multi tree #--- there can be multiple recursive called instances and they need to detect which is the first $ENV{"MULTI_PPID"}=$$ if (!defined($ENV{"MULTI_PPID"})); #--- persistency - on the way to check_multi 2.0 if ($opt{set}{test} && $opt{set}{persistent}) { #--- try to load module XML::Simple $opt{set}{use_xml_simple} = 1; unless (eval "require XML::Simple;1") { $opt{set}{use_xml_simple} = 0; DEBUG2("XML::Simple not available:$@"); } #--- successful load? if ($opt{set}{use_xml_simple}) { #--- take filename to store persistent data from HOSTNAME-SERVICEDESC if ($ENV{MULTI_HOSTNAME} && $ENV{MULTI_SERVICEDESC}) { $cmds[0]{key}="$ENV{MULTI_HOSTNAME}-$ENV{MULTI_SERVICEDESC}"; } elsif ($ENV{MULTI_HOSTNAME} && $ENV{"${NAGIOS}_SERVICEDESC"}) { $cmds[0]{key}="$ENV{MULTI_HOSTNAME}-".$ENV{"${NAGIOS}_SERVICEDESC"}; } else { print "process_input: need HOSTNAME and SERVICEDESC for persistent mode.\nPlease specify -s HOSTNAME= -s SERVICEDESC=\n"; return $UNKNOWN; } #--- replace whitechar with underscore $cmds[0]{key}=~s/\s+/_/g; DEBUG3("command key: $cmds[0]{key}"); #--- read persistent data my $in; my @xmlfiles=dir_entries("$opt{set}{tmp_dir}/$opt{set}{HOSTNAME}_$opt{set}{SERVICEDESC}",1); if ($#xmlfiles == 1) { DEBUG3("reading file $xmlfiles[0].xml"); $in=XML::Simple::XMLin("$opt{set}{tmp_dir}/$opt{set}{HOSTNAME}_$opt{set}{SERVICEDESC}/".$xmlfiles[0].".xml",KeyAttr => [ ]); } else { DEBUG2("no persistency file found in $opt{set}{tmp_dir}/$opt{set}{HOSTNAME}_$opt{set}{SERVICEDESC}"); } #--- read persistent data #--- debug output per Data::Dumper if (module("Data::Dumper")) { DEBUG3("loaded persistent data:"); DEBUG3(Dumper($in)); } else { $opt{set}{test}=0; } } } #--- do some debug output DEBUG3("$MYSELF - $VERSION"); DEBUG3("command line: $0 >".join('< >',@SAVEARGV)."<"); #--- any remaining parameters are orphaned - tell the caller what's going wrong here if (@ARGV) { print "Error: orphaned parameters found on command line:"; for (my $i=1; $#ARGV>-1; $i++) { print " ARG$i:",shift(@ARGV); } print "\n"; return $UNKNOWN; } #--- just debugging: print options foreach my $option (sort keys(%opt)) { DEBUG4("\$opt{$option}=$opt{$option}") if (defined($opt{$option})); } foreach my $option (sort keys(%{$opt{set}})) { DEBUG4("\$opt{set}{$option}=$opt{set}{$option}") if (defined($opt{set}{$option})); } return $OK; } #--- #--- short usage as quick reference #--- sub short_usage { print < [-n name] [-t timeout] [-T TIMEOUT] [-r level] [-l libexec_path] [-s option=value] $MYSELF [-h | --help] [-hh extended help] [-hhh complete help] $MYSELF [-v | --verbose] $MYSELF [-V | --version] [ more infos on http://my-plugin.de/check_multi ] SHORTEOF } #--- #--- long usage as detailed help (if anything else fails: read the instruction) #--- sub long_usage { print < specify level of details in output (level is binary coded, just sum up all options) default:$opt{set}{report} see more details with extended help option -hh -s, --set