"Fossies" - the Fresh Open Source Software Archive

Member "munin-2.0.67/plugins/node.d/hddtemp_smartctl.in" (22 Feb 2021, 10723 Bytes) of package /linux/misc/munin-2.0.67.tar.gz:


As a special service "Fossies" has tried to format the requested text file into HTML format (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "hddtemp_smartctl.in": 2.0.66_vs_2.0.67.

    1 #!@@PERL@@ -w
    2 # -*- perl -*-
    3 
    4 use strict;
    5 use warnings;
    6 
    7 =head1 NAME
    8 
    9 hddtemp_smartctl - Plugin to monitor harddrive temperatures through
   10 SMART
   11 
   12 =head1 CONFIGURATION
   13 
   14 This plugin needs to run as root or some other user that has access to
   15 the harddrive devices.
   16 
   17 The following environment variables are used
   18 
   19  smartctl  - path to smartctl executable
   20  drives	   - List drives to monitor. E.g. "env.drives hda hdc".
   21  type_$dev - device type for one drive, e.g. "env.type_sda 3ware,0"
   22              or more typically "env.type_sda ata" if sda is a SATA disk.
   23  args_$dev - additional arguments to smartctl for one drive,
   24              e.g. "env.args_hda -v 194,10xCelsius".  Use this to make
   25              the plugin use the --all or -a option if your disk will
   26              not return its temperature when only the -A option is
   27              used.
   28  dev_$dev  - monitoring device for one drive, e.g. twe0
   29 
   30 If the "smartctl" environment variable is not set the plugin will
   31 search your $PATH, /usr/bin, /usr/sbin, /usr/local/bin and
   32 /usr/local/sbin for a file called "smartctl", and use that.
   33 
   34 If the "drives" environment variable is not set, the plugin will
   35 attempt to search for drives to probe.
   36 
   37 =head1 MAGIC MARKERS
   38 
   39  #%# family=auto
   40  #%# capabilities=autoconf
   41 
   42 =head1 AUTHOR
   43 
   44 Copyright (c) 2005, Lutz Peter Christoph
   45 All rights reserved.
   46 
   47 2016-08-27, Gabriele Pohl (contact@dipohl.de)
   48 Fix for github issue #690
   49 
   50 =head1 LICENSE
   51 
   52 Redistribution and use in source and binary forms, with or without
   53 modification, are permitted provided that the following conditions
   54 are met:
   55 
   56   * Redistributions of source code must retain the above copyright
   57     notice, this list of conditions and the following disclaimer.
   58 
   59   * Redistributions in binary form must reproduce the above copyright
   60     notice, this list of conditions and the following disclaimer in
   61     the documentation and/or other materials provided with the
   62     distribution.
   63 
   64   * The name and aliases of Lutz Peter Christoph ("Lupe Christoph",
   65     "Lutz Christoph") may not be used to endorse or promote products
   66     derived from this software without specific prior written
   67     permission.
   68 
   69 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   70 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   71 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   72 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   73 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   74 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   75 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   76 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   77 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   78 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   79 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   80 
   81 =head1 NOTES
   82 
   83 Note for users of RAID controllers (smartmontools currently only
   84 supports 3ware): you can specify the drives attached to your RAID
   85 controller(s) as raiddev_num (e.g. sda_0). Then you must specify the
   86 type like this: type_sda_0 3ware,0.
   87 
   88 Recent versions of the kernel driver use a separate major device
   89 number for monitoring purposes, like /dev/twe<n> or /dev/twa<n>. This
   90 can be put in the e.g. dev_sda environment variable, to allow the user
   91 to keep sda as the name of the disk.
   92 
   93 To avoid spinning up sleeping disks smartctl will use the --nocheck
   94 parameter. If this parameter isn't supported by your version of
   95 smartctl then hdparm will be used.  Note that hdparm isn't available
   96 on all platforms.
   97 
   98 =cut
   99 
  100 use File::Spec::Functions qw(splitdir);
  101 use lib $ENV{'MUNIN_LIBDIR'};
  102 use Munin::Plugin;
  103 
  104 my $DEBUG = $ENV{'MUNIN_DEBUG'} || 0;
  105 
  106 my $smartctl;
  107 
  108 if (exists $ENV{smartctl}) {
  109    $smartctl = $ENV{smartctl};
  110    if (defined $ARGV[0] and $ARGV[0] eq 'autoconf') {
  111       # The real "autoconf" section follows later. But here we need to check for requirements, too.
  112       if (! -e $smartctl) {
  113         print "no (Predefined smartctl ($smartctl) does not exist)\n";
  114         exit 0;
  115       } elsif (! -x $smartctl) {
  116         print "no (Predefined smartctl ($smartctl) is not executable)\n";
  117         exit 0;
  118       }
  119    } else {
  120       # immediate failure is allowed outside of "autoconf"
  121       die "$smartctl does not exist\n" unless (-e $smartctl);
  122       die "$smartctl is not executable\n" unless (-x $smartctl);
  123    }
  124 } else {
  125    # Not defined in %ENV? Check obvious places
  126     my @dirs = split(':', $ENV{PATH});
  127     push (@dirs, qw(/usr/bin /usr/sbin /usr/local/bin /usr/local/sbin) );
  128 
  129     until ($smartctl or @dirs == 0) {
  130       my $dir = shift @dirs;
  131       my $path = $dir.'/smartctl';
  132       $smartctl = $path if -x $path;
  133     }
  134 
  135     unless ($smartctl) {
  136         if (defined $ARGV[0] and $ARGV[0] eq 'autoconf') {
  137             print "no ('smartctl' executable not found)\n";
  138             exit 0;
  139         } else {
  140             die "'smartctl' executable not found\n";
  141         }
  142     }
  143 }
  144 
  145 # Check version of smartctl to determine --nocheck capabilities
  146 my $use_nocheck = 0;
  147 if ($smartctl and `$smartctl --version` =~ / release (\d+\.\d+) /i) {
  148     $use_nocheck = $1 >= 5.37;
  149     warn "[DEBUG] Smartctl supports --nocheck\n" if $DEBUG;
  150 }
  151 
  152 # hdparm is used as a fallback
  153 my $hdparm = `sh -c 'command -v hdparm'`;
  154 chomp $hdparm;
  155 
  156 my @drives;
  157 
  158 # Try to get a default set of drives
  159 if ($^O eq 'linux') {
  160   # On Linux, we know how to enumerate ide drives.
  161   my @drivesIDE;
  162   if (-d '/proc/ide') {
  163     opendir(IDE, '/proc/ide');
  164     @drivesIDE = grep /hd[a-z]/, readdir IDE;
  165     closedir(IDE);
  166   }
  167 
  168   # Look for SCSI / SATA drives in /sys
  169   my @drivesSCSI;
  170   if (-d '/sys/block/') {
  171     opendir(SCSI, '/sys/block/');
  172     @drivesSCSI = grep /sd[a-z]/, readdir SCSI;
  173     closedir(SCSI);
  174   }
  175 
  176   # Look for NVMe drives in /sys
  177   my @drivesNVME;
  178   if (-d '/sys/block/') {
  179     opendir(NVME, '/sys/block/');
  180     @drivesNVME = grep /nvme[0-9]+n[0-9]+/, readdir NVME;
  181     closedir(NVME);
  182   }
  183 
  184   # Get list of all drives we found
  185   @drives=(@drivesIDE,@drivesSCSI,@drivesNVME);
  186 
  187 } elsif ($^O eq 'freebsd') {
  188   opendir(DEV, '/dev');
  189   @drives = grep /^(ada?|da)[0-9]+$/, readdir DEV;
  190   closedir(DEV);
  191 } elsif ($^O eq 'solaris') {
  192   @drives = map { s@.*/@@ ; $_ } glob '/dev/rdsk/c*t*d*s2';
  193 }
  194 
  195 @drives = split ' ', $ENV{drives} if exists $ENV{drives};
  196 
  197 # Sort list of drives
  198 @drives = sort @drives;
  199 
  200 warn "[DEBUG] Drives: ",join(', ',@drives),"\n" if $DEBUG;
  201 
  202 if (defined $ARGV[0]) {
  203   if ($ARGV[0] eq 'autoconf') {
  204     if (@drives) {
  205       my $cmd = command_for_drive_device($drives[0],
  206                                          device_for_drive($drives[0]));
  207       if (`$cmd` =~ /Temperature/) {
  208         print "yes\n";
  209       } else {
  210         print "no (first drive not supported, configure the plugin)\n";
  211       }
  212       exit 0;
  213     } else {
  214       print "no (no drives known)\n";
  215       exit 0;
  216     }
  217   } elsif ($ARGV[0] eq 'config') {
  218     print "graph_title HDD temperature\n";
  219     print "graph_vlabel Degrees Celsius\n";
  220     print "graph_category sensors\n";
  221     print "graph_info This graph shows the temperature in degrees Celsius of the hard drives in the machine.\n";
  222     foreach (@drives) {
  223         my @dirs = splitdir($_);
  224         print clean_fieldname($_) . ".label " . $dirs[-1] . "\n";
  225         print clean_fieldname($_) . ".max 100\n";
  226         print clean_fieldname($_) . ".warning 57\n";
  227         print clean_fieldname($_) . ".critical 60\n";
  228     }
  229     exit 0;
  230   }
  231 }
  232 
  233 foreach my $drive (@drives) {
  234   warn "[DEBUG] Processing $drive\n" if $DEBUG;
  235   my $fulldev = device_for_drive($drive);
  236 
  237   # Fall back to using hdparm for detecting disks in stand-by only if nocheck
  238   # isn't supported (hdparm isn't available on all platforms).
  239   if (!$use_nocheck && $hdparm && $fulldev =~ /\/dev\/[sh]d?/) {
  240     if (`$hdparm -C $fulldev 2>/dev/null` =~ /standby/) {
  241       warn "[DEBUG] Drive $fulldev is in standby mode, not checking\n"
  242         if $DEBUG;
  243       next;
  244     }
  245   }
  246 
  247   my $cmd = command_for_drive_device($drive, $fulldev, $use_nocheck);
  248   warn "[DEBUG] Command for $drive is % $cmd %\n" if $DEBUG;
  249 
  250   my $output = `$cmd`;
  251   my $cmd_exit = $?;
  252 
  253   # Strip header
  254   $output =~ s/.*?\n\n//s;
  255   # Strip trailer
  256   $output =~ s/Please specify device type with the -d option.\n//s;
  257   $output =~ s/Use smartctl -h to get a usage summary//s;
  258   $output =~ s/\n+$//s;
  259 
  260   if ($cmd_exit != 0) {
  261     print "$drive.value U\n";
  262     if ($cmd_exit == -1) {
  263       warn "[ERROR] Command $cmd on drive $drive failed to execute: $!";
  264     } else {
  265       my $smartctl_exit = $cmd_exit >> 8;
  266       print "$drive.extinfo Command '$cmd' on drive $drive failed with exit($smartctl_exit)\n";
  267 
  268       # exit (2) is a normal state with directive "--nocheck=standby" when device is in STANDBY or SLEEP mode
  269       if ($smartctl_exit == 2 and $use_nocheck) {
  270         if ($output =~ /(?:standby|sleep)/i) {
  271           next;
  272         }
  273       }
  274       warn "[ERROR] Command $cmd on drive $drive failed with exit($smartctl_exit): $output";
  275     }
  276     next;
  277   }
  278   if ($output =~ /Current Drive Temperature:\s*(\d+)/) {
  279     print "$drive.value $1\n";
  280   } elsif ($output =~ /^(194 Temperature_(Celsius|Internal).*)/m) {
  281     my @F = split /\s+/, $1;
  282     print "$drive.value $F[9]\n";
  283   } elsif ($output =~ /^(231 Temperature_Celsius.*)/m) {
  284     my @F = split ' ', $1;
  285     print "$drive.value $F[9]\n";
  286   } elsif ($output =~ /^(190 (Airflow_Temperature_Cel|Temperature_Case).*)/m) {
  287     my @F = split ' ', $1;
  288     print "$drive.value $F[9]\n";
  289   } elsif ($output =~ /Temperature:\s*(\d+) Celsius/) {
  290      print "$drive.value $1\n";
  291   } else {
  292       print "$drive.value U\n";
  293       print "$drive.extinfo Temperature not detected in smartctl output\n";
  294   }
  295 }
  296 
  297 
  298 sub device_for_drive {
  299     my ($drive) = @_;
  300 
  301     # The purpose of the following regular expression (removing a numeric suffix starting with an
  302     # underscore) is a mystery.  But it is probably meant to detect a partition and select the
  303     # parent block device in such a case.
  304     # In order to avoid misinterpreting a trailing serial number as such a partition number, we
  305     # limit the number of numeric characters after the underscore to just one or two.
  306     my $dev = $drive =~ /(.*)(?:_\d{1,2})$/ ? $1 : $drive;
  307 
  308     my $fulldev = '/dev/';
  309     $fulldev .= 'rdsk/' if $^O eq 'solaris';
  310     $fulldev .= exists $ENV{'dev_'.$drive} ? $ENV{'dev_'.$drive} : $dev;
  311 
  312     return $fulldev;
  313 }
  314 
  315 
  316 sub command_for_drive_device {
  317     my ($drive, $fulldev, $use_nocheck) = @_;
  318 
  319     my $cmd = $smartctl.' -A ';
  320     $cmd .= '--nocheck=standby ' if $use_nocheck;
  321     $cmd .= $ENV{'args_'.$drive}.' ' if exists $ENV{'args_'.$drive};
  322     $cmd .= '-d '.$ENV{'type_'.$drive}.' ' if exists $ENV{'type_'.$drive};
  323     $cmd .= $fulldev;
  324 
  325 }