[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.39

1.14      andrew      1: #!/usr/bin/perl -T
1.39    ! andrew      2: # $RedRiver: check_hw_sensors,v 1.38 2009/11/11 18:08:41 andrew Exp $
1.2       andrew      3: ########################################################################
1.26      andrew      4: # check_hw_sensors *** A nagios check for OpenBSD sysctl hw.sensors
                      5: #
1.31      andrew      6: # 2006.05.01 #*#*# andrew fresh <andrew@afresh1.com>
1.2       andrew      7: ########################################################################
                      8: use strict;
                      9: use warnings;
                     10:
1.33      andrew     11: local %ENV = ();
1.14      andrew     12:
1.34      andrew     13: my $NAGIOS_OUTPUT = 1;
1.4       andrew     14:
1.33      andrew     15: my $LICENSE = <<'EOL';
1.26      andrew     16: Copyright (c) 2009 Andrew Fresh <andrew@afresh1.com>
                     17: Permission to use, copy, modify, and distribute this software for any
                     18: purpose with or without fee is hereby granted, provided that the above
                     19: copyright notice and this permission notice appear in all copies.
                     20:
                     21: THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     22: WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     23: MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     24: ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     25: WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     26: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     27: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     28: EOL
                     29:
1.2       andrew     30: use POSIX;
1.20      andrew     31: use Config;
1.38      andrew     32: my $PREFIX;
                     33: BEGIN {
                     34:     ## no critic 'warnings'
                     35:     no warnings 'uninitialized';
                     36:     $PREFIX = "${PREFIX}" || '/usr/local'; # Magic for OpenBSD ports tree
                     37: }
                     38: use lib $PREFIX . '/libexec/nagios';
1.26      andrew     39: use utils qw($TIMEOUT %ERRORS &support);
1.2       andrew     40:
                     41: use Getopt::Long;
                     42: Getopt::Long::Configure('bundling');
                     43:
1.33      andrew     44: my $PROGNAME = 'check_hw_sensors';
1.2       andrew     45:
1.26      andrew     46: my $SYSCTL         = '/sbin/sysctl';
                     47: my $GETCAP         = '/usr/bin/getcap';
                     48: my $BASE           = 'hw.sensors';
1.4       andrew     49: my $DEFAULT_CONFIG = '/etc/sensorsd.conf';
1.26      andrew     50: my $OSVer          = $Config{'osvers'} || 0;
1.2       andrew     51:
1.26      andrew     52: my $state = 'UNKNOWN';    # tells whether the it is warning, critical, or OK
1.28      andrew     53: my $opt_V;
                     54: my $opt_h;
1.33      andrew     55: my $IGNORE_STATUS;
                     56: my $FILENAME;
                     57: my $SENSOR;
                     58: my $WARNING;
                     59: my $CRITICAL;
1.2       andrew     60:
                     61: #Option checking
1.33      andrew     62: my $getopt_status = GetOptions(
                     63:     'version|V'       => \$opt_V,
                     64:     'help|h'          => \$opt_h,
                     65:     'ignore-status|i' => \$IGNORE_STATUS,
                     66:     'filename|f:s'    => \$FILENAME,
                     67:     'sensor|s=s'      => \$SENSOR,
                     68:     'warning|w=s'     => \$WARNING,
                     69:     'critical|c=s'    => \$CRITICAL,
1.2       andrew     70: );
1.18      andrew     71:
1.33      andrew     72: if ( $getopt_status == 0 ) {
1.32      andrew     73:     print_help();
                     74:     exit $ERRORS{'OK'};
                     75: }
                     76:
                     77: if ($opt_V) {
1.39    ! andrew     78:     print_revision( $PROGNAME, '$Revision: 1.38 $ ' );
1.32      andrew     79:     exit $ERRORS{'OK'};
                     80: }
                     81:
                     82: if ($opt_h) {
                     83:     print_help();
                     84:     exit $ERRORS{'OK'};
                     85: }
                     86:
1.2       andrew     87: # set the default this way so it only happens if someone typed -f or --filename
1.33      andrew     88: $FILENAME = $DEFAULT_CONFIG if ( defined $FILENAME && $FILENAME eq q{} );
1.2       andrew     89:
1.18      andrew     90: # Stuff is output in this file by print_sensor()
                     91: # http://www.openbsd.org/cgi-bin/cvsweb/src/sbin/sysctl/sysctl.c
1.33      andrew     92: my @TYPE_MAP = (
1.26      andrew     93:     {   type  => 'temp',
1.33      andrew     94:         regex => qr/\sdegC$/xms,
1.26      andrew     95:     },
                     96:     {   type  => 'fanrpm',
1.33      andrew     97:         regex => qr/\sRPM$/xms,
1.26      andrew     98:     },
                     99:     {   type  => 'volts_dc',
1.33      andrew    100:         regex => qr/\sV\sDC$/xms,
1.26      andrew    101:     },
                    102:     {   type  => 'amps',
1.33      andrew    103:         regex => qr/\sA$/xms,
1.26      andrew    104:     },
                    105:     {   type  => 'watthour',
1.33      andrew    106:         regex => qr/\sWh$/xms,
1.26      andrew    107:     },
                    108:     {   type  => 'amphour',
1.33      andrew    109:         regex => qr/\sAh$/xms,
1.26      andrew    110:     },
                    111:     {   type  => 'indicator',
1.33      andrew    112:         regex => qr/^(On|Off)$/xms,
1.26      andrew    113:     },
                    114:     {   type  => 'integer',
1.33      andrew    115:         regex => qr/\sraw$/xms,
1.26      andrew    116:     },
                    117:     {   type  => 'percent',
1.33      andrew    118:         regex => qr/\s\%$/xms,
1.26      andrew    119:     },
                    120:     {   type  => 'lux',
1.33      andrew    121:         regex => qr/\slx$/xms,
1.26      andrew    122:     },
                    123:     {   type  => 'drive',
1.33      andrew    124:         regex => qr/^drive\s/xms,
1.26      andrew    125:     },
                    126:     {   type  => 'timedelta',
1.33      andrew    127:         regex => qr/\ssecs$/xms,
1.26      andrew    128:     },
                    129: );
                    130:
1.33      andrew    131: my $CHECK_SENSOR = $BASE;
                    132: my %CHECKS;
                    133: if ( defined $SENSOR ) {
                    134:     if ( $SENSOR !~ /^$BASE/xms ) {
                    135:         $SENSOR = join q{.}, $BASE, $SENSOR;
1.26      andrew    136:     }
1.33      andrew    137:     $CHECK_SENSOR = $SENSOR;
1.2       andrew    138:
1.37      andrew    139:     $CHECKS{$SENSOR}{'warn'} = $WARNING  if $WARNING;
                    140:     $CHECKS{$SENSOR}{'crit'} = $CRITICAL if $CRITICAL;
1.26      andrew    141: }
1.33      andrew    142: elsif ( defined $FILENAME ) {
                    143:     %CHECKS = read_file($FILENAME);
                    144: }
                    145:
                    146: my @SENSORS = read_sensors($CHECK_SENSOR);
                    147: my %STATES = check_sensors( \@SENSORS, \%CHECKS,
                    148:     { IGNORE_STATUS => $IGNORE_STATUS } );
                    149:
                    150: my $have_results = 0;
                    151: $state = 'OK';
1.37      andrew    152: foreach
                    153:     my $error ( reverse sort { $ERRORS{$a} <=> $ERRORS{$b} } keys %ERRORS )
                    154: {
1.33      andrew    155:     if ( exists $STATES{$error} ) {
                    156:         $have_results++;
                    157:         $state = $error if $ERRORS{$state} < $ERRORS{$error};
                    158:
                    159:         if ($NAGIOS_OUTPUT) {
1.36      andrew    160:             print $error . ' (' . scalar( @{ $STATES{$error} } ) . ')';
1.33      andrew    161:             if ( $error ne 'OK' ) {
                    162:                 print '<br>';
1.39    ! andrew    163:                 print map { " - $_<br>" } @{ $STATES{$error} };
1.33      andrew    164:             }
                    165:         }
                    166:         else {
1.36      andrew    167:             print $error . ' (' . scalar( @{ $STATES{$error} } ) . "):\n";
1.33      andrew    168:             foreach ( @{ $STATES{$error} } ) {
                    169:                 print "   $_\n";
                    170:             }
                    171:         }
                    172:     }
                    173: }
                    174: if ( $have_results == 0 ) {
                    175:     print "No results found\n";
1.2       andrew    176: }
1.33      andrew    177: exit $ERRORS{$state};
1.2       andrew    178:
1.33      andrew    179: sub read_sensors {
                    180:     my ($sensor) = @_;
                    181:     my @S;
                    182:     open my $sysctl, '-|', $SYSCTL, $sensor
                    183:         or die "Couldn't open sysctl: $!\n";
                    184:     while (<$sysctl>) {
                    185:         chomp;
                    186:         push @S, parse_sensor($_);
                    187:     }
                    188:     ## no critic 'die'
                    189:     close $sysctl
                    190:         or die $!
                    191:         ? "Error closing sysctl pipe: $!\n"
                    192:         : "Exit status $? from sysctl\n";
                    193:
                    194:     return @S;
                    195: }
                    196:
                    197: sub parse_sensor {
                    198:     my ($sensor) = @_;
                    199:
                    200:     my ( $id, $output ) = split /=/xms, $sensor;
1.32      andrew    201:     my @s = split /\./xms,   $id;
                    202:     my @o = split /,\s*/xms, $output;
1.26      andrew    203:
                    204:     my ( $type, $source, $descr, $data, $status );
                    205:
                    206:     $source = $o[0];
                    207:     $descr  = $o[1];
                    208:
                    209:     if ( $OSVer >= 4.1 ) {
                    210:         $data = $o[0];
1.32      andrew    211:         if ( $data =~ s/\s+\((.*)\).*$//xms ) {
1.26      andrew    212:             $descr = $1;
                    213:         }
                    214:         $status = $o[1];
1.32      andrew    215:         ( $source, $type ) = $id =~ /([^\.]+)\.([^\.]+?)\d+$/xms;
1.26      andrew    216:     }
                    217:     elsif ( $OSVer >= 4.0 ) {
                    218:         $data   = $o[2];
                    219:         $status = $o[3];
1.33      andrew    220:         foreach my $t (@TYPE_MAP) {
1.32      andrew    221:             if ( $data =~ /$t->{'regex'}/xms ) {
1.26      andrew    222:                 $type = $t->{'type'};
                    223:                 last;
                    224:             }
                    225:         }
                    226:     }
                    227:     else {
                    228:         $data   = $o[-1];
                    229:         $status = $o[2] if @o == 5;
                    230:         $type   = $o[-2];
                    231:     }
                    232:
                    233:     $type ||= 'unknown';
                    234:
1.33      andrew    235:     return {
1.26      andrew    236:         id          => $id,
                    237:         output      => $output,
                    238:         source      => $source,
                    239:         description => $descr,
                    240:         status      => $status,
                    241:         type        => $type,
                    242:         data        => $data,
1.33      andrew    243:     };
1.2       andrew    244: }
                    245:
1.33      andrew    246: sub read_file {
1.26      andrew    247:     my $filename = shift;
                    248:     my %contents;
1.2       andrew    249:
1.33      andrew    250:     die "file '$filename' does not exist.\n" unless -e $filename;
1.26      andrew    251:
                    252:     open my $fh, '-|', $GETCAP, '-a', '-f', $filename
1.33      andrew    253:         or die "Couldn't open '$GETCAP -a -f $filename': $!\n";
1.26      andrew    254:     while (<$fh>) {
                    255:         chomp;
1.33      andrew    256:         my ( $s, @c ) = split /\:/xms;
                    257:         $contents{$s} = parse_line(@c);
1.26      andrew    258:     }
1.33      andrew    259:     ## no critic 'die'
                    260:     close $fh
                    261:         or die $!
                    262:         ? "Error closing getcap pipe: $!\n"
                    263:         : "Exit status $? from getcap\n";
1.2       andrew    264:
1.26      andrew    265:     return %contents;
1.2       andrew    266: }
                    267:
1.33      andrew    268: sub parse_line {
                    269:     my (@c) = @_;
                    270:     my %c;
                    271:     foreach (@c) {
                    272:         my ( $k, $v ) = split /\=/xms;
                    273:         if    ( lc($k) eq 'ignore' ) { $c{'IGNORE'} = 1; }
                    274:         elsif ( lc($k) eq 'status' ) { $c{'STATUS'} = 1; }
                    275:         else                         { $c{$k}       = $v; }
                    276:     }
                    277:     return \%c;
                    278: }
                    279:
1.2       andrew    280: sub parse_check {
1.26      andrew    281:     my $check = shift;
1.2       andrew    282:
1.26      andrew    283:     return unless $check;
1.32      andrew    284:     return 'STATUS' if $check->{'STATUS'};
1.26      andrew    285:     return 'IGNORE' if $check->{'IGNORE'};
                    286:
                    287:     foreach my $code ( 'crit', 'warn' ) {
1.33      andrew    288:         if ( defined $check->{$code} && $check->{$code} =~ /:/xms ) {
                    289:             if ( my ( $low, $high ) = split /:/xms, $check->{$code} ) {
1.26      andrew    290:                 $check->{ $code . '.low' }  = $low  if length $low;
                    291:                 $check->{ $code . '.high' } = $high if length $high;
                    292:             }
                    293:             delete $check->{$code};
                    294:         }
                    295:
1.33      andrew    296:         foreach my $direction ( 'low', 'high' ) {
                    297:             my $c = $code . '.' . $direction;
                    298:             if ( defined $check->{$direction} ) {
                    299:                 $check->{$c} ||= $check->{$direction};
1.26      andrew    300:             }
1.33      andrew    301:
                    302:             if ( defined $check->{$c} ) {
                    303:                 my $old = $check->{$c};
                    304:                 $check->{$c} =~ s/[^\d\.]//gxms;
                    305:                 if ( !length $check->{$c} ) {
                    306:                     warn "INVALID CHECK ($old)\n";
                    307:                     delete $check->{$c};
                    308:                 }
                    309:             }
                    310:         }
                    311:
                    312:         if ( defined $check->{$code} ) {
                    313:             $check->{$code} = [ split /,\s*/xms, $check->{$code} ];
                    314:         }
                    315:         else {
                    316:             $check->{$code} = [];
1.26      andrew    317:         }
                    318:     }
1.2       andrew    319:
1.26      andrew    320:     return $check;
1.2       andrew    321: }
                    322:
1.33      andrew    323: sub check_sensors {
                    324:     my ( $S, $C, $O ) = @_;
                    325:
                    326:     my %states;
                    327:     foreach my $sensor ( @{$S} ) {
                    328:         my ( $r, $data );
                    329:         if ( exists $C->{ $sensor->{id} } ) {
                    330:             $r = check_sensor( $sensor, $C->{ $sensor->{id} } );
                    331:             $data = $sensor->{id} . '=' . $sensor->{output};
                    332:         }
1.37      andrew    333:         elsif ( $sensor->{status} && !$O->{IGNORE_STATUS} ) {
1.33      andrew    334:             $r = check_sensor( $sensor, { STATUS => 1 } );
                    335:             $data = $sensor->{id} . '=' . $sensor->{output};
                    336:         }
                    337:         else {
                    338:
                    339:             # ignore this sensor, theoretically you could do the check and
                    340:             # that would show unknown sensors.
                    341:         }
                    342:         if ( defined $r ) {
                    343:             push @{ $states{$r} }, $data;
                    344:         }
                    345:     }
                    346:
                    347:     return %states;
                    348: }
                    349:
1.2       andrew    350: sub check_sensor {
1.33      andrew    351:     my ( $sensor, $check ) = @_;
1.26      andrew    352:     my $result = 'UNKNOWN';
                    353:
                    354:     return $result unless ref $sensor eq 'HASH';
                    355:     $check = parse_check($check) if $check;
                    356:
1.33      andrew    357:     if ( !$check ) { return $result; }
1.32      andrew    358:     elsif ( $check eq 'STATUS' ) {
1.27      andrew    359:
1.33      andrew    360:         # It looks like returning $sensor->{status} should be safe, from
1.26      andrew    361:         # src/sbin/sysctl/sysctl.c
1.32      andrew    362:         return ( $sensor->{'status'} || $result );
1.26      andrew    363:     }
1.33      andrew    364:     elsif ( $check eq 'IGNORE' ) { return; }
                    365:
                    366:     my $type = $sensor->{'type'};
                    367:     if (grep { $type eq $_ }
                    368:         qw(
                    369:         fan fanrpm
                    370:         volt volts_dc
                    371:         amps watthour amphour
                    372:         integer raw percent
                    373:         lux temp timedelta
                    374:         )
                    375:         )
                    376:     {
                    377:         $result = check_sensor_numeric( $sensor->{'data'}, $check );
                    378:     }
                    379:     elsif ( grep { $type eq $_ } qw( drive indicator ) ) {
                    380:         my $data = $sensor->{'data'};
                    381:         $data =~ s/^drive\s+//xms;
                    382:         $result = check_sensor_list( $data, $check );
                    383:     }
                    384:     else {
                    385:         warn 'Unknown Sensor Type: ', $sensor->{'id'}, '=', $type, "\n";
                    386:     }
                    387:
                    388:     return $result;
                    389: }
                    390:
                    391: sub check_sensor_numeric {
                    392:     my ( $data, $check ) = @_;
                    393:
                    394:     my $result = 'UNKNOWN';
                    395:     my %errors = (
                    396:         'warn' => 'WARNING',
                    397:         'crit' => 'CRITICAL',
                    398:     );
                    399:
                    400:     $data =~ s/[^\d\.]//gxms;
                    401:     if ( !length $data ) {
                    402:         warn "INVALID DATA ($data)\n";
                    403:         return $result;
1.26      andrew    404:     }
                    405:
                    406:     foreach my $code ( 'warn', 'crit' ) {
1.36      andrew    407:         if (   defined $check->{ $code . '.low' }
                    408:             || defined $check->{ $code . '.high' } )
1.26      andrew    409:         {
1.36      andrew    410:             if ((   defined $check->{ $code . '.low' }
                    411:                     && $check->{ $code . '.low' } >= $data
1.33      andrew    412:                 )
1.36      andrew    413:                 || ( defined $check->{ $code . '.high' }
                    414:                     && $check->{ $code . '.high' } <= $data )
1.33      andrew    415:                 )
                    416:             {
                    417:                 $result = $errors{$code};
1.26      andrew    418:             }
1.33      andrew    419:             $result = 'OK' if $result eq 'UNKNOWN';
                    420:         }
                    421:         elsif ( @{ $check->{$code} } ) {
                    422:             my $matched = 0;
                    423:         NUMERIC_CHECK: foreach ( @{ $check->{$code} } ) {
                    424:                 my $c = $_;
                    425:                 $c =~ s/[^\d\.]//gxms;
                    426:                 if ( !length $c ) {
1.36      andrew    427:                     warn "INVALID CHECK ($_) for '$code'\n";
1.33      andrew    428:                     next;
1.26      andrew    429:                 }
                    430:
1.33      andrew    431:                 if ( $c eq $data ) {
                    432:                     $matched = 1;
                    433:                     last NUMERIC_CHECK;
1.26      andrew    434:                 }
1.33      andrew    435:             }
                    436:             if ($matched) {
1.32      andrew    437:                 $result = 'OK' if $result eq 'UNKNOWN';
1.26      andrew    438:             }
1.33      andrew    439:             else {
                    440:                 $result = $errors{$code};
1.26      andrew    441:             }
                    442:         }
1.33      andrew    443:     }
                    444:
                    445:     return $result;
                    446: }
1.26      andrew    447:
1.33      andrew    448: sub check_sensor_list {
                    449:     my ( $data, $check ) = @_;
1.26      andrew    450:
1.33      andrew    451:     my $result = 'UNKNOWN';
                    452:     my %errors = (
                    453:         'warn' => 'WARNING',
                    454:         'crit' => 'CRITICAL',
                    455:     );
1.26      andrew    456:
1.33      andrew    457:     foreach my $code ( 'warn', 'crit' ) {
                    458:         if ( @{ $check->{$code} } ) {
                    459:             my $matched = 0;
                    460:         LIST_CHECK: foreach ( @{ $check->{$code} } ) {
                    461:                 if ( $_ eq $data ) {
                    462:                     $matched = 1;
                    463:                     last LIST_CHECK;
1.26      andrew    464:                 }
1.33      andrew    465:             }
                    466:             if ($matched) {
1.32      andrew    467:                 $result = 'OK' if $result eq 'UNKNOWN';
1.26      andrew    468:             }
1.33      andrew    469:             else {
                    470:                 $result = $errors{$code};
1.26      andrew    471:             }
                    472:         }
                    473:     }
1.2       andrew    474:
1.26      andrew    475:     return $result;
1.2       andrew    476: }
                    477:
                    478: sub print_help {
1.26      andrew    479:     print <<"EOL";
                    480: $PROGNAME - monitors sysctl hw.sensors on OpenBSD
1.30      andrew    481:     $PROGNAME [-i] [-f [<FILENAME>]|(-s <hw.sensors id> [-w limit] [-c limit])]
1.2       andrew    482:
                    483: Usage:
1.15      andrew    484:     -i, --ignore-status
1.26      andrew    485:         Don't automatically check the status of sensors that report it.
1.12      andrew    486:     -f, --filename=FILE
                    487:         FILE to load checks from (defaults to /etc/sensorsd.conf)
                    488:     -s, --sensor=ID
1.26      andrew    489:         ID of a single sensor.  "-s kate0.temp0" means hw.sensors.kate0.temp0
1.27      andrew    490:         Overrides --filename.
1.12      andrew    491:     -w, --warning=RANGE or single ENTRY
                    492:         Exit with WARNING status if outside of RANGE or if != ENTRY
1.13      andrew    493:     -c, --critical=RANGE or single ENTRY
1.12      andrew    494:         Exit with CRITICAL status if outside of RANGE or if != ENTRY
                    495:
1.13      andrew    496: FILE is in the same format as sensorsd.conf(5) plus some additional
                    497: entries.  These additional entries in the file are ignored by
1.35      andrew    498: sensorsd(8).  This means you can use the same config file for $PROGNAME
1.26      andrew    499: as well as sensorsd(8).
1.12      andrew    500:
                    501: $PROGNAME understands the following entries:
                    502:
1.15      andrew    503:     low, high, crit, warn, crit.low, crit.high, warn.low, warn.high,
                    504:     ignore, status
1.12      andrew    505:
                    506: An ENTRY depends on the type.  The descriptions in sensorsd.conf(5)
                    507: can be used when appropriate, or you can use the following:
                    508:
1.20      andrew    509:     fanrpm, volts_dc, amps, watthour, amphour, integer (raw), percent,
1.33      andrew    510:     lux, temp or timedelta - Anything that includes digits.  Both the
                    511:     value of the check and the value of the sensor response that are not
                    512:     either a digit or period are stripped and then the two resultant
                    513:     values are compared.
1.12      andrew    514:
                    515:     indicator or drive - does a case sensitive match of each
                    516:     entry in the comma separated list and if it does not match
1.20      andrew    517:     any of the entries, it sets the status.
1.12      andrew    518:
                    519: The entries 'crit' or 'warn' (or the -c or -w on the command line)
                    520: may be a RANGE or a comma separated list of acceptable values.
                    521: The comma separated list of values contains a list of things that
                    522: will NOT cause the status.  This is possibly counterintuitive, but
                    523: you are more likely to know good values than bad values.
                    524:
                    525: A RANGE is a low ENTRY and a high ENTRY separated by a colon (:).
                    526: It can also be low: or :high with the other side left blank to only
1.26      andrew    527: make the single check.
1.2       andrew    528:
1.15      andrew    529: An entry marked "ignore" will cause that sensor to be skipped.
                    530: Generally used with state checking of all sensors to ignore sensors you
                    531: don't care about or that report incorrectly.
                    532:
                    533: If you are using --ignore-status, you can still check the status of
                    534: individual sensors with a status entry.
                    535:
1.2       andrew    536: EOL
1.15      andrew    537:
1.39    ! andrew    538:     print_revision( $PROGNAME, '$Revision: 1.38 $' );
1.26      andrew    539:
1.33      andrew    540:     print $LICENSE;
                    541:
                    542:     return;
1.2       andrew    543: }
1.26      andrew    544:
                    545: sub print_revision {
1.27      andrew    546:     my ( $prog, $rev ) = @_;
1.26      andrew    547:     $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
                    548:
                    549:     print "$prog $rev\n";
1.33      andrew    550:
                    551:     return;
1.26      andrew    552: }
1.2       andrew    553:

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