Annotation of nagios/check_hw_sensors/check_hw_sensors, Revision 1.3
1.2 andrew 1: #!/usr/bin/perl
1.3 ! andrew 2: # $RedRiver: check_hw_sensors,v 1.2 2006/05/02 01:29:33 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: ########################################################################
8: use strict;
9: use warnings;
10:
11: #use Data::Dumper;
12:
13: use POSIX;
14: #use lib "/usr/local/libexec/nagios";
15: use lib $ENV{'HOME'};
16: use utils qw($TIMEOUT %ERRORS &print_revision &support);
17:
18: use Getopt::Long;
19: Getopt::Long::Configure('bundling');
20:
21: my $PROGNAME = "check_hw_sensors";
22:
23: my $SYSCTL = 'sysctl';
24: my $GETCAP = 'getcap';
25: my $BASE = 'hw.sensors';
26:
27: my $CHECK_SENSOR = $BASE;
28: my %CHECKS;
29: my %SENSORS;
30:
31: my $state = 'UNKNOWN'; # tells whether the it is warning, critical, or OK
32: my %states; # This stores the count of states;
33: my $filename;
34: my $sensor;
35: my $warning;
36: my $critical;
37: my $opt_h ;
38: my $opt_V ;
39:
40:
41: #Option checking
42: my $status = GetOptions(
1.3 ! andrew 43: "version|V" => \$opt_V,
! 44: "help|h" => \$opt_h,
1.2 andrew 45: "filename|f:s" => \$filename,
46: "sensor|s=s" => \$sensor,
47: "warning|w=s" => \$warning,
48: "critical|c=s" => \$critical,
49: );
50:
51: # set the default this way so it only happens if someone typed -f or --filename
52: $filename = '/etc/sensorsd.conf' if (defined $filename && $filename eq '');
53:
54: if ($status == 0) {
55: print_help() ;
56: exit $ERRORS{'OK'};
57: }
58:
59: if ($opt_V) {
60: print_revision($PROGNAME,'$Revision: 1.2 $ ');
61: exit $ERRORS{'OK'};
62: }
63:
64: if ($opt_h) {
65: print_help();
66: exit $ERRORS{'OK'};
67: }
68:
69: unless ( (
70: defined $filename ||
71: (defined $sensor && ($warning || $critical))
72: ) &&
73: ( (!defined $filename) || (!defined $sensor) )
74: ) {
75: print_help();
76: exit $ERRORS{'OK'};
77: }
78:
79:
80: if (defined $sensor) {
81: if ($sensor !~ /^$BASE/) {
82: $sensor = $BASE . '.' . $sensor;
83: }
84: $CHECK_SENSOR = $sensor;
85:
86: $CHECKS{$sensor} = {
87: 'warning' => $warning,
88: 'critical' => $critical,
89: };
90: } elsif (defined $filename) {
91: %CHECKS = parse_file($filename);
92: }
93:
94: open my $sysctl, "-|", $SYSCTL, $CHECK_SENSOR
95: or die "Couldn't open sysctl: $!";
96: while (<$sysctl>) {
97: #while (<>) {
98: chomp;
99: my ($id, $output) = split /=/;
100: my @o = split /,\s*/, $output;
101:
102: my $source = $o[0];
103: my $descr = $o[1];
104: my $status = $o[2] if @o == 5;
105: my $type = $o[-2];
106: my $data = $o[-1];
107:
108: $SENSORS{$id} = {
109: id => $id,
110: output => $output,
111: source => $source,
112: description => $descr,
113: status => $status,
114: type => $type,
115: data => $data,
116: };
117:
118: }
119: close $sysctl;
120:
121: sub as_if_numeric {
122: my $_a = $a;
123: my $_b = $b;
124: $_a =~ s/\D//g;
125: $_b =~ s/\D//g;
126: $_a <=> $_b;
127: }
128:
129: foreach my $check (sort as_if_numeric keys %CHECKS) {
130: if (exists $SENSORS{$check}) {
131: my $r = check_sensor($CHECKS{$check}, $SENSORS{$check});
132: push @{ $states{ $r } },
133: $check . '=' . $SENSORS{$check}{'output'};
134: } else {
135: # XXX Error missing sensor
136: push @{ $states{'UNKNOWN'} }, $check . '=No sensor with this id';
137: }
138: }
139:
140: #print Dumper \%states;
141:
142: $state = 'OK';
143: foreach my $error (sort { $ERRORS{$a} <=> $ERRORS{$b} } keys %ERRORS) {
144: if (exists $states{$error}) {
145: $state = $error;
146: print "$error (" . scalar(@{ $states{ $error } }) . "):\n";
147: foreach (@{ $states{ $error } }) {
148: print " $_\n";
149: }
150: }
151: }
152: exit $ERRORS{$state};
153:
154:
155: sub parse_file {
156: my $filename = shift;
157: my %contents;
158:
159: open my $fh, '-|', $GETCAP, '-a', '-f', $filename
160: or die "Couldn't open FILE '$GETCAP -a -f $filename': $!";
161: while (<$fh>) {
162: chomp;
163: my ($key, @c) = split /\:/;
164: foreach (@c) {
165: my ($k, $v) = split /\=/;
166: $contents{$key}{$k} = $v;
167: }
168: }
169: close $fh;
170:
171: return %contents;
172: }
173:
174: sub parse_check {
175: my $type = shift;
176: my $check = shift;
177:
178: $check->{'warn'} = $check->{'warning'} if (!defined $check->{'warn'});
179: $check->{'crit'} = $check->{'critical'} if (!defined $check->{'crit'});
180:
181: if (defined $check->{'warn'} && $check->{'warn'} =~ /:/) {
182: if (my ($low, $high) = split /:/, $check->{'warn'}) {
183: $check->{'warn.low'} = $low;
184: $check->{'warn.high'} = $high;
185: }
186: delete $check->{'warn'};
187: }
188: if (defined $check->{'crit'} && $check->{'crit'} =~ /:/) {
189: if (my ($low, $high) = split /:/, $check->{'crit'}) {
190: $check->{'crit.low'} = $low;
191: $check->{'crit.high'} = $high;
192: }
193: delete $check->{'crit'};
194: }
195:
196: if (defined $check->{'low'}) {
197: $check->{'warn.low'} = $check->{'low'}
198: unless defined $check->{'warn.low'};
199: $check->{'crit.low'} = $check->{'low'}
200: unless defined $check->{'crit.low'};
201: }
202: if (defined $check->{'high'}) {
203: $check->{'warn.high'} = $check->{'high'}
204: unless defined $check->{'warn.high'};
205: $check->{'crit.high'} = $check->{'high'}
206: unless defined $check->{'crit.high'};
207: }
208:
209: no warnings 'uninitialized';
210: $check->{'warn'} = [ split /,\s*/, $check->{'warn'} ];
211: $check->{'crit'} = [ split /,\s*/, $check->{'crit'} ];
212:
213: return $check;
214: }
215:
216: sub check_sensor {
217: my $check = shift;
218: my $sensor = shift;
219: my $result = 'UNKNOWN';
220: my %errors = (
221: 'warn' => 'WARNING',
222: 'crit' => 'CRITICAL',
223: );
224:
225: return $result unless ref $sensor eq 'HASH';
226:
227: $check = parse_check($sensor->{'type'}, $check);
228: #print Dumper $check, $sensor;
229:
230: $result = 'OK';
231:
232: foreach my $code ('warn', 'crit') {
233: if (
234: $sensor->{'type'} eq 'volts_dc' ||
235: $sensor->{'type'} eq 'fanrpm' ||
236: $sensor->{'type'} eq 'raw'
237: ) {
238: my $data = $sensor->{'data'};
239: $data =~ s/[^\d\.]//g;
240: unless (length $data) {
241: warn "INVALID DATA ($sensor->{'data'}) for '$sensor->{'id'}'";
242: next;
243: }
244:
245: if (
246: defined $check->{$code . ".low"} ||
247: defined $check->{$code . ".high"}
248: ) {
249: if (defined $check->{$code . ".low"}) {
250: my $c = $check->{$code . ".low"};
251: $c =~ s/[^\d\.]//g;
252:
253: unless (length $c) {
254: warn "INVALID CHECK (" . $check->{$code . ".low"} .
255: ") for '$sensor->{'id'}:$code.low'";
256: next;
257: }
258:
259: $result = $errors{$code}
260: if ($c >= $data);
261: }
262: if (defined $check->{$code . ".high"}) {
263: my $c = $check->{$code . ".high"};
264: $c =~ s/[^\d\.]//g;
265: unless (length $c) {
266: warn "INVALID CHECK (" . $check->{$code . ".high"} .
267: ") for '$sensor->{'id'}:$code.high'";
268: next;
269: }
270:
271: $result = $errors{$code}
272: if ($c <= $data);
273: }
274: } elsif (defined $check->{$code}) {
275: my $matched = 0;
276: foreach my $c (@{ $check->{$code} }) {
277: $c =~ s/[^\d\.]//g;
278: unless (length $c) {
279: warn "INVALID CHECK (" . $check->{$code} .
280: ") for '$sensor->{'id'}:$code'";
281: next;
282: }
283:
284: if ($_ eq $data) {
285: $matched = 1;
286: last;
287: }
288: }
289: $result = $errors{$code} unless $matched;
290: }
291:
292: } elsif ($sensor->{'type'} eq 'temp') {
293: my ($degC, $degF) = split /\//, $sensor->{'data'};
294: $degC =~ s/[^\d\.]//g;
295: $degF =~ s/[^\d\.]//g;
296: if (
297: defined $check->{$code . ".low"} ||
298: defined $check->{$code . ".high"}
299: ) {
300: if (defined $check->{$code . ".low"}) {
301: my $c = $check->{$code . ".low"};
302: my $data = $degC;
303:
304: $data = $degF if ($c =~ /F/i);
305: unless (length $data) {
306: warn "INVALID DATA (" . $sensor->{'data'} .
307: ") for '$sensor->{'id'}'";
308: next;
309: }
310:
311: $c =~ s/[^\d\.]//g;
312: unless (length $c) {
313: warn "INVALID CHECK (" . $check->{$code . ".low"} .
314: ") for '$sensor->{'id'}':$code.low";
315: next;
316: }
317:
318: $result = $errors{$code}
319: if ($c >= $data);
320: }
321: if (defined $check->{$code . ".high"}) {
322: my $c = $check->{$code . ".high"};
323:
324: my $data = $degC;
325: $data = $degF if ($c =~ /F/i);
326: unless (length $data) {
327: warn "INVALID DATA (" . $sensor->{'data'} .
328: ") for '$sensor->{'id'}'";
329: next;
330: }
331:
332: $c =~ s/[^\d\.]//g;
333: unless (length $c) {
334: warn "INVALID CHECK (" . $check->{$code . ".high"} .
335: ") for '$sensor->{'id'}:$code.high'";
336: next;
337: }
338:
339: $result = $errors{$code}
340: if ($c <= $data);
341: }
342: } elsif (defined $check->{$code}) {
343:
344: my $matched = 0;
345: foreach my $c (@{ $check->{$code} }) {
346: my $data = $degC;
347:
348: $data = $degF if ($c =~ /F/i);
349: unless (length $data) {
350: warn "INVALID DATA (" . $sensor->{'data'} .
351: ") for '$sensor->{'id'}'";
352: next;
353: }
354:
355: $c =~ s/[^\d\.]//g;
356: unless (length $c) {
357: warn "INVALID CHECK (" . $check->{$code} .
358: ") for '$sensor->{'id'}':$code";
359: next;
360: }
361:
362: if ($_ eq $data) {
363: $matched = 1;
364: last;
365: }
366: }
367: $result = $errors{$code} unless $matched;
368: }
369:
370: } elsif (
371: $sensor->{'type'} eq 'drive' ||
372: $sensor->{'type'} eq 'indicator'
373: ) {
374: if (defined $check->{$code}) {
375: my $matched = 0;
376: foreach (@{ $check->{$code} }) {
377: if ($_ eq $sensor->{'data'}) {
378: $matched = 1;
379: last;
380: }
381: }
382: $result = $errors{$code} unless $matched;
383: }
384:
385: } else {
386: $result = 'UNKNOWN';
387: }
388:
389: }
390:
391: return $result;
392: }
393:
394: sub print_help {
395: print <<EOL;
396: $PROGNAME plugin for Nagios monitors sysctl hw.sensors on OpenBSD
397: $PROGNAME (-f [<FILENAME>]|(-s <hw.sensors id> -w limit -c limit))
398:
399: Usage:
400: -f, --filename=FILE
401: FILE to load checks from (defaults to /etc/sensorsd.conf)
402: -s, --sensor=ID
403: ID of a single sensor. "-s 0" means hw.sensors.0.
404: -w, --warning=RANGE or single ENTRY
405: Exit with WARNING status if outside of RANGE or if != ENTRY
406: -c, --critical=INTEGER
407: Exit with CRITICAL status if outside of RANGE or if != ENTRY
408:
409: -h (--help) usage help
410:
411: 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:
412: low, high, crit, warn, crit.low, crit.high, warn.low, warn.high
413:
414: An ENTRY depends on the type. The descriptions in sensorsd.conf(5) can be used when appropriate, or you can use the following:
415: 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.
416: temp - Can be as above, but if the entry has an F in it, it compares farenheit, otherwise it uses celcius.
417: 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.
418:
419: 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.
420:
421: A RANGE is a low ENTRY and a high ENTRY separated by a colon (:).
422:
423: EOL
424:
425: print_revision($PROGNAME, '$Revision: 1.2 $');
426: }
427:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>