You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4534 lines
152 KiB
Perl
4534 lines
152 KiB
Perl
#!/usr/local/bin/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 (0 && (!$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 (0) {
|
|
$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 (1) {
|
|
eval("use FindBin");
|
|
if (! $@) {
|
|
$opt{set}{libexec}="$FindBin::Bin";
|
|
unshift @INC, "$FindBin::Bin";
|
|
}
|
|
}
|
|
}
|
|
use lib "/usr/local/monitoring/check_multi-0.26/libexec";
|
|
#-------------------------------------------------------------------------------
|
|
#--- vars ----------------------------------------------------------------------
|
|
#-------------------------------------------------------------------------------
|
|
$timezero=time;
|
|
$MYSELF="check_multi";
|
|
$nagios=lc("nagios");
|
|
$NAGIOS=uc("nagios");
|
|
$Nagios=ucfirst(lc("nagios"));
|
|
$VERSION='check_multi_0.26_506_2011-11-18-20:28'.
|
|
"\nconfigure '--prefix=/usr/local/monitoring/check_multi-0.26' '--with-snmp_community=IMS_m0nit0r'";
|
|
#
|
|
#--- 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 => 0,
|
|
# place tags at the beginning of list entries (e.g. tag_host_servicedesc)
|
|
add_tag_to_list_entries => 1,
|
|
# cancel child check before it can reach global timeout
|
|
cancel_before_global_timeout => 0,
|
|
# checkresults_dir
|
|
checkresults_dir => "/usr/local/monitoring/check_multi-0.26/var/spool/checkresults",
|
|
# child_interval
|
|
child_interval => "0.0",
|
|
# update config file (if HTTP/FTP/etc) once a day (in seconds)
|
|
cmdfile_update_interval => 86400,
|
|
# should check_multi collapse child check_multi checks per default?
|
|
collapse => 1,
|
|
# complain about undefined macros instead of only silently removing
|
|
complain_unknown_macros => 1,
|
|
# path to check_multi config directory
|
|
config_dir => "/usr/local/monitoring/check_multi-0.26/etc/check_multi",
|
|
# how many rows should be displayed for cumulate statement?
|
|
cumulate_max_rows => 5,
|
|
# should cumulate ignore zero values?
|
|
cumulate_ignore_zero => 1,
|
|
# 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 => 0,
|
|
# show child checks also in status view (only in HTML mode)
|
|
extinfo_in_status => "0",
|
|
# print extended perfdata, count of states and overall state
|
|
extended_perfdata => "0",
|
|
# flag which determines if feed_passive services should be autocreated
|
|
# based on multi-child tags
|
|
feed_passive_autocreate => "1",
|
|
# directory to contain automatically created service definitions
|
|
# for passive feeded check_multi services
|
|
feed_passive_dir_permissions => "41777",
|
|
# directory to contain automatically created service definitions
|
|
# for passive feeded check_multi services
|
|
feed_passive_dir => "/usr/local/monitoring/check_multi-0.26/etc/check_multi/feed_passive",
|
|
# standard extension of check_multi command files,
|
|
# to be searched in directories
|
|
file_extension => "cmd",
|
|
# if set, return UNKNOWN if command file is not found
|
|
ignore_missing_cmd_file => 0,
|
|
# characters to cleanup from command files
|
|
illegal_chars => "
|
|
",
|
|
# path to Nagios images
|
|
image_path => "/nagios/images",
|
|
# indentation character(s)
|
|
indent => " ",
|
|
# child checks indented?
|
|
indent_label => 1,
|
|
# plugin directory to be added to check_multi search path
|
|
libexec => "/usr/local/monitoring/check_multi-0.26/libexec",
|
|
# livestatus socket
|
|
livestatus => "/usr/local/monitoring/check_multi-0.26/var/rw/live",
|
|
# loose (German) performance data (replace commata by decimal points)
|
|
loose_perfdata => "1",
|
|
# pnp_version needed for mouseover: 0.6,0,4
|
|
perfdata_pass_through => "0",
|
|
# pnp_version needed for mouseover: 0.6,0,4
|
|
pnp_version => "0.6",
|
|
# label to be shown in output: <name> OK, ...
|
|
name => "",
|
|
# what RC should be returned if no checks are defined
|
|
no_checks_rc => 3,
|
|
# see tag_notes_link: URL to be added
|
|
notes_url => "",
|
|
# objects.cache path
|
|
objects_cache => "/usr/local/monitoring/check_multi-0.26/var/objects.cache",
|
|
# objects.cache delimiter character
|
|
objects_cache_delimiter => ",",
|
|
# omd environment
|
|
omd_environment => 0,
|
|
# create persistent data
|
|
persistent => 0,
|
|
# path to additional plugins
|
|
plugin_path => "/usr/local/monitoring/nagios/libexec",
|
|
# PNP URL addon
|
|
pnp_add2url => "",
|
|
# PNP URL
|
|
pnp_url => "/pnp4nagios",
|
|
# report option, binary coded as sum of detail options
|
|
report => 13,
|
|
# report option, binary coded as sum of detail options
|
|
report_inherit_mask => -1,
|
|
#--- 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 => "/usr/local/monitoring/check_multi-0.26/sbin/send_nsca",
|
|
#--- send_nsca_cfg: path to config file
|
|
send_nsca_cfg => "/usr/local/monitoring/check_multi-0.26/etc/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 => 11,
|
|
#--- 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 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 => 3,
|
|
# snmp_community
|
|
snmp_community => "IMS_m0nit0r",
|
|
# snmp_community
|
|
snmp_port => "161",
|
|
# path to status.dat
|
|
status_dat => "/usr/local/monitoring/check_multi-0.26/var/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 => "",
|
|
# frame target for action_url and notes_url
|
|
target => "_self",
|
|
# internal development and test mode
|
|
test => 0,
|
|
# child check timeout (small t)
|
|
timeout => 11,
|
|
# global check_multi timeout (BIG T)
|
|
TIMEOUT => 60,
|
|
# directory where check_multi stores its temporary output files
|
|
tmp_dir => "/tmp/check_multi",
|
|
# temporary etc dir for local copies of configuration files
|
|
tmp_etc => "/tmp/check_multi/etc",
|
|
# octal permissions of tmp_dir
|
|
tmp_dir_permissions => "41777",
|
|
# 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 => 0,
|
|
# 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>) { $stdin.=$_; }
|
|
DEBUG3("found <STDIN> >$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=<hostname> -s SERVICEDESC=<service description>\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 <<SHORTEOF;
|
|
Usage:
|
|
$MYSELF -f <config file> [-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 <<LONGEOF;
|
|
Common options:
|
|
-f, --filename
|
|
config file which contains commands to be executed
|
|
multiple files can be specified serially
|
|
if filename is a directory, all '.cmd' files will be taken
|
|
(file format follows nrpe style: command[tag]=plugin command line)
|
|
-n, --name
|
|
multi plugin name (shown in output), default:$opt{set}{name}
|
|
-r, --report <level>
|
|
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 <option>=<value>
|
|
<KEY>=<VALUE> - set multi variable \$<KEY>\$ to <VALUE>
|
|
see more details with complete help option -hhh
|
|
-t, --timeout
|
|
timeout for one command, default:$opt{set}{timeout}
|
|
-T, --TIMEOUT
|
|
TIMEOUT for all commands, default:$opt{set}{TIMEOUT}
|
|
-l, --libexec
|
|
path to plugins, default:$opt{set}{libexec}
|
|
-h, --help
|
|
print detailed help screen (extended help with -hh, complete help with -hhh)
|
|
-v, --verbose
|
|
prints debug output (multiple -v extend debug level)
|
|
-V, --version
|
|
print version information
|
|
|
|
Extended command line - command file options also available on command line:
|
|
-x, --execute "command [ tag ] = check_xyz"
|
|
-i, --instant "tag"
|
|
-y, --inventory
|
|
-w, --warning <expression>
|
|
-c, --critical <expression>
|
|
-u, --unknown <expression>
|
|
-o, --ok <expression>
|
|
|
|
LONGEOF
|
|
|
|
}
|
|
|
|
#---
|
|
#--- extended usage - report options
|
|
#---
|
|
sub extended_usage {
|
|
print <<EXTENDED_EOF;
|
|
Extended options:
|
|
-r, --report <level>
|
|
specify level of details in output (level is binary coded, just sum up all options)
|
|
default:$opt{set}{report}
|
|
1: mention service names in plugin_output, e.g.
|
|
"24 plugins checked, 1 critical (http), 0 warning, 0 unknown, 23 ok"
|
|
2: add HTML coloring of output for extinfo
|
|
4: show STDERR (if any) in each line of plugin output
|
|
8: show performance data (with check_multi_style)
|
|
16: show full list of states, normally '0 warning' is omitted
|
|
32: show old type of performance data (without check_multi style)
|
|
64: add explicit status (OK,WARNING,CRITICAL,UNKNOWN) in front of output
|
|
128: add action link if performance data available
|
|
256: XML: print structured XML output
|
|
512: Nagios 2 compatibility: one summary line of output only
|
|
1024: show notes_url
|
|
2048: print Nagios service definition for passive check feeded by check_multi
|
|
4096: send_nsca: all child checks will be reported to Nagios via send_nsca
|
|
8192: checkresults_file: all child checks are reported to Nagios via checkresults_file
|
|
16384: print commands encoded (alternative to config files)
|
|
32768: hide OK state child checks
|
|
|
|
EXTENDED_EOF
|
|
}
|
|
|
|
#---
|
|
#--- details for set options
|
|
#---
|
|
sub detailed_usage {
|
|
print <<DETAILED_EOF;
|
|
Set options:
|
|
-s, --set <option>=<value>
|
|
<KEY>=<VALUE> - set multi variable \$<KEY>\$ to <VALUE>
|
|
action_mouseover=<0|1> - mouse triggers PNP popup chart
|
|
add_tag_to_list_entries=<0|1> - add tag as a prefix to list entries (default:$opt{set}{add_tag_to_list_entries})
|
|
checkresults_dir=<path> - path to Nagios checkresults directory (direct insertion of checkresults)
|
|
child_interval=<time> - time in seconds to sleep between child_checks, may be fraction of seconds (default:$opt{set}{child_interval})
|
|
cmdfile_update_interval=<seconds> - update config file (if HTTP/FTP/...), default:$opt{set}{cmdfile_update_interval}
|
|
collapse=<0|1> - should check_multi collapse child check_multi checks? default:$opt{set}{collapse}
|
|
complain_unknown_macros=<0|1> - flag defines if check_multi shall complain about undefined macros, default:$opt{set}{complain_unknown_macros}
|
|
config_dir=</path/to/config_dir> - path to check_multi config directory, default:$opt{set}{config_dir}
|
|
cumulate_max_rows=<N> - number of rows which cumulate [ TAG ] should display
|
|
cumulate_ignore_zero=<0|1> - should cumulate ignore zero values? default:$opt{set}{cumulate_ignore_zero}
|
|
empty_output_is_unknown=<0|1> - should empty output be flagged as UNKNOWN?, default:$opt{set}{empty_output_is_unknown}
|
|
exec_open3=<0|1> - use open3 method to exec child checks (to be tested though), default:$opt{set}{exec_open3}
|
|
extinfo_in_status=<0|1> - show child checks also in Nagios status.cgi (HTML), default:$opt{set}{extinfo_in_status}
|
|
feed_passive_autocreate=<0|1> - determines if passive feeded service descriptions should be created automatically, default:$opt{set}{feed_passive_autocreate}
|
|
feed_passive_dir=<path> - path to passive service definition tree, default:$opt{set}{feed_passive_dir}
|
|
file_extension=<ext> - standard file extension for config files, default:$opt{set}{file_extension}
|
|
ignore_missing_cmd_file=<0|1> - do not complain if config file missing, default:$opt{set}{ignore_missing_cmd_file}
|
|
illegal_chars=<characters> - specify characters to cleanup from command files
|
|
image_path=</path/to/imagefiles> - relative path to Nagios imagefiles: default:$opt{set}{image_path}
|
|
indent=<characters> - string to indent child checks, default:$opt{set}{indent}
|
|
indent_label=<0|1> - indentation width is child check label width (HTML), default:$opt{set}{indent_label}
|
|
libexec=</path/to/plugins> - directory to be added to search path: default:$opt{set}{libexec}
|
|
livestatus=<livestatus_path> - Where is the livestatus Unix / TCP socket? default:$opt{set}{livestatus}
|
|
loose_perfdata=<0|1> - accept non standard performance data, e.g. german commata, default:$opt{set}{loose_perfdata}
|
|
name=<string> - label to be shown in output: <name> OK, default:$opt{set}{name}
|
|
no_checks_rc=<RC> - which RC should be returned if no checks are defined, default:$opt{set}{no_checks_rc}
|
|
notes_url=<URL> - URL to be added to child check, see Nagios notes_url
|
|
objects_cache=</path/to/objects.cache> - position of nagios objects.cache file, default:$opt{set}{objects_cache}
|
|
objects_cache_delimiter=<delimiter character> - character to separate object cache results, default:$opt{set}{objects_cache_delimiter}
|
|
persistent=<0|1> - run check_multi in persistent mode, default:$opt{set}{persistent}
|
|
pnp_url=</path/to/PNP-CGIs> - specify PNP URL, default:$opt{set}{pnp_url}
|
|
pnp_add2url=<string> - PNP URL addon string, default:$opt{set}{pnp_add2url}
|
|
pnp_version=<0.6|0.4> - pnp_version needed for mouseover popups? default:$opt{set}{pnp_version}
|
|
report=<number> - binary coded report option, default:$opt{set}{report}
|
|
report_inherit_mask=<0|1> - report option will be inherited and masked, default:$opt{set}{report_inherit_mask}
|
|
send_nsca=<path/to/send_nsca> - path to send_nsca binary, default:$opt{set}{send_nsca}
|
|
send_nsca_cfg=</path/to/send_nsca.cfg> - path to send_nsca cfg file, default:$opt{set}{send_nsca_cfg}
|
|
send_nsca_srv=<NSCA server> - server to send NSCA messages to, default:$opt{set}{send_nsca_srv}
|
|
send_nsca_port=<port number> - port where NSCA daemon runs on NSCA server, default:$opt{set}{send_nsca_port}
|
|
send_nsca_timeout=<seconds> - timeout for send_nsca, default:$opt{set}{send_nsca_timeout}
|
|
send_nsca_delim=<string> - delimiter characters to separate NSCA fields, default:$opt{set}{send_nsca_delim}
|
|
service_definition_template=</path/to/template> - template for passive service definition
|
|
status_dat=</path/to/status.dat> - where is Nagios status.dat? default:$opt{set}{status_dat}
|
|
style_plus_minus=<HTML style> - style of HTML plus/minus characters
|
|
suppress_perfdata=<tag1>[,<tag2>][,...] - do not provide perfdata for check tag1,tag2...
|
|
suppress_service=<tag1>[,<tag2>][,...] - do not report service data for check tag1,tag2...
|
|
tag_notes_link=<0|1> - notes URL as link in the child checks TAG (default:$opt{set}{tag_notes_link}
|
|
target=<target> - frame target for action_url and notes_url, default:$opt{set}{target}
|
|
timeout=<seconds> - child check timeout (small t), default:$opt{set}{timeout}
|
|
TIMEOUT=<seconds> - global check_multi timeout (BIG T), default:$opt{set}{TIMEOUT}
|
|
tmp_dir=<directory> - path for temporary files, default:$opt{set}{tmp_dir}
|
|
tmp_dir_permissions=<octal> - permissions of temporary directory, default:$opt{set}{tmp_dir_permissions}
|
|
tmp_etc=<directory> - path for local copies of command files, default:$opt{set}{tmp_etc}
|
|
verbose=<0|1|2> - level of verbosity, from 1 (less verbose) to 3 (much verbose), default:$opt{set}{verbose}
|
|
|
|
DETAILED_EOF
|
|
}
|
|
|
|
#---
|
|
#--- load perl module and set variable for state
|
|
#---
|
|
sub module {
|
|
my $module_name=shift;
|
|
my $fatal=shift;
|
|
my $module_parameters="";
|
|
|
|
#--- split module name and parameters
|
|
if ($module_name=~/(\S+)\s+(.*)\s+/) {
|
|
$module_name=$1;
|
|
$module_parameters=$2;
|
|
}
|
|
|
|
#--- check if module already loaded
|
|
if (defined($opt{set}{"use_$module_name"}) && $opt{set}{"use_$module_name"}) {
|
|
DEBUG3("Module \'$module_name\' already loaded");
|
|
#--- load module
|
|
} elsif (eval("use $module_name $module_parameters;1")) {
|
|
#--- if successful
|
|
$opt{set}{"use_$module_name"}=1;
|
|
DEBUG3("Module \'$module_name\' loaded");
|
|
return 1;
|
|
} else {
|
|
fatal("Perl module \'$module_name\' not available: $@") if ($fatal);
|
|
$opt{set}{"use_$module_name"}=0;
|
|
DEBUG3("Module \'$module_name\' not available");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- numerical sort
|
|
#---
|
|
sub numerically { $a <=> $b }
|
|
|
|
#---
|
|
#--- trim input string if found any chars from trim string
|
|
#---
|
|
sub mytrim {
|
|
my ($src, $trim)=@_;
|
|
DEBUG3("src:\'$src\' trim:\'$trim\'");
|
|
return ($src=~/[$trim]*(.*)[$trim]*/) ? $1 : $src;
|
|
}
|
|
|
|
#---
|
|
#--- simple hexdump function
|
|
#---
|
|
sub hexdump {
|
|
my $bytes_per_line=shift;
|
|
my @bytes=map { sprintf("%02x", $_) } unpack('C*', join(' ',@_));
|
|
my $hexstr="";
|
|
my $line=0;
|
|
while (@bytes) {
|
|
$hexstr.=sprintf "\n%08o %s", $bytes_per_line*$line++, join(' ', splice(@bytes,0,$bytes_per_line));
|
|
}
|
|
return $hexstr;
|
|
}
|
|
|
|
#---
|
|
#--- human readable timestamp with japanese format, therefore sortable
|
|
#---
|
|
sub readable_sortable_timestamp {
|
|
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(shift);
|
|
return sprintf "%04d-%02d-%02d_%02d:%02d:%02d",
|
|
$year+1900, $mon+1, $mday,
|
|
$hour, $min, $sec;
|
|
}
|
|
|
|
#---
|
|
#--- set alarm timeout for child checks
|
|
#---
|
|
sub set_alarm {
|
|
my $no=shift;
|
|
my $timeout=shift;
|
|
|
|
#--- if timeout exceeds global timeout, reduce it accordingly
|
|
if (int(time+$timeout) > int($cmds[0]{timeouttime})) {
|
|
$timeout=int($cmds[0]{timeouttime})-int(time);
|
|
$timeout=1 if ($timeout <= 0);
|
|
DEBUG2(sprintf("set_alarm: $no - time exceeds global timeout with %d seconds, adjusting alarm to $timeout seconds",
|
|
int(time)+int($timeout)-int($cmds[0]{timeouttime})));
|
|
add_error($no,"timeout reduced to $timeout seconds due to global timeout");
|
|
}
|
|
DEBUG3("$no - alarm set to $timeout seconds");
|
|
$cmds[$no]{timeout}=$timeout;
|
|
alarm($timeout);
|
|
}
|
|
|
|
#---
|
|
#--- create unique hash from input
|
|
#---
|
|
sub my_hash {
|
|
my $string=shift;
|
|
my $hash=crypt($string,"hash_$MYSELF");
|
|
$hash=~s/(.)/sprintf("%02x", ord($1))/seg;
|
|
DEBUG3("inputstring \'$string\' -> $hash");
|
|
return $hash;
|
|
}
|
|
|
|
#---
|
|
#--- detection of numbers / strings in input
|
|
#--- gracefully taken from perlmonks (no 408607)
|
|
#---
|
|
sub seems_like_number {
|
|
my $thing = shift;
|
|
use warnings qw/FATAL all/; # Promote warnings to fatal, so
|
|
# they can be trapped. The effect is
|
|
# lexically scoped.
|
|
eval {
|
|
$thing += 0;
|
|
};
|
|
DEBUG4(sprintf "input >$thing< has %sbeen identified as a number", $@ ? "not " : "");
|
|
return $@ ? 0 : 1;
|
|
}
|
|
|
|
#---
|
|
#--- directories should not have separator characters
|
|
#---
|
|
sub valid_dir {
|
|
my $dir=shift;
|
|
my $separator=($^O eq "Win32") ? "\\" : "/";
|
|
$dir=~s#$separator#_#g;
|
|
return $dir;
|
|
}
|
|
|
|
#---
|
|
#--- creates directory (including leading paths) with given permissions
|
|
#---
|
|
sub my_mkdir {
|
|
my ($directory,$perms)=@_;
|
|
$perms="0755" if (!defined($perms));
|
|
my $path="";
|
|
my $separator=($^O eq "Win32") ? "\\" : "/";
|
|
foreach my $part (split($separator,$directory)) {
|
|
$path.=$part.$separator;
|
|
if (! -d "$path") {
|
|
return 0 if (! mkdir("$path", oct("$perms")));
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#---
|
|
#--- return list of n files from a directory
|
|
#---
|
|
sub dir_entries {
|
|
my $dir=shift;
|
|
my $n=shift;
|
|
|
|
#--- open directory
|
|
opendir(my $dirhandle, $dir) || die "can't opendir $dir: $!";
|
|
|
|
#--- read all existing files
|
|
my @entries = grep { ! /^\./ && -f "$dir/$_" } readdir($dirhandle);
|
|
closedir $dirhandle;
|
|
|
|
#--- return last n files
|
|
return (sort { $b<=>$a } @entries)[0..$n-1];
|
|
}
|
|
|
|
|
|
#---
|
|
#--- creates string from all checks: name+type+command
|
|
#---
|
|
sub config_string {
|
|
my $cmds=shift;
|
|
my $string="";
|
|
my $len=0;
|
|
#--- add numnber, name, type and command to string
|
|
for (my $i=0;$i<=$#{$cmds};$i++) {
|
|
$string.=join('_',"_$i",$$cmds[$i]{name},$$cmds[$i]{type},$$cmds[$i]{command});
|
|
}
|
|
#--- replace all non alphanumerical characters with underscores
|
|
$string=~s/[^A-Za-z0-9_-]+/_/g;
|
|
#--- make sure, that there are no duplicate underscores
|
|
$string=~s/__/_/g;
|
|
return $string;
|
|
}
|
|
|
|
#---
|
|
#--- taken from perl cookbook: trim
|
|
#---
|
|
sub trim {
|
|
my @output = @_;
|
|
for (@output) {
|
|
s/^\s+//;
|
|
s/\s+$//;
|
|
}
|
|
return wantarray ? @output : $output[0];
|
|
}
|
|
|
|
#---
|
|
#--- substitute macros a la $HOSTNAME$ from environment
|
|
#---
|
|
sub substitute_macros {
|
|
my ($input)=@_;
|
|
|
|
|
|
if (! $input) {
|
|
DEBUG3("empty input - nothing to do");
|
|
return $input;
|
|
}
|
|
|
|
#--- first pass: replace 'normal' vars (without ':')
|
|
#--- to prepare objects.cache substitution after that
|
|
DEBUG3("checking expression \'$input\'");
|
|
foreach my $match_var (($input=~/\$([$opt{set}{valid_macro_chars}]+)(?=\$)/gi)) {
|
|
if ($ENV{"MULTI_${match_var}"}) {
|
|
DEBUG3("1st run: var - replacing var \'$match_var\' with \'$ENV{\"MULTI_${match_var}\"}\'");
|
|
$input=~s/\$${match_var}\$/$ENV{"MULTI_${match_var}"}/gi;
|
|
} elsif ($ENV{"${NAGIOS}_${match_var}"}) {
|
|
DEBUG3("1st run: var - replacing var \'$match_var\' with \'$ENV{\"${NAGIOS}_${match_var}\"}\'");
|
|
$input=~s/\$${match_var}\$/$ENV{"${NAGIOS}_${match_var}"}/gi;
|
|
} else {
|
|
DEBUG3("1st run: var - var \'$match_var\' matching neither MULTI_var nor ${NAGIOS}_var!");
|
|
}
|
|
}
|
|
DEBUG3("expression \'$input\' after 1st run (nagios var substitution)");
|
|
|
|
#--- NOT case insensitive macro checking (Nagios does not either :-/)
|
|
#--- Note: due to blanks and other separating characters its possible, that
|
|
#--- eval statements with '$' are interpreted as variables. To include
|
|
#--- the last '$' as potential character of the new macro, the backtrack
|
|
#--- operator ?= is used.
|
|
while ($input=~/\$([$opt{set}{valid_macro_chars}]+)(?=\$)/g) {
|
|
my $var=$1;
|
|
|
|
DEBUG3("checking var \'$var\'");
|
|
#--- 1. check for MULTI var
|
|
if (defined($ENV{"MULTI_${var}"})) {
|
|
DEBUG3("2nd run - replacing env var \'MULTI_${var}\' with \'$ENV{\"MULTI_${var}\"}\'");
|
|
$input=~s/\$$var\$/$ENV{"MULTI_${var}"}/g;
|
|
#--- 2. check for nagios var
|
|
} elsif (defined($ENV{"${NAGIOS}_${var}"})) {
|
|
DEBUG3("2nd run - replacing env var \'${NAGIOS}_${var} with \'$ENV{\"${NAGIOS}_${var}\"}\'");
|
|
$input=~s/\$$var\$/$ENV{"${NAGIOS}_${var}"}/g;
|
|
#--- 3. check for own variable $opt{set}{VAR}
|
|
} elsif (defined($opt{set}{"${var}"})) {
|
|
DEBUG3("2nd run - replacing var \'${var}\' with \'$opt{set}{${var}}\'");
|
|
$input=~s/\$$var\$/$opt{set}{"${var}"}/g;
|
|
#--- 4. objects.cache substitution
|
|
} elsif ($var=~/^[a-z_]+:[a-z_]+:[a-z_]+:/) {
|
|
DEBUG3("2nd run - checking objects_cache var \'$var\'");
|
|
my $query=objects_cache_query($var);
|
|
my $substitution=parse_objects_cache($opt{set}{objects_cache},$query);
|
|
DEBUG3("2nd run - replacing objects_cache var \'$var\' with \'$substitution\'");
|
|
$input=~s/\$$var\$/$substitution/g;
|
|
} else {
|
|
DEBUG3("2nd run - \'$var\' was not handled - remove var");
|
|
add_error($no, "Macro \$$var\$ not found") if ($opt{set}{complain_unknown_macros});
|
|
$input=~s/\$$var\$//g;
|
|
}
|
|
}
|
|
DEBUG3("expression \'$input\' after 2nd run: MULTI/NAGIOS/objects.cache substitution");
|
|
return $input;
|
|
}
|
|
|
|
#---
|
|
#--- creates query object to search in objects.cache
|
|
#--- macro format: $type:return-field:search-field:search-value$
|
|
#---
|
|
sub objects_cache_query {
|
|
my ($input)=@_;
|
|
my $query=undef;
|
|
if ($input=~/([a-z_]+):([a-z_]+):([a-z_]+:\S+.*)/) {
|
|
$query->{type}=$1;
|
|
$query->{target}=$2;
|
|
$input=$3;
|
|
while ($input=~/([^:]+):([^:]+)(.*)/) {
|
|
$query->{expr}->{$1}=$2;
|
|
$input=$3;
|
|
}
|
|
DEBUG4("searching for $query->{type} returning field $query->{target}");
|
|
foreach my $key (keys %{$query->{expr}}) {
|
|
DEBUG4("-> key:$key value:$query->{expr}->{$key}");
|
|
}
|
|
} else {
|
|
add_error(0,"objects_cache_query: invalid query expression $input, should be \$type:return-field:key:expr[:key:expr]\$");
|
|
}
|
|
return $query;
|
|
}
|
|
|
|
#---
|
|
#--- parses objects.cache for all objects matching query object
|
|
#---
|
|
sub parse_objects_cache {
|
|
my ($filename,$query)=@_;
|
|
|
|
my %typelist=();
|
|
my $objectcount=0;
|
|
my $type="";
|
|
my @results=();
|
|
my $temp=undef;
|
|
unless (open(OBJECTS_CACHE,"$filename")) {
|
|
add_error(0,"parse_objects_cache: error opening file $filename:$!");
|
|
return "";
|
|
}
|
|
while (<OBJECTS_CACHE>) {
|
|
#--- begin of object, determine type
|
|
if (/^define ([a-z0-9\_\-]+) {$/) {
|
|
$type="$1";
|
|
$typelist{$type}++;
|
|
$objectcount++;
|
|
} else {
|
|
next;
|
|
}
|
|
|
|
#--- wrong object? continue search
|
|
next if ("$type" ne "$query->{type}");
|
|
|
|
#--- parse particular object
|
|
while (<OBJECTS_CACHE>) {
|
|
if (/^\t([a-z0-9\_\-]+)\s+(.*)$/) {
|
|
$temp->{"$1"}="$2";
|
|
} elsif (/^\t}$/) {
|
|
#--- check if all query elements fit to current object
|
|
my $match=1;
|
|
foreach my $key (keys(%{$query->{expr}})) {
|
|
DEBUG4("searching field $key for match of $query->{expr}->{$key}");
|
|
if (!defined($temp->{$key})) {
|
|
DEBUG4("type $type - field $key not found");
|
|
$match=0;
|
|
last;
|
|
} elsif ($temp->{$key}!~/$query->{expr}->{$key}/) {
|
|
DEBUG4("type $type - $temp->{$key} did not match $query->{expr}->{$key}");
|
|
$match=0;
|
|
last;
|
|
} else {
|
|
DEBUG4("type $type - $temp->{$key} matched");
|
|
}
|
|
}
|
|
if ($match) {
|
|
if (defined($temp->{"$query->{target}"})) {
|
|
push @results, $temp->{"$query->{target}"};
|
|
} else {
|
|
add_error(0,"parse_objects_cache: could not find target field \"".$query->{target}."\"");
|
|
}
|
|
}
|
|
$temp=undef;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
if (!defined($typelist{$query->{type}})) {
|
|
add_error(0,"parse_objects_cache: unknown object type \"$query->{type}\" - valid types are ".join(',',sort keys %typelist));
|
|
} else {
|
|
DEBUG3("$objectcount objects parsed");
|
|
foreach my $type (sort keys %typelist) {
|
|
DEBUG3("$typelist{$type} of $type");
|
|
}
|
|
}
|
|
return (join($opt{set}{objects_cache_delimiter},@results));
|
|
}
|
|
#---
|
|
#--- replaces keywords with real results
|
|
#---
|
|
sub substitute_states {
|
|
my ($input)=@_;
|
|
|
|
#--- empty input?
|
|
if (! $input) {
|
|
DEBUG3("empty input - nothing to do");
|
|
return $input;
|
|
}
|
|
|
|
#--- 1. replace COUNT(WARNING)
|
|
$input=~s/\bCOUNT\s*\(\s*(OK)\s*\)/$rc{count}[$OK]/ig;
|
|
$input=~s/\bCOUNT\s*\(\s*(WARNING)\s*\)/$rc{count}[$WARNING]/ig;
|
|
$input=~s/\bCOUNT\s*\(\s*(CRITICAL)\s*\)/$rc{count}[$CRITICAL]/ig;
|
|
$input=~s/\bCOUNT\s*\(\s*(UNKNOWN)\s*\)/$rc{count}[$UNKNOWN]/ig;
|
|
$input=~s/\bCOUNT\s*\(\s*(ALL)\s*\)/$#cmds/ig;
|
|
|
|
#--- 2. replace all STATES (OK)
|
|
$input=~s/\b(OK)\b/$OK/ig;
|
|
$input=~s/\b(WARNING)\b/$WARNING/ig;
|
|
$input=~s/\b(CRITICAL)\b/$CRITICAL/ig;
|
|
$input=~s/\b(UNKNOWN)\b/$UNKNOWN/ig;
|
|
|
|
#--- 3. replace all vars with RC
|
|
for (my $no=1;$no<=$#cmds;$no++) {
|
|
$input=~s/\b($cmds[$no]{name})\b/$cmds[$no]{rc}/ig;
|
|
}
|
|
#--- 4. replace IGNORE
|
|
$input=~s/\b(IGNORE)\b/(0==1)/ig;
|
|
return $input;
|
|
}
|
|
|
|
|
|
#---
|
|
#--- sets settings as environment variables
|
|
#---
|
|
sub set_env_settings {
|
|
#--- add all set options as environment variables, so client scripts can use it
|
|
foreach my $option (sort keys(%{$opt{set}})) {
|
|
$ENV{"MULTI_set_".$option}=$opt{set}{$option} if defined($opt{set}{$option});
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- sets result environment variables
|
|
#---
|
|
sub set_env_vars {
|
|
my ($no)=@_;
|
|
|
|
my $name=($no==0)?"head":$cmds[$no]{name};
|
|
|
|
if (defined($cmds[$no]{output})) {
|
|
$ENV{"MULTI_$name"}="$cmds[$no]{output}";
|
|
} elsif (defined(error($no))) {
|
|
$ENV{"MULTI_$name"}=error($no);
|
|
}
|
|
$ENV{"MULTI_ERROR_$name"}=error($no);
|
|
$ENV{"MULTI_STATE_$name"}="$cmds[$no]{rc}";
|
|
$ENV{"MULTI_LABEL_$name"}="$def{label}{$cmds[$no]{rc}}";
|
|
$ENV{"MULTI_${no}"}="$cmds[$no]{output}";
|
|
$ENV{"MULTI_${no}_STATE"}="$cmds[$no]{rc}";
|
|
$ENV{"MULTI_${no}_RC"}="$cmds[$no]{rc}";
|
|
$ENV{"MULTI_${no}_LABEL"}="$def{label}{$cmds[$no]{rc}}";
|
|
$ENV{"MULTI_${no}_RC_LABEL"}=($cmds[$no]{rc}<=$UNKNOWN)?$def{label}{$cmds[$no]{rc}}:"INVALID";
|
|
|
|
DEBUG4(
|
|
sprintf "environment vars set\n".
|
|
"1. MULTI_$name=%s\n".
|
|
"2. MULTI_STATE_$name=%s\n".
|
|
"3. MULTI_ERROR_$name=%s\n".
|
|
"4. MULTI_LABEL_$name=%s\n".
|
|
"5. MULTI_${no}=%s\n".
|
|
"6. MULTI_${no}_STATE=%s\n".
|
|
"7. MULTI_${no}_LABEL=%s\n",
|
|
defined($ENV{"MULTI_$name"})?$ENV{"MULTI_$name"}:"undef",
|
|
defined($ENV{"MULTI_STATE_$name"})?$ENV{"MULTI_STATE_$name"}:"undef",
|
|
defined($ENV{"MULTI_ERROR_$name"})?$ENV{"MULTI_ERROR_$name"}:"undef",
|
|
defined($ENV{"MULTI_LABEL_$name"})?$ENV{"MULTI_LABEL_$name"}:"undef",
|
|
defined($ENV{"MULTI_${no}"})?$ENV{"MULTI_${no}"}:"undef",
|
|
defined($ENV{"MULTI_${no}_STATE"})?$ENV{"MULTI_${no}_STATE"}:"undef",
|
|
defined($ENV{"MULTI_${no}_LABEL"})?$ENV{"MULTI_${no}_LABEL"}:"undef",
|
|
);
|
|
}
|
|
|
|
#---
|
|
#--- get environment vars
|
|
#---
|
|
sub get_env_vars {
|
|
my ($pattern)=@_;
|
|
my @result=();
|
|
foreach my $key (sort keys(%ENV)) {
|
|
push @result, "$key=$ENV{$key}" if ($key=~/$pattern/);
|
|
}
|
|
@result;
|
|
}
|
|
|
|
#---
|
|
#--- determines hostname from env vars or parameters
|
|
#---
|
|
sub get_hostname {
|
|
my ($trigger)=shift || "ALL";
|
|
my $hostname="";
|
|
my $origin="";
|
|
if ($trigger=~/HOSTNAME|ALL/ && $ENV{"MULTI_HOSTNAME"}) {
|
|
$hostname=$ENV{"MULTI_HOSTNAME"};
|
|
$origin="MULTI_HOSTNAME";
|
|
} elsif ($trigger=~/HOSTADDRESS|ALL/ && $ENV{"MULTI_HOSTADDRESS"}) {
|
|
$hostname=$ENV{"MULTI_HOSTADDRESS"};
|
|
$origin="MULTI_HOSTADDRESS";
|
|
} elsif ($trigger=~/HOSTNAME|ALL/ && $ENV{"${NAGIOS}_HOSTNAME"}) {
|
|
$hostname=$ENV{"${NAGIOS}_HOSTNAME"};
|
|
$origin="${NAGIOS}_HOSTNAME";
|
|
} elsif ($trigger=~/HOSTADDRESS|ALL/ && $ENV{"${NAGIOS}_HOSTADDRESS"}) {
|
|
$hostname=$ENV{"${NAGIOS}_HOSTADDRESS"};
|
|
$origin="${NAGIOS}_HOSTADDRESS";
|
|
} elsif ($trigger=~/HOSTNAME|ALL/) {
|
|
$hostname=`uname -n`;
|
|
$origin="uname -n";
|
|
} elsif ($trigger=~/HOSTADDRESS/) {
|
|
$hostname="127.0.0.1";
|
|
$origin="localhost";
|
|
}
|
|
chomp($hostname);
|
|
DEBUG3("Hostname is derived from $origin: $hostname");
|
|
return $hostname;
|
|
}
|
|
|
|
#---
|
|
#--- print debug message (see Macro DEBUG)
|
|
#---
|
|
sub debug_message {
|
|
#my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = caller(1);
|
|
my @c0=caller(0); my @c1=caller(1);
|
|
foreach (@_) {
|
|
if ($opt{set}{verbose}>2) {
|
|
printf "%08.5f %s ($c0[2]): ", time-$timezero, ($c1[3])?$c1[3]:"";
|
|
}
|
|
print "$_\n";
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- install signal handlers
|
|
#---
|
|
sub install_signal_handler {
|
|
my ($handler, @signals)=@_;
|
|
my $all_signals=join(' ',sort keys %SIG);
|
|
#print("install_signal_handler: signals to install: ".join(' ',@signals)."\n");
|
|
#print("install_signal_handler: signals available: $all_signals\n");
|
|
foreach my $signal (@signals) {
|
|
if ($all_signals=~/$signal/) {
|
|
#print("install_signal_handler: installing handler for signal $signal\n");
|
|
$SIG{$signal} = \&signal_handler;
|
|
} else {
|
|
#print("install_signal_handler: signal $signal not existing\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- got signal? report what we have and terminate savely
|
|
#---
|
|
sub signal_handler {
|
|
my $signal=$_[0]; chomp $signal;
|
|
my @loc=caller(1);
|
|
|
|
#--- reinstall signal_handler (just paranoid ;)
|
|
install_signal_handler(\install_signal_handler, $signal);
|
|
|
|
#--- do reports before quitting
|
|
add_error(0,"$signal generated at line $loc[2] in $loc[1] ($loc[3])");
|
|
DEBUG4("with message $signal");
|
|
&global_result_rating;
|
|
DEBUG4("RC code $cmds[0]{rc}");
|
|
&report_all;
|
|
|
|
#--- cleanup end exit
|
|
unlink $tmp_stdout, $tmp_stderr if (!$opt{set}{exec_open3});
|
|
|
|
#--- exit with predefined exit code or with rc of parent
|
|
my $RC=(defined($opt{set}{signal_rc})) ? $opt{set}{signal_rc} : $cmds[0]{rc};
|
|
DEBUG4("signal_rc is $opt{set}{signal_rc} - exiting with RC code $RC");
|
|
exit($RC);
|
|
}
|
|
|
|
#---
|
|
#--- add error(s) to error list
|
|
#---
|
|
sub add_error {
|
|
my ($no)=shift;
|
|
while (@_) {
|
|
my $error=shift;
|
|
next if (!$error); # skip empty entries
|
|
$error=~s/[$opt{set}{illegal_chars}]*//g if ($opt{set}{illegal_chars});
|
|
$error=~s/\n//g;
|
|
chomp($error);
|
|
HTML::Entities::encode_entities($error)
|
|
if ($opt{set}{report} & $DETAIL_HTML && module("HTML::Entities"));
|
|
|
|
if ($no >= 0 && $no <= $#cmds) {
|
|
push @{$cmds[$no]{error}}, $error;
|
|
debug_message("check [$no] - added error message \'$error\'") if ($opt{set}{verbose}>=3);
|
|
} else {
|
|
add_error(0,"add_error: invalid check index $no encountered");
|
|
}
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- return error from error list
|
|
#---
|
|
sub error {
|
|
my ($no)=shift;
|
|
my ($separator)=shift || ',';
|
|
return "" if ($no < 0 || $no > $#cmds || ! ref($cmds[$no]{error}));
|
|
my $errmsg=join($separator,@{$cmds[$no]{error}});
|
|
return ($errmsg)?" [$errmsg]":"";
|
|
}
|
|
|
|
#---
|
|
#--- fatal - immediate exit with UNKNOWN state
|
|
#---
|
|
sub fatal {
|
|
DEBUG3("exit with error message \'@_\'");
|
|
print "$MYSELF fatal error: @_\n";
|
|
exit $UNKNOWN;
|
|
}
|
|
|
|
#---
|
|
#--- create unique tmpfile and try to create it
|
|
#---
|
|
sub get_tmpfile {
|
|
my ($path,$prefix)=@_;
|
|
my $attempt=0;
|
|
my $tmpfile="";
|
|
|
|
#--- check existance of path and create it if necessary
|
|
if (! my_mkdir($path,$opt{set}{tmp_dir_permissions})) {
|
|
add_error(0,"get_tmpfile: error creating tmp_path $path with permissions $opt{set}{tmp_dir_permissions}:$!");
|
|
return "";
|
|
}
|
|
|
|
#--- do 5 attempts to create tmpfile
|
|
while ($attempt++ < 5) {
|
|
my $suffix=int(rand(89999))+10000;
|
|
$tmpfile="$path/$prefix.$suffix";
|
|
next if (-f $tmpfile);
|
|
if (open(TMP,">$tmpfile")) {
|
|
close TMP;
|
|
DEBUG4("created $tmpfile");
|
|
return $tmpfile;
|
|
}
|
|
}
|
|
fatal("get_tmpfile: giving up creating $tmpfile:$!");
|
|
}
|
|
|
|
#---
|
|
#--- read file and return its contents
|
|
#---
|
|
sub readfile {
|
|
my ($filename)=@_;
|
|
unless (open(FILE,$filename)) {
|
|
add_error(0,"readfile: error opening $filename:$!");
|
|
return "";
|
|
}
|
|
my @lines=<FILE>;
|
|
close(FILE);
|
|
return join("", @lines);
|
|
}
|
|
|
|
#---
|
|
#--- writes file and return its contents
|
|
#---
|
|
sub writefile {
|
|
my ($filename,@lines)=@_;
|
|
unless (open(FILE,">$filename")) {
|
|
add_error(0,"writefile: error opening $filename:$!");
|
|
return "";
|
|
}
|
|
print FILE @lines;
|
|
close(FILE);
|
|
return join("", @lines);
|
|
}
|
|
|
|
#---
|
|
#--- parse command file and call line parser
|
|
#---
|
|
sub parse_files {
|
|
my ($filenames)=@_; # allow multiple filenames (array reference)
|
|
my (@lines)=();
|
|
|
|
#--- loop over filenames
|
|
foreach my $filename (@{$filenames}) {
|
|
|
|
#--- encoded flat file
|
|
if ($filename=~/%0A/) {
|
|
$filename=~s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
|
|
@lines=(split(/\n/,$filename));
|
|
push @lines, ""; # add empty line to avoid last line eval problem
|
|
DEBUG3("-" x 80);
|
|
DEBUG3("parsing encoded $filename with $#lines lines");
|
|
DEBUG3("-" x 80);
|
|
#--- error opening file
|
|
} elsif (!open(FILE, $filename)) {
|
|
next if ($opt{set}{ignore_missing_cmd_file});
|
|
add_error(0,"parse_files: cannot open config file $filename: $!");
|
|
$cmds[0]{rc}=$UNKNOWN; # head RC to UNKNOWN
|
|
$cmds[0]{state}[$OK]="0==1"; # OK: false
|
|
next;
|
|
#--- read file
|
|
} else {
|
|
@lines=<FILE>;
|
|
push @lines, ""; # add empty line to avoid last line eval problem
|
|
close FILE;
|
|
DEBUG3("-" x 80);
|
|
DEBUG3("parsing file $filename with $#lines lines");
|
|
DEBUG3("-" x 80);
|
|
}
|
|
|
|
parse_lines(@lines);
|
|
|
|
}
|
|
return $#cmds+1;
|
|
}
|
|
|
|
|
|
#---
|
|
#--- parse command array and fill %cmds structure
|
|
#---
|
|
sub parse_lines {
|
|
my @lines=@_;
|
|
my ($line,$cmd,$type,$name,$plugin,$pplugin,$expr,$lineno,$i,$last_command);
|
|
|
|
$lineno=0; # count sourcefile lines
|
|
DEBUG4("starting with " . $#cmds . " command(s)");
|
|
LINE: while (@lines) {
|
|
|
|
$line = shift(@lines);
|
|
chomp($line);
|
|
$line = trim($line);
|
|
|
|
#--- encode lines
|
|
if ($opt{set}{report} & $DETAIL_ENCODE_COMMANDS) {
|
|
$line=~s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
|
|
print $line;
|
|
next LINE;
|
|
}
|
|
|
|
#--- count lines
|
|
$lineno++;
|
|
|
|
#--- skip empty lines and comments
|
|
next LINE if ($line=~/^\s*$/);
|
|
next LINE if ($line=~/^\s*#/);
|
|
|
|
#--- remove unwanted characters from input line
|
|
if ($opt{set}{illegal_chars} && $line=~/$opt{set}{illegal_chars}/) {
|
|
DEBUG2("found illegal characters in line $lineno, removing them");
|
|
$line=~s/$opt{set}{illegal_chars}//g;
|
|
}
|
|
|
|
#--- remove trailing backslash if any (compatibility mode)
|
|
$line=~s/\\\s*$// if ($line=~/\\\s*/);
|
|
|
|
#--- header line (regex contains all allowed commands)
|
|
if (($type,$name,$cmd)=($line=~/^\s*($opt{cmdregex})\s*\[\s*(\S[$opt{set}{valid_tag_chars}]*\S|\S{1})\s*\]\s*=\s*(.*)\s*/i)) {
|
|
|
|
DEBUG4("command detected: command:$1 tag:$2 rest:$3");
|
|
#--- remove remaining blanks
|
|
$name=trim($name);
|
|
|
|
#--- macros?
|
|
$name=substitute_macros($name);
|
|
|
|
#--- still macro '$' in name? then macro was not substituted
|
|
if ($name=~/\$([^\$]+)\$/) {
|
|
add_error(0,"parse_lines: tag \'$name\' is invalid - macro \$$1\$ not found");
|
|
next LINE;
|
|
} elsif ($name eq "") {
|
|
add_error(0,"parse_lines: empty tag \'\' is invalid");
|
|
next LINE;
|
|
} elsif ($name=~/^[0-9]+$/) {
|
|
add_error(0,"parse_lines: pure numerical tag \'$name\' is invalid");
|
|
next LINE;
|
|
} elsif ($name=~/[\[\]]+/) {
|
|
add_error(0,"parse_lines: tag \'$name\' is invalid - [ brackets ] are not allowed");
|
|
next LINE;
|
|
}
|
|
|
|
#--- store vars, last_command contains index of cmd or name of state
|
|
$last_command=parse_header($type,$name,$cmd,$lineno);
|
|
|
|
#--- proper header line with invalid keyword
|
|
} elsif ($line=~/^\s*(\w+)\s*\[\s*(\S[$opt{set}{valid_tag_chars}]*\S|\S{1})\s*\]\s*=\s*(.*)\s*/) {
|
|
DEBUG4("invalid keyword detected: $1");
|
|
add_error(0,"Invalid keyword \'$1\' specified, valid are \'$opt{cmdregex}\'");
|
|
return;
|
|
|
|
#--- continuation line for previous command
|
|
} else {
|
|
if (!defined($last_command)) {
|
|
DEBUG4("continuation line detected - no allowed header before: $line");
|
|
add_error(0,"parse_lines: invalid format in line $lineno: $line");
|
|
next LINE;
|
|
} elsif ($last_command=~/OK|UNKNOWN|WARNING|CRITICAL/i) {
|
|
DEBUG4("continuation line detected: state continuation for ".lc($last_command).": $line");
|
|
$cmds[0]{state}[$def{code}{lc($last_command)}].=$line;
|
|
} elsif ($last_command > 0) {
|
|
DEBUG4("continuation line detected, appending \'$line\' to command \'$cmds[$last_command]{command}\'");
|
|
$cmds[$last_command]{command}.=' '.$line;
|
|
} else {
|
|
DEBUG4("continuation line detected: invalid format: $line");
|
|
add_error(0,"parse_lines: invalid format in line $lineno: $line");
|
|
next LINE;
|
|
}
|
|
}
|
|
}
|
|
DEBUG4("end - now we have " . $#cmds . " command(s)");
|
|
}
|
|
|
|
#---
|
|
#--- fills child check attributes related to the type of check
|
|
#---
|
|
sub parse_header {
|
|
my ($type,$name,$cmd,$lineno)=@_;
|
|
my ($i,$key,$host,$service);
|
|
|
|
#--- split name::key. If key is not available, its empty.
|
|
($name,$key)=split(/::/,$name);
|
|
$key="" if (!defined($key));
|
|
$key=lc($key); # force lower key for keys
|
|
DEBUG4("type:$type name:$name key:$key cmd:$cmd lineno:$lineno");
|
|
|
|
#--- $name = 'head'? then $i = 0
|
|
if ($name eq "HEAD") {
|
|
DEBUG4("head specified - settings values for \$cmds[0]");
|
|
$i=0;
|
|
|
|
#--- overloading: if child check with same name exists,
|
|
#--- overload its attributes. Otherwise append new check
|
|
} elsif ($type=~/($opt{cmdregex})/) {
|
|
for ($i=1; $i<=$#cmds;$i++) {
|
|
last if ($cmds[$i]{name} eq $name);
|
|
}
|
|
if (defined($cmds[$i])) {
|
|
DEBUG2(defined($cmds[$i]{command})
|
|
? "parse_header: overloaded \'$name\' with type $type \'$cmd\'"
|
|
: "parse_header: added \'$name\' with type $type \'$cmd\'"
|
|
);
|
|
}
|
|
}
|
|
|
|
#--- format: 'attribute [ tag::variable ] = value'
|
|
if ($type eq "attribute") {
|
|
#--- 1. 'attribute [ variable ] = value' - update set variable
|
|
if ($key eq "") {
|
|
if (!defined($opt{set}{$name})) {
|
|
DEBUG2("created variable \$opt{set}{$name}=$cmd");
|
|
} else {
|
|
DEBUG2("updated variable \$opt{set}{$name}=$cmd");
|
|
}
|
|
$opt{set}{$name}=$cmd;
|
|
#--- 2. 'attribute [ tag::variable ] = value' - tag not found
|
|
} elsif (! defined($cmds[$i]) || !defined($cmds[$i]{name})) {
|
|
add_error(0,"parse_header: attribute [ ${name}::${key} ] can not be set: child check with name \'$name\' not found");
|
|
return undef;
|
|
#--- 3. 'attribute [ tag::variable ] = value' - update attribute
|
|
} else {
|
|
if (!defined($cmds[$i]{$key})) {
|
|
DEBUG2("key $key attribute \'$cmd\' inserted into object $name (\$cmds[$i])");
|
|
} else {
|
|
DEBUG2("key $key attribute \'$cmd\' changed in object $name (\$cmds[$i]), was: \'$cmds[$i]{$key}\'");
|
|
}
|
|
$cmds[$i]{$key}=$cmd;
|
|
}
|
|
#--- format: 'command [ tag[::plugin] ] = plugin command line'
|
|
} elsif ($type eq "command") {
|
|
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
#--- normally the plugin name is the first token of $cmd
|
|
my $plugin="";
|
|
if ($cmd=~/\s+/) {
|
|
$plugin=(split(/\s+/,"$cmd"))[0];
|
|
} else {
|
|
$plugin=$cmd;
|
|
}
|
|
$plugin=~s/.*\///g; # basename(plugin) (thx Gerhard)
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{name}=$name;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{plugin}=$plugin;
|
|
$cmds[$i]{pplugin}=($key)?$key:$plugin;
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
|
|
#--- format: 'cumulate [ tag[::plugin] ] = expression'
|
|
} elsif ($type eq "cumulate") {
|
|
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{name}=$name;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{plugin}=$type;
|
|
$cmds[$i]{pplugin}="";
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
|
|
#--- format: 'eval [ tag[::plugin] ] = expression'
|
|
} elsif ($type eq "eval") {
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{name}=$name;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{plugin}="eval";
|
|
$cmds[$i]{pplugin}=($key)?$key:"eval";
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[$i]{displayed}=0;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
|
|
#--- format: 'eeval [ tag[::plugin] ] = expression'
|
|
} elsif ($type eq "eeval") {
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{name}=$name;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{plugin}="eval";
|
|
$cmds[$i]{pplugin}=($key)?$key:"eval";
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
|
|
#--- format: 'livestatus [ tag[::plugin] ] = host'
|
|
#--- format: 'livestatus [ tag[::plugin] ] = host:service'
|
|
} elsif ($type eq "livestatus") {
|
|
|
|
if (!$opt{set}{use_monitoring_livestatus}) {
|
|
#--- try to load module Monitoring::Livestatus
|
|
$opt{set}{use_monitoring_livestatus}=1;
|
|
unless (eval "require Monitoring::Livestatus;1") {
|
|
$opt{set}{use_monitoring_livestatus} = 0;
|
|
add_error(0,"parse_header: perl module Monitoring::Livestatus is not installed, could not add check");
|
|
DEBUG2("Monitoring::Livestatus not available:$@");
|
|
return -1;
|
|
}
|
|
}
|
|
#--- livestatus: split host and service from command, format: host:service
|
|
if ((my $host,$service)=($cmd=~/\s*([^:]*)\s*\:\s*(.*)\s*/)) {
|
|
$host=substitute_macros($host);
|
|
$service=substitute_macros($service);
|
|
|
|
DEBUG4("expanding livestatus host:service \'$host:$service\'");
|
|
my @result=expand_livestatus_service($opt{set}{livestatus}, $host, $service);
|
|
|
|
if (!defined($result[0])) {
|
|
add_error(0,"Could not expand host:service \'$host:$service\' from livestatus $opt{set}{livestatus}");
|
|
return -1;
|
|
}
|
|
while(@result) {
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{runtime}=0;
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{host}=shift(@result);
|
|
$cmds[$i]{service}=shift(@result);
|
|
$cmds[$i]{rc}=shift(@result);
|
|
$cmds[$i]{output}=shift(@result);
|
|
$cmds[$i]{performance}=shift(@result);
|
|
$cmds[$i]{plugin}="lifestatus";
|
|
$cmds[$i]{pplugin}=shift(@result);
|
|
if ($cmds[$i]{host} eq "-1") {
|
|
$cmds[$i]{host}="UNKNOWN";
|
|
$cmds[$i]{service}="UNKNOWN";
|
|
}
|
|
$cmds[$i]{name}=($opt{set}{add_tag_to_list_entries})?"${name}_":"";
|
|
$cmds[$i]{name}.="$cmds[$i]{host}_$cmds[$i]{service}";
|
|
$cmds[$i]{name}=~s/[^A-Z0-9_-]+/_/gi;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
DEBUG4("added host:$cmds[$i]{host} / service:$cmds[$i]{service} / output:$cmds[$i]{output}");
|
|
$i++;
|
|
}
|
|
} elsif (($host)=($cmd=~/\s*([^:]*)\s*/)) {
|
|
$host=substitute_macros($host);
|
|
DEBUG4("expanding livestatus host \'$host\'");
|
|
my @result=expand_livestatus_host($opt{set}{livestatus}, $host);
|
|
|
|
if (!defined($result[0])) {
|
|
add_error(0,"Could not expand host \'$host\' from livestatus $opt{set}{livestatus}");
|
|
return -1;
|
|
}
|
|
while(@result) {
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{runtime}=0;
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{host}=shift(@result);
|
|
$cmds[$i]{rc}=shift(@result);
|
|
$cmds[$i]{output}=shift(@result);
|
|
$cmds[$i]{performance}=shift(@result);
|
|
$cmds[$i]{plugin}="lifestatus";
|
|
$cmds[$i]{pplugin}=shift(@result);
|
|
if ($cmds[$i]{host} eq "-1") {
|
|
$cmds[$i]{host}="UNKNOWN";
|
|
}
|
|
$cmds[$i]{name}=($opt{set}{add_tag_to_list_entries})?"${name}_":"";
|
|
$cmds[$i]{name}.="$cmds[$i]{host}";
|
|
$cmds[$i]{name}=~s/[^A-Z0-9_-]+/_/gi;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
DEBUG4("added host:$cmds[$i]{host} / output:$cmds[$i]{output}");
|
|
$i++;
|
|
}
|
|
} else {
|
|
add_error(0,"parse_header: invalid host or service in livestatus line \'$lineno\', format should be \'livestatus [ name ] = host[:service]\'");
|
|
return -1;
|
|
}
|
|
|
|
#--- format: 'snmp [ tag ] = OID'
|
|
} elsif ($type eq "snmp") {
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{name}=$name;
|
|
$cmds[$i]{translate}=0xff;
|
|
if ($cmd=~/\s*([^:]*)\s*\:\s*(.*)\s*\:\s*(.*)\s*/) {
|
|
$cmds[$i]{host}=$1;
|
|
$cmds[$i]{host}=substitute_macros($cmds[$i]{host});
|
|
$cmds[$i]{command}=$2;
|
|
$cmds[$i]{translate}=$3;
|
|
} elsif ($cmd=~/\s*([^:]*)\s*\:\s*(.*)\s*/) {
|
|
$cmds[$i]{host}=$1;
|
|
$cmds[$i]{host}=substitute_macros($cmds[$i]{host});
|
|
$cmds[$i]{command}=$2;
|
|
} else {
|
|
$cmds[$i]{host}=$opt{set}{HOSTNAME};
|
|
$cmds[$i]{command}=$cmd;
|
|
}
|
|
$cmds[$i]{plugin}="snmp";
|
|
$cmds[$i]{pplugin}=($key)?$key:"snmp";
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
|
|
#--- format: 'statusdat [ tag[::plugin] ] = host'
|
|
#--- format: 'statusdat [ tag[::plugin] ] = host, service'
|
|
} elsif ($type eq "statusdat") {
|
|
|
|
$cmd=substitute_macros($cmd);
|
|
|
|
#--- statusdat service: split host and service from command, format: host:service
|
|
if ($cmd=~/\s*([^:]*)\s*\:\s*(.*)\s*/) {
|
|
DEBUG4("host:service format - expanding $1:$2");
|
|
my @result=expand_status_dat_service($opt{set}{status_dat}, $1, $2);
|
|
if (!defined($result[0])) {
|
|
add_error(0,"Could not expand host:service $1:$2 from status_dat $opt{set}{status_dat}");
|
|
return -1;
|
|
}
|
|
while(@result) {
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{plugin}=undef;
|
|
$cmds[$i]{pplugin}=undef;
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[$i]{host}=shift(@result);
|
|
$cmds[$i]{service}=shift(@result);
|
|
$cmds[$i]{performance}="";
|
|
$cmds[$i]{pplugin}="";
|
|
$cmds[$i]{plugin}="statusdat";
|
|
if ($cmds[$i]{host} eq "-1") {
|
|
$cmds[$i]{host}="UNKNOWN";
|
|
$cmds[$i]{service}="UNKNOWN";
|
|
}
|
|
$cmds[$i]{name}=($opt{set}{add_tag_to_list_entries})?"${name}_":"";
|
|
$cmds[$i]{name}.="$cmds[$i]{host}_$cmds[$i]{service}";
|
|
$cmds[$i]{name}=~s/[^A-Z0-9_-]+/_/gi;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
DEBUG4("added host:$cmds[$i]{host} / service:$cmds[$i]{service}");
|
|
if (module("Data::Dumper")) {
|
|
DEBUG4(Dumper($cmds[$i]));
|
|
}
|
|
$i++;
|
|
}
|
|
#--- statusdat host: split host from command, format: host
|
|
} elsif ($cmd=~/\s*([^:]*)\s*/) {
|
|
DEBUG4("host format - expanding $1");
|
|
my @result=expand_status_dat_host($opt{set}{status_dat}, $1);
|
|
if (!defined($result[0])) {
|
|
add_error(0,"Could not expand host $1 from status_dat $opt{set}{status_dat}");
|
|
return -1;
|
|
}
|
|
while(@result) {
|
|
#--- increase counters if no overload
|
|
if (!defined($cmds[$i]{number})) {
|
|
$cmds[0]{nallchecks}++;
|
|
$cmds[0]{nchecks}++;
|
|
$cmds[$i]{number}=$cmds[0]{nchecks};
|
|
}
|
|
|
|
$cmds[$i]{type}=$type;
|
|
$cmds[$i]{command}=$cmd;
|
|
$cmds[$i]{plugin}=undef;
|
|
$cmds[$i]{pplugin}=undef;
|
|
$cmds[$i]{ok}="";
|
|
$cmds[$i]{warning}="";
|
|
$cmds[$i]{critical}="";
|
|
$cmds[$i]{unknown}="";
|
|
$cmds[$i]{rc}=$OK;
|
|
$cmds[$i]{displayed}=1;
|
|
$cmds[$i]{output}="";
|
|
$cmds[$i]{error}=[];
|
|
$cmds[$i]{runtime}=0;
|
|
$cmds[$i]{host}=shift(@result);
|
|
$cmds[$i]{performance}="";
|
|
$cmds[$i]{pplugin}="";
|
|
$cmds[$i]{plugin}="statusdat";
|
|
if ($cmds[$i]{host} eq "-1") {
|
|
$cmds[$i]{host}="UNKNOWN";
|
|
}
|
|
$cmds[$i]{name}=($opt{set}{add_tag_to_list_entries})?"${name}_":"";
|
|
$cmds[$i]{name}.="$cmds[$i]{host}";
|
|
$cmds[$i]{name}=~s/[^A-Z0-9_-]+/_/gi;
|
|
if ($opt{set}{suppress_perfdata} &&
|
|
$opt{set}{suppress_perfdata}=~/\b$name\b/i) {
|
|
$cmds[$i]{process_perfdata}=0;
|
|
DEBUG2("perfdata of [ $name ] will be suppressed");
|
|
} else {
|
|
$cmds[$i]{process_perfdata}=1;
|
|
}
|
|
DEBUG4("added host:$cmds[$i]{host}");
|
|
$i++;
|
|
}
|
|
} else {
|
|
add_error(0,"parse_header: invalid host [or service] in statusdat line \'$lineno\', format should be \'statusdat [ name ] = host[:service]\'");
|
|
return -1;
|
|
}
|
|
|
|
#--- format: state [ {UNKNOWN,WARNING,CRITICAL,OK} ] = <perl expression>
|
|
} elsif ($type eq "state") {
|
|
#DEBUG1("def{code}{$name}:$def{code}{$name}");
|
|
if (!defined($def{code}{$name})) {
|
|
add_error(0,"parse_header: invalid state code specified in line $lineno: $cmd");
|
|
return -1;
|
|
}
|
|
#--- store state expression only if NOT set via commandline
|
|
if (!defined($opt{lc($name)})) {
|
|
DEBUG3("added state{$def{code}{$name}} expression: $cmd");
|
|
$cmds[0]{state}[$def{code}{$name}]=$cmd;
|
|
} else {
|
|
DEBUG3("command line precedence over state expression. Using \$opt{$name}: $opt{$name}");
|
|
}
|
|
$i=$name;
|
|
|
|
#--- format: output [ tag ] = formatstr, parm1, parm2, ...
|
|
} elsif ($type eq "output") {
|
|
my $found=0;
|
|
if ($name=~/^(\d+)$/ && $name<=$#cmds) {
|
|
($cmds[$1]{fmtstr},@{$cmds[$1]{parms}})=split(',',$cmd);
|
|
$found=1;
|
|
} elsif ($name=~/head/i) {
|
|
($cmds[0]{fmtstr},@{$cmds[0]{parms}})=split(',',$cmd);
|
|
$found=1;
|
|
} else {
|
|
for ($i=0; $found==0 && $i<=$#cmds;$i++) {
|
|
if ($cmds[$i]{name} eq $name) {
|
|
($cmds[$i]{fmtstr},@{$cmds[$i]{parms}})=split(',',$cmd);
|
|
$found=1;
|
|
}
|
|
}
|
|
}
|
|
if (!$found) {
|
|
add_error("parse_header: invalid tag output [ $name ] specified in line $lineno, " .
|
|
"possible reason: defined output statement before command / eval statement");
|
|
return -1;
|
|
}
|
|
|
|
#--- no type found? then its invalid
|
|
} else {
|
|
add_error(0,"store_header: unknown command type \'$type\'");
|
|
return -1;
|
|
}
|
|
|
|
#--- return value: either number of changed command or name of state
|
|
return $i;
|
|
}
|
|
|
|
#---
|
|
#--- execute command number $no from %cmds
|
|
#---
|
|
sub exec_command {
|
|
my ($no)=@_;
|
|
|
|
#--- start with proper RC
|
|
$?=0;
|
|
|
|
#--- at runtime: substitute $MACRO$ macros and states
|
|
$cmds[$no]{command}=substitute_macros($cmds[$no]{command});
|
|
chomp($cmds[$no]{command});
|
|
$ENV{"MULTI_${no}_NAME"}=$cmds[$no]{name};
|
|
DEBUG4("[$no] = >$cmds[$no]{command}<");
|
|
|
|
#--- measure command runtime;
|
|
$cmds[$no]{starttime}=time;
|
|
|
|
if ($cmds[$no]{type} eq "command" || $cmds[$no]{type} eq "cumulate") {
|
|
|
|
eval {
|
|
set_alarm($no,$opt{set}{timeout});
|
|
|
|
#--- classic execution with temporary files
|
|
if (!$opt{set}{exec_open3}) {
|
|
|
|
#--- prepare tmpfiles for stdout and stderr
|
|
$tmp_stdout=&get_tmpfile("$opt{set}{tmp_dir}", "${MYSELF}_stdout_$$");
|
|
$tmp_stderr=&get_tmpfile("$opt{set}{tmp_dir}", "${MYSELF}_stderr_$$");
|
|
|
|
#--- execute command and store stdout/stderr/return code
|
|
if (!defined(my $child=fork())) {
|
|
fatal("cannot fork: $!");
|
|
#--- child: execute command
|
|
} elsif ($child==0) {
|
|
#--- assign own process group for child check, thx Henry Rolofs
|
|
setpgrp(0,0) if ($^O!~/Win32/);
|
|
exec("$cmds[$no]{command} 1>$tmp_stdout 2>$tmp_stderr");
|
|
#--- parent - start timeout for child
|
|
} else {
|
|
$SIG{'ALRM'} = sub {
|
|
add_error($no,"timeout encountered after $cmds[$no]{timeout}s");
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
#--- kill both ways: with prepended minus some perl versions kill the
|
|
#--- whole process group while positive numbers kill exact one process
|
|
#--- we want to end all child check processes in order to clean up
|
|
#--- properly after timeouts
|
|
kill -15,$child;
|
|
kill 15,$child;
|
|
sleep 1;
|
|
kill -9,$child;
|
|
kill 9,$child;
|
|
};
|
|
waitpid($child,0);
|
|
alarm(0);
|
|
}
|
|
$cmds[$no]{rc}=($cmds[$no]{rc})?$cmds[$no]{rc}:$? >> 8;
|
|
|
|
#--- store stdout/stderr and cleanup tmpfiles
|
|
$cmds[$no]{output}=readfile($tmp_stdout);
|
|
$cmds[$no]{output}=~s/[$opt{set}{illegal_chars}]*//g if ($opt{set}{illegal_chars});
|
|
chomp $cmds[$no]{output};
|
|
DEBUG3("raw output >$cmds[$no]{output}<");
|
|
DEBUG3("hex output:".hexdump(16,$cmds[$no]{output}));
|
|
add_error($no,readfile($tmp_stderr));
|
|
DEBUG3("raw stderr >".join(',',@{$cmds[$no]{error}})."<");
|
|
DEBUG3("hex stderr:".hexdump(16,join(',',@{$cmds[$no]{error}})));
|
|
unlink $tmp_stdout, $tmp_stderr;
|
|
|
|
#--- new open3 exec (to be tested carefully before getting standard ;-))
|
|
} elsif (module("IPC::Open3",1) && module("IO::Select",1)) {
|
|
my $pid=open3(*CIN,*COUT,*CERR,$cmds[$no]{command});
|
|
close(CIN); # not needed: close STDIN filehandle
|
|
$SIG{CHLD}=sub {
|
|
$cmds[$no]{rc}=$?>>8 if waitpid($pid, 0) > 0;
|
|
DEBUG4("REAPER status $cmds[$no]{rc} on $pid");
|
|
};
|
|
|
|
my $sel=IO::Select->new();
|
|
$sel->add(*CERR,*COUT);
|
|
while (my @ready = $sel->can_read) {
|
|
foreach my $fh (@ready) {
|
|
if (fileno($fh) == fileno(CERR)) {
|
|
add_error($no,scalar <CERR>);
|
|
} else {
|
|
$cmds[$no]{output}.=scalar <COUT>;
|
|
}
|
|
$sel->remove($fh) if eof($fh);
|
|
}
|
|
}
|
|
close(COUT);
|
|
close(CERR);
|
|
chomp $cmds[$no]{output};
|
|
DEBUG3("open3 raw output >$cmds[$no]{output}<");
|
|
DEBUG3("open3 raw stderr >".join(',',$cmds[$no]{error})."<");
|
|
}
|
|
|
|
#--- unknown return code? change it explicitly to UNKNOWN and add error
|
|
#--- this prevents the result rating routine from dealing with erraneous RCs
|
|
#--- but keeps the information
|
|
if (! defined($def{r2s}{$cmds[$no]{rc}})) {
|
|
add_error($no,"RC was $cmds[$no]{rc}!");
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
}
|
|
|
|
#--- remove white chars from output (Wolfgang Barth)
|
|
$cmds[$no]{stdout}=HTML::Entities::encode_entities($cmds[$no]{stdout})
|
|
if ($opt{set}{report} & $DETAIL_HTML && module("HTML::Entities") && $cmds[$no]{stdout});
|
|
|
|
#--- split performance data from standard output
|
|
if ($cmds[$no]{output}=~/\|/ && ! $opt{set}{perfdata_pass_through}) {
|
|
DEBUG4("(1) - output contains \|");
|
|
#--- multiline perfdata
|
|
if ($cmds[$no]{output}=~/([^\n]*)\n(.*)/s) {
|
|
DEBUG4("(2) - output contains \\n");
|
|
my $rest=$2;
|
|
DEBUG4("(3) - \$1:$1");
|
|
DEBUG4("(4) - \$2:$2");
|
|
($cmds[$no]{output},$cmds[$no]{performance})=split(/\|/,$1);
|
|
DEBUG4("(5) - rest:>$rest<");
|
|
if ($rest=~/(.*)\|(.*)/s) {
|
|
$cmds[$no]{output}.="\n$1";
|
|
$cmds[$no]{performance}.=" " if ($cmds[$no]{performance});
|
|
$cmds[$no]{performance}.=join(' ', split(/\n/,$2));
|
|
} else {
|
|
$cmds[$no]{output}.="\n$rest";
|
|
}
|
|
DEBUG4("(6) - output:$cmds[$no]{output} performance:$cmds[$no]{performance}");
|
|
#--- error: multiple '|' in performance data
|
|
} elsif ($cmds[$no]{output}=~/(.*)\|(.*\|.*)/) {
|
|
add_error($no,"Invalid perfdata discarded - multiple pipe chars: \'$2\'");
|
|
$cmds[$no]{output}=$1;
|
|
$cmds[$no]{performance}=undef;
|
|
#--- single line perfdata
|
|
} else {
|
|
($cmds[$no]{output},$cmds[$no]{performance})=split(/\|/,$cmds[$no]{output});
|
|
DEBUG4("performance string: $cmds[$no]{performance}");
|
|
$cmds[$no]{performance}=mytrim($cmds[$no]{performance},"\\s");
|
|
}
|
|
|
|
#--- check performance data and suppress if found errors
|
|
if (($opt{set}{report} & $DETAIL_PERFORMANCE) && $cmds[$no]{process_perfdata}) {
|
|
if (my $errstr=parse_perfdata($cmds[$no]{performance})) {
|
|
add_error($no,"$cmds[$no]{name} perfdata discarded for $errstr");
|
|
$cmds[$no]{performance}=undef;
|
|
}
|
|
}
|
|
#--- silently supress empty or blank perfdata
|
|
$cmds[$no]{performance}=undef if (defined($cmds[$no]{performance}) && $cmds[$no]{performance}=~/^\s*$/);
|
|
}
|
|
|
|
alarm(0);
|
|
};
|
|
} elsif ($cmds[$no]{type} eq "eval" || $cmds[$no]{type} eq "eeval") {
|
|
local $SIG{ALRM} = sub {
|
|
add_error($no,"timeout after $opt{set}{timeout}s");
|
|
$?=$UNKNOWN<<8;
|
|
};
|
|
set_alarm($no,$opt{set}{timeout});
|
|
$cmds[$no]{output}=eval($cmds[$no]{command});
|
|
if ($cmds[$no]{type} eq "eeval") {
|
|
$cmds[$no]{rc}=$?>>8;
|
|
#--- unknown return code? change it explicitly to UNKNOWN
|
|
if (! defined($def{r2s}{$cmds[$no]{rc}})) {
|
|
add_error($no,"RC was $cmds[$no]{rc}!");
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
}
|
|
} elsif ($cmds[$no]{type} eq "eval") {
|
|
$cmds[$no]{rc}=$OK;
|
|
}
|
|
if ($@) {
|
|
chomp($@);
|
|
$cmds[$no]{output}.="[$@]";
|
|
$cmds[$no]{rc}=$WARNING;
|
|
} else {
|
|
if (!defined($cmds[$no]{output})) {
|
|
$cmds[$no]{output}="";
|
|
} else {
|
|
chomp($cmds[$no]{output});
|
|
}
|
|
#--- split performance data from standard output
|
|
if ($cmds[$no]{output}=~/\|/&& ! $opt{set}{perfdata_pass_through}) {
|
|
($cmds[$no]{output},$cmds[$no]{performance})=split(/\|/,$cmds[$no]{output});
|
|
$cmds[$no]{performance}=mytrim($cmds[$no]{performance},"\\s");
|
|
|
|
#--- check performance data and suppress if found errors
|
|
if (($opt{set}{report} & $DETAIL_PERFORMANCE) && $cmds[$no]{process_perfdata}) {
|
|
if (my $errstr=parse_perfdata($cmds[$no]{performance})) {
|
|
add_error($no,"$cmds[$no]{name} perfdata discarded for $errstr");
|
|
$cmds[$no]{performance}=undef;
|
|
}
|
|
}
|
|
}
|
|
DEBUG4("environment var MULTI_$cmds[$no]{name}=$cmds[$no]{output}");
|
|
}
|
|
$cmds[$no]{endtime}=time;
|
|
$cmds[$no]{runtime}=$cmds[$no]{endtime}-$cmds[$no]{starttime};
|
|
alarm(0);
|
|
|
|
#--- set environment variable states
|
|
set_env_vars($no);
|
|
|
|
#--- evaluate single command RC rating
|
|
single_result_rating($no);
|
|
|
|
#--- set environment variable states
|
|
set_env_vars($no);
|
|
|
|
#--- prior exit
|
|
return $cmds[$no]{rc};
|
|
|
|
} elsif ($cmds[$no]{type} eq "statusdat") {
|
|
local $SIG{ALRM} = sub {
|
|
add_error($no,"$cmds[$no]{name}: timeout after $opt{set}{timeout}s");
|
|
$?=$UNKNOWN<<8;
|
|
};
|
|
set_alarm($no,$opt{set}{timeout});
|
|
#--- service check
|
|
if (defined($cmds[$no]{service})) {
|
|
($cmds[$no]{rc}, $cmds[$no]{output}, $cmds[$no]{plugin}, $cmds[$no]{performance})=
|
|
get_status_dat_service($opt{set}{status_dat},$cmds[$no]{host},$cmds[$no]{service});
|
|
if ($cmds[$no]{rc} == -1) {
|
|
add_error($no,"statusdat $cmds[$no]{name}: cannot find service \'$cmds[$no]{service}\' on host \'$cmds[$no]{host}\' in \'$opt{set}{status_dat}\'");
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
}
|
|
#--- host check
|
|
} else {
|
|
($cmds[$no]{rc}, $cmds[$no]{output}, $cmds[$no]{plugin}, $cmds[$no]{performance})=
|
|
get_status_dat_host($opt{set}{status_dat},$cmds[$no]{host});
|
|
if ($cmds[$no]{rc} == -1) {
|
|
add_error($no,"statusdat $cmds[$no]{name}: cannot find host \'$cmds[$no]{host}\' in \'$opt{set}{status_dat}\'");
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
#--- DOWN host becomes CRITICAL check_multi service
|
|
} elsif ($cmds[$no]{rc} == 1) {
|
|
$cmds[$no]{rc}=$CRITICAL;
|
|
#--- UNREACHABLE host becomes UNKNOWN check_multi service
|
|
} elsif ($cmds[$no]{rc} == 2) {
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
}
|
|
}
|
|
} elsif ($cmds[$no]{type} eq "snmp") {
|
|
local $SIG{ALRM} = sub {
|
|
add_error($no,"$cmds[$no]{name}: timeout after $opt{set}{timeout}s");
|
|
$?=$UNKNOWN<<8;
|
|
};
|
|
set_alarm($no,$opt{set}{timeout});
|
|
($cmds[$no]{rc},$cmds[$no]{output})=get_snmp($cmds[$no]{host},$opt{set}{snmp_port},$opt{set}{snmp_community},$cmds[$no]{command},$cmds[$no]{translate});
|
|
alarm(0);
|
|
}
|
|
|
|
$cmds[$no]{endtime}=time;
|
|
$cmds[$no]{runtime}=$cmds[$no]{endtime}-$cmds[$no]{starttime};
|
|
|
|
#--- any oddities during command execution?
|
|
if ($@) {
|
|
#--- timeout encountered: store status
|
|
if ($@ =~ /timeout/) {
|
|
$cmds[$no]{output}="UNKNOWN - $cmds[$no]{plugin} cancelled after timeout ($opt{set}{timeout}s)";
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
$cmds[$no]{output}.=readfile($tmp_stdout);
|
|
add_error($no,readfile($tmp_stderr));
|
|
#--- catchall for unknown errors
|
|
} else {
|
|
alarm(0);
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
add_error($no,"unexpected exception encountered:$@");
|
|
}
|
|
unlink $tmp_stdout, $tmp_stderr;
|
|
} else {
|
|
#--- if there is neither output nor any error message: return UNKNOWN because there went something wrong
|
|
#--- probably the temporary directory is full ;)
|
|
if ($opt{set}{empty_output_is_unknown} &&
|
|
$cmds[$no]{output} eq "" &&
|
|
error($no) eq "" ) {
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
add_error($no,"$cmds[$no]{name}: no output, no stderr - check your plugin and the tmp directory $opt{set}{tmp_dir}]");
|
|
}
|
|
#--- postprocessing for cumulate
|
|
if ($cmds[$no]{type} eq "cumulate") {
|
|
#--- now output contains rows of 'key value' pairs
|
|
#--- and some top rows will be placed into @top
|
|
my @top=get_cumulated_top($cmds[$no]{output},$opt{set}{cumulate_max_rows});
|
|
if ($#top<0) {
|
|
$cmds[$no]{output}="No output.";
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
} else {
|
|
#--- fill new array members hashes
|
|
for (my $i=0;$i<=$#top;$i++) {
|
|
#--- name and output already set
|
|
$top[$i]{command}=$cmds[$no]{command};
|
|
$top[$i]{plugin}=$cmds[$no]{type};
|
|
$top[$i]{pplugin}=$cmds[$no]{plugin};
|
|
$top[$i]{ok}="";
|
|
$top[$i]{warning}="";
|
|
$top[$i]{critical}="";
|
|
$top[$i]{unknown}="";
|
|
$top[$i]{rc}=$OK;
|
|
$top[$i]{number}=$no+$i;
|
|
$top[$i]{displayed}=1;
|
|
$top[$i]{error}=[];
|
|
$top[$i]{feeded}=1;
|
|
$top[$i]{runtime}=0;
|
|
$top[$i]{type}=$cmds[$no]{type};
|
|
$top[$i]{process_perfdata}=1;
|
|
$top[$i]{performance}="$cmds[$no]{name}::$cmds[$no]{type}::$top[$i]{name}=$top[$i]{output}";
|
|
}
|
|
#--- replace original part with new array
|
|
splice(@cmds,$no,1,@top);
|
|
$cmds[0]{nallchecks}+=$#top;
|
|
$cmds[0]{nchecks}+=$#top;
|
|
}
|
|
}
|
|
|
|
#--- set environment variable states
|
|
set_env_vars($no);
|
|
|
|
#--- evaluate single command RC rating
|
|
single_result_rating($no);
|
|
|
|
#--- set environment variable states
|
|
set_env_vars($no);
|
|
|
|
#--- suppress_perfdata set? ignore perfdata
|
|
if ($cmds[$no]{process_perfdata} && $cmds[$no]{performance}) {
|
|
$ENV{"MULTI_PERFDATA_".$cmds[$no]{name}}="$cmds[$no]{performance}";
|
|
DEBUG4("environment var MULTI_PERFDATA_$cmds[$no]{name}=$cmds[$no]{performance}");
|
|
}
|
|
}
|
|
return $cmds[$no]{rc};
|
|
}
|
|
|
|
#---
|
|
#--- read output and cumulate
|
|
#---
|
|
sub get_cumulated_top {
|
|
my ($output, $max_row)=@_;
|
|
my %c=();
|
|
foreach my $line (split('\n',$output)) {
|
|
if ($line=~/\s*(\S*)\s+([\d\.]+)\s*/) {
|
|
#--- zero value? count it if cumulate_ignore_zero=0
|
|
if ($opt{set}{cumulate_ignore_zero}) {
|
|
$c{"$1"}+=$2 if ($2);
|
|
DEBUG4("$1 += $2 -> $c{$1}\n") if ($2);
|
|
} else {
|
|
$c{"$1"}+=$2;
|
|
DEBUG4("$1 += $2 -> $c{$1}\n");
|
|
}
|
|
} else {
|
|
DEBUG2("invalid line format: $line\n");
|
|
}
|
|
}
|
|
my @top_keys = sort { $c{$b}<=>$c{$a} } keys(%c);
|
|
my @result=();
|
|
foreach my $key (@top_keys) {
|
|
last if (--$max_row<0);
|
|
push @result, {};
|
|
$result[$#result]{name}=$key;
|
|
$result[$#result]{output}=$c{$key};
|
|
}
|
|
@result;
|
|
}
|
|
|
|
#---
|
|
#--- get attributes of a particular status.dat service
|
|
#---
|
|
sub get_status_dat_host {
|
|
my ($statusdat_path, $host_name)=@_;
|
|
|
|
if (!$host_name) {
|
|
add_error(0,"get_status_dat_host: empty host_name=$host_name specified");
|
|
return (-1,"");
|
|
}
|
|
if (!defined($status_dat) || !$status_dat) {
|
|
DEBUG4("1st read, creating service tree");
|
|
$status_dat=read_status_dat($statusdat_path);
|
|
if (!defined($status_dat) || !$status_dat) {
|
|
add_error(0,"get_status_dat_host: could not read $statusdat_path");
|
|
return (-1,"");
|
|
}
|
|
} else {
|
|
DEBUG4("subsequent read, reading from service tree");
|
|
}
|
|
if (! defined($status_dat->{hoststatus}->{$host_name})) {
|
|
DEBUG2("did not found RC and output for $host_name");
|
|
return (-1,"");
|
|
} else {
|
|
DEBUG4("found data for $host_name: RC $status_dat->{hoststatus}->{$host_name}->{last_hard_state} and output \'$status_dat->{hoststatus}->{$host_name}->{plugin_output}\', long_output \'$status_dat->{hoststatus}->{$host_name}->{long_plugin_output}\'");
|
|
my $output=$status_dat->{hoststatus}->{$host_name}->{plugin_output}."\n".
|
|
$status_dat->{hoststatus}->{$host_name}->{long_plugin_output};
|
|
chomp $output;
|
|
return (
|
|
$status_dat->{hoststatus}->{$host_name}->{last_hard_state},
|
|
$output,
|
|
$status_dat->{hoststatus}->{$host_name}->{check_command},
|
|
$status_dat->{hoststatus}->{$host_name}->{performance_data},
|
|
);
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- reads list of status.dat hosts and returns all hosts which match host name
|
|
#---
|
|
sub expand_status_dat_host {
|
|
my ($statusdat_path, $host_name)=@_;
|
|
|
|
DEBUG4("expanding $host_name");
|
|
if (!$host_name) {
|
|
add_error(0,"expand_status_dat_host: empty host_name=$host_name specified");
|
|
return (undef,undef);
|
|
}
|
|
if (!defined($status_dat) || !$status_dat) {
|
|
DEBUG4("1st read, creating tree");
|
|
$status_dat=read_status_dat($statusdat_path);
|
|
if (!defined($status_dat) || !$status_dat) {
|
|
add_error(0,"expand_status_dat_host: could not read $statusdat_path");
|
|
return (undef,undef);
|
|
}
|
|
} else {
|
|
DEBUG4("subsequent read, reading from tree");
|
|
}
|
|
|
|
#--- expand simple tokens to REGEX, if necessary
|
|
$host_name=($host_name=~/\/(.*)\//)?$1:'^'.$host_name.'$';
|
|
DEBUG4("$host_name after REGEX preparation");
|
|
|
|
my @result=();
|
|
foreach my $host (sort keys(%{$status_dat->{hoststatus}})) {
|
|
#--- proceed if host and service names do fit
|
|
if ($host=~/$host_name/) {
|
|
push @result,$host;
|
|
DEBUG4("$host matched $host_name");
|
|
} else {
|
|
DEBUG4("$host did not match $host_name");
|
|
}
|
|
}
|
|
DEBUG4("returns " . ($#result+1)/2 . " results");
|
|
return @result;
|
|
}
|
|
|
|
#---
|
|
#--- get attributes of a particular status.dat service
|
|
#---
|
|
sub get_status_dat_service {
|
|
my ($statusdat_path, $host_name, $service_description)=@_;
|
|
|
|
if (!defined($host_name) || !$host_name) {
|
|
add_error(0,"get_status_dat_service: empty host_name specified");
|
|
return (-1,"");
|
|
}
|
|
if (!defined($service_description) || !$service_description) {
|
|
add_error(0,"get_status_dat_service: empty service_description specified");
|
|
return (-1,"");
|
|
}
|
|
if (!$status_dat) {
|
|
DEBUG4("1st read, creating service tree");
|
|
$status_dat=read_status_dat($statusdat_path);
|
|
if (!$status_dat) {
|
|
add_error(0,"get_status_dat_service: could not read $statusdat_path");
|
|
return (-1,"");
|
|
}
|
|
} else {
|
|
DEBUG4("subsequent read, reading from service tree");
|
|
}
|
|
if (! defined($status_dat->{servicestatus}->{$host_name}->{$service_description})) {
|
|
DEBUG2("did not found RC and output for $host_name:$service_description");
|
|
return (-1,"");
|
|
} else {
|
|
DEBUG4("found data for $host_name:$service_description: RC $status_dat->{servicestatus}->{$host_name}->{$service_description}->{last_hard_state} and output \'$status_dat->{servicestatus}->{$host_name}->{$service_description}->{plugin_output}\', long_output \'$status_dat->{servicestatus}->{$host_name}->{$service_description}->{long_plugin_output}\'");
|
|
my $output=
|
|
$status_dat->{servicestatus}->{$host_name}->{$service_description}->{plugin_output}."\n".
|
|
$status_dat->{servicestatus}->{$host_name}->{$service_description}->{long_plugin_output};
|
|
chomp $output;
|
|
return (
|
|
$status_dat->{servicestatus}->{$host_name}->{$service_description}->{last_hard_state},
|
|
$output,
|
|
$status_dat->{servicestatus}->{$host_name}->{$service_description}->{check_command},
|
|
$status_dat->{servicestatus}->{$host_name}->{$service_description}->{performance_data},
|
|
);
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- reads list of status.dat services and returns all services which match host and service name
|
|
#---
|
|
sub expand_status_dat_service {
|
|
my ($statusdat_path, $host_name, $service_description)=@_;
|
|
|
|
DEBUG4("expanding $host_name:$service_description");
|
|
if (!defined($host_name) || !$host_name) {
|
|
add_error(0,"expand_status_dat_service: empty host_name specified");
|
|
return (undef,undef);
|
|
}
|
|
if (!defined($service_description) || !$service_description) {
|
|
add_error(0,"expand_status_dat_service: empty service_description specified");
|
|
return (undef,undef);
|
|
}
|
|
if (!$status_dat) {
|
|
DEBUG4("1st read, creating service tree");
|
|
$status_dat=read_status_dat($statusdat_path);
|
|
if (!$status_dat) {
|
|
add_error(0,"expand_status_dat_service: could not read $statusdat_path");
|
|
return (undef,undef);
|
|
}
|
|
} else {
|
|
DEBUG4("subsequent read, reading from service tree");
|
|
}
|
|
|
|
#--- expand simple tokens to REGEX, if necessary
|
|
$host_name=($host_name=~/\/(.*)\//)?$1:'^'.$host_name.'$';
|
|
$service_description=($service_description=~/\/(.*)\//)?$1:'^'.$service_description.'$';
|
|
DEBUG4("$host_name:$service_description after REGEX preparation");
|
|
|
|
my @result=();
|
|
foreach my $host (sort keys(%{$status_dat->{hoststatus}})) {
|
|
foreach my $service (sort keys(%{$status_dat->{servicestatus}->{$host}})) {
|
|
#--- proceed if host and service names do fit
|
|
if ($host=~/$host_name/ && $service=~/$service_description/) {
|
|
push @result,$host;
|
|
push @result,$service;
|
|
DEBUG4("$host:$service matched $host_name:$service_description");
|
|
} else {
|
|
DEBUG4("$host:$service did not match $host_name:$service_description");
|
|
}
|
|
}
|
|
}
|
|
DEBUG4("returns " . ($#result+1)/2 . " results");
|
|
return @result;
|
|
}
|
|
|
|
#---
|
|
#--- status.dat parsing routing
|
|
#---
|
|
sub read_status_dat {
|
|
my ($statusdat_path)=@_;
|
|
|
|
if (!open(DAT,$statusdat_path)) {
|
|
add_error(0,"read_status_dat: cannot open $statusdat_path:$!");
|
|
return undef;
|
|
}
|
|
|
|
#--- outer loop: look for type ${type}status
|
|
while (<DAT>) {
|
|
#--- read hosts
|
|
if (/^hoststatus\s+\{/) {
|
|
my %r=();
|
|
while (<DAT>) {
|
|
NEXT: if (/^\thost_name=(.+)$/) {
|
|
$r{host_name}=$1;
|
|
} elsif (/^\tcheck_command=([^!]+)/) {
|
|
$r{check_command}=$1;
|
|
chomp($r{check_command});
|
|
} elsif (/^\tlast_hard_state=(.*)/) {
|
|
$r{last_hard_state}=$1;
|
|
} elsif (/^\tplugin_output=(.*)/) {
|
|
$r{plugin_output}=$1;
|
|
} elsif (/^\tlong_plugin_output=(.*)/) {
|
|
chomp($r{long_plugin_output}=$1);
|
|
while (<DAT>) {
|
|
if (/^\t/) {
|
|
goto NEXT;
|
|
} else {
|
|
chomp;
|
|
$r{long_plugin_output}.="\n$_";
|
|
}
|
|
}
|
|
} elsif (/^\tperformance_data=(.*)/) {
|
|
$r{performance_data}=$1;
|
|
} elsif (/^\t\}$/) {
|
|
$status_dat->{hoststatus}->{"$r{host_name}"}->{check_command}=$r{check_command};
|
|
$status_dat->{hoststatus}->{"$r{host_name}"}->{last_hard_state}=$r{last_hard_state};
|
|
$status_dat->{hoststatus}->{"$r{host_name}"}->{long_plugin_output}=$r{long_plugin_output};
|
|
$status_dat->{hoststatus}->{"$r{host_name}"}->{performance_data}=$r{performance_data};
|
|
$status_dat->{hoststatus}->{"$r{host_name}"}->{plugin_output}=$r{plugin_output};
|
|
last;
|
|
}
|
|
}
|
|
#--- read services
|
|
} elsif (/^servicestatus\s+\{/) {
|
|
my %r=();
|
|
while (<DAT>) {
|
|
NEXT: if (/^\thost_name=(.+)$/) {
|
|
$r{host_name}=$1;
|
|
} elsif (/^\tservice_description=(.+)$/) {
|
|
$r{service_description}=$1;
|
|
} elsif (/^\tcheck_command=([^!]+)/) {
|
|
$r{check_command}=$1;
|
|
chomp($r{check_command});
|
|
} elsif (/^\tlast_hard_state=(.*)/) {
|
|
$r{last_hard_state}=$1;
|
|
} elsif (/^\tplugin_output=(.*)/) {
|
|
$r{plugin_output}=$1;
|
|
} elsif (/^\tlong_plugin_output=(.*)/) {
|
|
chomp($r{long_plugin_output}=$1);
|
|
while (<DAT>) {
|
|
if (/^\t/) {
|
|
goto NEXT;
|
|
} else {
|
|
chomp;
|
|
$r{long_plugin_output}.="\n$_";
|
|
}
|
|
}
|
|
} elsif (/^\tperformance_data=(.*)/) {
|
|
$r{performance_data}=$1;
|
|
} elsif (/^\t\}$/) {
|
|
$status_dat->{servicestatus}->{"$r{host_name}"}->{"$r{service_description}"}->{check_command}=$r{check_command};
|
|
$status_dat->{servicestatus}->{"$r{host_name}"}->{"$r{service_description}"}->{last_hard_state}=$r{last_hard_state};
|
|
$status_dat->{servicestatus}->{"$r{host_name}"}->{"$r{service_description}"}->{performance_data}=$r{performance_data};
|
|
$status_dat->{servicestatus}->{"$r{host_name}"}->{"$r{service_description}"}->{plugin_output}=$r{plugin_output};
|
|
$status_dat->{servicestatus}->{"$r{host_name}"}->{"$r{service_description}"}->{long_plugin_output}=$r{long_plugin_output};
|
|
last;
|
|
}
|
|
}
|
|
#--- skip other records
|
|
} elsif (/^(\S+)/) {
|
|
DEBUG4("skipping $1");
|
|
}
|
|
}
|
|
close DAT;
|
|
if (module("Data::Dumper")) {
|
|
DEBUG4(Dumper($status_dat));
|
|
}
|
|
return $status_dat;
|
|
}
|
|
|
|
#---
|
|
#--- reads list of livestatus services and returns all services which match host and service name
|
|
#---
|
|
sub expand_livestatus_service {
|
|
my ($livestatus_path, $host_name, $service_description)=@_;
|
|
|
|
DEBUG4("expanding $host_name:$service_description");
|
|
if (!$host_name || !$service_description) {
|
|
add_error(0,"expand_livestatus_service: empty host_name=$host_name or service_description=$service_description specified");
|
|
return (undef,undef);
|
|
}
|
|
if (!$livestatus_service) {
|
|
DEBUG4("1st read, creating service tree");
|
|
|
|
$livestatus_service=read_livestatus($livestatus_path,"GET services\nColumns: host_name description last_hard_state plugin_output long_plugin_output perf_data check_command\n");
|
|
if (!$livestatus_service) {
|
|
add_error(0,"expand_livestatus_service: could not read $livestatus_path");
|
|
return (undef,undef);
|
|
}
|
|
DEBUG4("$#{$livestatus_service} results");
|
|
} else {
|
|
DEBUG4("subsequent read, reading from service tree");
|
|
}
|
|
|
|
#--- expand simple tokens to REGEX, if necessary
|
|
$host_name=($host_name=~/\/(.*)\//)?$1:'^'.$host_name.'$';
|
|
$service_description=($service_description=~/\/(.*)\//)?$1:'^'.$service_description.'$';
|
|
DEBUG4("$host_name:$service_description after REGEX preparation");
|
|
|
|
my @result=();
|
|
#--- $service points to an array item
|
|
foreach my $service (@{$livestatus_service}) {
|
|
if ($service->{host_name}=~/$host_name/ && $service->{description}=~/$service_description/) {
|
|
push @result,$service->{host_name};
|
|
push @result,$service->{description};
|
|
push @result,$service->{last_hard_state};
|
|
my $output=defined($service->{plugin_output})?$service->{plugin_output}:"";
|
|
chomp($output);
|
|
$output.=defined($service->{long_plugin_output})?"\n$service->{long_plugin_output}":"";
|
|
chomp($output);
|
|
push @result,$output;
|
|
push @result,defined($service->{perf_data})?$service->{perf_data}:"";
|
|
if ($service->{check_command}=~/([^!]+)!/) {
|
|
push @result, $1;
|
|
} else {
|
|
push @result, $service->{check_command};
|
|
}
|
|
DEBUG4("$service->{host_name}:$service->{description} matched $host_name:$service_description:$service->{last_hard_state}");
|
|
} else {
|
|
DEBUG4("$service->{host_name}:$service->{description} did not match $host_name:$service_description");
|
|
}
|
|
}
|
|
DEBUG4("returns " . ($#result+1)/5 . " results:" . join('|',@result) );
|
|
return @result;
|
|
}
|
|
|
|
#---
|
|
#--- reads list of livestatus hosts and returns all matching hosts
|
|
#---
|
|
sub expand_livestatus_host {
|
|
my ($livestatus_path, $host_name)=@_;
|
|
|
|
DEBUG4("expanding $host_name");
|
|
if (!$host_name) {
|
|
add_error(0,"expand_livestatus_host: empty host_name=$host_name specified");
|
|
return (undef,undef);
|
|
}
|
|
if (!$livestatus_host) {
|
|
DEBUG4("1st read, creating host tree");
|
|
|
|
$livestatus_host=read_livestatus($livestatus_path,"GET hosts\nColumns: host_name last_hard_state plugin_output long_plugin_output perf_data check_command\n");
|
|
if (!$livestatus_host) {
|
|
add_error(0,"expand_livestatus_host: could not read $livestatus_path");
|
|
return (undef,undef);
|
|
}
|
|
DEBUG4("$#{$livestatus_host} results");
|
|
} else {
|
|
DEBUG4("subsequent read, reading from host tree");
|
|
}
|
|
|
|
#--- expand simple tokens to REGEX, if necessary
|
|
$host_name=($host_name=~/\/(.*)\//)?$1:'^'.$host_name.'$';
|
|
DEBUG4("$host_name after REGEX preparation");
|
|
|
|
my @result=();
|
|
#--- $host points to an array item
|
|
foreach my $host (@{$livestatus_host}) {
|
|
if ("$host->{host_name}"=~/$host_name/) {
|
|
push @result,$host->{host_name};
|
|
push @result,$host->{last_hard_state};
|
|
my $output=defined($host->{plugin_output})?$host->{plugin_output}:"";
|
|
chomp($output);
|
|
$output.=defined($host->{long_plugin_output})?"\n$host->{long_plugin_output}":"";
|
|
chomp($output);
|
|
push @result,$output;
|
|
push @result,defined($host->{perf_data})?$host->{perf_data}:"";
|
|
if ($host->{check_command}=~/([^!]+)!/) {
|
|
push @result, $1;
|
|
} else {
|
|
push @result, $host->{check_command};
|
|
}
|
|
DEBUG4("$host->{host_name} matched $host_name, state $host->{last_hard_state}");
|
|
} else {
|
|
DEBUG4("$host->{host_name} did not match $host_name");
|
|
}
|
|
}
|
|
DEBUG4("returns " . ($#result+1)/5 . " results");
|
|
return @result;
|
|
}
|
|
|
|
sub read_livestatus {
|
|
my ($livestatus_path, $query)=@_;
|
|
|
|
my $ml=Monitoring::Livestatus->new(
|
|
peer=>$livestatus_path,
|
|
);
|
|
if($Monitoring::Livestatus::ErrorCode) {
|
|
add_error(0,"read_livestatus: $Monitoring::Livestatus::ErrorMessage");
|
|
return undef;
|
|
}
|
|
$ml->errors_are_fatal(0);
|
|
my $livestatus=$ml->selectall_arrayref($query,{Slice => 1});
|
|
if($Monitoring::Livestatus::ErrorCode) {
|
|
add_error(0,"read_livestatus: $Monitoring::Livestatus::ErrorMessage");
|
|
return undef;
|
|
}
|
|
DEBUG4("$#{$livestatus} elements read");
|
|
if (module("Data::Dumper")) {
|
|
DEBUG4(Dumper($livestatus));
|
|
}
|
|
return $livestatus;
|
|
}
|
|
|
|
#---
|
|
#--- read SNMP with Net::SNMP
|
|
#---
|
|
sub get_snmp {
|
|
my ($host,$port,$community,$oid,$translate)=@_;
|
|
|
|
#--- Net::SNMP is mandatory, exit fatal if not available
|
|
module("Net::SNMP",1);
|
|
|
|
my ($session, $error) = Net::SNMP->session(
|
|
-hostname=>$host,
|
|
-community=>$community,
|
|
-translate=>$translate,
|
|
);
|
|
if (!defined $session) {
|
|
DEBUG3("error creating Net::SNMP session: $error");
|
|
return (3,"error creating Net::SNMP session: $error");
|
|
}
|
|
|
|
my $result = $session->get_request(-varbindlist => [ $oid ],);
|
|
|
|
if (!defined $result) {
|
|
$error=$session->error();
|
|
$session->close();
|
|
DEBUG3("Net::SNMP session error: $error");
|
|
return (3,"Net::SNMP session error: $error");
|
|
}
|
|
|
|
DEBUG3("result for $host($port)-$oid: $result->{$oid}");
|
|
$session->close();
|
|
|
|
return (0,$result->{$oid});
|
|
}
|
|
|
|
#---
|
|
#--- eval state rules
|
|
#---
|
|
sub eval_result {
|
|
my ($input)=@_;
|
|
my $input_org=$input;
|
|
my $message="";
|
|
|
|
#--- empty input?
|
|
if (! $input) {
|
|
DEBUG3("empty input - nothing to do");
|
|
return ($UNKNOWN, "eval_result: invalid empty input");
|
|
}
|
|
|
|
#--- at runtime: substitute $MACRO$ macros and $STATES$
|
|
DEBUG4("input before substituting macros: $input");
|
|
$input=substitute_macros($input);
|
|
$input=substitute_states($input);
|
|
DEBUG4("input after substituting macros: $input");
|
|
|
|
#--- evaluate expression
|
|
my $result=eval "($input)";
|
|
|
|
#--- catch error
|
|
if ($@) {
|
|
$message="Evaluation error in \'$input_org\': $@\n";
|
|
$message=~s/\n/ /g;
|
|
DEBUG4("eval error $message");
|
|
return (-1,$message);
|
|
#--- return result
|
|
} else {
|
|
$message="eval_result: input:>$input_org< parsed:>$input< result:>$result<\n";
|
|
$message=~s/\n/ /g;
|
|
DEBUG4($message);
|
|
return ($result,$message);
|
|
}
|
|
}
|
|
|
|
|
|
#---
|
|
#--- split state string into token and return
|
|
#--- array of hashes with start,end,token
|
|
#---
|
|
sub get_state_token {
|
|
my ($state)=@_;
|
|
#
|
|
my @token=();
|
|
my $tno=0;
|
|
my $pos=0;
|
|
my $start=0;
|
|
my $end=0;
|
|
my $instring=0;
|
|
|
|
#--- read string (and one char more for handling of last token)
|
|
for (my $pos=0; $pos<=length($state); $pos++) {
|
|
|
|
my $char=substr($state,$pos,1);
|
|
my $nextchar=($pos<length($state)-1) ? substr($state,$pos+1,1) : "";
|
|
|
|
#--- skip white characters ' ' and '()'
|
|
if ($char=~/[ ()]/) {
|
|
if (!$instring) {
|
|
$start++;
|
|
}
|
|
|
|
#--- '||' or '&&' found: token end detected
|
|
} elsif (($char eq "|" && $nextchar eq "|") ||
|
|
($char eq "&" && $nextchar eq "&") ||
|
|
($char eq "" && $nextchar eq "" )) {
|
|
$token[$tno]{start}=$start;
|
|
$token[$tno]{end}=$end;
|
|
$token[$tno]{token}=substr($state,$start,$end-$start+1);
|
|
$token[$tno]{substituted}=substitute_macros($token[$tno]{token});
|
|
$token[$tno]{substituted}=substitute_states($token[$tno]{substituted});
|
|
($token[$tno]{substate},$token[$tno]{message})=eval_result($token[$tno]{substituted});
|
|
|
|
$tno++;
|
|
$pos+=2;
|
|
$start=$pos;
|
|
$end=$pos;
|
|
$instring=0;
|
|
|
|
#--- normal token char
|
|
} else {
|
|
#--- token init
|
|
if (!$instring) {
|
|
$instring=1;
|
|
$start=$pos;
|
|
$end=$pos;
|
|
} else {
|
|
$end=$pos;
|
|
}
|
|
}
|
|
}
|
|
my $output="\n" . "0123456789" x 8 ."\n$state\n\n";
|
|
$output.=sprintf "%3s %5s %5s %s\n", "No", "Start", "End", "Token";
|
|
|
|
for ($tno=0;$tno<=$#token;$tno++) {
|
|
|
|
#--- squeeze blanks to beautify output
|
|
$token[$tno]{token}=~s/ / /g;
|
|
|
|
$output.=sprintf "%3d %5d %5d >%s<\n",
|
|
$tno,
|
|
$token[$tno]{start},
|
|
$token[$tno]{end},
|
|
$token[$tno]{token};
|
|
}
|
|
#--- debugging
|
|
DEBUG3($output);
|
|
|
|
#--- return array
|
|
@token;
|
|
}
|
|
|
|
#---
|
|
#--- calculate sums from %cmds and %rc
|
|
#---
|
|
sub global_result_rating {
|
|
|
|
#--- measure runtime without reporting ;-)
|
|
$cmds[0]{runtime}=time - $cmds[0]{starttime};
|
|
|
|
#--- count return codes
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
#--- count only displayed (without eval)
|
|
if ($cmds[$no]{displayed}) {
|
|
$rc{count}[$cmds[$no]{rc}]++; # count displayed return codes
|
|
push @{$rc{list}[$cmds[$no]{rc}]},$cmds[$no]{name}; # add plugin to displayed list
|
|
}
|
|
#--- count all child checks
|
|
$rc{count_all}[$cmds[$no]{rc}]++; # count all return codes
|
|
push @{$rc{list_all}[$cmds[$no]{rc}]},$cmds[$no]{name}; # add plugin to all list
|
|
}
|
|
|
|
#--- sort in severity order
|
|
foreach my $index ($OK..$UNKNOWN) {
|
|
$ENV{"MULTI_$def{label}{$index}_COUNT"}=$rc{count}[$index];
|
|
$ENV{"MULTI_$def{label}{$index}_LIST"}=join(',',@{$rc{list}[$index]});
|
|
$ENV{"MULTI_$def{label}{$index}_COUNT_ALL"}=$rc{count_all}[$index];
|
|
$ENV{"MULTI_$def{label}{$index}_LIST_ALL"}=join(',',@{$rc{list_all}[$index]});
|
|
|
|
my $state=$def{s2r}{$index};
|
|
|
|
my ($result,$message)=eval_result($cmds[0]{state}[$state]);
|
|
if (! defined($result) || $result eq "") {
|
|
; # do nothing
|
|
} elsif ($result < 0) {
|
|
add_error(0,"global_result_rating: parsing error ($message)");
|
|
} else {
|
|
$rc{match}[$state]=1;
|
|
$cmds[0]{rc}=$state;
|
|
|
|
}
|
|
}
|
|
#--- set several environment vars for 'head'
|
|
set_env_vars(0);
|
|
}
|
|
|
|
#---
|
|
#--- behave like check_generic: evaluate RCs for single command
|
|
#---
|
|
sub single_result_rating {
|
|
my $no=shift;
|
|
my $old_rc=$cmds[$no]{rc};
|
|
|
|
#--- sort in severity order
|
|
foreach my $index (0..3) {
|
|
|
|
#--- getting state
|
|
my $state=$def{s2r}{$index};
|
|
DEBUG4("examining state $state ($def{llabel}{$state})");
|
|
|
|
#--- did we got a child check RC?
|
|
if ($state == $old_rc) {
|
|
$cmds[$no]{rc}=$old_rc;
|
|
DEBUG4("original RC was $old_rc -> moving this RC to child check RC");
|
|
}
|
|
|
|
#--- no special settings for evaluation? then do not evaluate!
|
|
if (!$cmds[$no]{$def{llabel}{$state}}) {
|
|
DEBUG4("no expression for $def{llabel}{$state} -> no evaluation");
|
|
next;
|
|
} else {
|
|
DEBUG4("expression found for $def{llabel}{$state}: >>>$cmds[$no]{$def{llabel}{$state}}<<<");
|
|
}
|
|
|
|
#my ($result,$message)=eval_result("\'$cmds[$no]{output}\'$cmds[$no]{$def{llabel}{$state}}");
|
|
my ($result,$message)=eval_result("$cmds[$no]{$def{llabel}{$state}}");
|
|
DEBUG4("evaluating expression \"$cmds[$no]{$def{llabel}{$state}}\", result is $result");
|
|
|
|
if (! defined($result) || $result eq "") {
|
|
; # do nothing
|
|
} elsif ($result < 0) {
|
|
add_error(0,"single_result_rating: parsing error $message");
|
|
DEBUG4("parsing error expresstion ($cmds[$no]{output}$cmds[$no]{$state})->($message)");
|
|
} else {
|
|
$cmds[$no]{rc}=$state;
|
|
add_error($no,"$def{label}{$state}: output matched rule \'$cmds[$no]{$def{llabel}{$state}}\'");
|
|
DEBUG4("setting RC of cmds[$no] to $state");
|
|
}
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- start different report routines
|
|
#---
|
|
sub report_all {
|
|
|
|
#--- some debugging first
|
|
DEBUG4("MULTI Environment (sorted):\n\t".join("\n\t",get_env_vars('^MULTI')));
|
|
DEBUG4("${NAGIOS} Environment (sorted):\n\t".join("\n\t",get_env_vars('^${NAGIOS}')));
|
|
|
|
#--- construction site for persistence
|
|
if ($opt{set}{test} && $opt{set}{persistent}) {
|
|
#unless (eval "use Data::Dumper;1") {
|
|
# $opt{set}{test}=0;
|
|
# DEBUG2("report_all: Data::Dumper not available");
|
|
#} else {
|
|
# DEBUG3("report_all: Data::Dumper module loaded");
|
|
# DEBUG4("report_all:" . Dumper($check_multi));
|
|
#}
|
|
module("XML::Simple",1);
|
|
|
|
#my $pdir="$opt{set}{tmp_dir}/".valid_dir($cmds[0]{hash});
|
|
my $pdir="$opt{set}{tmp_dir}/$opt{set}{HOSTNAME}_$opt{set}{SERVICEDESC}";
|
|
if (! my_mkdir("$pdir", $opt{set}{tmp_dir_permissions})) {
|
|
add_error(0,"report_all: cannot create persistent directory $pdir:$!");
|
|
} else {
|
|
DEBUG1("pdir:$pdir MYSELF:$MYSELF starttime:".readable_sortable_timestamp($cmds[0]{starttime})." name:$cmds[0]{names}");
|
|
writefile(
|
|
">$pdir/".time.".xml",
|
|
XML::Simple::XMLout(
|
|
$check_multi,
|
|
NoAttr=>1,
|
|
KeepRoot=>1,
|
|
RootName=>"$MYSELF",
|
|
SuppressEmpty=>undef,
|
|
)
|
|
);
|
|
}
|
|
} else {
|
|
#add_error(0,"report_all: persistent problem - persistent:$opt{set}{persistent} - use_xml_simple:$opt{set}{use_xml_simple}");
|
|
}
|
|
|
|
#--- print service definition
|
|
if ($opt{set}{report} & $DETAIL_SERVICE_DEFINITION) {
|
|
&report_service_definition;
|
|
return; # no normal output here
|
|
}
|
|
|
|
#--- classical report
|
|
if (!($opt{set}{report} & $DETAIL_HTML) &&
|
|
!($opt{set}{report} & $DETAIL_XML)) {
|
|
&report_ascii;
|
|
}
|
|
#--- report HTML output (and not XML)
|
|
if (($opt{set}{report} & $DETAIL_HTML) &&
|
|
!($opt{set}{report} & $DETAIL_XML)) {
|
|
&report_html;
|
|
}
|
|
#--- report XML output (and not HTML)
|
|
if (($opt{set}{report} & $DETAIL_XML) &&
|
|
!($opt{set}{report} & $DETAIL_HTML)) {
|
|
&report_xml;
|
|
}
|
|
#--- report to nsca (send_nsca wrapper)
|
|
if ($opt{set}{report} & $DETAIL_SEND_NSCA) {
|
|
&report_send_nsca;
|
|
}
|
|
#--- report to nsca (send_nsca wrapper)
|
|
if ($opt{set}{report} & $DETAIL_SEND_GEARMAN) {
|
|
&report_send_gearman;
|
|
}
|
|
#--- report to nsca (send_nsca wrapper)
|
|
if ($opt{set}{report} & $DETAIL_FEED_PASSIVE) {
|
|
&report_checkresult_file;
|
|
}
|
|
|
|
#--- at last: perfdata
|
|
&report_perfdata;
|
|
|
|
#--- final '\n' - dedicated to Wolfgang Barth ;-)
|
|
if ( !($opt{set}{report} & $DETAIL_NAGIOS2) &&
|
|
!($opt{set}{report} & $DETAIL_HTML) &&
|
|
!($opt{set}{report} & $DETAIL_XML)) {
|
|
print "\n";
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- report results stored in %cmds (ASCII report)
|
|
#---
|
|
sub report_ascii {
|
|
|
|
DEBUG1("\n","-" x 80);
|
|
DEBUG1("Plugin output");
|
|
DEBUG1("-" x 80);
|
|
|
|
if (defined($cmds[0]{fmtstr})) {
|
|
my $output="printf " . $cmds[0]{fmtstr};
|
|
$output.="," . join(",",@{$cmds[0]{parms}}) if (defined($cmds[0]{parms}));
|
|
$output=substitute_macros($output);
|
|
DEBUG4("output [ head ] = \'$output\'");
|
|
eval($output);
|
|
} elsif ($opt{set}{report} & $DETAIL_NAGIOS2) {
|
|
print "$opt{set}{name} " if $opt{set}{name};
|
|
print "$def{label}{$cmds[0]{rc}}";
|
|
} else {
|
|
#--- print header line (1): name, state, number of plugins
|
|
print "$opt{set}{name} " if $opt{set}{name};
|
|
print "$def{label}{$cmds[0]{rc}} - $cmds[0]{nchecks} plugins checked, ";
|
|
|
|
#--- print header line (2): summary for particular states
|
|
if ($opt{set}{report} & $DETAIL_LIST_FULL) {
|
|
print "$rc{count}[$CRITICAL] critical" . ((@{$rc{list}[$CRITICAL]}) ? " (" . join(', ',@{$rc{list}[$CRITICAL]}) . ')' : "") . ", " .
|
|
"$rc{count}[$WARNING] warning" . ((@{$rc{list}[$WARNING]}) ? " (" . join(', ',@{$rc{list}[$WARNING]}) . ')' : "") . ", " .
|
|
"$rc{count}[$UNKNOWN] unknown" . ((@{$rc{list}[$UNKNOWN]}) ? " (" . join(', ',@{$rc{list}[$UNKNOWN]}) . ')' : "") . ", " .
|
|
"$rc{count}[$OK] ok";
|
|
} elsif ($opt{set}{report} & $DETAIL_LIST) {
|
|
my @r=();
|
|
push @r, "$rc{count}[$CRITICAL] critical (" . join(', ',@{$rc{list}[$CRITICAL]}) . ")" if (@{$rc{list}[$CRITICAL]});
|
|
push @r, "$rc{count}[$WARNING] warning (" . join(', ',@{$rc{list}[$WARNING]}) . ")" if (@{$rc{list}[$WARNING]});
|
|
push @r, "$rc{count}[$UNKNOWN] unknown (" . join(', ',@{$rc{list}[$UNKNOWN]}) . ")" if (@{$rc{list}[$UNKNOWN]});
|
|
push @r, "$rc{count}[$OK] ok" if (@{$rc{list}[$OK]});
|
|
print join(", ", @r);
|
|
} else {
|
|
print "$rc{count}[$CRITICAL] critical, " .
|
|
"$rc{count}[$WARNING] warning, " .
|
|
"$rc{count}[$UNKNOWN] unknown, " .
|
|
"$rc{count}[$OK] ok";
|
|
}
|
|
foreach my $s (sort numerically keys %{$def{s2r}}) {
|
|
#--- if state matches
|
|
if (($cmds[0]{state}[$def{s2r}{$s}] ne $cmds[0]{state_default}[$def{s2r}{$s}] && $rc{match}[$def{s2r}{$s}]) ||
|
|
$opt{set}{report} & $DETAIL_LIST_FULL ) {
|
|
printf " [%s - %s - %s]", $def{label}{$def{s2r}{$s}}, $rc{match}[$def{s2r}{$s}]?"TRUE":"FALSE", $cmds[0]{state}[$def{s2r}{$s}];
|
|
} else {
|
|
DEBUG3("No match ($def{s2r}{$s} - $def{s2r}{$def{s2r}{$s}}): $rc{match}[$def{s2r}{$def{s2r}{$s}}]");
|
|
}
|
|
}
|
|
}
|
|
#--- print general errors if any occured
|
|
print error(0);
|
|
|
|
#--- loop over commands: report particular results for long plugin output
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
|
|
#--- special output statement defined?
|
|
if (defined($cmds[$no]{fmtstr})) {
|
|
my $output="printf " . $cmds[$no]{fmtstr};
|
|
$output.="," . join(",",@{$cmds[$no]{parms}}) if (defined($cmds[$no]{parms}));
|
|
$output=substitute_macros($output);
|
|
DEBUG4("output [ $cmds[$no]{name} ] = \'$output\'");
|
|
eval($output);
|
|
next;
|
|
}
|
|
|
|
#--- skip eval
|
|
next if ($cmds[$no]{type} eq "eval");
|
|
|
|
#--- if NAGIOS2 output: skip $OK results
|
|
if ($opt{set}{report} & $DETAIL_NAGIOS2) {
|
|
next if ($cmds[$no]{rc} == $OK);
|
|
$cmds[$no]{output}=~s/\n//g;
|
|
printf ", %s %s%s",
|
|
$cmds[$no]{name},
|
|
$cmds[$no]{output},
|
|
($opt{set}{report} & $DETAIL_STDERR) ? error($no) : "";
|
|
} else {
|
|
$cmds[$no]{output}=~s/\n/\n$opt{set}{indent}/g;
|
|
|
|
#--- hide all OK results if there is any check not OK
|
|
next if ($opt{set}{report} & $DETAIL_HIDEIFOK && $cmds[$no]{rc} == $OK);
|
|
|
|
printf "%s[%2.d] %s %s%s%s",
|
|
($opt{set}{report} & $DETAIL_NAGIOS2) ? ", " : "\n",
|
|
$cmds[$no]{number},
|
|
$cmds[$no]{name},
|
|
($opt{set}{report} & $DETAIL_STATUS &&
|
|
$cmds[$no]{output}!~/\b$def{label}{$cmds[$no]{rc}}\b/)
|
|
? "$def{label}{$cmds[$no]{rc}} "
|
|
: "",
|
|
($cmds[$no]{output}=~/\<!--(.*?)--\>/sg) ? $1 : $cmds[$no]{output},
|
|
($opt{set}{report} & $DETAIL_STDERR) ? error($no) : "";
|
|
}
|
|
}
|
|
|
|
my $maxcmdlen=0;
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
$maxcmdlen=length($cmds[$no]{name}) if (length($cmds[$no]{name})>$maxcmdlen);
|
|
}
|
|
|
|
#--- print results
|
|
DEBUG1("\n","-" x 80);
|
|
DEBUG1(sprintf("No Name%sRuntime RC Output", ' ' x ($maxcmdlen-3)));
|
|
DEBUG1("-" x 80);
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
DEBUG1(sprintf "[%2.d] %-${maxcmdlen}s %6.4fs %3d %s", $no, $cmds[$no]{name}, $cmds[$no]{runtime}, $cmds[$no]{rc}, $cmds[$no]{output});
|
|
DEBUG1(sprintf "%s%-12s%s", ' ' x ($maxcmdlen+6), $cmds[$no]{type}, $cmds[$no]{command});
|
|
}
|
|
|
|
#--- print state settings and RC evaluation result
|
|
DEBUG1("\n","-" x 80);
|
|
DEBUG1(sprintf "%-8s %-55s %s", "State","Expression","Evaluates to");
|
|
DEBUG1("-" x 80);
|
|
foreach my $s (sort numerically keys %{$def{s2r}}) {
|
|
DEBUG1(sprintf "%-8s %-55s %s", $def{label}{$def{s2r}{$s}}, $cmds[0]{state}[$def{s2r}{$s}], ($rc{match}[$def{s2r}{$s}]) ? "TRUE" : "FALSE");
|
|
DEBUG2(state_string_ascii($cmds[0]{state}[$def{s2r}{$s}]));
|
|
#--- if state matches
|
|
if (($cmds[0]{state}[$def{s2r}{$s}] ne $cmds[0]{state_default}[$def{s2r}{$s}] && $rc{match}[$def{s2r}{$s}]) ||
|
|
$opt{set}{report} & $DETAIL_LIST_FULL ) {
|
|
printf "\n[%2.2s] %-5s - %s", $def{label}{$def{s2r}{$s}}, $rc{match}[$def{s2r}{$s}]?"TRUE":"FALSE", $cmds[0]{state}[$def{s2r}{$s}];
|
|
} else {
|
|
DEBUG3("No match ($def{s2r}{$s} - $def{s2r}{$def{s2r}{$s}}): $rc{match}[$def{s2r}{$def{s2r}{$s}}]");
|
|
}
|
|
}
|
|
DEBUG1("-" x 80);
|
|
DEBUG1(sprintf "%8s %55s %s", "", "Overall state =>", $def{label}{$cmds[0]{rc}});
|
|
DEBUG1("-" x 80);
|
|
}
|
|
|
|
#---
|
|
#--- helper routine which provides single child check result
|
|
#---
|
|
sub state_string_ascii {
|
|
my ($state)=@_;
|
|
my $output="";
|
|
my @token=get_state_token($state);
|
|
for (my $tno=0;$tno<=$#token;$tno++) {
|
|
$output.=sprintf "%11s. %-52s %s\n",
|
|
chr(97+$tno), # 'a'+$tno
|
|
"'".$token[$tno]{token}."'" . " -> " . "'".$token[$tno]{substituted}."'",
|
|
($token[$tno]{substate}) ? "TRUE" : "FALSE";
|
|
}
|
|
$output;
|
|
}
|
|
|
|
#---
|
|
#--- report results stored in %cmds (HTML report)
|
|
#---
|
|
sub report_html {
|
|
my $output="";
|
|
|
|
if (defined($cmds[0]{fmtstr})) {
|
|
$output.="sprintf " . $cmds[0]{fmtstr};
|
|
$output.="," . join(",",@{$cmds[0]{parms}}) if (defined($cmds[0]{parms}));
|
|
$output=substitute_macros($output);
|
|
DEBUG4("output [ head ] = \'$output\'");
|
|
$output=eval($output);
|
|
} else {
|
|
#--- print header line (1): name, state, number of plugins
|
|
$output.="$opt{set}{name} " if $opt{set}{name};
|
|
$output.="$def{label}{$cmds[0]{rc}} - $cmds[0]{nchecks} plugins checked, ";
|
|
|
|
#--- print header line (2): summary for particular states
|
|
if ($opt{set}{report} & $DETAIL_LIST_FULL) {
|
|
$output.="$rc{count}[$CRITICAL] critical" . ((@{$rc{list}[$CRITICAL]}) ? " (" . join(', ',@{$rc{list}[$CRITICAL]}) . ')' : "") . ", " .
|
|
"$rc{count}[$WARNING] warning" . ((@{$rc{list}[$WARNING]}) ? " (" . join(', ',@{$rc{list}[$WARNING]}) . ')' : "") . ", " .
|
|
"$rc{count}[$UNKNOWN] unknown" . ((@{$rc{list}[$UNKNOWN]}) ? " (" . join(', ',@{$rc{list}[$UNKNOWN]}) . ')' : "") . ", " .
|
|
"$rc{count}[$OK] ok";
|
|
} elsif ($opt{set}{report} & $DETAIL_LIST) {
|
|
my @r=();
|
|
push @r, "$rc{count}[$CRITICAL] critical (" . join(', ',@{$rc{list}[$CRITICAL]}) . ")" if (@{$rc{list}[$CRITICAL]});
|
|
push @r, "$rc{count}[$WARNING] warning (" . join(', ',@{$rc{list}[$WARNING]}) . ")" if (@{$rc{list}[$WARNING]});
|
|
push @r, "$rc{count}[$UNKNOWN] unknown (" . join(', ',@{$rc{list}[$UNKNOWN]}) . ")" if (@{$rc{list}[$UNKNOWN]});
|
|
push @r, "$rc{count}[$OK] ok" if (@{$rc{list}[$OK]});
|
|
$output.=join(", ", @r);
|
|
} else {
|
|
$output.="$rc{count}[$CRITICAL] critical, " .
|
|
"$rc{count}[$WARNING] warning, " .
|
|
"$rc{count}[$UNKNOWN] unknown, " .
|
|
"$rc{count}[$OK] ok";
|
|
}
|
|
foreach my $s (sort numerically keys %{$def{s2r}}) {
|
|
#--- if state matches
|
|
if (($cmds[0]{state}[$def{s2r}{$s}] ne $cmds[0]{state_default}[$def{s2r}{$s}] && $rc{match}[$def{s2r}{$s}]) ||
|
|
$opt{set}{report} & $DETAIL_LIST_FULL ) {
|
|
$output.=sprintf " [%s - %s - %s]", $def{label}{$def{s2r}{$s}}, $rc{match}[$def{s2r}{$s}]?"TRUE":"FALSE", $cmds[0]{state}[$def{s2r}{$s}];
|
|
} else {
|
|
DEBUG3("No match ($def{s2r}{$s} - $def{s2r}{$def{s2r}{$s}}): $rc{match}[$def{s2r}{$def{s2r}{$s}}]");
|
|
}
|
|
}
|
|
}
|
|
|
|
#--- print general errors if any occured
|
|
$output.=error(0);
|
|
$output.=($opt{set}{extinfo_in_status})?"<br />":"\n";
|
|
|
|
#--- collapse of contents with javascript
|
|
if ($opt{set}{collapse} == 1) {
|
|
$output.="<script language='JavaScript'> function Toggle(node) { if (node.nextSibling.style.display == 'none') { if (node.childNodes.length > 0) { node.childNodes.item(0).replaceData(0,1,String.fromCharCode(8211)) } node.nextSibling.style.display = 'block' } else { if (node.childNodes.length > 0) { node.childNodes.item(0).replaceData(0,1,'+') } node.nextSibling.style.display = 'none' } } </script>";
|
|
if (($cmds[0]{rc} == $OK && $ENV{"MULTI_PPID"} != $$) ||
|
|
($cmds[0]{rc} == $OK && $opt{set}{extinfo_in_status})) {
|
|
$output.="<a onclick='Toggle(this)' ".$opt{set}{style_plus_minus}.">+</a>";
|
|
$output.="<div style='display:none'>";
|
|
} else {
|
|
$output.="<a onclick='Toggle(this)' ".$opt{set}{style_plus_minus}.">–</a>";
|
|
$output.="<div style='display:block'>";
|
|
}
|
|
}
|
|
|
|
#--- loop over commands: report particular results for long plugin output
|
|
$output.="<div><table style='border-left-width:1px; border-right-width:0px; border-left-style:dotted' id='multi_table'>";
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
#--- skip eval
|
|
next if ($cmds[$no]{type} eq "eval");
|
|
|
|
#--- hide all OK results if there is any check not OK (by Timo Kempkens)
|
|
next if (($opt{set}{report} & $DETAIL_HIDEIFOK) && ($cmds[$no]{rc} == $OK && $cmds[0]{rc} != $OK));
|
|
|
|
#--- allow commands to get tag name
|
|
$ENV{"MULTI_TAG"}=$cmds[$no]{name};
|
|
|
|
$output.=sprintf "<tr style='font-size:8pt' title='%s'><td nowrap><table style='background-color:%s'><tr style='vertical-align:middle'><td style='font-size:6pt' title='%s'>%2.d</td></tr></table></td>",
|
|
xml_encode(error($no)),
|
|
$def{color}{$cmds[$no]{rc}},
|
|
xml_encode(error($no)),
|
|
$cmds[$no]{number};
|
|
#--- Action url (standard version)
|
|
if ( ($opt{set}{report} & ($DETAIL_PERFORMANCE | $DETAIL_PERFORMANCE_CLASSIC)) &&
|
|
($opt{set}{report} & $DETAIL_PERFORMANCE_LINK) &&
|
|
defined($cmds[$no]{performance}) &&
|
|
$cmds[$no]{process_perfdata} &&
|
|
!$opt{set}{action_mouseover}) {
|
|
|
|
my $hostname=get_hostname();
|
|
DEBUG3("hostname is $hostname");
|
|
my $pnp_url=substitute_macros($opt{set}{pnp_url});
|
|
my $pnp_cgi=($opt{set}{pnp_version}=~/0.6/)?"graph":"index.php";
|
|
my $image_path=substitute_macros($opt{set}{image_path});
|
|
$output.=sprintf "<td nowrap>%s</td>",
|
|
"<a target=\'$opt{set}{target}\' href=\'$pnp_url/$pnp_cgi?host=${hostname}&srv=$cmds[$no]{name}\'> " .
|
|
"<img src=\'$image_path/action.gif\' width=\'20\' height=\'20\' border=\'0\' align=top alt='Show performance chart for $hostname / $cmds[$no]{plugin}' /></a>";
|
|
#--- Action url (mouseover version)
|
|
} elsif (($opt{set}{report} & ($DETAIL_PERFORMANCE | $DETAIL_PERFORMANCE_CLASSIC)) &&
|
|
($opt{set}{report} & $DETAIL_PERFORMANCE_LINK) &&
|
|
defined($cmds[$no]{performance}) &&
|
|
$cmds[$no]{process_perfdata} &&
|
|
$opt{set}{action_mouseover}) {
|
|
|
|
my $hostname=get_hostname();
|
|
DEBUG3("hostname is $hostname");
|
|
my $pnp_url=substitute_macros($opt{set}{pnp_url});
|
|
my $image_path=substitute_macros($opt{set}{image_path});
|
|
DEBUG4(">>>$opt{set}{target}:$pnp_url:${hostname}:$cmds[$no]{name}:${hostname}:$cmds[$no]{name}:$image_path:$hostname:$cmds[$no]{name}<<<");
|
|
my $mouseover_url="";
|
|
if ($opt{set}{pnp_version}=~/0.6/) {
|
|
$mouseover_url="$opt{set}{pnp_url}/graph?host=$opt{set}{HOSTNAME}&srv=$cmds[$no]{name}$opt{set}{pnp_add2url}\' class=\'tips\' rel=\'$opt{set}{pnp_url}/popup?host=$opt{set}{HOSTNAME}&srv=$cmds[$no]{name}$opt{set}{pnp_add2url}";
|
|
} elsif ($opt{set}{pnp_version}=~/0.4/) {
|
|
$mouseover_url="$opt{set}{pnp_url}/index.php?host=${hostname}&srv=$cmds[$no]{name}$opt{set}{pnp_add2url}\' onmouseout=\'clear_g()\' onmouseover=\"get_g(\'$opt{set}{HOSTNAME}\',\'$cmds[$no]{name}\')\"";
|
|
} else {
|
|
add_error(0,"Invalid mouseover URL type $opt{set}{pnp_version}, allowed are '0.6' and '0.4'");
|
|
$mouseover_url="$pnp_url/graph?host=${hostname}&srv=$cmds[$no]{name}$opt{set}{pnp_add2url}";
|
|
}
|
|
$output.=sprintf "<td nowrap>%s</td>",
|
|
"<a href=\'$mouseover_url\' target=\'$opt{set}{target}\'>" .
|
|
"<img src=\'$image_path/action.gif\' width=\'20\' height=\'20\' border=\'0\' align=top alt='Show performance chart for $hostname / $cmds[$no]{name}' /></a>";
|
|
} else {
|
|
$output.="<td></td>";
|
|
}
|
|
#--- Notes url
|
|
if ( ($opt{set}{report} & $DETAIL_NOTES_LINK) &&
|
|
defined($opt{set}{notes_url}) &&
|
|
!$opt{set}{tag_notes_link}) {
|
|
my $notes_url=substitute_macros($opt{set}{notes_url});
|
|
my $image_path=substitute_macros($opt{set}{image_path});
|
|
$output.=sprintf "<td nowrap>%s</td>",
|
|
"<a href=\'$notes_url\' target=\'$opt{set}{target}\'>" .
|
|
"<img src=\'$image_path/notes.gif\' width=\'20\' height=\'20\' border=\'0\' align=top alt='Show notes for $cmds[$no]{plugin}' /></a>";
|
|
} else {
|
|
$output.="<td></td>";
|
|
}
|
|
#--- and the rest...
|
|
if (!$opt{set}{tag_notes_link}) {
|
|
$output.=sprintf "<td>%s</td>", ($cmds[$no]{name}) ? $cmds[$no]{name} : "";
|
|
} else {
|
|
my $notes_url=substitute_macros($opt{set}{notes_url});
|
|
$output.=sprintf "<td><a href=\'$notes_url\' target=\'$opt{set}{target}\'>%s</a></td>", ($cmds[$no]{name}) ? $cmds[$no]{name} : "";
|
|
}
|
|
DEBUG3("output:$cmds[$no]{output}");
|
|
DEBUG3("HTML check_multi recursive levels: " . scalar ($cmds[$no]{output}=~s/multi_table/multi_table/g));
|
|
if (defined($cmds[$no]{fmtstr})) {
|
|
my $tmp="sprintf " . $cmds[$no]{fmtstr};
|
|
$tmp.="," . join(",",@{$cmds[$no]{parms}}) if (defined($cmds[$no]{parms}));
|
|
$tmp=substitute_macros($tmp);
|
|
DEBUG4("output [ $cmds[$no]{name} ] = \'$tmp\'");
|
|
$tmp=eval($tmp);
|
|
$output.=sprintf "<td>%s</td>",
|
|
($tmp=~/^([^\n]+)\n(.*)/is)
|
|
? (($opt{set}{indent_label})
|
|
? "$1</td></tr><tr style='font-size:8pt'><td colspan='4'></td><td colspan='1'>$2"
|
|
: "$1</td></tr><tr><td></td><td colspan='5'>$2")
|
|
: $tmp;
|
|
} else {
|
|
my $rclabel=($opt{set}{report} & $DETAIL_STATUS &&
|
|
$cmds[$no]{output}!~/\b$def{label}{$cmds[$no]{rc}}\b/)
|
|
? "$def{label}{$cmds[$no]{rc}} "
|
|
: "";
|
|
$output.=sprintf "<td>%s</td>",
|
|
($cmds[$no]{output}=~/^([^\n]+)\n(.*)/is)
|
|
? (($opt{set}{indent_label})
|
|
? "${rclabel}$1</td></tr><tr style='font-size:8pt'><td colspan='4'></td><td colspan='1'>$2"
|
|
: "${rclabel}$1</td></tr><tr><td></td><td colspan='5'>$2")
|
|
: "${rclabel}$cmds[$no]{output}";
|
|
}
|
|
$output.="</tr>";
|
|
}
|
|
foreach my $s (sort numerically keys %{$def{s2r}}) {
|
|
#--- if state matches
|
|
if (($cmds[0]{state}[$def{s2r}{$s}] ne $cmds[0]{state_default}[$def{s2r}{$s}] && $rc{match}[$def{s2r}{$s}]) ||
|
|
$opt{set}{report} & $DETAIL_LIST_FULL ) {
|
|
$output.=sprintf "<tr style='font-size:8pt'><td nowrap colspan='3'><table style='background-color:%s'><tr style='vertical-align:middle'><td style='font-size:6pt'>%4.4s</td></tr></table></td>",
|
|
$def{color}{$def{s2r}{$s}},
|
|
$def{label}{$def{s2r}{$s}};
|
|
$output.=sprintf "<td>%s</td><td>%s</td>", $rc{match}[$def{s2r}{$s}]?"TRUE":"FALSE", $cmds[0]{state}[$def{s2r}{$s}];
|
|
$output.="</tr>";
|
|
}
|
|
}
|
|
$output.="</table></div>";
|
|
$output.="</div>" if ($opt{set}{collapse} == 1 && ! $opt{set}{extinfo_in_status});
|
|
|
|
#--- print state evaluation if verbose flag set
|
|
if ($opt{set}{verbose} >= 2) {
|
|
if ($ENV{"MULTI_PPID"} != $$) {
|
|
$output.="<a onclick='Toggle(this)' style='".$opt{set}{style_plus_minus}."'>+</a>";
|
|
$output.="<div style='display:none'><h4>Child check overview</h4>";
|
|
}
|
|
my @colors=("silver","lightgrey");
|
|
my $flipflop=0;
|
|
$output.="<table><tr style='text-align:left' bgcolor='" . $colors[$flipflop] . "'><th>No</th><th>Name</th><th>Runtime</th><th>RC</th><th>Output</th></tr>";
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
$flipflop=!$flipflop;
|
|
$output.=sprintf "<tr bgcolor='" . $colors[$flipflop] . "'><td>%d</td><td>%s</td><td>%7.5f</td><td>%d</td><td>%s</td></tr>", $no, $cmds[$no]{name}, $cmds[$no]{runtime}, $cmds[$no]{rc}, $cmds[$no]{output};
|
|
$output.=sprintf "<tr bgcolor='" . $colors[$flipflop] . "'><td></td><td></td><td>%s</td><td></td><td>%s</td></tr>", $cmds[$no]{type}, $cmds[$no]{command};
|
|
}
|
|
$output.="</table></div>";
|
|
|
|
#--- print state settings and RC evaluation result
|
|
if ($ENV{"MULTI_PPID"} != $$) {
|
|
$output.="<a onclick='Toggle(this)' style='".$opt{set}{style_plus_minus}."'>+</a>";
|
|
$output.="<div style='display:none'><h4>State evaluation</h4>";
|
|
}
|
|
$flipflop=0;
|
|
$output.="<table><tr style='text-align:left' bgcolor='" . $colors[$flipflop] . "'><th>State</th><th>Expression</th><th>Evaluates to</th></tr>";
|
|
foreach my $s (sort numerically keys %{$def{s2r}}) {
|
|
$flipflop=!$flipflop;
|
|
$output.=sprintf "<tr bgcolor='" . $colors[$flipflop] . "'><td>%s</td><td>%s</td><td>%s</td></tr>", $def{label}{$def{s2r}{$s}}, $cmds[0]{state}[$def{s2r}{$s}], ($rc{match}[$def{s2r}{$s}]) ? "TRUE" : "FALSE";
|
|
}
|
|
$flipflop=!$flipflop;
|
|
$output.="<tr bgcolor='" . $colors[$flipflop] . "'><td></td><td style='text-align:right'>Overall state</td><td style='font-weight:bold'>$def{label}{$cmds[0]{rc}}</td></tr>";
|
|
$output.="</table></div>";
|
|
}
|
|
|
|
#--- verbose mode - add some tables
|
|
if ($opt{set}{verbose} >=2) {
|
|
|
|
if ($ENV{"MULTI_PPID"} != $$) {
|
|
$output.="<a onclick='Toggle(this)' style='".$opt{set}{style_plus_minus}."'>+</a>";
|
|
$output.="<div style='display:none'><h4>Child check details</h4>";
|
|
}
|
|
my @colors=("silver","lightgrey");
|
|
my $flipflop=0;
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
$output.="<h5>$no - $cmds[$no]{name}</h5>";
|
|
$output.="<table><tr style='text-align:left' bgcolor='" . $colors[$flipflop] . "'><th></th><th>Attribute</th><th>Value</th></tr>";
|
|
my %vars=(
|
|
1 => {
|
|
att => "\$no",
|
|
val => $no,
|
|
},
|
|
2 => {
|
|
att => "tag",
|
|
val => $cmds[$no]{name},
|
|
},
|
|
3 => {
|
|
att => "\$STATE_$cmds[$no]{name}\$",
|
|
val => $cmds[$no]{rc},
|
|
},
|
|
4 => {
|
|
att => "\$$cmds[$no]{name}\$",
|
|
val => $cmds[$no]{output},
|
|
},
|
|
5 => {
|
|
att => "type",
|
|
val => $cmds[$no]{type},
|
|
},
|
|
6 => {
|
|
att => "command",
|
|
val => $cmds[$no]{command},
|
|
},
|
|
7 => {
|
|
att => "runtime",
|
|
val => $cmds[$no]{runtime},
|
|
},
|
|
);
|
|
|
|
foreach my $i (sort keys %vars) {
|
|
$flipflop=!$flipflop;
|
|
$output.=sprintf "<tr bgcolor='" . $colors[$flipflop] . "'><td>%d</td><td>%s</td><td>%s</td></tr>", $i, $vars{$i}{att}, $vars{$i}{val};
|
|
}
|
|
$output.="</table>";
|
|
}
|
|
$output.="</div>";
|
|
}
|
|
|
|
#--- status_in_extinfo? escape newlines by '<br>'
|
|
if ($opt{set}{extinfo_in_status}) {
|
|
#--- status_in_extinfo? escape newlines by '<br>'
|
|
$output=~s/\n/<br \/>/g;
|
|
#--- close div if collapsed
|
|
$output.="</div>" if ($opt{set}{collapse} == 1);
|
|
}
|
|
|
|
#--- last not least: output ;-)
|
|
print $output;
|
|
}
|
|
|
|
#---
|
|
#--- report in XML (at the moment hidden in HTML comment)
|
|
#---
|
|
sub report_xml {
|
|
|
|
#--- xml header
|
|
my $xmlstr="<?xml version=\"1.0\"?>\n";
|
|
$xmlstr.="<?xml-stylesheet type=\"text/xsl\" href=\"extinfo.xsl\"?>\n";
|
|
$xmlstr.="<div id=\"check_multi_xml\" style='display:none'>\n";
|
|
|
|
#--- begin with cmds[0] - parent
|
|
my $no=0;
|
|
$xmlstr.="<$MYSELF>\n";
|
|
$xmlstr.="\t<CHILD>\n";
|
|
$xmlstr.="\t\t<no>$no</no>\n";
|
|
foreach my $token (sort keys %{$cmds[$no]}) {
|
|
next if ($token=~/output|error/);
|
|
if (defined($cmds[$no]{$token})) {
|
|
if (ref($cmds[$no]{$token}) eq "ARRAY") {
|
|
foreach my $subtoken (@{$cmds[$no]{$token}}) {
|
|
$xmlstr.=sprintf "\t\t<%s>%s</%s>\n",$token,xml_encode($subtoken),$token;
|
|
}
|
|
} else {
|
|
$xmlstr.=sprintf "\t\t<%s>%s</%s>\n",$token,xml_encode($cmds[$no]{$token}),$token;
|
|
}
|
|
}
|
|
}
|
|
#--- extra: output
|
|
$xmlstr.="\t\t<output>";
|
|
if (defined($cmds[0]{fmtstr})) {
|
|
my $output="sprintf " . $cmds[0]{fmtstr};
|
|
$output.="," . join(",",@{$cmds[0]{parms}}) if (defined($cmds[0]{parms}));
|
|
$output=substitute_macros($output);
|
|
DEBUG4("output [ head ] = \'$output\'");
|
|
$xmlstr.=xml_encode(eval($output));
|
|
} else {
|
|
#--- print header line (2): summary for particular states
|
|
if ($opt{set}{report} & $DETAIL_LIST_FULL) {
|
|
$xmlstr.="$rc{count}[$CRITICAL] critical" . ((@{$rc{list}[$CRITICAL]}) ? " (" . join(', ',@{$rc{list}[$CRITICAL]}) . ')' : "") . ", " .
|
|
"$rc{count}[$WARNING] warning" . ((@{$rc{list}[$WARNING]}) ? " (" . join(', ',@{$rc{list}[$WARNING]}) . ')' : "") . ", " .
|
|
"$rc{count}[$UNKNOWN] unknown" . ((@{$rc{list}[$UNKNOWN]}) ? " (" . join(', ',@{$rc{list}[$UNKNOWN]}) . ')' : "") . ", " .
|
|
"$rc{count}[$OK] ok";
|
|
} elsif ($opt{set}{report} & $DETAIL_LIST) {
|
|
my @r=();
|
|
push @r, "$rc{count}[$CRITICAL] critical (" . join(', ',@{$rc{list}[$CRITICAL]}) . ")" if (@{$rc{list}[$CRITICAL]});
|
|
push @r, "$rc{count}[$WARNING] warning (" . join(', ',@{$rc{list}[$WARNING]}) . ")" if (@{$rc{list}[$WARNING]});
|
|
push @r, "$rc{count}[$UNKNOWN] unknown (" . join(', ',@{$rc{list}[$UNKNOWN]}) . ")" if (@{$rc{list}[$UNKNOWN]});
|
|
push @r, "$rc{count}[$OK] ok" if (@{$rc{list}[$OK]});
|
|
$xmlstr.=join(", ", @r);
|
|
} else {
|
|
$xmlstr.="$rc{count}[$CRITICAL] critical, " .
|
|
"$rc{count}[$WARNING] warning, " .
|
|
"$rc{count}[$UNKNOWN] unknown, " .
|
|
"$rc{count}[$OK] ok";
|
|
}
|
|
}
|
|
$xmlstr.="</output>\n";
|
|
$xmlstr.="\t\t<error>" . join("</error>\n\t\t<error>",error(0)) . "</error>\n" if (error(0) ne "");
|
|
$xmlstr.="\t</CHILD>\n";
|
|
|
|
#--- loop over child checks
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
next if (!$cmds[$no]{displayed});
|
|
$xmlstr.="\t<CHILD>\n";
|
|
$xmlstr.="\t\t<no>$cmds[$no]{number}</no>\n";
|
|
foreach my $token (sort keys %{$cmds[$no]}) {
|
|
if (defined($cmds[$no]{$token})) {
|
|
if (ref($cmds[$no]{$token}) eq "ARRAY") {
|
|
$xmlstr.="\t\t<$token>" . join("</$token>\n\t\t<$token>",@{$cmds[$no]{$token}}) . "</$token>\n";
|
|
} else {
|
|
$xmlstr.=sprintf "\t\t<%s>%s</%s>\n",$token,xml_encode($cmds[$no]{$token}),$token;
|
|
}
|
|
} else {
|
|
add_error($no,"report_xml: XML element $token not found");
|
|
}
|
|
}
|
|
$xmlstr.="\t</CHILD>\n";
|
|
}
|
|
$xmlstr.="</$MYSELF>\n</div>\n";
|
|
print $xmlstr;
|
|
}
|
|
|
|
#---
|
|
#--- inventory report in XML
|
|
#---
|
|
sub report_inventory {
|
|
|
|
#--- xml header
|
|
my $xmlstr="<?xml version=\"1.0\"?>\n";
|
|
$xmlstr.="<?xml-stylesheet type=\"text/xsl\" href=\"extinfo.xsl\"?>\n";
|
|
$xmlstr.="<div id=\"check_multi_xml\" style='display:none'>\n";
|
|
|
|
#--- begin with cmds[0] - parent
|
|
my $no=0;
|
|
$xmlstr.="<$MYSELF>\n";
|
|
$xmlstr.="\t<CHILD>\n";
|
|
$xmlstr.="\t\t<no>$no</no>\n";
|
|
foreach my $token (sort keys %{$cmds[$no]}) {
|
|
next if ($token!~/^name$|^rc$|^type$/);
|
|
if (defined($cmds[$no]{$token})) {
|
|
$xmlstr.=sprintf "\t\t<%s>%s</%s>\n",$token,xml_encode($cmds[$no]{$token}),$token;
|
|
}
|
|
}
|
|
$xmlstr.="\t</CHILD>\n";
|
|
|
|
#--- loop over child checks
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
$cmds[$no]{rc}=$OK;
|
|
next if (!$cmds[$no]{displayed});
|
|
$xmlstr.="\t<CHILD>\n";
|
|
$xmlstr.="\t\t<no>$no</no>\n";
|
|
foreach my $token (sort keys %{$cmds[$no]}) {
|
|
next if ($token!~/^name$|^rc$|^type$/);
|
|
if (defined($cmds[$no]{$token})) {
|
|
$xmlstr.=sprintf "\t\t<%s>%s</%s>\n",$token,xml_encode($cmds[$no]{$token}),$token;
|
|
} else {
|
|
add_error($no,"report_xml: XML element $token not found");
|
|
}
|
|
}
|
|
$xmlstr.="\t</CHILD>\n";
|
|
}
|
|
$xmlstr.="</$MYSELF>\n</div>\n";
|
|
print $xmlstr;
|
|
}
|
|
|
|
#---
|
|
#--- helper routine which encodes several XML specific characters
|
|
#---
|
|
sub xml_encode {
|
|
my $input=shift;
|
|
my %transtab=(
|
|
'\'' => ''',
|
|
'&' => '&',
|
|
'<' => '<',
|
|
'>' => '>',
|
|
'\|' => '|',
|
|
);
|
|
for (keys(%transtab)) {
|
|
$input=~s/$_/$transtab{$_}/g;
|
|
}
|
|
$input;
|
|
}
|
|
|
|
#---
|
|
#--- print out all perfdata
|
|
#--- if its compliant to developers guidelines ;)
|
|
#---
|
|
sub report_perfdata {
|
|
if (!$opt{set}{report} & $DETAIL_HTML) {
|
|
DEBUG1("\n","-" x 80);
|
|
DEBUG1("Plugin performance data");
|
|
DEBUG1("-" x 80);
|
|
}
|
|
#--- report performance data?
|
|
if ($opt{set}{report} & $DETAIL_PERFORMANCE) {
|
|
|
|
#--- take name and - if empty - check_multi
|
|
my $name=($opt{set}{name})?$opt{set}{name}:$MYSELF;
|
|
#--- if whitespace within name, wrap it with single quotes
|
|
my $delim=($name=~/\s+/)?"'":"";
|
|
my $perftmp="";
|
|
DEBUG3("(DETAIL_PERFORMANCE): name=" . ($opt{set}{name}) ? $opt{set}{name} : $MYSELF);
|
|
print "\|";
|
|
printf "$delim%s::%s::plugins$delim=%d time=%f ",
|
|
$name,
|
|
$MYSELF,
|
|
$cmds[0]{nallchecks},
|
|
$cmds[0]{runtime};
|
|
#--- extended performance data output for check_multi
|
|
if ($opt{set}{extended_perfdata}) {
|
|
printf "$delim%s_extended::check_multi_extended::count_ok$delim=%d count_warning=%d count_critical=%d count_unknown=%d overall_state=%d ",
|
|
$name,
|
|
$rc{count}[$OK],
|
|
$rc{count}[$WARNING],
|
|
$rc{count}[$CRITICAL],
|
|
$rc{count}[$UNKNOWN],
|
|
$cmds[0]{rc};
|
|
}
|
|
#--- one line per command, format: tag=output
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
|
|
#--- suppress_perfdata set? ignore perfdata
|
|
next if (! $cmds[$no]{process_perfdata});
|
|
|
|
if (defined($cmds[$no]{performance})) {
|
|
|
|
#--- macros found? substitute
|
|
$cmds[$no]{performance}=substitute_macros($cmds[$no]{performance});
|
|
|
|
#--- prevent concatenation of multi-labels if called recursively
|
|
if ($cmds[$no]{performance}=~/check_multi::check_multi::(.*)/) {
|
|
$perftmp="${name}::check_multi::$1 ";
|
|
} elsif ($cmds[$no]{performance}=~/::check_multi::/) {
|
|
$perftmp="$cmds[$no]{performance} ";
|
|
#--- cumulate perfdata: pass through
|
|
} elsif ($cmds[$no]{performance}=~/::cumulate::/) {
|
|
$perftmp="$cmds[$no]{performance} ";
|
|
} else {
|
|
#--- do we have an explicit plugin specification? take it
|
|
my $plugin=(defined($cmds[$no]{pplugin}) && $cmds[$no]{pplugin} ne "")
|
|
? $cmds[$no]{pplugin}
|
|
: $cmds[$no]{plugin};
|
|
DEBUG4("plugin is $plugin");
|
|
|
|
#--- perflabel already with single quotes? then keep it
|
|
if ($cmds[$no]{performance}=~/^\s*\'([^']*)\'=(\S+)(.*)/) {
|
|
$perftmp="\'$cmds[$no]{name}::${plugin}::$1\'=$2$3 ";
|
|
DEBUG4("quoting detection: already quoted before:" .$perftmp);
|
|
#--- child check name with spaces? then use single quotes
|
|
} elsif ($cmds[$no]{name}=~/\s+/) {
|
|
$cmds[$no]{performance}=~/([^=]*)=(.*)\s*/;
|
|
$perftmp="\'$cmds[$no]{name}::${plugin}::$1\'=$2 ";
|
|
DEBUG4("quoting detection: blanks found:" .$perftmp);
|
|
} else {
|
|
$cmds[$no]{performance}=~s/\s*(.*)/$1/;
|
|
$perftmp="$cmds[$no]{name}::${plugin}::$cmds[$no]{performance} ";
|
|
DEBUG4("quoting detection: standard without blanks:" .$perftmp);
|
|
}
|
|
}
|
|
$cmds[$no]{performance}="";
|
|
DEBUG4("before splitting: $perftmp");
|
|
#--- preserve '::' as multi delimiter - replace all '::' with '_'
|
|
while ($perftmp=~/\s*([^=]+)=(\S+)\s*(.*)/) {
|
|
my $label=$1;
|
|
my $data=$2;
|
|
$perftmp=$3;
|
|
|
|
my @tmparr=split(/::/,$label);
|
|
DEBUG4("token before splitting: $label [0-$#tmparr]:" . join("|",@tmparr));
|
|
if ($#tmparr > 1) {
|
|
$cmds[$no]{performance}.=shift(@tmparr)."::".shift(@tmparr)."::";
|
|
$cmds[$no]{performance}.=join("_",@tmparr);
|
|
} else {
|
|
$cmds[$no]{performance}.=$label;
|
|
}
|
|
$cmds[$no]{performance}.="=$data ";
|
|
DEBUG4("remaining perftmp: $perftmp");
|
|
}
|
|
DEBUG4("complete after splitting: $cmds[$no]{performance}");
|
|
print $cmds[$no]{performance};
|
|
}
|
|
}
|
|
} elsif ($opt{set}{report} & $DETAIL_PERFORMANCE_CLASSIC) {
|
|
print "\|";
|
|
|
|
#--- one line per command, format: tag=output
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
|
|
#--- suppress_perfdata set? ignore perfdata
|
|
next if (! $cmds[$no]{process_perfdata});
|
|
|
|
#--- macros found? substitute
|
|
$cmds[$no]{performance}=substitute_macros($cmds[$no]{performance});
|
|
|
|
if (defined($cmds[$no]{performance})) {
|
|
if (my $errstr=parse_perfdata($cmds[$no]{performance})) {
|
|
add_error($no,"$cmds[$no]{name} perfdata discarded for $errstr");
|
|
} else {
|
|
print "$cmds[$no]{performance} ";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- find errors in perfdata
|
|
#---
|
|
sub parse_perfdata {
|
|
my $perfdata=shift;
|
|
my $label="";
|
|
my $data="";
|
|
my $error="";
|
|
my $uom="";
|
|
|
|
#--- accepting loose perfdata
|
|
if ($opt{set}{loose_perfdata}) {
|
|
DEBUG4("loose_perfdata=1");
|
|
#--- replace decimal comma with decimal point
|
|
if (defined($ENV{LANG}) && $ENV{LANG} =~ /de_DE/) {
|
|
DEBUG4("LANG is $ENV{LANG}, replacing commata by decimal points");
|
|
($perfdata) =~ s/,/./g;
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- 'label'=value[UOM];[warn];[crit];[min];[max]
|
|
#---
|
|
#--- loop over perfdata
|
|
while ($perfdata) {
|
|
DEBUG4("parsing perfdata:$perfdata");
|
|
#--- label w/ \'
|
|
if ($perfdata=~/^\s*(\'[^']*\')=([^ ]+)(.*)/) {
|
|
$label=$1;
|
|
$data=$2;
|
|
$perfdata=$3;
|
|
DEBUG4("parsed perfdata -'label':$1 data:$2 rest:$3");
|
|
#--- label w/o \'
|
|
} elsif ($perfdata=~/^\s*([^\s=']+)=([^ ]+)(.*)/) {
|
|
$label=$1;
|
|
$data=$2;
|
|
$perfdata=$3;
|
|
DEBUG4("parsed perfdata - label:$1 data:$2 rest:$3");
|
|
} else {
|
|
DEBUG2("general parsing error - invalid perfdata");
|
|
return "general error in '$perfdata'";
|
|
}
|
|
$perfdata=~s/^\s+//; $perfdata=~s/\s+$//;
|
|
|
|
#--- perfdata has label and data, now do detailed checks
|
|
my ($value,$warning,$critical,$min,$max)=split(/;/,$data);
|
|
return "$label: no value in data \'$data\'" if ($value eq "");
|
|
if ($value=~/([-0-9.]+)([^0-9-.]*)/) {
|
|
$value=$1;
|
|
$uom=$2;
|
|
}
|
|
|
|
#--- invalid perfdata with trailing ';' after max value
|
|
if (($data=~tr/;//)>=5) {
|
|
DEBUG2("invalid ';' after max value $max");
|
|
return "error in '$data': invalid ';' after max value $max" if (! $opt{set}{loose_perfdata});
|
|
} else {
|
|
DEBUG4("number of ';' ok in $data: " . scalar($data=~tr/;//));
|
|
}
|
|
|
|
$error.= "$label: bad value \'$value\' in data \'$data\' " if ($value && $value !~/^[-0-9.]+$/);
|
|
$error.= "$label: bad UOM \'$uom\' in data \'$data\' " if ($uom ne "" && ($uom!~/^[um]*s$/i && $uom!~/^%$/ && $uom!~/^[kmgt]*b$/i && $uom!~/^c$/i));
|
|
$error.= "$label: bad warning \'$warning\' in data \'$data\' " if ($warning && $warning !~/^[\@\~]?[-0-9.:]+$/);
|
|
$error.= "$label: bad critical \'$critical\' in data \'$data\' " if ($critical && $critical !~/^[\@\~]?[-0-9.:]+$/);
|
|
$error.= "$label: bad min \'$min\' in data \'$data\' " if ($min && $min !~/^[-0-9.]+$/);
|
|
$error.= "$label: bad max \'$max\' in data \'$data\' " if ($max && $max !~/^[-0-9.]+$/);
|
|
return $error if ($error);
|
|
|
|
#--- done: perfdata is ok
|
|
}
|
|
DEBUG3("no errors found");
|
|
return undef;
|
|
}
|
|
|
|
#---
|
|
#--- helper routine to generate Nagios service check definitions
|
|
#--- mostly needed for 'check_multi_feeds_passive'
|
|
sub report_service_definition {
|
|
my @tpl="";
|
|
#--- search for template file and read contents
|
|
if (defined($opt{set}{service_definition_template}) && $opt{set}{service_definition_template}) {
|
|
if (-f "$opt{set}{service_definition_template}") {
|
|
if (open(DEF, $opt{set}{service_definition_template})) {
|
|
@tpl=<DEF>;
|
|
close DEF;
|
|
} else {
|
|
print "#--- Error: cannot open $opt{set}{service_definition_template}:$?\n";
|
|
return 2;
|
|
}
|
|
} else {
|
|
print "#--- Error: service definition template file \'$opt{set}{service_definition_template}\' not found\n";
|
|
return 1;
|
|
}
|
|
#--- check_for default template variable
|
|
} elsif (defined($opt{set}{service_definition_template_default})) {
|
|
@tpl=$opt{set}{service_definition_template_default};
|
|
#--- bail out with error message
|
|
} else {
|
|
print "#--- Error: variable \'service_definition_template_default\' not defined\n";
|
|
return 1;
|
|
}
|
|
#--- bail out if HOSTNAME are not set
|
|
if (! $opt{set}{HOSTNAME} && ! $ENV{"${NAGIOS}_HOSTNAME"}) {
|
|
print "#--- Error: need HOSTNAME for service definition.\n";
|
|
print "#--- Please specify -s HOSTNAME=<hostname> or provide environment variable HOSTNAME\n";
|
|
return 3;
|
|
} else {
|
|
DEBUG4(sprintf "HOSTNAME specified: \'%s\'", ($opt{set}{HOSTNAME}) ? $opt{set}{HOSTNAME} : $ENV{"${NAGIOS}_HOSTNAME"});
|
|
}
|
|
|
|
my $output="";
|
|
for($no=1;$no<=$#cmds;$no++) {
|
|
next if ($cmds[$no]{type} eq "eval");
|
|
my @svc=@tpl;
|
|
foreach my $line (@svc) {
|
|
DEBUG4("before substitution: $line");
|
|
$line=~s/THIS/$no/g;
|
|
$line=substitute_macros($line);
|
|
DEBUG4("after substitution: $line");
|
|
$output.=$line;
|
|
}
|
|
}
|
|
print $output;
|
|
}
|
|
|
|
|
|
|
|
#---
|
|
#--- send check_multi results to NSCA daemon
|
|
#---
|
|
sub report_send_nsca {
|
|
#
|
|
# NSCA Client 2.5
|
|
# Copyright (c) 2000-2006 Ethan Galstad (www.nagios.org)
|
|
# Last Modified: 01-21-2006
|
|
# License: GPL v2
|
|
# Encryption Routines: NOT AVAILABLE
|
|
#
|
|
# Usage: /usr/local/monitoring/nagios/sbin/send_nsca -H <host_address> [-p port] [-to to_sec] [-d delim] [-c config_file]
|
|
#
|
|
# Options:
|
|
# <host_address> = The IP address of the host running the NSCA daemon
|
|
# [port] = The port on which the daemon is running - default is 5667
|
|
# [to_sec] = Number of seconds before connection attempt times out.
|
|
# (default timeout is 10 seconds)
|
|
# [delim] = Delimiter to use when parsing input (defaults to a tab)
|
|
# [config_file] = Name of config file to use
|
|
#
|
|
# Note:
|
|
# This utility is used to send passive check results to the NSCA daemon. Host and
|
|
# Service check data that is to be sent to the NSCA daemon is read from standard
|
|
# input. Input should be provided in the following format (tab-delimited unless
|
|
# overriden with -d command line argument, one entry per line):
|
|
#
|
|
# Service Checks:
|
|
# <host_name>[tab]<svc_description>[tab]<return_code>[tab]<plugin_output>[newline]
|
|
#
|
|
# Host Checks:
|
|
# <host_name>[tab]<return_code>[tab]<plugin_output>[newline]
|
|
#
|
|
my $sent=0;
|
|
|
|
if (! -x "$opt{set}{send_nsca}") {
|
|
add_error(0,"report_send_nsca: $opt{set}{send_nsca} not found or not executable:$!");
|
|
return $sent;
|
|
}
|
|
if (! -f "$opt{set}{send_nsca_cfg}") {
|
|
add_error(0,"report_send_nsca: $opt{set}{send_nsca_cfg} not found or not readable for UID $>:$!");
|
|
return $sent;
|
|
}
|
|
my $hostname=get_hostname();
|
|
my $nsca_cmdline=
|
|
"$opt{set}{send_nsca} " .
|
|
"-H $opt{set}{send_nsca_srv} " .
|
|
"-p $opt{set}{send_nsca_port} " .
|
|
"-to $opt{set}{send_nsca_timeout} " .
|
|
"-d \'$opt{set}{send_nsca_delim}\' " .
|
|
"-c $opt{set}{send_nsca_cfg}";
|
|
DEBUG3("cmdline is \'$nsca_cmdline\'");
|
|
for (my $i=1; $i<=$#cmds; $i++) {
|
|
|
|
#--- service will be suppressed if its mentioned in '-s suppress_service=<service1>,<service2>,...
|
|
if ($opt{set}{suppress_service} &&
|
|
$opt{set}{suppress_service}=~/\b$cmds[$i]{name}\b/i) {
|
|
DEBUG2("checkresult file of [ $cmds[$i]{name} ] will be suppressed");
|
|
next;
|
|
}
|
|
|
|
#--- call send_ncsa
|
|
if (!open(SEND_NSCA, "|$nsca_cmdline >/dev/null")) {
|
|
add_error(0,"report_send_nsca: error calling command line $nsca_cmdline");
|
|
return $sent;
|
|
}
|
|
printf SEND_NSCA "%s;%s;%s;%s\n",
|
|
$hostname,
|
|
$cmds[$i]{name},
|
|
$cmds[$i]{rc},
|
|
$cmds[$i]{output};
|
|
if ($?) {
|
|
add_error(0,"report_send_nsca: error sending data to nsca:$?");
|
|
} else {
|
|
$sent++;
|
|
}
|
|
close SEND_NSCA;
|
|
}
|
|
return $sent;
|
|
}
|
|
|
|
#---
|
|
#--- report child checks as check_result files
|
|
#---
|
|
sub report_checkresult_file {
|
|
|
|
#--- use File::Temp for generation of temp file, its available in standard perl
|
|
module ("File::Temp qw(tempfile)",1);
|
|
|
|
#--- loop over child checks
|
|
for (my $i=1; $i<=$#cmds; $i++) {
|
|
|
|
#--- service will be suppressed if its mentioned in '-s suppress_service=<service1>,<service2>,...
|
|
if ($opt{set}{suppress_service} &&
|
|
$opt{set}{suppress_service}=~/\b$cmds[$i]{name}\b/i) {
|
|
DEBUG2("checkresult file of [ $cmds[$i]{name} ] will be suppressed");
|
|
next;
|
|
}
|
|
|
|
#--- create service definition file (if enabled)
|
|
if ($opt{set}{feed_passive_autocreate}) {
|
|
check_and_create_service_definition($opt{set}{HOSTNAME}, $cmds[$i]{name});
|
|
}
|
|
|
|
#--- create checkresults file
|
|
my ($th,$tf)=File::Temp::tempfile("cXXXXXX",DIR=>"$opt{set}{checkresults_dir}");
|
|
my $escaped_output=$cmds[$i]{output}; $escaped_output=~s/\n/\\n/g;
|
|
if ($cmds[$i]{process_perfdata} && defined($cmds[$i]{performance}) && $cmds[$i]{performance}) {
|
|
$escaped_output.='|'.$cmds[$i]{performance}.' ['.$cmds[$i]{plugin}.']';
|
|
}
|
|
$escaped_output="(No output returned from plugin)" if ($escaped_output eq "");
|
|
DEBUG4("escaped_output:\'$escaped_output\'");
|
|
my $content=checkresult(
|
|
"host_name=$opt{set}{HOSTNAME}",
|
|
"service_description=$cmds[$i]{name}",
|
|
"start_time=".sprintf("%17.6f",$cmds[$i]{starttime}),
|
|
"finish_time=".sprintf("%17.6f",$cmds[$i]{endtime}),
|
|
"return_code=".$cmds[$i]{rc},
|
|
"output=".$escaped_output,
|
|
);
|
|
DEBUG4("file content is >$content<");
|
|
print $th $content;
|
|
close $th;
|
|
|
|
#--- write OK file
|
|
if (!open(OKFILE, ">$tf.ok")) {
|
|
add_error(0,"report_checkresult_file: Cannot write OK file to $tf.ok:$!");
|
|
} else {
|
|
DEBUG4("$tf.ok written");
|
|
close OKFILE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#---
|
|
#--- helper routine to build up checkresult output
|
|
#---
|
|
sub checkresult {
|
|
my (@parms)=@_;
|
|
|
|
my %att=(
|
|
"host_name" => undef,
|
|
"service_description" => undef,
|
|
"check_type" => 1,
|
|
"check_options" => 0,
|
|
"scheduled_check" => 0,
|
|
"reschedule_check" => 0,
|
|
"latency" => 0,
|
|
"start_time" => undef,
|
|
"finish_time" => undef,
|
|
"early_timeout" => 0,
|
|
"exited_ok" => 1,
|
|
"return_code" => undef,
|
|
"output" => undef,
|
|
);
|
|
#--- read and check parameter pairs (should be key=value)
|
|
foreach my $parameter (@parms) {
|
|
if (my ($key,$val)=($parameter=~/\s*([^=]+)\s*=\s*(.*)\s*/)) {
|
|
#--- perform only allowed (= already defined) pairs
|
|
if (exists($att{$key})) {
|
|
$att{$key}=$val;
|
|
DEBUG4("add key $key ($val) to attributes");
|
|
} else {
|
|
add_error(0,"checkresult: unknown attribute $key");
|
|
}
|
|
} else {
|
|
add_error(0,"checkresult: unknown attribute specification $parameter");
|
|
}
|
|
}
|
|
|
|
#--- create checkresult content
|
|
my $checkresult=sprintf(
|
|
"### check_multi passive check result file ###\n".
|
|
"file_time=%ld\n\n".
|
|
"### child check result ###\n".
|
|
"# Time: %s\n",
|
|
int(time),
|
|
scalar(localtime($att{start_time}))
|
|
);
|
|
#--- add attributes, output at last
|
|
foreach my $attribute (sort keys %att) {
|
|
#--- skip output, append later
|
|
next if ($attribute eq "output");
|
|
#--- add attribute to checkresult
|
|
$checkresult.="$attribute=$att{$attribute}\n" if (defined($att{$attribute}));
|
|
}
|
|
#--- at last: output
|
|
$checkresult.="output=$att{output}";
|
|
return $checkresult;
|
|
}
|
|
|
|
#---
|
|
#--- send check_multi results to send_gearman
|
|
#---
|
|
sub report_send_gearman {
|
|
# Usage:
|
|
#
|
|
# send_gearman [ --debug=<lvl> ]
|
|
# [ --help|-h ]
|
|
#
|
|
# [ --config=<configfile> ]
|
|
#
|
|
# [ --server=<server> ]
|
|
#
|
|
# [ --encryption=<yes|no> ]
|
|
# [ --key=<string> ]
|
|
# [ --keyfile=<file> ]
|
|
#
|
|
# [ --host=<hostname> ]
|
|
# [ --service=<servicename> ]
|
|
# [ --result_queue=<queue> ]
|
|
# [ --message|-m=<pluginoutput> ]
|
|
# [ --returncode|-r=<returncode> ]
|
|
#
|
|
# for sending active checks:
|
|
# [ --active ]
|
|
# [ --starttime=<unixtime> ]
|
|
# [ --finishtime=<unixtime> ]
|
|
# [ --latency=<seconds> ]
|
|
#
|
|
# plugin output is read from stdin unless --message is used.
|
|
#
|
|
my $sent=0;
|
|
if (! -x "$opt{set}{send_gearman}") {
|
|
add_error(0,"report_send_gearman: $opt{set}{send_gearman} not found or not executable:$!");
|
|
return $sent;
|
|
}
|
|
#if (! -f "$opt{set}{send_gearman_cfg}") {
|
|
#add_error(0,"report_send_gearman: $opt{set}{send_gearman_cfg} not found or not readable for UID $>:$!");
|
|
#return $sent;
|
|
#}
|
|
#--- determine hostname
|
|
my $hostname=get_hostname();
|
|
my $keyoption=(-f $opt{set}{send_gearman_key}) ? "keyfile" : "key";
|
|
my $resultqueueoption=($opt{set}{send_gearman_resultqueue})? "--result_queue=" : "";
|
|
my $encryption=($opt{set}{send_gearman_encryption}) ? "yes" : "no";
|
|
my $gearman_cmdline=
|
|
"$opt{set}{send_gearman} " .
|
|
"--server=$opt{set}{send_gearman_srv} " .
|
|
"--encryption=$encryption " .
|
|
"--$keyoption=$opt{set}{send_gearman_key} " .
|
|
"${resultqueueoption}$opt{set}{send_gearman_resultqueue} ";
|
|
DEBUG3("cmdline is \'$gearman_cmdline\'");
|
|
for (my $i=1; $i<=$#cmds; $i++) {
|
|
|
|
#--- service will be suppressed if its mentioned in '-s suppress_service=<service1>,<service2>,...
|
|
if ($opt{set}{suppress_service} &&
|
|
$opt{set}{suppress_service}=~/\b$cmds[$i]{name}\b/i) {
|
|
DEBUG2("checkresult file of [ $cmds[$i]{name} ] will be suppressed");
|
|
next;
|
|
}
|
|
|
|
#--- call send_gearman
|
|
unless (open(SEND_GEARMAN,
|
|
"|$gearman_cmdline ".
|
|
"--returncode=$cmds[$i]{rc} " .
|
|
"--host=\'$hostname\' " .
|
|
"--service=\'$cmds[$i]{name}\'" )) {
|
|
add_error(0,"report_send_gearman: error calling command line $gearman_cmdline");
|
|
return $sent;
|
|
}
|
|
print SEND_GEARMAN "$cmds[$i]{output}";
|
|
if (!close SEND_GEARMAN) {
|
|
add_error(0,"report_send_gearman: error sending data to gearman:$?");
|
|
} else {
|
|
$sent++;
|
|
}
|
|
}
|
|
return $sent;
|
|
}
|
|
|
|
#
|
|
# helper routine which automatically creates
|
|
# service definitions for passive feeded services
|
|
#
|
|
sub check_and_create_service_definition {
|
|
my ($hostname, $service_definition)=@_;
|
|
|
|
#--- check / create directory for passive feeded services
|
|
if (! my_mkdir("$opt{set}{feed_passive_dir}/$hostname",$opt{set}{feed_passive_dir_permissions})) {
|
|
add_error(0,"check_and_create_service_definition: cannot create directory $opt{set}{feed_passive_dir}/$hostname: $!");
|
|
}
|
|
#--- check age of service definition file and recreate if too old
|
|
my $cfgfile_age=time-(stat("$opt{set}{feed_passive_dir}/$hostname/${service_definition}.cfg"))[10]
|
|
if (-f "$opt{set}{feed_passive_dir}/$hostname/${service_definition}.cfg");
|
|
|
|
#--- create in any case if it does not exist
|
|
if (! -f "$opt{set}{feed_passive_dir}/$hostname/${service_definition}.cfg" ||
|
|
$cfgfile_age>$opt{set}{cmdfile_update_interval}) {
|
|
|
|
my $content=$opt{set}{service_definition_template_default};
|
|
$content=~s/\$HOSTNAME\$/$hostname/g;
|
|
$content=~s/\$SERVICEDESC\$/$service_definition/g;
|
|
writefile(
|
|
"$opt{set}{feed_passive_dir}/$hostname/${service_definition}.cfg",
|
|
($content)
|
|
);
|
|
DEBUG4("created service definition file for service ${service_definition}");
|
|
return 1;
|
|
} else {
|
|
DEBUG4("service definition file $opt{set}{feed_passive_dir}/$hostname/${service_definition}.cfg already there");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
#--- main ----------------------------------------------------------------------
|
|
#-------------------------------------------------------------------------------
|
|
|
|
#--- take care against signals
|
|
install_signal_handler(\install_signal_handler, @{$opt{set}{signals}});
|
|
|
|
#--- parse command line options and STDIN
|
|
exit $UNKNOWN if (&process_input != $OK);
|
|
|
|
#--- don't run this as root ;-)
|
|
add_error(0,"please don't run plugins as root!") if ($opt{set}{user}=~/root/ && !$opt{set}{dont_be_paranoid});
|
|
|
|
#--- parse command file (nrpe format)
|
|
&parse_files($opt{filename});
|
|
|
|
#--- parse single command lines
|
|
&parse_lines(@{$opt{execute}});
|
|
|
|
#--- provide settings as env variables
|
|
&set_env_settings;
|
|
|
|
#--- quick exit if only encoding of commands requested
|
|
if ($opt{set}{report} & $DETAIL_ENCODE_COMMANDS) {
|
|
print "%0A\n"; # end up with NEWLINE
|
|
exit $OK;
|
|
}
|
|
|
|
#--- no child checks defined yet? Throw UNKNOWN
|
|
if ($#cmds<1) {
|
|
add_error(0,"no checks defined");
|
|
$cmds[0]{rc}=$opt{set}{no_checks_rc};
|
|
$cmds[0]{state}[$OK]="0==1" if ($opt{set}{no_checks_rc} != $OK);
|
|
} else {
|
|
DEBUG4(($#cmds - 1) . " child checks running");
|
|
if (module("Data::Dumper")) {
|
|
DEBUG4(Dumper(@cmds));
|
|
}
|
|
|
|
}
|
|
|
|
#--- create unique hash for check
|
|
$cmds[0]{hash}=&my_hash(&config_string(\@cmds));
|
|
|
|
#--- initialize timer for overall timeout
|
|
$cmds[0]{starttime}=time;
|
|
$cmds[0]{timeouttime}=$cmds[0]{starttime} + $opt{set}{TIMEOUT};
|
|
|
|
#--- instant check execution defined? then do it right now
|
|
if ($opt{set}{instant}) {
|
|
DEBUG4("Instant execution of \'$opt{set}{instant}\' requested");
|
|
for ($no=1;$no<=$#cmds;$no++) {
|
|
if ($cmds[$no]{name} eq $opt{set}{instant}) {
|
|
exec_command($no);
|
|
DEBUG4("Instant execution of \'$opt{set}{instant}\': $cmds[$no]{output}, RC:$cmds[$no]{rc}");
|
|
print "$cmds[$no]{output}\n";
|
|
exit $cmds[$no]{rc};
|
|
}
|
|
}
|
|
#--- instant tag not found: bail out with error
|
|
print "UNKNOWN - tag \'$opt{set}{instant}\' not found for instant execution\n";
|
|
exit $UNKNOWN;
|
|
}
|
|
|
|
#--- inventory defined? then do it right now
|
|
if ($opt{set}{inventory}) {
|
|
report_inventory();
|
|
exit $OK;
|
|
}
|
|
|
|
#--- loop over commands in order of config file
|
|
$no=1;
|
|
while ($no<=$#cmds) {
|
|
|
|
#--- if total timeout is going to be exceeded, cancel next commands
|
|
if ($opt{set}{cancel_before_global_timeout} && time + $opt{set}{timeout} > $cmds[0]{timeouttime}) {
|
|
$cmds[$no]{output}="UNKNOWN - execution cancelled due to global timeout ($opt{set}{TIMEOUT}s)";
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
$cmds[$no]{runtime}=0;
|
|
} elsif (time > $cmds[0]{timeouttime}) {
|
|
$cmds[$no]{output}="UNKNOWN - execution cancelled due to global timeout ($opt{set}{TIMEOUT}s)";
|
|
$cmds[$no]{rc}=$UNKNOWN;
|
|
$cmds[$no]{runtime}=0;
|
|
} elsif ($cmds[$no]{feeded}) {
|
|
#--- do nothing - this cmd has been feeded in via STDIN
|
|
} else {
|
|
#--- wait interval between child checks if more than one check
|
|
if ($no>1) {
|
|
sleep($opt{set}{child_interval});
|
|
DEBUG3("-> sleeping $opt{set}{child_interval} seconds before child[$no]");
|
|
$cmds[0]{sleeped}+=$opt{set}{child_interval};
|
|
}
|
|
#--- execute command
|
|
&exec_command($no);
|
|
#DEBUG4("Executed command $no: $cmds[$no]{output}, RC:$cmds[$no]{rc}");
|
|
}
|
|
|
|
$no++;
|
|
}
|
|
$cmds[0]{endtime}=time;
|
|
|
|
#--- prepare output
|
|
&global_result_rating;
|
|
|
|
#--- report
|
|
&report_all;
|
|
|
|
#--- return rc with highest severity
|
|
DEBUG4("This is the end - with RC $cmds[0]{rc}");
|
|
exit $cmds[0]{rc};
|