package GLPlugin; use strict; use IO::File; use File::Basename; use Digest::MD5 qw(md5_hex); use Errno; use AutoLoader; our $AUTOLOAD; use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 }; { our $mode = undef; our $plugin = undef; our $pluginname = basename($0); our $blacklist = undef; our $info = []; our $extendedinfo = []; our $summary = []; our $variables = {}; } sub new { my $class = shift; my %params = @_; my $self = {}; bless $self, $class; $GLPlugin::plugin = GLPlugin::Commandline->new(%params); return $self; } sub init { my $self = shift; if ($self->opts->can("blacklist") && $self->opts->blacklist && -f $self->opts->blacklist) { $self->opts->blacklist = do { local (@ARGV, $/) = $self->opts->blacklist; <> }; } } sub dumper { my $self = shift; my $object = shift; my $run = $object->{runtime}; delete $object->{runtime}; printf STDERR "%s\n", Data::Dumper::Dumper($object); $object->{runtime} = $run; } sub no_such_mode { my $self = shift; printf "Mode %s is not implemented for this type of device\n", $self->opts->mode; exit 3; } ######################################################### # framework-related. setup, options # sub add_modes { my $self = shift; my $modes = shift; my $modestring = ""; my @modes = @{$modes}; my $longest = length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0]); my $format = " %-". (length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0])). "s\t(%s)\n"; foreach (@modes) { $modestring .= sprintf $format, $_->[1], $_->[3]; } $modestring .= sprintf "\n"; $GLPlugin::plugin->{modestring} = $modestring; } sub add_arg { my $self = shift; my %args = @_; if ($args{help} =~ /^--mode/) { $args{help} .= "\n".$GLPlugin::plugin->{modestring}; } $GLPlugin::plugin->{opts}->add_arg(%args); } sub add_mode { my $self = shift; my %args = @_; push(@{$GLPlugin::plugin->{modes}}, \%args); my $longest = length ((reverse sort {length $a <=> length $b} map { $_->{spec} } @{$GLPlugin::plugin->{modes}})[0]); my $format = " %-". (length ((reverse sort {length $a <=> length $b} map { $_->{spec} } @{$GLPlugin::plugin->{modes}})[0])). "s\t(%s)\n"; $GLPlugin::plugin->{modestring} = ""; foreach (@{$GLPlugin::plugin->{modes}}) { $GLPlugin::plugin->{modestring} .= sprintf $format, $_->{spec}, $_->{help}; } $GLPlugin::plugin->{modestring} .= "\n"; } sub validate_args { my $self = shift; if ($self->opts->mode =~ /^my-([^\-.]+)/) { my $param = $self->opts->mode; $param =~ s/\-/::/g; $self->add_mode( internal => $param, spec => $self->opts->mode, alias => undef, help => 'my extension', ); } elsif ($self->opts->mode eq 'encode') { my $input = <>; chomp $input; $input =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg; printf "%s\n", $input; exit 0; } elsif ((! grep { $self->opts->mode eq $_ } map { $_->{spec} } @{$GLPlugin::plugin->{modes}}) && (! grep { $self->opts->mode eq $_ } map { defined $_->{alias} ? @{$_->{alias}} : () } @{$GLPlugin::plugin->{modes}})) { printf "UNKNOWN - mode %s\n", $self->opts->mode; $self->opts->print_help(); exit 3; } if ($self->opts->name && $self->opts->name =~ /(%22)|(%27)/) { my $name = $self->opts->name; $name =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $self->override_opt('name', $name); } $GLPlugin::mode = ( map { $_->{internal} } grep { ($self->opts->mode eq $_->{spec}) || ( defined $_->{alias} && grep { $self->opts->mode eq $_ } @{$_->{alias}}) } @{$GLPlugin::plugin->{modes}} )[0]; if ($self->opts->multiline) { $ENV{NRPE_MULTILINESUPPORT} = 1; } else { $ENV{NRPE_MULTILINESUPPORT} = 0; } if (! $self->opts->statefilesdir) { if (exists $ENV{OMD_ROOT}) { $self->override_opt('statefilesdir', $ENV{OMD_ROOT}."/var/tmp/".$GLPlugin::plugin->{name}); } else { $self->override_opt('statefilesdir', "/var/tmp/".$GLPlugin::plugin->{name}); } } $GLPlugin::plugin->{statefilesdir} = $self->opts->statefilesdir; if ($self->opts->can("warningx") && $self->opts->warningx) { foreach my $key (keys %{$self->opts->warningx}) { $self->set_thresholds(metric => $key, warning => $self->opts->warningx->{$key}); } } if ($self->opts->can("criticalx") && $self->opts->criticalx) { foreach my $key (keys %{$self->opts->criticalx}) { $self->set_thresholds(metric => $key, critical => $self->opts->criticalx->{$key}); } } $self->set_timeout_alarm() if ! $SIG{'ALRM'}; } sub set_timeout_alarm { my $self = shift; $SIG{'ALRM'} = sub { printf "UNKNOWN - %s timed out after %d seconds\n", $GLPlugin::plugin->{name}, $self->opts->timeout; exit 3; }; alarm($self->opts->timeout); } ######################################################### # global helpers # sub set_variable { my $self = shift; my $key = shift; my $value = shift; $GLPlugin::variables->{$key} = $value; } sub get_variable { my $self = shift; my $key = shift; my $fallback = shift; return exists $GLPlugin::variables->{$key} ? $GLPlugin::variables->{$key} : $fallback; } sub debug { my $self = shift; my $format = shift; my $tracefile = "/tmp/".$0.".trace"; $self->{trace} = -f $tracefile ? 1 : 0; if ($self->get_variable("verbose") && $self->get_variable("verbose") > $self->get_variable("verbosity", 10)) { printf("%s: ", scalar localtime); printf($format, @_); printf "\n"; } if ($self->{trace}) { my $logfh = new IO::File; $logfh->autoflush(1); if ($logfh->open($tracefile, "a")) { $logfh->printf("%s: ", scalar localtime); $logfh->printf($format, @_); $logfh->printf("\n"); $logfh->close(); } } } sub filter_namex { my $self = shift; my $opt = shift; my $name = shift; if ($opt) { if ($self->opts->regexp) { if ($name =~ /$opt/i) { return 1; } } else { if (lc $opt eq lc $name) { return 1; } } } else { return 1; } return 0; } sub filter_name { my $self = shift; my $name = shift; return $self->filter_namex($self->opts->name, $name); } sub filter_name2 { my $self = shift; my $name = shift; return $self->filter_namex($self->opts->name2, $name); } sub filter_name3 { my $self = shift; my $name = shift; return $self->filter_namex($self->opts->name3, $name); } sub version_is_minimum { my $self = shift; my $version = shift; my $installed_version; my $newer = 1; if ($self->get_variable("version")) { $installed_version = $self->get_variable("version"); } elsif (exists $self->{version}) { $installed_version = $self->{version}; } else { return 0; } my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version); my @v2 = split(/\./, $installed_version); if (scalar(@v1) > scalar(@v2)) { push(@v2, (0) x (scalar(@v1) - scalar(@v2))); } elsif (scalar(@v2) > scalar(@v1)) { push(@v1, (0) x (scalar(@v2) - scalar(@v1))); } foreach my $pos (0..$#v1) { if ($v2[$pos] > $v1[$pos]) { $newer = 1; last; } elsif ($v2[$pos] < $v1[$pos]) { $newer = 0; last; } } return $newer; } sub accentfree { my $self = shift; my $text = shift; # thanks mycoyne who posted this accent-remove-algorithm # http://www.experts-exchange.com/Programming/Languages/Scripting/Perl/Q_23275533.html#a21234612 my @transformed; my %replace = ( '9a' => 's', '9c' => 'oe', '9e' => 'z', '9f' => 'Y', 'c0' => 'A', 'c1' => 'A', 'c2' => 'A', 'c3' => 'A', 'c4' => 'A', 'c5' => 'A', 'c6' => 'AE', 'c7' => 'C', 'c8' => 'E', 'c9' => 'E', 'ca' => 'E', 'cb' => 'E', 'cc' => 'I', 'cd' => 'I', 'ce' => 'I', 'cf' => 'I', 'd0' => 'D', 'd1' => 'N', 'd2' => 'O', 'd3' => 'O', 'd4' => 'O', 'd5' => 'O', 'd6' => 'O', 'd8' => 'O', 'd9' => 'U', 'da' => 'U', 'db' => 'U', 'dc' => 'U', 'dd' => 'Y', 'e0' => 'a', 'e1' => 'a', 'e2' => 'a', 'e3' => 'a', 'e4' => 'a', 'e5' => 'a', 'e6' => 'ae', 'e7' => 'c', 'e8' => 'e', 'e9' => 'e', 'ea' => 'e', 'eb' => 'e', 'ec' => 'i', 'ed' => 'i', 'ee' => 'i', 'ef' => 'i', 'f0' => 'o', 'f1' => 'n', 'f2' => 'o', 'f3' => 'o', 'f4' => 'o', 'f5' => 'o', 'f6' => 'o', 'f8' => 'o', 'f9' => 'u', 'fa' => 'u', 'fb' => 'u', 'fc' => 'u', 'fd' => 'y', 'ff' => 'y', ); my @letters = split //, $text;; for (my $i = 0; $i <= $#letters; $i++) { my $hex = sprintf "%x", ord($letters[$i]); $letters[$i] = $replace{$hex} if (exists $replace{$hex}); } push @transformed, @letters; return join '', @transformed; } sub dump { my $self = shift; my $class = ref($self); $class =~ s/^.*:://; if (exists $self->{flat_indices}) { printf "[%s_%s]\n", uc $class, $self->{flat_indices}; } else { printf "[%s]\n", uc $class; } foreach (grep !/^(info|trace|warning|critical|blacklisted|extendedinfo|flat_indices|indices)/, sort keys %{$self}) { printf "%s: %s\n", $_, $self->{$_} if defined $self->{$_} && ref($self->{$_}) ne "ARRAY"; } if ($self->{info}) { printf "info: %s\n", $self->{info}; } printf "\n"; foreach (grep !/^(info|trace|warning|critical|blacklisted|extendedinfo|flat_indices|indices)/, sort keys %{$self}) { if (defined $self->{$_} && ref($self->{$_}) eq "ARRAY") { foreach my $obj (@{$self->{$_}}) { $obj->dump(); } } } } sub table_ascii { my $self = shift; my $table = shift; my $titles = shift; my $text = ""; my $column_length = {}; my $column = 0; foreach (@{$titles}) { $column_length->{$column++} = length($_); } foreach my $tr (@{$table}) { @{$tr} = map { ref($_) eq "ARRAY" ? $_->[0] : $_; } @{$tr}; $column = 0; foreach my $td (@{$tr}) { if (length($td) > $column_length->{$column}) { $column_length->{$column} = length($td); } $column++; } } $column = 0; foreach (@{$titles}) { $column_length->{$column} = "%".($column_length->{$column} + 3)."s"; $column++; } $column = 0; foreach (@{$titles}) { $text .= sprintf $column_length->{$column++}, $_; } $text .= "\n"; foreach my $tr (@{$table}) { $column = 0; foreach my $td (@{$tr}) { $text .= sprintf $column_length->{$column++}, $td; } $text .= "\n"; } return $text; } sub table_html { my $self = shift; my $table = shift; my $titles = shift; my $text = ""; $text .= ""; $text .= ""; foreach (@{$titles}) { $text .= sprintf "", $_; } $text .= ""; foreach my $tr (@{$table}) { $text .= ""; foreach my $td (@{$tr}) { my $class = "statusOK"; if (ref($td) eq "ARRAY") { $class = { 0 => "statusOK", 1 => "statusWARNING", 2 => "statusCRITICAL", 3 => "statusUNKNOWN", }->{$td->[1]}; $td = $td->[0]; } $text .= sprintf "", $class, $td; } $text .= ""; } $text .= "
%s
%s
"; return $text; } sub load_my_extension { my $self = shift; if ($self->opts->mode =~ /^my-([^-.]+)/) { my $class = $1; my $loaderror = undef; substr($class, 0, 1) = uc substr($class, 0, 1); if (! $self->opts->get("with-mymodules-dyn-dir")) { $self->override_opt("with-mymodules-dyn-dir", ""); } my $plugin_name = basename($0); $plugin_name =~ /check_(.*?)_health/; $plugin_name = "Check".uc(substr($1, 0, 1)).substr($1, 1)."Health"; foreach my $libpath (split(":", $self->opts->get("with-mymodules-dyn-dir"))) { foreach my $extmod (glob $libpath."/".$plugin_name."*.pm") { my $stderrvar; *SAVEERR = *STDERR; open OUT ,'>',\$stderrvar; *STDERR = *OUT; eval { $self->debug(sprintf "loading module %s", $extmod); require $extmod; }; *STDERR = *SAVEERR; if ($@) { $loaderror = $extmod; $self->debug(sprintf "failed loading module %s: %s", $extmod, $@); } } } my $original_class = ref($self); my $original_init = $self->can("init"); bless $self, "My$class"; if ($self->isa("GLPlugin")) { my $new_init = $self->can("init"); if ($new_init == $original_init) { $self->add_unknown( sprintf "Class %s needs an init() method", ref($self)); } else { # now go back to check_*_health.pl where init() will be called } } else { bless $self, $original_class; $self->add_unknown( sprintf "Class %s is not a subclass of GLPlugin%s", "My$class", $loaderror ? sprintf " (syntax error in %s?)", $loaderror : "" ); my ($code, $message) = $self->check_messages(join => ', ', join_all => ', '); $self->nagios_exit($code, $message); } } } ######################################################### # runtime methods # sub mode { my $self = shift; return $GLPlugin::mode; } sub statefilesdir { my $self = shift; return $GLPlugin::plugin->{statefilesdir}; } sub opts { # die beiden _nicht_ in AUTOLOAD schieben, das kracht! my $self = shift; return $GLPlugin::plugin->opts(); } sub getopts { my $self = shift; my $envparams = shift || []; $GLPlugin::plugin->getopts(); # es kann sein, dass beim aufraeumen zum schluss als erstes objekt # das $GLPlugin::plugin geloescht wird. in anderen destruktoren # (insb. fuer dbi disconnect) steht dann $self->opts->verbose # nicht mehr zur verfuegung bzw. $GLPlugin::plugin->opts ist undef. $self->set_variable("verbose", $self->opts->verbose); # # die gueltigkeit von modes wird bereits hier geprueft und nicht danach # in validate_args. (zwischen getopts und validate_args wird # normalerweise classify aufgerufen, welches bereits eine verbindung # zum endgeraet herstellt. bei falschem mode waere das eine verschwendung # bzw. durch den exit3 ein evt. unsauberes beenden der verbindung. if ((! grep { $self->opts->mode eq $_ } map { $_->{spec} } @{$GLPlugin::plugin->{modes}}) && (! grep { $self->opts->mode eq $_ } map { defined $_->{alias} ? @{$_->{alias}} : () } @{$GLPlugin::plugin->{modes}})) { printf "UNKNOWN - mode %s\n", $self->opts->mode; $self->opts->print_help(); exit 3; } } sub add_ok { my $self = shift; my $message = shift || $self->{info}; $self->add_message(OK, $message); } sub add_warning { my $self = shift; my $message = shift || $self->{info}; $self->add_message(WARNING, $message); } sub add_critical { my $self = shift; my $message = shift || $self->{info}; $self->add_message(CRITICAL, $message); } sub add_unknown { my $self = shift; my $message = shift || $self->{info}; $self->add_message(UNKNOWN, $message); } sub add_message { my $self = shift; my $level = shift; my $message = shift || $self->{info}; $GLPlugin::plugin->add_message($level, $message) unless $self->is_blacklisted(); if (exists $self->{failed}) { if ($level == UNKNOWN && $self->{failed} == OK) { $self->{failed} = $level; } elsif ($level > $self->{failed}) { $self->{failed} = $level; } } } sub clear_ok { my $self = shift; $self->clear_messages(OK); } sub clear_warning { my $self = shift; $self->clear_messages(WARNING); } sub clear_critical { my $self = shift; $self->clear_messages(CRITICAL); } sub clear_unknown { my $self = shift; $self->clear_messages(UNKNOWN); } sub clear_all { # deprecated, use clear_messages my $self = shift; $self->clear_ok(); $self->clear_warning(); $self->clear_critical(); $self->clear_unknown(); } sub set_level { my $self = shift; my $code = shift; $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/; $code = lc $code; if (! exists $self->{tmp_level}) { $self->{tmp_level} = { ok => 0, warning => 0, critical => 0, unknown => 0, }; } $self->{tmp_level}->{$code}++; } sub get_level { my $self = shift; return OK if ! exists $self->{tmp_level}; my $code = OK; $code ||= CRITICAL if $self->{tmp_level}->{critical}; $code ||= WARNING if $self->{tmp_level}->{warning}; $code ||= UNKNOWN if $self->{tmp_level}->{unknown}; return $code; } ######################################################### # blacklisting # sub blacklist { my $self = shift; $self->{blacklisted} = 1; } sub add_blacklist { my $self = shift; my $list = shift; $GLPlugin::blacklist = join('/', (split('/', $self->opts->blacklist), $list)); } sub is_blacklisted { my $self = shift; if (! $self->opts->can("blacklist")) { return 0; } if (! exists $self->{blacklisted}) { $self->{blacklisted} = 0; } if (exists $self->{blacklisted} && $self->{blacklisted}) { return $self->{blacklisted}; } # FAN:459,203/TEMP:102229/ENVSUBSYSTEM # FAN_459,FAN_203,TEMP_102229,ENVSUBSYSTEM if ($self->opts->blacklist =~ /_/) { foreach my $bl_item (split(/,/, $self->opts->blacklist)) { if ($bl_item eq $self->internal_name()) { $self->{blacklisted} = 1; } } } else { foreach my $bl_items (split(/\//, $self->opts->blacklist)) { if ($bl_items =~ /^(\w+):([\:\d\-,]+)$/) { my $bl_type = $1; my $bl_names = $2; foreach my $bl_name (split(/,/, $bl_names)) { if ($bl_type."_".$bl_name eq $self->internal_name()) { $self->{blacklisted} = 1; } } } elsif ($bl_items =~ /^(\w+)$/) { if ($bl_items eq $self->internal_name()) { $self->{blacklisted} = 1; } } } } return $self->{blacklisted}; } ######################################################### # additional info # sub add_info { my $self = shift; my $info = shift; $info = $self->is_blacklisted() ? $info.' (blacklisted)' : $info; $self->{info} = $info; push(@{$GLPlugin::info}, $info); } sub annotate_info { # deprecated my $self = shift; my $annotation = shift; my $lastinfo = pop(@{$GLPlugin::info}); $lastinfo .= sprintf ' (%s)', $annotation; push(@{$GLPlugin::info}, $lastinfo); } sub add_extendedinfo { # deprecated my $self = shift; my $info = shift; $self->{extendedinfo} = $info; return if ! $self->opts->extendedinfo; push(@{$GLPlugin::extendedinfo}, $info); } sub get_info { my $self = shift; my $separator = shift || ' '; return join($separator , @{$GLPlugin::info}); } sub get_extendedinfo { my $self = shift; my $separator = shift || ' '; return join($separator, @{$GLPlugin::extendedinfo}); } sub add_summary { # deprecated my $self = shift; my $summary = shift; push(@{$GLPlugin::summary}, $summary); } sub get_summary { my $self = shift; return join(', ', @{$GLPlugin::summary}); } ######################################################### # persistency # sub valdiff { my $self = shift; my $pparams = shift; my %params = %{$pparams}; my @keys = @_; my $now = time; my $newest_history_set = {}; my $last_values = $self->load_state(%params) || eval { my $empty_events = {}; foreach (@keys) { if (ref($self->{$_}) eq "ARRAY") { $empty_events->{$_} = []; } else { $empty_events->{$_} = 0; } } $empty_events->{timestamp} = 0; if ($self->opts->lookback) { $empty_events->{lookback_history} = {}; } $empty_events; }; $self->{'delta_timestamp'} = $now - $last_values->{timestamp}; foreach (@keys) { if ($self->opts->lookback) { # find a last_value in the history which fits lookback best # and overwrite $last_values->{$_} with historic data if (exists $last_values->{lookback_history}->{$_}) { foreach my $date (sort {$a <=> $b} keys %{$last_values->{lookback_history}->{$_}}) { $newest_history_set->{$_} = $last_values->{lookback_history}->{$_}->{$date}; $newest_history_set->{timestamp} = $date; } foreach my $date (sort {$a <=> $b} keys %{$last_values->{lookback_history}->{$_}}) { if ($date >= ($now - $self->opts->lookback)) { $last_values->{$_} = $last_values->{lookback_history}->{$_}->{$date}; $last_values->{timestamp} = $date; last; } else { delete $last_values->{lookback_history}->{$_}->{$date}; } } } } if ($self->{$_} =~ /^\d+$/) { $last_values->{$_} = 0 if ! exists $last_values->{$_}; if ($self->{$_} >= $last_values->{$_}) { $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_}; } else { # vermutlich db restart und zaehler alle auf null $self->{'delta_'.$_} = $self->{$_}; } $self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_}); $self->{$_.'_per_sec'} = $self->{'delta_timestamp'} ? $self->{'delta_'.$_} / $self->{'delta_timestamp'} : 0; } elsif (ref($self->{$_}) eq "ARRAY") { if ((! exists $last_values->{$_} || ! defined $last_values->{$_}) && exists $params{lastarray}) { # innerhalb der lookback-zeit wurde nichts in der lookback_history # gefunden. allenfalls irgendwas aelteres. normalerweise # wuerde jetzt das array als [] initialisiert. # d.h. es wuerde ein delta geben, @found s.u. # wenn man das nicht will, sondern einfach aktuelles array mit # dem array des letzten laufs vergleichen will, setzt man lastarray $last_values->{$_} = %{$newest_history_set} ? $newest_history_set->{$_} : [] } elsif ((! exists $last_values->{$_} || ! defined $last_values->{$_}) && ! exists $params{lastarray}) { $last_values->{$_} = [] if ! exists $last_values->{$_}; } elsif (exists $last_values->{$_} && ! defined $last_values->{$_}) { # $_ kann es auch ausserhalb des lookback_history-keys als normalen # key geben. der zeigt normalerweise auf den entspr. letzten # lookback_history eintrag. wurde der wegen ueberalterung abgeschnitten # ist der hier auch undef. $last_values->{$_} = %{$newest_history_set} ? $newest_history_set->{$_} : [] } my %saved = map { $_ => 1 } @{$last_values->{$_}}; my %current = map { $_ => 1 } @{$self->{$_}}; my @found = grep(!defined $saved{$_}, @{$self->{$_}}); my @lost = grep(!defined $current{$_}, @{$last_values->{$_}}); $self->{'delta_found_'.$_} = \@found; $self->{'delta_lost_'.$_} = \@lost; } } $params{save} = eval { my $empty_events = {}; foreach (@keys) { $empty_events->{$_} = $self->{$_}; } $empty_events->{timestamp} = $now; if ($self->opts->lookback) { $empty_events->{lookback_history} = $last_values->{lookback_history}; foreach (@keys) { $empty_events->{lookback_history}->{$_}->{$now} = $self->{$_}; } } $empty_events; }; $self->save_state(%params); } sub create_statefilesdir { my $self = shift; if (! -d $self->statefilesdir()) { eval { use File::Path; mkpath $self->statefilesdir(); }; if ($@ || ! -w $self->statefilesdir()) { $self->add_message(UNKNOWN, sprintf "cannot create status dir %s! check your filesystem (permissions/usage/integrity) and disk devices", $self->statefilesdir()); } } elsif (! -w $self->statefilesdir()) { $self->add_message(UNKNOWN, sprintf "cannot write status dir %s! check your filesystem (permissions/usage/integrity) and disk devices", $self->statefilesdir()); } } sub create_statefile { my $self = shift; my %params = @_; my $extension = ""; $extension .= $params{name} ? '_'.$params{name} : ''; $extension =~ s/\//_/g; $extension =~ s/\(/_/g; $extension =~ s/\)/_/g; $extension =~ s/\*/_/g; $extension =~ s/\s/_/g; return sprintf "%s/%s%s", $self->statefilesdir(), $self->opts->mode, lc $extension; } sub schimpf { my $self = shift; printf "statefilesdir %s is not writable.\nYou didn't run this plugin as root, didn't you?\n", $self->statefilesdir(); } # $self->protect_value('1.1-flat_index', 'cpu_busy', 'percent'); sub protect_value { my $self = shift; my $ident = shift; my $key = shift; my $validfunc = shift; if (ref($validfunc) ne "CODE" && $validfunc eq "percent") { $validfunc = sub { my $value = shift; return ($value < 0 || $value > 100) ? 0 : 1; }; } elsif (ref($validfunc) ne "CODE" && $validfunc eq "positive") { $validfunc = sub { my $value = shift; return ($value < 0) ? 0 : 1; }; } if (&$validfunc($self->{$key})) { $self->save_state(name => 'protect_'.$ident.'_'.$key, save => { $key => $self->{$key}, exception => 0, }); } else { # if the device gives us an clearly wrong value, simply use the last value. my $laststate = $self->load_state(name => 'protect_'.$ident.'_'.$key); $self->debug(sprintf "self->{%s} is %s and invalid for the %dth time", $key, $self->{$key}, $laststate->{exception} + 1); if ($laststate->{exception} <= 5) { # but only 5 times. # if the error persists, somebody has to check the device. $self->{$key} = $laststate->{$key}; } $self->save_state(name => 'protect_'.$ident.'_'.$key, save => { $key => $laststate->{$key}, exception => $laststate->{exception}++, }); } } sub save_state { my $self = shift; my %params = @_; $self->create_statefilesdir(); my $statefile = $self->create_statefile(%params); my $tmpfile = $self->statefilesdir().'/check__health_tmp_'.$$; if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) { $params{save}->{localtime} = scalar localtime $params{save}->{timestamp}; } my $seekfh = new IO::File; if ($seekfh->open($tmpfile, "w")) { $seekfh->printf("%s", Data::Dumper::Dumper($params{save})); $seekfh->flush(); $seekfh->close(); $self->debug(sprintf "saved %s to %s", Data::Dumper::Dumper($params{save}), $statefile); } if (! rename $tmpfile, $statefile) { $self->add_message(UNKNOWN, sprintf "cannot write status file %s! check your filesystem (permissions/usage/integrity) and disk devices", $statefile); } } sub load_state { my $self = shift; my %params = @_; my $statefile = $self->create_statefile(%params); if ( -f $statefile) { our $VAR1; eval { require $statefile; }; if($@) { printf "rumms\n"; } $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1)); return $VAR1; } else { return undef; } } ######################################################### # daemon mode # sub check_pidfile { my $self = shift; my $fh = new IO::File; if ($fh->open($self->{pidfile}, "r")) { my $pid = $fh->getline(); $fh->close(); if (! $pid) { $self->debug("Found pidfile %s with no valid pid. Exiting.", $self->{pidfile}); return 0; } else { $self->debug("Found pidfile %s with pid %d", $self->{pidfile}, $pid); kill 0, $pid; if ($! == Errno::ESRCH) { $self->debug("This pidfile is stale. Writing a new one"); $self->write_pidfile(); return 1; } else { $self->debug("This pidfile is held by a running process. Exiting"); return 0; } } } else { $self->debug("Found no pidfile. Writing a new one"); $self->write_pidfile(); return 1; } } sub write_pidfile { my $self = shift; if (! -d dirname($self->{pidfile})) { eval "require File::Path;"; if (defined(&File::Path::mkpath)) { import File::Path; eval { mkpath(dirname($self->{pidfile})); }; } else { my @dirs = (); map { push @dirs, $_; mkdir(join('/', @dirs)) if join('/', @dirs) && ! -d join('/', @dirs); } split(/\//, dirname($self->{pidfile})); } } my $fh = new IO::File; $fh->autoflush(1); if ($fh->open($self->{pidfile}, "w")) { $fh->printf("%s", $$); $fh->close(); } else { $self->debug("Could not write pidfile %s", $self->{pidfile}); die "pid file could not be written"; } } sub AUTOLOAD { my $self = shift; return if ($AUTOLOAD =~ /DESTROY/); $self->debug("AUTOLOAD %s\n", $AUTOLOAD) if $self->opts->verbose >= 2; if ($AUTOLOAD =~ /^(.*)::analyze_and_check_(.*)_subsystem$/) { my $class = $1; my $subsystem = $2; my $analyze = sprintf "analyze_%s_subsystem", $subsystem; my $check = sprintf "check_%s_subsystem", $subsystem; my @params = @_; if (@params) { # analyzer class my $subsystem_class = shift @params; $self->{components}->{$subsystem.'_subsystem'} = $subsystem_class->new(); $self->debug(sprintf "\$self->{components}->{%s_subsystem} = %s->new()", $subsystem, $subsystem_class); } else { $self->$analyze(); $self->debug("call %s()", $analyze); } $self->$check(); } elsif ($AUTOLOAD =~ /^(.*)::check_(.*)_subsystem$/) { my $class = $1; my $subsystem = sprintf "%s_subsystem", $2; $self->{components}->{$subsystem}->check(); $self->{components}->{$subsystem}->dump() if $self->opts->verbose >= 2; } elsif ($AUTOLOAD =~ /^.*::(status_code|check_messages|nagios_exit|html_string|perfdata_string|selected_perfdata|check_thresholds|get_thresholds|opts)$/) { return $GLPlugin::plugin->$1(@_); } elsif ($AUTOLOAD =~ /^.*::(clear_messages|suppress_messages|add_html|add_perfdata|override_opt|create_opt|set_thresholds|force_thresholds)$/) { $GLPlugin::plugin->$1(@_); } else { $self->debug("AUTOLOAD: class %s has no method %s\n", ref($self), $AUTOLOAD); } } package GLPlugin::Commandline; use strict; use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3, DEPENDENT => 4 }; our %ERRORS = ( 'OK' => OK, 'WARNING' => WARNING, 'CRITICAL' => CRITICAL, 'UNKNOWN' => UNKNOWN, 'DEPENDENT' => DEPENDENT, ); our %STATUS_TEXT = reverse %ERRORS; sub new { my $class = shift; my %params = @_; my $self = { perfdata => [], messages => { ok => [], warning => [], critical => [], unknown => [], }, args => [], opts => GLPlugin::Commandline::Getopt->new(%params), modes => [], statefilesdir => undef, }; foreach (qw(shortname usage version url plugin blurb extra license timeout)) { $self->{$_} = $params{$_}; } bless $self, $class; $self->{name} = $self->{plugin}; $GLPlugin::plugin = $self; } sub AUTOLOAD { my $self = shift; return if ($AUTOLOAD =~ /DESTROY/); $self->debug("AUTOLOAD %s\n", $AUTOLOAD) if $self->{opts}->verbose >= 2; if ($AUTOLOAD =~ /^.*::(add_arg|override_opt|create_opt)$/) { $self->{opts}->$1(@_); } } sub DESTROY { my $self = shift; # ohne dieses DESTROY rennt nagios_exit in obiges AUTOLOAD rein # und fliegt aufs Maul, weil {opts} bereits nicht mehr existiert. # Unerklaerliches Verhalten. } sub debug { my $self = shift; my $format = shift; my $tracefile = "/tmp/".$0.".trace"; $self->{trace} = -f $tracefile ? 1 : 0; if ($self->opts->verbose && $self->opts->verbose > 10) { printf("%s: ", scalar localtime); printf($format, @_); printf "\n"; } if ($self->{trace}) { my $logfh = new IO::File; $logfh->autoflush(1); if ($logfh->open($tracefile, "a")) { $logfh->printf("%s: ", scalar localtime); $logfh->printf($format, @_); $logfh->printf("\n"); $logfh->close(); } } } sub opts { my $self = shift; return $self->{opts}; } sub getopts { my $self = shift; $self->opts->getopts(); } sub add_message { my $self = shift; my ($code, @messages) = @_; $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/; $code = lc $code; push @{$self->{messages}->{$code}}, @messages; } sub selected_perfdata { my $self = shift; my $label = shift; if ($self->opts->can("selectedperfdata") && $self->opts->selectedperfdata) { my $pattern = $self->opts->selectedperfdata; return ($label =~ /$pattern/i) ? 1 : 0; } else { return 1; } } sub add_perfdata { my ($self, %args) = @_; #printf "add_perfdata %s\n", Data::Dumper::Dumper(\%args); #printf "add_perfdata %s\n", Data::Dumper::Dumper($self->{thresholds}); # # wenn warning, critical, dann wird von oben ein expliziter wert mitgegeben # wenn thresholds # wenn label in # warningx $self->{thresholds}->{$label}->{warning} existiert # dann nimm $self->{thresholds}->{$label}->{warning} # ansonsten thresholds->default->warning # my $label = $args{label}; my $value = $args{value}; my $uom = $args{uom} || ""; my $format = '%d'; if ($value =~ /\./) { if (defined $args{places}) { $value = sprintf '%.'.$args{places}.'f', $value; } else { $value = sprintf "%.2f", $value; } } else { $value = sprintf "%d", $value; } my $warn = ""; my $crit = ""; my $min = defined $args{min} ? $args{min} : ""; my $max = defined $args{max} ? $args{max} : ""; if ($args{thresholds} || (! exists $args{warning} && ! exists $args{critical})) { if (exists $self->{thresholds}->{$label}->{warning}) { $warn = $self->{thresholds}->{$label}->{warning}; } elsif (exists $self->{thresholds}->{default}->{warning}) { $warn = $self->{thresholds}->{default}->{warning}; } if (exists $self->{thresholds}->{$label}->{critical}) { $crit = $self->{thresholds}->{$label}->{critical}; } elsif (exists $self->{thresholds}->{default}->{critical}) { $crit = $self->{thresholds}->{default}->{critical}; } } else { if ($args{warning}) { $warn = $args{warning}; } if ($args{critical}) { $crit = $args{critical}; } } if ($uom eq "%") { $min = 0; $max = 100; } if (defined $args{places}) { # cut off excessive decimals which may be the result of a division # length = places*2, no trailing zeroes if ($warn ne "") { $warn = join("", map { s/\.0+$//; $_ } map { s/(\.[1-9]+)0+$/$1/; $_ } map { /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_; } split(/([\+\-\d\.]+)/, $warn)); } if ($crit ne "") { $crit = join("", map { s/\.0+$//; $_ } map { s/(\.[1-9]+)0+$/$1/; $_ } map { /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_; } split(/([\+\-\d\.]+)/, $crit)); } if ($min ne "") { $min = join("", map { s/\.0+$//; $_ } map { s/(\.[1-9]+)0+$/$1/; $_ } map { /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_; } split(/([\+\-\d\.]+)/, $min)); } if ($max ne "") { $max = join("", map { s/\.0+$//; $_ } map { s/(\.[1-9]+)0+$/$1/; $_ } map { /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_; } split(/([\+\-\d\.]+)/, $max)); } } push @{$self->{perfdata}}, sprintf("'%s'=%s%s;%s;%s;%s;%s", $label, $value, $uom, $warn, $crit, $min, $max) if $self->selected_perfdata($label); } sub add_html { my $self = shift; my $line = shift; push @{$self->{html}}, $line; } sub suppress_messages { my $self = shift; $self->{suppress_messages} = 1; } sub clear_messages { my $self = shift; my $code = shift; $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/; $code = lc $code; $self->{messages}->{$code} = []; } sub check_messages { my $self = shift; my %args = @_; # Add object messages to any passed in as args for my $code (qw(critical warning unknown ok)) { my $messages = $self->{messages}->{$code} || []; if ($args{$code}) { unless (ref $args{$code} eq 'ARRAY') { if ($code eq 'ok') { $args{$code} = [ $args{$code} ]; } } push @{$args{$code}}, @$messages; } else { $args{$code} = $messages; } } my %arg = %args; $arg{join} = ' ' unless defined $arg{join}; # Decide $code my $code = OK; $code ||= CRITICAL if @{$arg{critical}}; $code ||= WARNING if @{$arg{warning}}; $code ||= UNKNOWN if @{$arg{unknown}}; return $code unless wantarray; # Compose message my $message = ''; if ($arg{join_all}) { $message = join( $arg{join_all}, map { @$_ ? join( $arg{'join'}, @$_) : () } $arg{critical}, $arg{warning}, $arg{unknown}, $arg{ok} ? (ref $arg{ok} ? $arg{ok} : [ $arg{ok} ]) : [] ); } else { $message ||= join( $arg{'join'}, @{$arg{critical}} ) if $code == CRITICAL; $message ||= join( $arg{'join'}, @{$arg{warning}} ) if $code == WARNING; $message ||= join( $arg{'join'}, @{$arg{unknown}} ) if $code == UNKNOWN; $message ||= ref $arg{ok} ? join( $arg{'join'}, @{$arg{ok}} ) : $arg{ok} if $arg{ok}; } return ($code, $message); } sub status_code { my $self = shift; my $code = shift; $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/; $code = uc $code; $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code}; $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code}; return "$STATUS_TEXT{$code}"; } sub perfdata_string { my $self = shift; if (scalar (@{$self->{perfdata}})) { return join(" ", @{$self->{perfdata}}); } else { return ""; } } sub html_string { my $self = shift; if (scalar (@{$self->{html}})) { return join(" ", @{$self->{html}}); } else { return ""; } } sub nagios_exit { my $self = shift; my ($code, $message, $arg) = @_; $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code}; $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code}; $message = '' unless defined $message; if (ref $message && ref $message eq 'ARRAY') { $message = join(' ', map { chomp; $_ } @$message); } else { chomp $message; } if ($self->opts->negate) { foreach my $from (keys %{$self->opts->negate}) { if ((uc $from) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/ && (uc $self->opts->negate->{$from}) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/) { if ($code == $ERRORS{uc $from}) { $code = $ERRORS{uc $self->opts->negate->{$from}}; } } } } my $output = "$STATUS_TEXT{$code}"; $output .= " - $message" if defined $message && $message ne ''; if (scalar (@{$self->{perfdata}})) { $output .= " | ".$self->perfdata_string(); } $output .= "\n"; if (! exists $self->{suppress_messages}) { print $output; } exit $code; } sub set_thresholds { my $self = shift; my %params = @_; if (exists $params{metric}) { my $metric = $params{metric}; # erst die hartcodierten defaultschwellwerte $self->{thresholds}->{$metric}->{warning} = $params{warning}; $self->{thresholds}->{$metric}->{critical} = $params{critical}; # dann die defaultschwellwerte von der kommandozeile if (defined $self->opts->warning) { $self->{thresholds}->{$metric}->{warning} = $self->opts->warning; } if (defined $self->opts->critical) { $self->{thresholds}->{$metric}->{critical} = $self->opts->critical; } # dann die ganz spezifischen schwellwerte von der kommandozeile if ($self->opts->warningx) { # muss nicht auf defined geprueft werden, weils ein hash ist foreach my $key (keys %{$self->opts->warningx}) { next if $key ne $metric; $self->{thresholds}->{$metric}->{warning} = $self->opts->warningx->{$key}; } } if ($self->opts->criticalx) { foreach my $key (keys %{$self->opts->criticalx}) { next if $key ne $metric; $self->{thresholds}->{$metric}->{critical} = $self->opts->criticalx->{$key}; } } } else { $self->{thresholds}->{default}->{warning} = defined $self->opts->warning ? $self->opts->warning : defined $params{warning} ? $params{warning} : 0; $self->{thresholds}->{default}->{critical} = defined $self->opts->critical ? $self->opts->critical : defined $params{critical} ? $params{critical} : 0; } } sub force_thresholds { my $self = shift; my %params = @_; if (exists $params{metric}) { my $metric = $params{metric}; $self->{thresholds}->{$metric}->{warning} = $params{warning} || 0; $self->{thresholds}->{$metric}->{critical} = $params{critical} || 0; } else { $self->{thresholds}->{default}->{warning} = $params{warning} || 0; $self->{thresholds}->{default}->{critical} = $params{critical} || 0; } } sub get_thresholds { my $self = shift; my @params = @_; if (scalar(@params) > 1) { my %params = @params; my $metric = $params{metric}; return ($self->{thresholds}->{$metric}->{warning}, $self->{thresholds}->{$metric}->{critical}); } else { return ($self->{thresholds}->{default}->{warning}, $self->{thresholds}->{default}->{critical}); } } sub check_thresholds { my $self = shift; my @params = @_; my $level = $ERRORS{OK}; my $warningrange; my $criticalrange; my $value; if (scalar(@params) > 1) { my %params = @params; $value = $params{value}; my $metric = $params{metric}; if ($metric ne 'default') { $warningrange = exists $self->{thresholds}->{$metric}->{warning} ? $self->{thresholds}->{$metric}->{warning} : $self->{thresholds}->{default}->{warning}; $criticalrange = exists $self->{thresholds}->{$metric}->{critical} ? $self->{thresholds}->{$metric}->{critical} : $self->{thresholds}->{default}->{critical}; } else { $warningrange = (defined $params{warning}) ? $params{warning} : $self->{thresholds}->{default}->{warning}; $criticalrange = (defined $params{critical}) ? $params{critical} : $self->{thresholds}->{default}->{critical}; } } else { $value = $params[0]; $warningrange = $self->{thresholds}->{default}->{warning}; $criticalrange = $self->{thresholds}->{default}->{critical}; } if ($warningrange =~ /^([-+]?[0-9]*\.?[0-9]+)$/) { # warning = 10, warn if > 10 or < 0 $level = $ERRORS{WARNING} if ($value > $1 || $value < 0); } elsif ($warningrange =~ /^([-+]?[0-9]*\.?[0-9]+):$/) { # warning = 10:, warn if < 10 $level = $ERRORS{WARNING} if ($value < $1); } elsif ($warningrange =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) { # warning = ~:10, warn if > 10 $level = $ERRORS{WARNING} if ($value > $1); } elsif ($warningrange =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) { # warning = 10:20, warn if < 10 or > 20 $level = $ERRORS{WARNING} if ($value < $1 || $value > $2); } elsif ($warningrange =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) { # warning = @10:20, warn if >= 10 and <= 20 $level = $ERRORS{WARNING} if ($value >= $1 && $value <= $2); } if ($criticalrange =~ /^([-+]?[0-9]*\.?[0-9]+)$/) { # critical = 10, crit if > 10 or < 0 $level = $ERRORS{CRITICAL} if ($value > $1 || $value < 0); } elsif ($criticalrange =~ /^([-+]?[0-9]*\.?[0-9]+):$/) { # critical = 10:, crit if < 10 $level = $ERRORS{CRITICAL} if ($value < $1); } elsif ($criticalrange =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) { # critical = ~:10, crit if > 10 $level = $ERRORS{CRITICAL} if ($value > $1); } elsif ($criticalrange =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) { # critical = 10:20, crit if < 10 or > 20 $level = $ERRORS{CRITICAL} if ($value < $1 || $value > $2); } elsif ($criticalrange =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) { # critical = @10:20, crit if >= 10 and <= 20 $level = $ERRORS{CRITICAL} if ($value >= $1 && $value <= $2); } return $level; } package GLPlugin::Commandline::Getopt; use strict; use File::Basename; use Getopt::Long qw(:config no_ignore_case bundling); # Standard defaults my %DEFAULT = ( timeout => 15, verbose => 0, license => "This monitoring plugin is free software, and comes with ABSOLUTELY NO WARRANTY. It may be used, redistributed and/or modified under the terms of the GNU General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).", ); # Standard arguments my @ARGS = ({ spec => 'usage|?', help => "-?, --usage\n Print usage information", }, { spec => 'help|h', help => "-h, --help\n Print detailed help screen", }, { spec => 'version|V', help => "-V, --version\n Print version information", }, { #spec => 'extra-opts:s@', #help => "--extra-opts=[
[@]]\n Section and/or config_file from which to load extra options (may repeat)", }, { spec => 'timeout|t=i', help => sprintf("-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)", $DEFAULT{timeout}), default => $DEFAULT{timeout}, }, { spec => 'verbose|v+', help => "-v, --verbose\n Show details for command-line debugging (can repeat up to 3 times)", default => $DEFAULT{verbose}, }, ); # Standard arguments we traditionally display last in the help output my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose); sub _init { my $self = shift; my %params = @_; # Check params my $plugin = basename($0); #my %attr = validate( @_, { my %attr = ( usage => 1, version => 0, url => 0, plugin => { default => $plugin }, blurb => 0, extra => 0, 'extra-opts' => 0, license => { default => $DEFAULT{license} }, timeout => { default => $DEFAULT{timeout} }, ); # Add attr to private _attr hash (except timeout) $self->{timeout} = delete $attr{timeout}; $self->{_attr} = { %attr }; foreach (keys %{$self->{_attr}}) { if (exists $params{$_}) { $self->{_attr}->{$_} = $params{$_}; } else { $self->{_attr}->{$_} = $self->{_attr}->{$_}->{default} if ref ($self->{_attr}->{$_}) eq 'HASH' && exists $self->{_attr}->{$_}->{default}; } } # Chomp _attr values chomp foreach values %{$self->{_attr}}; # Setup initial args list $self->{_args} = [ grep { exists $_->{spec} } @ARGS ]; $self } sub new { my $class = shift; my $self = bless {}, $class; $self->_init(@_); } sub add_arg { my $self = shift; my %arg = @_; push (@{$self->{_args}}, \%arg); } sub getopts { my $self = shift; my %commandline = (); my @params = map { $_->{spec} } @{$self->{_args}}; if (! GetOptions(\%commandline, @params)) { $self->print_help(); exit 0; } else { no strict 'refs'; no warnings 'redefine'; do { $self->print_help(); exit 0; } if $commandline{help}; do { $self->print_version(); exit 0 } if $commandline{version}; do { $self->print_usage(); exit 3 } if $commandline{usage}; foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; } @{$self->{_args}}) { my $field = $_; *{"$field"} = sub { return $self->{opts}->{$field}; }; } foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; } grep { exists $_->{required} && $_->{required} } @{$self->{_args}}) { do { $self->print_usage(); exit 0 } if ! exists $commandline{$_}; } foreach (grep { exists $_->{default} } @{$self->{_args}}) { $_->{spec} =~ /^([\w\-]+)/; my $spec = $1; $self->{opts}->{$spec} = $_->{default}; } foreach (keys %commandline) { $self->{opts}->{$_} = $commandline{$_}; } foreach (grep { exists $_->{env} } @{$self->{_args}}) { $_->{spec} =~ /^([\w\-]+)/; my $spec = $1; if (exists $ENV{'NAGIOS__HOST'.$_->{env}}) { $self->{opts}->{$spec} = $ENV{'NAGIOS__HOST'.$_->{env}}; } if (exists $ENV{'NAGIOS__SERVICE'.$_->{env}}) { $self->{opts}->{$spec} = $ENV{'NAGIOS__SERVICE'.$_->{env}}; } } foreach (grep { exists $_->{aliasfor} } @{$self->{_args}}) { my $field = $_->{aliasfor}; $_->{spec} =~ /^([\w\-]+)/; my $aliasfield = $1; next if $self->{opts}->{$field}; *{"$field"} = sub { return $self->{opts}->{$aliasfield}; }; } } } sub create_opt { my $self = shift; my $key = shift; no strict 'refs'; *{"$key"} = sub { return $self->{opts}->{$key}; }; } sub override_opt { my $self = shift; my $key = shift; my $value = shift; $self->{opts}->{$key} = $value; } sub get { my $self = shift; my $opt = shift; return $self->{opts}->{$opt}; } sub print_help { my $self = shift; $self->print_version(); printf "\n%s\n", $self->{_attr}->{license}; printf "\n%s\n\n", $self->{_attr}->{blurb}; $self->print_usage(); foreach (@{$self->{_args}}) { printf " %s\n", $_->{help}; } exit 0; } sub print_usage { my $self = shift; printf $self->{_attr}->{usage}, $self->{_attr}->{plugin}; print "\n"; } sub print_version { my $self = shift; printf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version}; printf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url}; print "\n"; } sub print_license { my $self = shift; printf "%s\n", $self->{_attr}->{license}; print "\n"; } package GLPlugin::Item; our @ISA = qw(GLPlugin); use strict; sub new { my $class = shift; my %params = @_; my $self = { blacklisted => 0, info => undef, extendedinfo => undef, }; bless $self, $class; $self->init(%params); return $self; } sub check { my $self = shift; my $lists = shift; my @lists = $lists ? @{$lists} : grep { ref($self->{$_}) eq "ARRAY" } keys %{$self}; foreach my $list (@lists) { $self->add_info('checking '.$list); foreach my $element (@{$self->{$list}}) { $element->blacklist() if $self->is_blacklisted(); $element->check(); } } } package GLPlugin::TableItem; our @ISA = qw(GLPlugin::Item); use strict; sub new { my $class = shift; my %params = @_; my $self = {}; bless $self, $class; foreach (keys %params) { $self->{$_} = $params{$_}; } if ($self->can("finish")) { $self->finish(%params); } return $self; } sub check { my $self = shift; # some tableitems are not checkable, they are only used to enhance other # items (e.g. sensorthresholds enhance sensors) # normal tableitems should have their own check-method }