From 98ee8b8de41a96e443051d89978225cc1c024f7f Mon Sep 17 00:00:00 2001 From: mike Date: Thu, 2 Jan 2020 22:14:03 -0800 Subject: [PATCH] Import script from MikePeraltaScripts, I guess --- smart-disk-status.php | 293 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 smart-disk-status.php diff --git a/smart-disk-status.php b/smart-disk-status.php new file mode 100644 index 0000000..823a849 --- /dev/null +++ b/smart-disk-status.php @@ -0,0 +1,293 @@ + +$device){ + + // + $match=true; + + // + print "\n".Str_Repeat('*',30); + print "\n*** Entry: {$name}"; + print "\nPath={$device['path']}; Type={$device['device']}"; + + // + $device_safe=EscapeShellArg($device['device']); + $path_safe=EscapeShellArg($device['path']); + + // If the path doesn't exist, possibly fail + Exec("[ -e {$path_safe} ]", $output_lines, $return_code); + if($return_code!=0){ + print "\nPath does not exist!"; + if($device['bRequired']){ + DoError("Path doesn't exist but was required: {$device['path']}"); + } + print "\n"; + continue; + } + + // Is this even a block device? + Exec("[ -b {$path_safe} ]", $output_lines, $return_code); + if($return_code!=0){ + DoError("Path doesn't seem to point to a block device?"); + continue; + } + + // Ask smartctl for status + $command="smartctl --all --device {$device_safe} {$path_safe}"; + $output_lines=false; + Exec($command,$output_lines,$smart_return_code); + $lines=Implode("\n",$output_lines); + + // Output any messages indicated by smartctl + $messages=CheckNamedBits($smart_return_code); + foreach($messages as $message){ + print "\nMESSAGE: {$message}"; + } + + // Test Status: + if(Preg_Match('@^Self-test execution status.+?(?P[0-9]+\% of test remaining)\.@smi',$lines,$match)){ + print "\n{$match['remainder']}"; + } + else if(Preg_Match('@^Self-test execution status.+?(completed\s+without\s+error)@smi',$lines,$match)){ + + // + print "\nNo test seems to be running"; + + // Start short test? + if($bStartTests_short){ + print "; Will start short test now!\n"; + Exec("smartctl --test=short --device {$device_safe} {$path_safe}"); + continue; + } + + // Start long test? + else if($bStartTests_long){ + print "; Will start long test now!\n"; + Exec("smartctl --test=long --device {$device_safe} {$path_safe}"); + continue; + } + } + else{ + DoError("Could not locate execution status information"); + } + + // Check for terrible states + if(CheckBitPosition($smart_return_code,3)){ + $error_message=Str_Repeat('!',10)." ".Str_Repeat('*',10)." CATASTROPHY WARNING!!! DISK IS FAILING ".Str_Repeat('*',10)." ".Str_Repeat('!',10); + DoError($error_message); + } + if(CheckBitPosition($smart_return_code,4)){ + $error_message=Str_Repeat('!',10)." ".Str_Repeat('*',10)." DISK IS IN PREFAIL STATE ".Str_Repeat('*',10)." ".Str_Repeat('!',10); + DoError($error_message); + } + + // + print "\n"; +} + +// +function LoadConfig($config_path) +{ + // + $config=Array( + 'global'=>Array(), + 'devices'=>Array() + ); + + // Load config + $ini=Parse_Ini_File($config_path,true); + if(!$ini){ + DoError("Unable to parse config file: {$config_path}"); + } + + // Booleans + foreach($ini as $section_name=>&$section){ + foreach($section as $key=>&$value){ + + // + $v=StrToLower($value); + switch($v) + { + // + case 'true': + case '1': + case 'on': + case 'yes': + $value=true; + break; + + // + case 'false': + case '0': + case 'off': + case 'no': + $value=false; + break; + } + + }Unset($value); + }Unset($section); + + // Pull out global config + if(IsSet($ini['global'])){ + $config['global']=$ini['global']; + Unset($ini['global']); + } + + // The remainder are devices + $config['devices']=$ini; + + return $config; +} + +// +function CheckNamedBits($return_code) +{ + // + $messages=Array(); + + // + $map=Array( + '0'=>"Command line did not parse.", + '1'=>"Device open failed, device did not return an IDENTIFY DEVICE structure, or device is in a low-power mode", + '2'=>"Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure", + '3'=>"SMART status check returned \"DISK FAILING\".", + '4'=>"We found prefail Attributes <= threshold.", + '5'=>"SMART status check returned \"DISK OK\" but we found that some (usage or prefail) Attributes have been <= threshold at some time in the past.", + '6'=>"The device error log contains records of errors.", + '7'=>"The device self-test log contains records of errors. [ATA only] Failed self-tests outdated by a newer successful extended self-test are ignored." + ); + + // + foreach($map as $bitIndex=>$message){ + if(CheckBitPosition($return_code,$bitIndex)){ + $messages[]=$message; + } + } + + return $messages; +} + +// +function CheckBitPosition($toCheck,$index) +{ + // + $value=Pow(2,$index); + + // + $bIsSet=false; + if($toCheck == ($toCheck|$value)){ + $bIsSet=true; + } + + // + //print "\n toCheck=$toCheck, index=$index, value=$value, IsSet=".($bIsSet?'true':'false'); + + return $bIsSet; +} + +// +function DoError($message) +{ + //print "\nError: {$message}"; + LogError($message); + MailError($message); +} + +// +function LogError($message) +{ + // + $prefix="[".(BaseName(__FILE__))."] "; + + // + $stderr = fOpen('php://stderr', 'w'); + fWrite($stderr,"\n{$message}"); + fClose($stderr); + + // + $message_safe=EscapeShellArg("{$prefix}{$message}"); + Shell_Exec("logger {$message_safe}"); +} + +// +function MailError($message) +{ + // + global $config; + + // + $hostname=Shell_Exec("hostname"); + + // + if(!IsSet($config['global']['mailtos'])){ + return false; + } + $mailtos=Explode(",",$config['global']['mailtos']); + + // + foreach($mailtos as $mailto){ + + // + $mailto=Trim($mailto); + + // + if(Filter_Var($mailto,FILTER_VALIDATE_EMAIL)===FALSE){ + LogError("This doesn't seem to be a valid email address: {$mailto}"); + continue; + } + if(!mail( + $mailto, + "{$hostname} - smartctl error", + $message + )) + { + LogError("PHP failed to send email to {$mailto}, for some reason"); + } + } +} + +?> + +Done +