".ucfirst(implode(' ',$words)).""; } function size($val) { return str_replace(',','',$val); } function duration(&$hrs) { $time = ceil(time()/3600)*3600; $run = size($hrs); $now = new DateTime("@$time"); $poh = new DateTime("@".($time-$run*3600)); $age = date_diff($poh,$now); $hrs = "$hrs (".($age->y?"{$age->y}y, ":"").($age->m?"{$age->m}m, ":"").($age->d?"{$age->d}d, ":"")."{$age->h}h)"; } function blocks_size(&$blks,$blk_size) { $blks = "$blks (".my_scale($blks*$blk_size,$unit)." $unit)"; } function append(&$ref, &$info) { if ($info) $ref .= ($ref ? " " : "").$info; } $name = $_POST['name']??''; $port = $_POST['port']??''; if ($name) { $disk = &$disks[$name]; $type = get_value($disk,'smType',''); get_ctlr_options($type, $disk); } else { $disk = []; $type = ''; } $port = port_name($disk['smDevice'] ?? $port); switch ($_POST['cmd']??'') { case "attributes": $select = get_value($disk,'smSelect',0); $level = get_value($disk,'smLevel',1); $events = explode('|',get_value($disk,'smEvents',$numbers)); extract(parse_plugin_cfg('dynamix',true)); [$hotNVME,$maxNVME] = _var($disk,'transport')=='nvme' ? get_nvme_info(_var($disk,'device'),'temp') : [-1,-1]; $hot = _var($disk,'hotTemp',-1)>=0 ? $disk['hotTemp'] : ($hotNVME>=0 ? $hotNVME : (_var($disk,'rotational',1)==0 && $display['hotssd']>=0 ? $display['hotssd'] : $display['hot'])); $max = _var($disk,'maxTemp',-1)>=0 ? $disk['maxTemp'] : ($maxNVME>=0 ? $maxNVME : (_var($disk,'rotational',1)==0 && $display['maxssd']>=0 ? $display['maxssd'] : $display['max'])); $top = $_POST['top'] ?? 120; $ssd_remaining = NULL; $empty = true; exec("smartctl -n standby -A $type ".escapeshellarg("/dev/$port"),$output); // remove empty rows $output = array_filter($output); $start = 0; // find start of attributes list (if existing) foreach ($output as $row) if (stripos($row,'smart attributes data structure')===false) $start++; else break; if ($start < count($output)-3) { // remove header part $output = array_slice($output, $start+3); foreach ($output as $row) { $info = explode(' ', trim(preg_replace('/\s+/',' ',$row)), 10); if (count($info)<10) continue; $color = ""; $highlight = strpos($info[8],'FAILING_NOW')!==false || ($select ? $info[5]>0 && $info[3]<=$info[5]*$level : $info[9]>0); if (in_array($info[0], $events) && $highlight) $color = " class='warn'"; elseif (in_array($info[0], [190,194])) { if (exceed($info[9],$max,$top)) $color = " class='alert'"; elseif (exceed($info[9],$hot,$top)) $color = " class='warn'"; } if ($info[8]=='-') $info[8] = 'Never'; if ($info[0]==9 && is_numeric(size($info[9]))) duration($info[9]); if (str_starts_with($info[1], 'Total_LBAs_')) blocks_size($info[9],512); // Assumes 512 byte sectors if (str_ends_with($info[1], '_32MiB')) blocks_size($info[9],32*1024*1024); echo "".implode('',array_map('normalize', $info)).""; $empty = false; } } else { // probably a NVMe or SAS device that smartmontools doesn't know how to parse in to a SMART Attributes Data Structure foreach ($output as $row) { if (strpos($row,':')===false) continue; [$name,$value] = array_map('trim',explode(':', $row)); $name = ucfirst(strtolower($name)); $color = ''; switch ($name) { case 'Temperature': $temp = strtok($value,' '); if (exceed($temp,$max)) $color = " class='alert'"; elseif (exceed($temp,$hot)) $color = " class='warn'"; break; case 'Power on hours': if (is_numeric(size($value))) duration($value); break; case 'Percentage used': $ssd_remaining = 100 - str_replace('%', '', $value); break; } if (str_ends_with($name, ', hours') && str_starts_with($value, 'minutes ')) { $name = substr($name, 0, -7); $value = substr($value, 8); if (is_numeric(size($value))) duration($value); } echo "-$name$value"; $empty = false; } } if (is_null($ssd_remaining)) { // Try to look up SSD's 'Percentage Used Endurance Indicator' with special command exec("smartctl -n standby -l ssd $type ".escapeshellarg("/dev/$port"), $ssd_out); $ssd_out = array_filter($ssd_out); foreach ($ssd_out as $row) { if (str_ends_with($row, 'Percentage Used Endurance Indicator')) { // Probably a SATA SSD $info = explode(' ', trim(preg_replace('/\s+/',' ',$row)), 6); $ssd_remaining = 100 - $info[3]; } elseif (str_starts_with($row, 'Percentage used endurance indicator:')) { // Probably a SAS SSD [$name,$value] = array_map('trim',explode(':', $row)); $ssd_remaining = 100 - str_replace('%','',$value); } } } if (!is_null($ssd_remaining)) { echo "-SSD endurance remaining$ssd_remaining %"; } if ($empty) echo ""._('Attributes not available').""; break; case "capabilities": echo '
' ; exec("smartctl -n standby -c $type ".escapeshellarg("/dev/$port")."|awk 'NR>5'",$output); $row = ['','','']; $empty = true; $nvme = substr($port,0,4)=="nvme"; $nvme_section="info" ; foreach ($output as $line) { if (!$line) {echo "" ;continue;} $line = preg_replace('/^_/','__',preg_replace(['/__+/','/_ +_/'],'_',str_replace([chr(9),')','('],'_',$line))); $info = array_map('trim', explode('_', preg_replace('/_( +)_ /','__',$line), 3)); if ($nvme && $info[0]=="Supported Power States" ) { $nvme_section="psheading" ;echo "
'._('Feature').''._('Value').''._('Information').'
{$line}
"; $row = ['','',''] ; continue ;} if ($nvme && $info[0]=="Supported LBA Sizes" ) { echo "
{$info[0]} {$info[1]} {$info[2]}
"; $row = ['','','']; $nvme_section="lbaheading" ; continue ; } append($row[0],$info[0]); append($row[1],$info[1]); append($row[2],$info[2]); if (substr($row[2],-1)=='.' || ($nvme && $nvme_section=="info")) { echo "{$row[0]}{$row[1]}{$row[2]}"; $row = ['','','']; $empty = false; } if ($nvme && $nvme_section == "psheading") { echo '' ; $nvme_section = "psdetail"; preg_match('/^(?P.\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)$/',$line, $psheadings); for ($i = 1; $i <= 11; $i++) { echo "" ; } $row = ['','','']; echo '' ; } if ($nvme && $nvme_section == "psdetail") { $nvme_section = "psdetail"; echo '' ; preg_match('/^(?P.\S+)\s+(?P\S\s+)\s+(?P\S+)\s+(?P\S\s+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)$/',$line, $psdetails); for ($i = 1; $i <= 11; $i++) { echo "" ; } $row = ['','','']; echo '' ; } if ($nvme && $nvme_section == "lbaheading") { echo '
"._var($psheadings,'data'.$i)."
"._var($psdetails,'data'.$i)."
' ; $nvme_section = "lbadetail"; preg_match('/^(?P.\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)$/',$line, $lbaheadings); for ($i = 1; $i <= 5; $i++) { echo "" ; } $row = ['','','']; echo '' ; } if ($nvme && $nvme_section == "lbadetail") { $nvme_section = "lbadetail"; preg_match('/^(?P.\S+)\s+(?P\S\s+)\s+(?P\S+)\s+(?P\S\s+)\s+(?P\S+)$/',$line, $lbadetails); echo '' ; for ($i = 1; $i <= 5; $i++) { echo "" ; } $row = ['','','']; echo '' ; } } if ($empty) echo ""; echo "
"._var($lbaheadings,'data'.$i)."
"._var($lbadetails,'data'.$i)."
"._('Capabilities not available')."
" ; break; case "identify": $passed = ['PASSED','OK']; $failed = ['FAILED','NOK']; if ($disk["transport"] == "scsi") $standby = " -n standby " ; else $standby = "" ; exec("smartctl -i $type $standby ".escapeshellarg("/dev/$port")."|awk 'NR>4'",$output); exec("smartctl -n standby -H $type ".escapeshellarg("/dev/$port")."|grep -Pom1 '^SMART.*: [A-Z]+'|sed 's:self-assessment test result::'",$output); $empty = true; foreach ($output as $line) { if (!$line) continue; if (strpos($line,'VALID ARGUMENTS')!==false) break; [$title,$info] = array_map('trim', my_explode(':',$line)); if (in_array($info,$passed)) $info = ""._('Passed').""; if (in_array($info,$failed)) $info = ""._('Failed').""; echo "".normalize(preg_replace('/ is:$/',':',"$title:"),' ')."$info"; $empty = false; } if ($empty) { $spundown = $disk['spundown'] ? "(device spundown, spinup to get information)" : "" ; echo ""._('Identification not available'.$spundown).""; } else { $file = '/boot/config/disk.log'; $extra = file_exists($file) ? parse_ini_file($file,true) : []; $disk = $disks[$name]['id']; $info = &$extra[$disk]; $periods = ['6','12','18','24','36','48','60']; echo ""._('Manufacturing date').":"; echo ""._('Date of purchase').":"; echo ""._('Warranty period').":"; } break; case "save": exec("smartctl -x $type ".escapeshellarg("/dev/$port")." >".escapeshellarg("$docroot/{$_POST['file']}")); break; case "delete": if (strpos(realpath("/var/tmp/{$_POST['file']}"), "/var/tmp/") === 0) { @unlink("/var/tmp/{$_POST['file']}"); } break; case "short": exec("smartctl -t short $type ".escapeshellarg("/dev/$port")); break; case "long": exec("smartctl -t long $type ".escapeshellarg("/dev/$port")); break; case "stop": exec("smartctl -X $type ".escapeshellarg("/dev/$port")); break; case "update": $transport = _var($disk,'transport'); if ($transport == 'scsi' || $transport == 'nvme') { $progress = exec("smartctl -n standby -l selftest $type ".escapeshellarg("/dev/$port")."|grep -Pom1 '\d+%'"); if ($progress) { if ($transport == 'nvme') echo " "._('self-test in progress').", ".(substr($progress,0,-1))."% "._('complete').""; else echo " "._('self-test in progress').", ".(100-substr($progress,0,-1))."% "._('complete').""; break; } } else { $progress = exec("smartctl -n standby -c $type ".escapeshellarg("/dev/$port")."|grep -Pom1 '\d+%'"); if ($progress) { echo " "._('self-test in progress').", ".(100-substr($progress,0,-1))."% "._('complete').""; break; } } if ($transport == 'scsi') $result = trim(exec("smartctl -n standby -l selftest $type ".escapeshellarg("/dev/$port")."|grep -m1 '^# 1'|cut -c24-50")); else if ($transport == 'nvme') $result = trim(exec("smartctl -n standby -l selftest $type ".escapeshellarg("/dev/$port")."|grep -m1 '^ 0'|cut -c24-50")); else $result = trim(exec("smartctl -n standby -l selftest $type ".escapeshellarg("/dev/$port")."|grep -m1 '^# 1'|cut -c26-55")); if (!$result) { $spundown = $disk['spundown'] ? "Device spundown, spinup to get information" : "No self-tests logged on this disk" ; echo ""._($spundown).""; break; } if (strpos($result, "Completed, segment failed")!==false) { echo ""._($result).""; break; } if (strpos($result, "Completed without error")!==false || strpos($result, "Completed")!==false ) { echo ""._($result).""; break; } if (strpos($result, "Aborted")!==false or strpos($result, "Interrupted")!==false) { echo ""._($result).""; break; } if (strpos($result, "Failed")!==false) { echo ""._($result).""; break; } echo ""._('Errors occurred - Check SMART report').""; break; case "selftest": echo shell_exec("smartctl -n standby -l selftest $type ".escapeshellarg("/dev/$port")."|awk 'NR>5'"); break; case "errorlog": echo shell_exec("smartctl -n standby -l error $type ".escapeshellarg("/dev/$port")."|awk 'NR>5'"); break; } ?>