[BACK]Return to check_hw_sensors CVS log [TXT][DIR] Up to [local] / nagios / check_hw_sensors

Annotation of nagios/check_hw_sensors/check_hw_sensors, Revision 1.17

1.14      andrew      1: #!/usr/bin/perl -T
1.17    ! andrew      2: # $RedRiver: check_hw_sensors,v 1.16 2006/10/25 18:36:46 andrew Exp $
1.2       andrew      3: ########################################################################
                      4: # check_hw_sensors *** A nagios check for OpenBSD hw.sensors
                      5: #
                      6: # 2006.05.01 #*#*# andrew fresh <andrew@mad-techies.org>
                      7: ########################################################################
1.4       andrew      8: # TODO:
1.12      andrew      9: #   Really need real documentation.
1.4       andrew     10: ########################################################################
1.2       andrew     11: use strict;
                     12: use warnings;
                     13:
1.14      andrew     14: %ENV = ();
                     15:
1.16      andrew     16: use constant NAGIOS_OUTPUT => 1;
1.4       andrew     17:
1.2       andrew     18: use POSIX;
1.4       andrew     19: use lib "/usr/local/libexec/nagios";
1.2       andrew     20: use utils qw($TIMEOUT %ERRORS &print_revision &support);
                     21:
                     22: use Getopt::Long;
                     23: Getopt::Long::Configure('bundling');
                     24:
                     25: my $PROGNAME = "check_hw_sensors";
                     26:
1.4       andrew     27: my $SYSCTL = '/sbin/sysctl';
                     28: my $GETCAP = '/usr/bin/getcap';
1.2       andrew     29: my $BASE   = 'hw.sensors';
1.4       andrew     30: my $DEFAULT_CONFIG = '/etc/sensorsd.conf';
1.2       andrew     31:
                     32: my $state = 'UNKNOWN'; # tells whether the it is warning, critical, or OK
                     33: my %states; # This stores the count of states;
                     34: my $filename;
1.15      andrew     35: my $ignore_status;
1.2       andrew     36: my $sensor;
                     37: my $warning;
                     38: my $critical;
1.10      andrew     39: my $opt_h;
                     40: my $opt_V;
1.2       andrew     41:
1.4       andrew     42: my $CHECK_SENSOR = $BASE;
                     43: my %CHECKS;
                     44: my %SENSORS;
1.2       andrew     45:
                     46: #Option checking
                     47: my $status = GetOptions(
1.15      andrew     48:        "version|V"       => \$opt_V,
                     49:        "filename|f:s"    => \$filename,
                     50:        "ignore-status|i" => \$ignore_status,
                     51:        "sensor|s=s"      => \$sensor,
                     52:        "warning|w=s"     => \$warning,
                     53:        "critical|c=s"    => \$critical,
1.2       andrew     54: );
                     55:
                     56: # set the default this way so it only happens if someone typed -f or --filename
1.4       andrew     57: $filename = $DEFAULT_CONFIG if (defined $filename && $filename eq '');
1.2       andrew     58:
                     59: if ($status == 0) {
                     60:        print_help() ;
                     61:        exit $ERRORS{'OK'};
                     62: }
                     63:
                     64: if ($opt_V) {
1.17    ! andrew     65:        print_revision($PROGNAME,'$Revision: 1.16 $ ');
1.2       andrew     66:        exit $ERRORS{'OK'};
                     67: }
                     68:
1.15      andrew     69: unless (
                     70:        (
1.10      andrew     71:                defined $filename ||
1.15      andrew     72:                (not defined $ignore_status) ||
1.10      andrew     73:                (defined $sensor && ($warning || $critical))
                     74:          ) &&
                     75:          ( (!defined $filename) || (!defined $sensor) )
1.2       andrew     76: ) {
                     77:        print_help();
                     78:        exit $ERRORS{'OK'};
                     79: }
                     80:
                     81:
                     82: if (defined $sensor) {
                     83:        if ($sensor !~ /^$BASE/) {
                     84:                $sensor = $BASE . '.' . $sensor;
                     85:        }
                     86:        $CHECK_SENSOR = $sensor;
                     87:
1.15      andrew     88:        $CHECKS{$sensor}{'warn'} = $warning  if defined $warning;
                     89:        $CHECKS{$sensor}{'crit'} = $critical if defined $critical;
                     90:
1.2       andrew     91: } elsif (defined $filename) {
                     92:        %CHECKS = parse_file($filename);
                     93: }
                     94:
                     95: open my $sysctl, "-|", $SYSCTL, $CHECK_SENSOR
                     96:     or die "Couldn't open sysctl: $!";
                     97: while (<$sysctl>) {
                     98: #while (<>) {
                     99:        chomp;
                    100:        my ($id, $output) = split /=/;
                    101:        my @o = split /,\s*/, $output;
                    102:
                    103:        my $source = $o[0];
                    104:        my $descr  = $o[1];
                    105:        my $status = $o[2] if @o == 5;
                    106:        my $type   = $o[-2];
                    107:        my $data   = $o[-1];
                    108:
                    109:        $SENSORS{$id} = {
                    110:                id          => $id,
                    111:                output      => $output,
                    112:                source      => $source,
                    113:                description => $descr,
                    114:                status      => $status,
                    115:                type        => $type,
                    116:                data        => $data,
                    117:        };
                    118:
                    119: }
                    120: close $sysctl;
                    121:
                    122: sub as_if_numeric {
                    123:        my $_a = $a;
                    124:        my $_b = $b;
                    125:        $_a =~ s/\D//g;
                    126:        $_b =~ s/\D//g;
                    127:        $_a <=> $_b;
                    128: }
                    129:
1.15      andrew    130: foreach my $s (sort as_if_numeric keys %SENSORS) {
                    131:        my ($r, $data);
                    132:        if (exists $CHECKS{$s}) {
                    133:                $r    = check_sensor($SENSORS{$s}, $CHECKS{$s});
                    134:                $data = $s . '=' . $SENSORS{$s}{'output'};
                    135:        } elsif (not $ignore_status) {
                    136:                $r = check_sensor($SENSORS{$s});
                    137:                $data = $s . '=' . $SENSORS{$s}{'output'};
1.2       andrew    138:        } else {
1.15      andrew    139:                # ignore this sensor
1.2       andrew    140:        }
1.15      andrew    141:        next unless defined $r;
                    142:        push @{ $states{ $r } }, $data;
1.2       andrew    143: }
                    144:
                    145: $state = 'OK';
1.5       andrew    146: my $have_results = 0;
                    147: foreach my $error (sort { $ERRORS{$a} <=> $ERRORS{$b} } keys %ERRORS) {
                    148:        if (exists $states{$error}) {
                    149:                $have_results++;
                    150:                $state = $error;
                    151:        }
                    152: }
1.4       andrew    153: foreach my $error (sort { $ERRORS{$b} <=> $ERRORS{$a} } keys %ERRORS) {
                    154:        if (exists $states{$error}) {
                    155:                if (NAGIOS_OUTPUT) {
1.8       andrew    156:                        print "$error (" . scalar(@{ $states{ $error } }) . ")";
1.5       andrew    157:                        unless ($error eq 'OK') {
1.9       andrew    158:                                print '<br>';
1.15      andrew    159:                                print map {" - $_<br>"} @{ $states{ $error } };
1.4       andrew    160:                        }
                    161:                } else {
                    162:                        print "$error (" . scalar(@{ $states{ $error } }) . "):\n";
                    163:                        foreach (@{ $states{ $error } }) {
                    164:                                print "   $_\n";
                    165:                        }
                    166:                }
                    167:        }
                    168: }
1.5       andrew    169: if ($have_results == 0) {
                    170:        print "No results found\n";
                    171: }
1.2       andrew    172: exit $ERRORS{$state};
                    173:
                    174:
                    175: sub parse_file {
                    176:        my $filename = shift;
                    177:        my %contents;
1.15      andrew    178:
                    179:        die "file '$filename' does not exist." unless -e $filename;
1.2       andrew    180:
                    181:        open my $fh, '-|', $GETCAP, '-a', '-f', $filename
                    182:                or die "Couldn't open FILE '$GETCAP -a -f $filename': $!";
1.15      andrew    183:        LINE: while (<$fh>) {
1.2       andrew    184:                chomp;
                    185:                my ($key, @c) = split /\:/;
                    186:                foreach (@c) {
                    187:                        my ($k, $v) = split /\=/;
1.15      andrew    188:                        if (lc($k) eq 'ignore') {
                    189:                                $contents{$key}{'IGNORE'} = 1;
                    190:                        } elsif (lc($k) eq 'status') {
                    191:                                $contents{$key}{'STATUS'} = 1;
                    192:                        } else {
                    193:                                $contents{$key}{$k} = $v;
                    194:                        }
1.2       andrew    195:                }
                    196:        }
                    197:        close $fh;
                    198:
                    199:        return %contents;
                    200: }
                    201:
                    202: sub parse_check {
                    203:        my $type  = shift;
                    204:        my $check = shift;
                    205:
1.15      andrew    206:        return undef unless $check;
                    207:        return undef    if $check->{'STATUS'};
                    208:        return 'IGNORE' if $check->{'IGNORE'};
                    209:
1.10      andrew    210:        foreach my $code ('crit', 'warn') {
                    211:                if (defined $check->{$code} && $check->{$code} =~ /:/) {
                    212:                        if (my ($low, $high) = split /:/, $check->{$code}) {
                    213:                                $check->{$code . '.low'}  = $low;
                    214:                                $check->{$code . '.high'} = $high;
                    215:                        }
                    216:                        delete $check->{$code};
1.2       andrew    217:                }
1.10      andrew    218:
                    219:                foreach my $severity ('low', 'high') {
                    220:                        if (defined $check->{$severity}) {
                    221:                                $check->{ $code . '.' . $severity } = $check->{$severity}
                    222:                                        unless defined $check->{ $code . '.' . $severity };
                    223:                        }
1.2       andrew    224:                }
1.10      andrew    225:                no warnings 'uninitialized';
                    226:                $check->{$code} = [ split /,\s*/, $check->{$code} ];
1.2       andrew    227:        }
                    228:
                    229:        return $check;
                    230: }
                    231:
                    232: sub check_sensor {
1.15      andrew    233:        my $sensor = shift;
1.2       andrew    234:        my $check  = shift;
                    235:        my $result = 'UNKNOWN';
                    236:        my %errors = (
                    237:                'warn' => 'WARNING',
                    238:                'crit' => 'CRITICAL',
                    239:        );
                    240:
                    241:        return $result unless ref $sensor eq 'HASH';
1.15      andrew    242:        $check = parse_check($sensor->{'type'}, $check) if $check;
                    243:
                    244:        unless ($check) {
                    245:                if ($sensor->{'status'}) {
                    246:                        # It looks like doing this should be safe, from
                    247:                        # src/sbin/sysctl/sysctl.c
                    248:                        $result = $sensor->{'status'}
                    249:                } else {
                    250:                        return undef;
                    251:                }
                    252:                return $result;
                    253:        }
1.2       andrew    254:
1.15      andrew    255:        return undef if $check eq 'IGNORE';
1.2       andrew    256:
                    257:        $result = 'OK';
                    258:        foreach my $code ('warn', 'crit') {
                    259:                if (
                    260:                        $sensor->{'type'} eq 'volts_dc' ||
                    261:                        $sensor->{'type'} eq 'fanrpm'   ||
                    262:                        $sensor->{'type'} eq 'raw'
                    263:                ) {
                    264:                        my $data = $sensor->{'data'};
                    265:                        $data =~ s/[^\d\.]//g;
                    266:                        unless (length $data) {
                    267:                                warn "INVALID DATA ($sensor->{'data'}) for '$sensor->{'id'}'";
                    268:                                next;
                    269:                        }
                    270:
                    271:                        if (
                    272:                                defined $check->{$code . ".low"} ||
                    273:                                defined $check->{$code . ".high"}
                    274:                        ) {
                    275:                                if (defined $check->{$code . ".low"}) {
                    276:                                        my $c =  $check->{$code . ".low"};
                    277:                                        $c =~ s/[^\d\.]//g;
                    278:
                    279:                                        unless (length $c) {
                    280:                                                warn "INVALID CHECK (" . $check->{$code . ".low"} .
                    281:                                                      ") for '$sensor->{'id'}:$code.low'";
                    282:                                                next;
                    283:                                        }
                    284:
                    285:                                        $result = $errors{$code}
                    286:                                                if ($c >= $data);
                    287:                                }
                    288:                                if (defined $check->{$code . ".high"}) {
                    289:                                        my $c =  $check->{$code . ".high"};
                    290:                                        $c =~ s/[^\d\.]//g;
                    291:                                        unless (length $c) {
                    292:                                                warn "INVALID CHECK (" . $check->{$code . ".high"} .
                    293:                                                      ") for '$sensor->{'id'}:$code.high'";
                    294:                                                next;
                    295:                                        }
                    296:
                    297:                                        $result = $errors{$code}
                    298:                                                if ($c <= $data);
                    299:                                }
1.9       andrew    300:                        } elsif (@{ $check->{$code} }) {
1.2       andrew    301:                                my $matched = 0;
                    302:                                foreach my $c (@{ $check->{$code} }) {
                    303:                                        $c =~ s/[^\d\.]//g;
                    304:                                        unless (length $c) {
1.7       andrew    305:                                                warn "INVALID CHECK (" . $c .
1.2       andrew    306:                                                      ") for '$sensor->{'id'}:$code'";
                    307:                                                next;
                    308:                                        }
                    309:
1.15      andrew    310:                                        if ($c eq $data) {
1.2       andrew    311:                                                $matched = 1;
                    312:                                                last;
                    313:                                        }
                    314:                                }
                    315:                                $result = $errors{$code} unless $matched;
                    316:                        }
                    317:
                    318:                } elsif ($sensor->{'type'} eq 'temp') {
                    319:                        my ($degC, $degF) = split /\//, $sensor->{'data'};
                    320:                        $degC =~ s/[^\d\.]//g;
                    321:                        $degF =~ s/[^\d\.]//g;
                    322:                        if (
                    323:                                defined $check->{$code . ".low"} ||
                    324:                                defined $check->{$code . ".high"}
                    325:                        ) {
                    326:                                if (defined $check->{$code . ".low"}) {
                    327:                                        my $c =  $check->{$code . ".low"};
                    328:                                        my $data = $degC;
                    329:
                    330:                                        $data = $degF if ($c =~ /F/i);
                    331:                                        unless (length $data) {
                    332:                                                warn "INVALID DATA (" . $sensor->{'data'} .
                    333:                                                          ") for '$sensor->{'id'}'";
                    334:                                                next;
                    335:                                        }
                    336:
                    337:                                        $c =~ s/[^\d\.]//g;
                    338:                                        unless (length $c) {
                    339:                                                warn "INVALID CHECK (" . $check->{$code . ".low"} .
                    340:                                                         ") for '$sensor->{'id'}':$code.low";
                    341:                                                next;
                    342:                                        }
                    343:
                    344:                                        $result = $errors{$code}
                    345:                                                if ($c >= $data);
                    346:                                }
                    347:                                if (defined $check->{$code . ".high"}) {
                    348:                                        my $c =  $check->{$code . ".high"};
                    349:
                    350:                                        my $data = $degC;
                    351:                                        $data = $degF if ($c =~ /F/i);
                    352:                                        unless (length $data) {
                    353:                                                warn "INVALID DATA (" . $sensor->{'data'} .
                    354:                                                          ") for '$sensor->{'id'}'";
                    355:                                                next;
                    356:                                        }
                    357:
                    358:                                        $c =~ s/[^\d\.]//g;
                    359:                                        unless (length $c) {
                    360:                                                warn "INVALID CHECK (" . $check->{$code . ".high"} .
                    361:                                                         ") for '$sensor->{'id'}:$code.high'";
                    362:                                                next;
                    363:                                        }
                    364:
                    365:                                        $result = $errors{$code}
                    366:                                                if ($c <= $data);
                    367:                                }
1.9       andrew    368:                        } elsif (@{ $check->{$code} }) {
1.2       andrew    369:
                    370:                                my $matched = 0;
                    371:                                foreach my $c (@{ $check->{$code} }) {
                    372:                                        my $data = $degC;
                    373:
                    374:                                        $data = $degF if ($c =~ /F/i);
                    375:                                        unless (length $data) {
                    376:                                                warn "INVALID DATA (" . $sensor->{'data'} .
                    377:                                                          ") for '$sensor->{'id'}'";
                    378:                                                next;
                    379:                                        }
                    380:
                    381:                                        $c =~ s/[^\d\.]//g;
                    382:                                        unless (length $c) {
1.7       andrew    383:                                                warn "INVALID CHECK (" . $c .
1.2       andrew    384:                                                         ") for '$sensor->{'id'}':$code";
                    385:                                                next;
                    386:                                        }
                    387:
1.15      andrew    388:                                        if ($c eq $data) {
1.2       andrew    389:                                                $matched = 1;
                    390:                                                last;
                    391:                                        }
                    392:                                }
                    393:                                $result = $errors{$code} unless $matched;
                    394:                        }
                    395:
                    396:                } elsif (
                    397:                        $sensor->{'type'} eq 'drive' ||
                    398:                        $sensor->{'type'} eq 'indicator'
                    399:                ) {
1.9       andrew    400:                        if (@{ $check->{$code} }) {
1.2       andrew    401:                                my $matched = 0;
                    402:                                foreach (@{ $check->{$code} }) {
                    403:                                        if ($_ eq $sensor->{'data'}) {
                    404:                                                $matched = 1;
                    405:                                                last;
                    406:                                        }
                    407:                                }
                    408:                                $result = $errors{$code} unless $matched;
                    409:                        }
                    410:
                    411:                } else {
                    412:                        $result = 'UNKNOWN';
                    413:                }
                    414:
                    415:        }
                    416:
                    417:        return $result;
                    418: }
                    419:
                    420: sub print_help {
                    421:        print <<EOL;
                    422: $PROGNAME plugin for Nagios monitors sysctl hw.sensors on OpenBSD
1.15      andrew    423:     $PROGNAME [-i] (-f [<FILENAME>]|(-s <hw.sensors id> [-w limit] [-c limit]))
1.2       andrew    424:
                    425: Usage:
1.15      andrew    426:     -i, --ignore-status
1.17    ! andrew    427:         Don't check the status of sensors that report it.
1.12      andrew    428:     -f, --filename=FILE
                    429:         FILE to load checks from (defaults to /etc/sensorsd.conf)
                    430:     -s, --sensor=ID
                    431:         ID of a single sensor.  "-s 0" means hw.sensors.0.
                    432:     -w, --warning=RANGE or single ENTRY
                    433:         Exit with WARNING status if outside of RANGE or if != ENTRY
1.13      andrew    434:     -c, --critical=RANGE or single ENTRY
1.12      andrew    435:         Exit with CRITICAL status if outside of RANGE or if != ENTRY
                    436:
1.13      andrew    437: FILE is in the same format as sensorsd.conf(5) plus some additional
                    438: entries.  These additional entries in the file are ignored by
                    439: sensorsd(8).
1.12      andrew    440:
                    441: $PROGNAME understands the following entries:
                    442:
1.15      andrew    443:     low, high, crit, warn, crit.low, crit.high, warn.low, warn.high,
                    444:     ignore, status
1.12      andrew    445:
                    446: An ENTRY depends on the type.  The descriptions in sensorsd.conf(5)
                    447: can be used when appropriate, or you can use the following:
                    448:
                    449:     volts_dc, fanrpm or raw - Anything that includes digits.
                    450:     Both the value of the check and the value of the sensor
                    451:     response that are not either a digit or period are stripped
                    452:     and then the two resultant values are compared.
                    453:
                    454:     temp - Can be as above, but if the entry has an F in it,
                    455:     it compares farenheit, otherwise it uses celcius.
                    456:
                    457:     indicator or drive - does a case sensitive match of each
                    458:     entry in the comma separated list and if it does not match
                    459:     any of the entries, it matches the status.
                    460:
                    461: The entries 'crit' or 'warn' (or the -c or -w on the command line)
                    462: may be a RANGE or a comma separated list of acceptable values.
                    463: The comma separated list of values contains a list of things that
                    464: will NOT cause the status.  This is possibly counterintuitive, but
                    465: you are more likely to know good values than bad values.
                    466:
                    467: A RANGE is a low ENTRY and a high ENTRY separated by a colon (:).
                    468: It can also be low: or :high with the other side left blank to only
                    469: make the single check..
1.2       andrew    470:
1.15      andrew    471: An entry marked "ignore" will cause that sensor to be skipped.
                    472: Generally used with state checking of all sensors to ignore sensors you
                    473: don't care about or that report incorrectly.
                    474:
                    475: If you are using --ignore-status, you can still check the status of
                    476: individual sensors with a status entry.
                    477:
1.2       andrew    478: EOL
1.15      andrew    479:
1.17    ! andrew    480:         print_revision($PROGNAME, '$Revision: 1.16 $');
1.2       andrew    481: }
                    482:

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>