$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/Preselect.php";
// add translations
$_SERVER['REQUEST_URI'] = 'main';
require_once "$docroot/webGui/include/Translations.php";
$disks = array_merge_recursive(@parse_ini_file('state/disks.ini',true)?:[], @parse_ini_file('state/devs.ini',true)?:[]);
require_once "$docroot/webGui/include/CustomMerge.php";
function normalize($text, $glue='_') {
$words = explode($glue,$text);
foreach ($words as &$word) $word = $word==strtoupper($word) ? $word : preg_replace(['/^(ct|cnt)$/','/^blk$/'],['count','block'],strtolower($word));
return "
".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 '| '._('Feature').' | '._('Value').' | '._('Information').' | ' ;
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 "