version 1.1, 2006/05/01 19:11:23 |
version 1.2, 2006/05/02 02:29:33 |
|
|
#!/usr/bin/perl
|
#!/usr/bin/perl |
# $RedRiver$
|
# $RedRiver: check_sensors,v 1.1 2006/05/01 18:11:23 andrew Exp $ |
########################################################################
|
######################################################################## |
# check_sensors *** A nagios check for OpenBSD sensors
|
# check_hw_sensors *** A nagios check for OpenBSD hw.sensors |
#
|
# |
# 2006.05.01 #*#*# andrew fresh <andrew@mad-techies.org>
|
# 2006.05.01 #*#*# andrew fresh <andrew@mad-techies.org> |
########################################################################
|
######################################################################## |
use strict;
|
use strict; |
use warnings;
|
use warnings; |
|
|
|
#use Data::Dumper; |
|
|
|
use POSIX; |
|
#use lib "/usr/local/libexec/nagios"; |
|
use lib $ENV{'HOME'}; |
|
use utils qw($TIMEOUT %ERRORS &print_revision &support); |
|
|
|
use Getopt::Long; |
|
Getopt::Long::Configure('bundling'); |
|
|
|
my $PROGNAME = "check_hw_sensors"; |
|
|
|
my $SYSCTL = 'sysctl'; |
|
my $GETCAP = 'getcap'; |
|
my $BASE = 'hw.sensors'; |
|
|
|
my $CHECK_SENSOR = $BASE; |
|
my %CHECKS; |
|
my %SENSORS; |
|
|
|
my $state = 'UNKNOWN'; # tells whether the it is warning, critical, or OK |
|
my %states; # This stores the count of states; |
|
my $filename; |
|
my $sensor; |
|
my $warning; |
|
my $critical; |
|
my $opt_h ; |
|
my $opt_V ; |
|
|
|
|
|
#Option checking |
|
my $status = GetOptions( |
|
"V" => \$opt_V, "version" => \$opt_V, |
|
"h" => \$opt_h, "help" => \$opt_h, |
|
"filename|f:s" => \$filename, |
|
"sensor|s=s" => \$sensor, |
|
"warning|w=s" => \$warning, |
|
"critical|c=s" => \$critical, |
|
); |
|
|
|
# set the default this way so it only happens if someone typed -f or --filename |
|
$filename = '/etc/sensorsd.conf' if (defined $filename && $filename eq ''); |
|
|
|
if ($status == 0) { |
|
print_help() ; |
|
exit $ERRORS{'OK'}; |
|
} |
|
|
|
if ($opt_V) { |
|
print_revision($PROGNAME,'$Revision$ '); |
|
exit $ERRORS{'OK'}; |
|
} |
|
|
|
if ($opt_h) { |
|
print_help(); |
|
exit $ERRORS{'OK'}; |
|
} |
|
|
|
unless ( ( |
|
defined $filename || |
|
(defined $sensor && ($warning || $critical)) |
|
) && |
|
( (!defined $filename) || (!defined $sensor) ) |
|
) { |
|
print_help(); |
|
exit $ERRORS{'OK'}; |
|
} |
|
|
|
|
|
if (defined $sensor) { |
|
if ($sensor !~ /^$BASE/) { |
|
$sensor = $BASE . '.' . $sensor; |
|
} |
|
$CHECK_SENSOR = $sensor; |
|
|
|
$CHECKS{$sensor} = { |
|
'warning' => $warning, |
|
'critical' => $critical, |
|
}; |
|
} elsif (defined $filename) { |
|
%CHECKS = parse_file($filename); |
|
} |
|
|
|
open my $sysctl, "-|", $SYSCTL, $CHECK_SENSOR |
|
or die "Couldn't open sysctl: $!"; |
|
while (<$sysctl>) { |
|
#while (<>) { |
|
chomp; |
|
my ($id, $output) = split /=/; |
|
my @o = split /,\s*/, $output; |
|
|
|
my $source = $o[0]; |
|
my $descr = $o[1]; |
|
my $status = $o[2] if @o == 5; |
|
my $type = $o[-2]; |
|
my $data = $o[-1]; |
|
|
|
$SENSORS{$id} = { |
|
id => $id, |
|
output => $output, |
|
source => $source, |
|
description => $descr, |
|
status => $status, |
|
type => $type, |
|
data => $data, |
|
}; |
|
|
|
} |
|
close $sysctl; |
|
|
|
sub as_if_numeric { |
|
my $_a = $a; |
|
my $_b = $b; |
|
$_a =~ s/\D//g; |
|
$_b =~ s/\D//g; |
|
$_a <=> $_b; |
|
} |
|
|
|
foreach my $check (sort as_if_numeric keys %CHECKS) { |
|
if (exists $SENSORS{$check}) { |
|
my $r = check_sensor($CHECKS{$check}, $SENSORS{$check}); |
|
push @{ $states{ $r } }, |
|
$check . '=' . $SENSORS{$check}{'output'}; |
|
} else { |
|
# XXX Error missing sensor |
|
push @{ $states{'UNKNOWN'} }, $check . '=No sensor with this id'; |
|
} |
|
} |
|
|
|
#print Dumper \%states; |
|
|
|
$state = 'OK'; |
|
foreach my $error (sort { $ERRORS{$a} <=> $ERRORS{$b} } keys %ERRORS) { |
|
if (exists $states{$error}) { |
|
$state = $error; |
|
print "$error (" . scalar(@{ $states{ $error } }) . "):\n"; |
|
foreach (@{ $states{ $error } }) { |
|
print " $_\n"; |
|
} |
|
} |
|
} |
|
exit $ERRORS{$state}; |
|
|
|
|
|
sub parse_file { |
|
my $filename = shift; |
|
my %contents; |
|
|
|
open my $fh, '-|', $GETCAP, '-a', '-f', $filename |
|
or die "Couldn't open FILE '$GETCAP -a -f $filename': $!"; |
|
while (<$fh>) { |
|
chomp; |
|
my ($key, @c) = split /\:/; |
|
foreach (@c) { |
|
my ($k, $v) = split /\=/; |
|
$contents{$key}{$k} = $v; |
|
} |
|
} |
|
close $fh; |
|
|
|
return %contents; |
|
} |
|
|
|
sub parse_check { |
|
my $type = shift; |
|
my $check = shift; |
|
|
|
$check->{'warn'} = $check->{'warning'} if (!defined $check->{'warn'}); |
|
$check->{'crit'} = $check->{'critical'} if (!defined $check->{'crit'}); |
|
|
|
if (defined $check->{'warn'} && $check->{'warn'} =~ /:/) { |
|
if (my ($low, $high) = split /:/, $check->{'warn'}) { |
|
$check->{'warn.low'} = $low; |
|
$check->{'warn.high'} = $high; |
|
} |
|
delete $check->{'warn'}; |
|
} |
|
if (defined $check->{'crit'} && $check->{'crit'} =~ /:/) { |
|
if (my ($low, $high) = split /:/, $check->{'crit'}) { |
|
$check->{'crit.low'} = $low; |
|
$check->{'crit.high'} = $high; |
|
} |
|
delete $check->{'crit'}; |
|
} |
|
|
|
if (defined $check->{'low'}) { |
|
$check->{'warn.low'} = $check->{'low'} |
|
unless defined $check->{'warn.low'}; |
|
$check->{'crit.low'} = $check->{'low'} |
|
unless defined $check->{'crit.low'}; |
|
} |
|
if (defined $check->{'high'}) { |
|
$check->{'warn.high'} = $check->{'high'} |
|
unless defined $check->{'warn.high'}; |
|
$check->{'crit.high'} = $check->{'high'} |
|
unless defined $check->{'crit.high'}; |
|
} |
|
|
|
no warnings 'uninitialized'; |
|
$check->{'warn'} = [ split /,\s*/, $check->{'warn'} ]; |
|
$check->{'crit'} = [ split /,\s*/, $check->{'crit'} ]; |
|
|
|
return $check; |
|
} |
|
|
|
sub check_sensor { |
|
my $check = shift; |
|
my $sensor = shift; |
|
my $result = 'UNKNOWN'; |
|
my %errors = ( |
|
'warn' => 'WARNING', |
|
'crit' => 'CRITICAL', |
|
); |
|
|
|
return $result unless ref $sensor eq 'HASH'; |
|
|
|
$check = parse_check($sensor->{'type'}, $check); |
|
#print Dumper $check, $sensor; |
|
|
|
$result = 'OK'; |
|
|
|
foreach my $code ('warn', 'crit') { |
|
if ( |
|
$sensor->{'type'} eq 'volts_dc' || |
|
$sensor->{'type'} eq 'fanrpm' || |
|
$sensor->{'type'} eq 'raw' |
|
) { |
|
my $data = $sensor->{'data'}; |
|
$data =~ s/[^\d\.]//g; |
|
unless (length $data) { |
|
warn "INVALID DATA ($sensor->{'data'}) for '$sensor->{'id'}'"; |
|
next; |
|
} |
|
|
|
if ( |
|
defined $check->{$code . ".low"} || |
|
defined $check->{$code . ".high"} |
|
) { |
|
if (defined $check->{$code . ".low"}) { |
|
my $c = $check->{$code . ".low"}; |
|
$c =~ s/[^\d\.]//g; |
|
|
|
unless (length $c) { |
|
warn "INVALID CHECK (" . $check->{$code . ".low"} . |
|
") for '$sensor->{'id'}:$code.low'"; |
|
next; |
|
} |
|
|
|
$result = $errors{$code} |
|
if ($c >= $data); |
|
} |
|
if (defined $check->{$code . ".high"}) { |
|
my $c = $check->{$code . ".high"}; |
|
$c =~ s/[^\d\.]//g; |
|
unless (length $c) { |
|
warn "INVALID CHECK (" . $check->{$code . ".high"} . |
|
") for '$sensor->{'id'}:$code.high'"; |
|
next; |
|
} |
|
|
|
$result = $errors{$code} |
|
if ($c <= $data); |
|
} |
|
} elsif (defined $check->{$code}) { |
|
my $matched = 0; |
|
foreach my $c (@{ $check->{$code} }) { |
|
$c =~ s/[^\d\.]//g; |
|
unless (length $c) { |
|
warn "INVALID CHECK (" . $check->{$code} . |
|
") for '$sensor->{'id'}:$code'"; |
|
next; |
|
} |
|
|
|
if ($_ eq $data) { |
|
$matched = 1; |
|
last; |
|
} |
|
} |
|
$result = $errors{$code} unless $matched; |
|
} |
|
|
|
} elsif ($sensor->{'type'} eq 'temp') { |
|
my ($degC, $degF) = split /\//, $sensor->{'data'}; |
|
$degC =~ s/[^\d\.]//g; |
|
$degF =~ s/[^\d\.]//g; |
|
if ( |
|
defined $check->{$code . ".low"} || |
|
defined $check->{$code . ".high"} |
|
) { |
|
if (defined $check->{$code . ".low"}) { |
|
my $c = $check->{$code . ".low"}; |
|
my $data = $degC; |
|
|
|
$data = $degF if ($c =~ /F/i); |
|
unless (length $data) { |
|
warn "INVALID DATA (" . $sensor->{'data'} . |
|
") for '$sensor->{'id'}'"; |
|
next; |
|
} |
|
|
|
$c =~ s/[^\d\.]//g; |
|
unless (length $c) { |
|
warn "INVALID CHECK (" . $check->{$code . ".low"} . |
|
") for '$sensor->{'id'}':$code.low"; |
|
next; |
|
} |
|
|
|
$result = $errors{$code} |
|
if ($c >= $data); |
|
} |
|
if (defined $check->{$code . ".high"}) { |
|
my $c = $check->{$code . ".high"}; |
|
|
|
my $data = $degC; |
|
$data = $degF if ($c =~ /F/i); |
|
unless (length $data) { |
|
warn "INVALID DATA (" . $sensor->{'data'} . |
|
") for '$sensor->{'id'}'"; |
|
next; |
|
} |
|
|
|
$c =~ s/[^\d\.]//g; |
|
unless (length $c) { |
|
warn "INVALID CHECK (" . $check->{$code . ".high"} . |
|
") for '$sensor->{'id'}:$code.high'"; |
|
next; |
|
} |
|
|
|
$result = $errors{$code} |
|
if ($c <= $data); |
|
} |
|
} elsif (defined $check->{$code}) { |
|
|
|
my $matched = 0; |
|
foreach my $c (@{ $check->{$code} }) { |
|
my $data = $degC; |
|
|
|
$data = $degF if ($c =~ /F/i); |
|
unless (length $data) { |
|
warn "INVALID DATA (" . $sensor->{'data'} . |
|
") for '$sensor->{'id'}'"; |
|
next; |
|
} |
|
|
|
$c =~ s/[^\d\.]//g; |
|
unless (length $c) { |
|
warn "INVALID CHECK (" . $check->{$code} . |
|
") for '$sensor->{'id'}':$code"; |
|
next; |
|
} |
|
|
|
if ($_ eq $data) { |
|
$matched = 1; |
|
last; |
|
} |
|
} |
|
$result = $errors{$code} unless $matched; |
|
} |
|
|
|
} elsif ( |
|
$sensor->{'type'} eq 'drive' || |
|
$sensor->{'type'} eq 'indicator' |
|
) { |
|
if (defined $check->{$code}) { |
|
my $matched = 0; |
|
foreach (@{ $check->{$code} }) { |
|
if ($_ eq $sensor->{'data'}) { |
|
$matched = 1; |
|
last; |
|
} |
|
} |
|
$result = $errors{$code} unless $matched; |
|
} |
|
|
|
} else { |
|
$result = 'UNKNOWN'; |
|
} |
|
|
|
} |
|
|
|
return $result; |
|
} |
|
|
|
sub print_help { |
|
print <<EOL; |
|
$PROGNAME plugin for Nagios monitors sysctl hw.sensors on OpenBSD |
|
$PROGNAME (-f [<FILENAME>]|(-s <hw.sensors id> -w limit -c limit)) |
|
|
|
Usage: |
|
-f, --filename=FILE |
|
FILE to load checks from (defaults to /etc/sensorsd.conf) |
|
-s, --sensor=ID |
|
ID of a single sensor. "-s 0" means hw.sensors.0. |
|
-w, --warning=RANGE or single ENTRY |
|
Exit with WARNING status if outside of RANGE or if != ENTRY |
|
-c, --critical=INTEGER |
|
Exit with CRITICAL status if outside of RANGE or if != ENTRY |
|
|
|
-h (--help) usage help |
|
|
|
FILE is in the same format as sensorsd.conf(5). These additional entries in the file are ignored by sensorsd(8). $PROGNAME understands the following entries: |
|
low, high, crit, warn, crit.low, crit.high, warn.low, warn.high |
|
|
|
An ENTRY depends on the type. The descriptions in sensorsd.conf(5) can be used when appropriate, or you can use the following: |
|
volts_dc, fanrpm or raw - Anything that includes digits. Anything that isn't a digit or period is stripped from the entry and the sensor output and they are compared. |
|
temp - Can be as above, but if the entry has an F in it, it compares farenheit, otherwise it uses celcius. |
|
indicator or drive - does a case sensitive match of each entry in the comma separated list and if it does not match any of the entries, it matches the status. |
|
|
|
The entries 'crit' or 'warn' (or the -c or -w on the command line) may be a RANGE or a comma separated list of acceptable values. The comma separated list of values contains a list of things that will NOT cause the status. This is possibly counterintuitive, but you are more likely to know good values than bad values. |
|
|
|
A RANGE is a low ENTRY and a high ENTRY separated by a colon (:). |
|
|
|
EOL |
|
|
|
print_revision($PROGNAME, '$Revision$'); |
|
} |
|
|