Subversion Repositories vnag

Rev

Rev 77 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * VNag - Nagios Framework for PHP
  5.  * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com>
  6.  * Licensed under the terms of the Apache 2.0 license
  7.  *
  8.  * Revision 2023-10-13
  9.  */
  10.  
  11. declare(ticks=1);
  12.  
  13. class SmartCheck extends VNag {
  14.         protected $argType = null;
  15.  
  16.         public function __construct() {
  17.                 parent::__construct();
  18.  
  19.                 if ($this->is_http_mode()) {
  20.                         // Don't allow the standard arguments via $_REQUEST
  21.                         $this->registerExpectedStandardArguments('');
  22.                 } else {
  23.                         $this->registerExpectedStandardArguments('Vhtv');
  24.                 }
  25.  
  26.                 $this->getHelpManager()->setPluginName('vnag_smart');
  27.                 $this->getHelpManager()->setVersion('2023-10-13');
  28.                 $this->getHelpManager()->setShortDescription('This plugin checks the contents of the SMART data and warns when a harddisk has failed.');
  29.                 $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.');
  30.                 $this->getHelpManager()->setSyntax('$SCRIPTNAME$ [-T <type>]');
  31.                 $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com');
  32.  
  33.                 // TODO: also add a command to check a single drive   [ -d /dev/sda,/dev/sdb ]
  34.                 // Individual (non-standard) arguments:
  35.                 $this->addExpectedArgument($this->argType = new VNagArgument('T', 'type', VNagArgument::VALUE_REQUIRED, 'type', 'Explicit drive type e.g. for RAID devices "sat+cciss,0" for drive 0.'));
  36.         }
  37.  
  38.         private function check_smart($dev) {
  39.                 if (!`which which`) {
  40.                         throw new VNagException("Program 'which' is not installed on your system");
  41.                 }
  42.  
  43.                 if (!`which smartctl`) {
  44.                         throw new VNagException("Program 'smartctl' (usually included in package smartmontools) is not installed on your system");
  45.                 }
  46.  
  47.                 $code = 0;
  48.                 $out = array();
  49.  
  50.                 if ($this->argType->getValue() != '') {
  51.                         // Note: Requires root
  52.                         exec('smartctl --all '.escapeshellarg($dev).' -d '.escapeshellarg($this->argType->getValue()), $out, $code);
  53.                 } else {
  54.                         // Note: Requires root
  55.                         exec('smartctl --all '.escapeshellarg($dev), $out, $code);
  56.                 }
  57.                 $cont = implode("\n", $out);
  58.  
  59.                 $msg = array();
  60.                 $status = -1;
  61.  
  62.                 if (stripos($cont, 'device lacks SMART capability') !== false)  {
  63.                         // At my system (Debian 9), I get exit code 4 (which is not fully accurate)
  64.                         $msg[] = 'Device lacks SMART capability';
  65.                         #$status = VNag::STATUS_UNKNOWN;
  66.                 } else if ($code == 0) {
  67.                         $status = VNag::STATUS_OK;
  68.                 } else {
  69.                         if ($code & 1) {
  70.                                 throw new VNagException("smartctl reports 'command line did not parse' (code $code).");
  71.                         }
  72.                         if ($code & 2) {
  73.                                 $msg[] = "Device open failed. It is either completely defective, or in low-power mode.";
  74.                                 $status = max($status, VNag::STATUS_CRITICAL);
  75.                         }
  76.                         if ($code & 4) {
  77.                                 $msg[] = "SMART command failed or checksum is wrong.";
  78.                                 $status = max($status, VNag::STATUS_WARNING);
  79.                         }
  80.                         if ($code & 8) {
  81.                                 $msg[] = "SMART status returns 'DISK FAILING'";
  82.                                 $status = max($status, VNag::STATUS_CRITICAL);
  83.                         }
  84.                         if ($code & 16) {
  85.                                 $msg[] = "SMART found prefail attributes below threshold";
  86.                                 $status = max($status, VNag::STATUS_WARNING);
  87.                         }
  88.                         if ($code & 32) {
  89.                                 $msg[] = "SMART status is 'OK' but usage/prefail attributes have been below threshold in the past.";
  90.                                 $status = max($status, VNag::STATUS_WARNING);
  91.                         }
  92.                         if ($code & 64) {
  93.                                 $msg[] = "The device error log contains records of errors.";
  94.                                 $status = max($status, VNag::STATUS_WARNING);
  95.                         }
  96.                         if ($code & 128) {
  97.                                 $msg[] = "The self-test logs contains records of errors.";
  98.                                 $status = max($status, VNag::STATUS_WARNING);
  99.                         }
  100.                 }
  101.  
  102.                 $messages = implode(", ", $msg);
  103.                 if ($messages != '') $messages = ": $messages";
  104.  
  105.                 if ($status == VNag::STATUS_CRITICAL) {
  106.                         $this->addVerboseMessage("$dev (Critical)$messages", VNag::VERBOSITY_SUMMARY);
  107.                 } else if ($status == VNag::STATUS_WARNING) {
  108.                         $this->addVerboseMessage("$dev (Warning)$messages", VNag::VERBOSITY_SUMMARY);
  109.                 } else if ($status == VNag::STATUS_OK) {
  110.                         $this->addVerboseMessage("$dev (OK)$messages", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
  111.                 } else {
  112.                         $status = VNag::STATUS_UNKNOWN;
  113.                         $this->addVerboseMessage("$dev (Unknown)$messages", VNag::VERBOSITY_SUMMARY);
  114.                 }
  115.                 $this->setStatus($status);
  116.                 return $status;
  117.         }
  118.  
  119.         protected function cbRun() {
  120.                 $devices = array();
  121.                 $devices = array_merge($devices, glob('/dev/sd?'));
  122.                 $devices = array_merge($devices, glob('/dev/hd?'));
  123.  
  124.                 if (count($devices) == 0) {
  125.                         throw new VNagException("No SDx or HDx drives found");
  126.                 }
  127.  
  128.                 if (strpos($this->argType->getValue(),'cciss') !== false) {
  129.                         $devices = array($devices[0]); // we just need a "fake" drive; the drive number is given as parameter to cciss
  130.                 }
  131.  
  132.                 $count_total = 0;
  133.                 $count_ok = 0;
  134.                 $count_warning = 0;
  135.                 $count_critical = 0;
  136.                 $count_unknown = 0;
  137.                 foreach ($devices as $dev) {
  138.                         $count_total++;
  139.                         switch ($this->check_smart($dev)) {
  140.                                 case VNag::STATUS_OK:
  141.                                         $count_ok++;
  142.                                         break;
  143.                                 case VNag::STATUS_WARNING:
  144.                                         $count_warning++;
  145.                                         break;
  146.                                 case VNag::STATUS_CRITICAL:
  147.                                         $count_critical++;
  148.                                         break;
  149.                                 case VNag::STATUS_UNKNOWN:
  150.                                         $count_unknown++;
  151.                                         break;
  152.                         }
  153.                 }
  154.  
  155.                 $this->setHeadline(sprintf('Checked %d drives (%d OK, %d warning, %d critical, %d unknown)', $count_total, $count_ok, $count_warning, $count_critical, $count_unknown));
  156.         }
  157. }
  158.