diff --git a/plugins/dynamix/nchan/device_list b/plugins/dynamix/nchan/device_list
new file mode 100755
index 000000000..fcb056cc2
--- /dev/null
+++ b/plugins/dynamix/nchan/device_list
@@ -0,0 +1,440 @@
+#!/usr/bin/php -q
+
+
+$docroot = '/usr/local/emhttp';
+$varroot = '/var/local/emhttp';
+$smartALL = '/boot/config/smart-all.cfg';
+$smartONE = '/boot/config/smart-one.cfg';
+
+// add translations
+$_SERVER['REQUEST_URI'] = 'main';
+require_once "$docroot/webGui/include/Translations.php";
+require_once "$docroot/webGui/include/Helpers.php";
+require_once "$docroot/webGui/include/publish.php";
+
+function initSum() {
+ return ['count'=>0, 'temp'=>0, 'fsSize'=>0, 'fsUsed'=>0, 'fsFree'=>0, 'ioReads'=>0, 'ioWrites'=>0, 'numReads'=>0, 'numWrites'=>0, 'numErrors'=>0];
+}
+function model($id) {
+ return substr($id,0,strrpos($id,'_'));
+}
+function device_info(&$disk,$online) {
+ global $pools, $var, $crypto;
+ $name = $disk['name'];
+ $fancyname = compress(_(my_disk($name),3),16,5);
+ $type = $disk['type']=='Flash' ? $disk['type'] : 'Device';
+ $action = strpos($disk['color'],'blink')===false ? 'down' : 'up';
+ switch ($disk['color']) {
+ case 'green-on': $orb = 'circle'; $color = 'green'; $help = _('Normal operation, device is active'); break;
+ case 'green-blink': $orb = 'circle'; $color = 'grey'; $help = _('Device is in standby mode (spun-down)'); break;
+ case 'blue-on': $orb = 'square'; $color = 'blue'; $help = _('New device'); break;
+ case 'blue-blink': $orb = 'square'; $color = 'grey'; $help = _('New device, in standby mode (spun-down)'); break;
+ case 'yellow-on': $orb = 'warning'; $color = 'yellow'; $help = $disk['type']=='Parity' ? _('Parity is invalid') : _('Device contents emulated'); break;
+ case 'yellow-blink': $orb = 'warning'; $color = 'grey'; $help = $disk['type']=='Parity' ? _('Parity is invalid, in standby mode (spun-down)') : _('Device contents emulated, in standby mode (spun-down)'); break;
+ case 'red-on': case 'red-blink': $orb = 'times'; $color = 'red'; $help = $disk['type']=='Parity' ? _('Parity device is disabled') : _('Device is disabled, contents emulated'); break;
+ case 'red-off': $orb = 'times'; $color = 'red'; $help = $disk['type']=='Parity' ? _('Parity device is missing') : _('Device is missing (disabled), contents emulated'); break;
+ case 'grey-off': $orb = 'square'; $color = 'grey'; $help = _('Device not present'); break;
+ }
+ $ctrl = '';
+ if ($var['fsState']=='Started' && $type!='Flash' && strpos($disk['status'],'_NP')===false) {
+ $ctrl = " style='cursor:pointer' onclick=\"toggle_state('$type','$name','$action')\"";
+ $help .= "
"._("Click to spin $action device");
+ }
+ $status = "$help";
+ $link = ($disk['type']=='Parity' && strpos($disk['status'],'_NP')===false) ||
+ ($disk['type']=='Data' && $disk['status']!='DISK_NP') ||
+ ($disk['type']=='Cache' && $disk['status']!='DISK_NP') ||
+ ($disk['name']=='flash') || in_array($disk['name'],$pools) ||
+ $disk['type']=='New' ? "$fancyname" : $fancyname;
+ if ($crypto) switch ($disk['luksState']) {
+ case 0:
+ if (!vfs_luks($disk['fsType']))
+ $luks = "";
+ else
+ $luks = ""._('Device to be encrypted')."";
+ break;
+ case 1:
+ if ($online) {
+ $luks = ""._('Device encrypted and unlocked')."";
+ break;
+ }
+ /* fall thru */
+ case 2:
+ $luks = ""._('Device encrypted')."";
+ break;
+ case 3:
+ $luks = ""._('Device locked: wrong encryption key')."";
+ break;
+ default:
+ $luks = ""._('Device locked: unknown error')."";
+ break;
+ } else $luks = '';
+ return $status.$luks.$link;
+}
+function device_browse(&$disk) {
+ $dir = $disk['name']=='flash' ? "/boot" : "/mnt/{$disk['name']}";
+ return "
";
+}
+function device_desc(&$disk) {
+ global $var;
+ $size = my_scale($disk['size'] ? $disk['size']*1024 : $disk['sectors']*$disk['sector_size'],$unit,-1);
+ switch ($disk['type']) {
+ case 'Flash' : $type = 'usb'; break;
+ case 'Parity': $type = $disk['rotational'] ? 'disk' : 'nvme'; break;
+ case 'Data' :
+ case 'Cache' : $type = $disk['rotational'] ? ($disk['luksState'] ? 'disk-encrypted' : 'disk') : 'nvme'; break;
+ default : $type = 'disk'; break;
+ }
+ $log = $var['fsState']=='Started'
+ ? ""._('Disk Log Information').""
+ : "";
+ return $log."".my_id($disk['id'])." - $size $unit ({$disk['device']})";
+}
+function assignment(&$disk) {
+ global $var, $devs;
+ $out = "
";
+}
+function vfs_type($fs) {
+ return str_replace('luks:','',$fs);
+}
+function vfs_luks($fs) {
+ return ($fs != vfs_type($fs));
+}
+function fs_info(&$disk) {
+ global $display;
+ $echo = [];
+ if (empty($disk['fsStatus'])) {
+ return ($disk['type']=='Cache') ? ""._('Device is part of a pool')." | | " : " | ";
+ } elseif ($disk['fsStatus']=='Mounted') {
+ $echo[] = "".vfs_type($disk['fsType'])." | ";
+ $echo[] = "".my_scale(($disk['fsSize']??0)*1024,$unit,-1)." $unit | ";
+ if ($display['text']%10==0) {
+ $echo[] = "".my_scale($disk['fsUsed']*1024,$unit)." $unit | ";
+ } else {
+ $used = isset($disk['fsSize']) && $disk['fsSize']>0 ? 100-round(100*$disk['fsFree']/$disk['fsSize']) : 0;
+ $echo[] = "".my_scale($disk['fsUsed']*1024,$unit)." $unit | ";
+ }
+ if ($display['text']<10 ? $display['text']%10==0 : $display['text']%10!=0) {
+ $echo[] = "".my_scale($disk['fsFree']*1024,$unit)." $unit | ";
+ } else {
+ $free = isset($disk['fsSize']) && $disk['fsSize']>0 ? round(100*$disk['fsFree']/$disk['fsSize']) : 0;
+ $echo[] = "".my_scale($disk['fsFree']*1024,$unit)." $unit | ";
+ }
+ $echo[] = "".device_browse($disk)." | ";
+ } else
+ $echo[] = "".vfs_type($disk['fsType'])." | "._($disk['fsStatus']);
+ return implode('',$echo);
+}
+function my_diskio($data) {
+ return my_scale($data,$unit,1)." $unit/s";
+}
+function array_offline(&$disk,$pool='') {
+ global $var, $disks;
+ $echo = [];
+ if (strpos($var['mdState'],'ERROR:')===false) {
+ $text = ""._('All existing data on this device will be OVERWRITTEN when array is Started')."";
+ if ($disk['type']=='Cache') {
+ if (!empty($disks[$pool]['uuid']) && $disk['status']=='DISK_NEW') $warning = $text;
+ } else {
+ if ($var['mdState']=='NEW_ARRAY') {
+ if ($disk['type']=='Parity') $warning = $text;
+ } else if ($var['mdNumInvalid']<=1) {
+ if (in_array($disk['status'],['DISK_INVALID','DISK_DSBL_NEW','DISK_WRONG','DISK_NEW'])) $warning = $text;
+ }
+ }
+ }
+ $echo[] = " | ";
+ switch ($disk['status']) {
+ case 'DISK_NP':
+ case 'DISK_NP_DSBL':
+ $echo[] = "| ".device_info($disk,false)." | ";
+ $echo[] = "".assignment($disk)." | ";
+ $echo[] = " | ";
+ break;
+ case 'DISK_NP_MISSING':
+ $echo[] = "".device_info($disk,false)." "._('Missing')." | ";
+ $echo[] = "".assignment($disk)."{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024,$unit)." $unit | ";
+ $echo[] = " | ";
+ break;
+ case 'DISK_OK':
+ case 'DISK_DSBL':
+ case 'DISK_INVALID':
+ case 'DISK_DSBL_NEW':
+ case 'DISK_NEW':
+ $echo[] = "".device_info($disk,false)." | ";
+ $echo[] = "".assignment($disk)." | ";
+ $echo[] = "".my_temp($disk['temp'])." | ";
+ $echo[] = "$warning | ";
+ break;
+ case 'DISK_WRONG':
+ $echo[] = "".device_info($disk,false)." "._('Wrong')." | ";
+ $echo[] = "".assignment($disk)."{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024,$unit)." $unit | ";
+ $echo[] = "".my_temp($disk['temp'])." | ";
+ $echo[] = "$warning | ";
+ break;
+ }
+ $echo[] = "
";
+ return implode('',$echo);
+}
+function array_online(&$disk) {
+ global $pools, $sum, $diskio;
+ $echo = [];
+ if ($disk['device']!='') {
+ $dev = $disk['device'];
+ $data = explode(' ',$diskio[$dev] ?? '0 0');
+ $sum['ioReads'] += $data[0];
+ $sum['ioWrites'] += $data[1];
+ }
+ if (is_numeric($disk['temp'])) {
+ $sum['count']++;
+ $sum['temp'] += $disk['temp'];
+ }
+ $sum['numReads'] += $disk['numReads'];
+ $sum['numWrites'] += $disk['numWrites'];
+ $sum['numErrors'] += $disk['numErrors'];
+ if (isset($disk['fsFree'])) {
+ $sum['fsSize'] += $disk['fsSize'];
+ $sum['fsUsed'] += $disk['fsUsed'];
+ $sum['fsFree'] += $disk['fsFree'];
+ }
+ $echo[] = "";
+ switch ($disk['status']) {
+ case 'DISK_NP':
+ if (in_array($disk['name'],$pools)) {
+ $echo[] = "| ".device_info($disk,true)." | ";
+ $echo[] = ""._('Not installed')." | ";
+ $echo[] = " | ";
+ $echo[] = fs_info($disk);
+ }
+ break;
+ case 'DISK_NP_DSBL':
+ $echo[] = "".device_info($disk,true)." | ";
+ $echo[] = ""._('Not installed')." | ";
+ $echo[] = " | ";
+ $echo[] = fs_info($disk);
+ break;
+ case 'DISK_DSBL':
+ default:
+ $echo[] = "".device_info($disk,true)." | ";
+ $echo[] = "".device_desc($disk)." | ";
+ $echo[] = "".my_temp($disk['temp'])." | ";
+ $echo[] = "".my_diskio($data[0])."".my_number($disk['numReads'])." | ";
+ $echo[] = "".my_diskio($data[1])."".my_number($disk['numWrites'])." | ";
+ $echo[] = "".my_number($disk['numErrors'])." | ";
+ $echo[] = fs_info($disk);
+ break;
+ }
+ $echo[] = "
";
+ return implode('',$echo);
+}
+function show_totals($text,$array,$name) {
+ global $var, $display, $sum;
+ $echo = [];
+ $ctrl1 = "onclick=\"toggle_state('Device','$name','down')\"";
+ $ctrl2 = "onclick=\"toggle_state('Device','$name','up')\"";
+ $help1 = _('Spin Down').' '._(ucfirst(substr($name,0,-1)));
+ $help2 = _('Spin Up').' '._(ucfirst(substr($name,0,-1)));
+ $echo[] = "";
+ $echo[] = "| $help1$help2 | ";
+ $echo[] = "$text | ";
+ $echo[] = "".($sum['count']>0 ? my_temp(round($sum['temp']/$sum['count'],1)) : '*')." | ";
+ $echo[] = "".my_diskio($sum['ioReads'])."".my_number($sum['numReads'])." | ";
+ $echo[] = "".my_diskio($sum['ioWrites'])."".my_number($sum['numWrites'])." | ";
+ $echo[] = "".my_number($sum['numErrors'])." | ";
+ $echo[] = " | ";
+ if ($array && ($var['startMode']=='Normal')) {
+ $echo[] = "".my_scale($sum['fsSize']*1024,$unit,-1)." $unit | ";
+ if ($display['text']%10==0) {
+ $echo[] = "".my_scale($sum['fsUsed']*1024,$unit)." $unit | ";
+ } else {
+ $used = $sum['fsSize'] ? 100-round(100*$sum['fsFree']/$sum['fsSize']) : 0;
+ $echo[] = "".my_scale($sum['fsUsed']*1024,$unit)." $unit | ";
+ }
+ if ($display['text']<10 ? $display['text']%10==0 : $display['text']%10!=0) {
+ $echo[] = "".my_scale($sum['fsFree']*1024,$unit)." $unit | ";
+ } else {
+ $free = $sum['fsSize'] ? round(100*$sum['fsFree']/$sum['fsSize']) : 0;
+ $echo[] = "".my_scale($sum['fsFree']*1024,$unit)." $unit | ";
+ }
+ $echo[] = " | ";
+ } else {
+ $echo[] = " | ";
+ }
+ $echo[] = "
";
+ return implode('',$echo);
+}
+function array_slots() {
+ global $var;
+ $min = max($var['sbNumDisks'], 3);
+ $max = $var['MAX_ARRAYSZ'];
+ $out = "";
+ return $out;
+}
+function cache_slots($off,$pool,$min,$slots) {
+ global $var;
+ $off = $off && $min ? ' disabled' : '';
+ $max = $var['MAX_CACHESZ'];
+ $out = "";
+ return $out;
+}
+
+while (true) {
+ $var = (array)parse_ini_file("$varroot/var.ini");
+ $devs = (array)parse_ini_file("$varroot/devs.ini",true);
+ $disks = (array)parse_ini_file("$varroot/disks.ini",true);
+ $sec = (array)parse_ini_file("$varroot/sec.ini",true);
+ $diskio = @(array)parse_ini_file("$varroot/diskload.ini");
+ extract(parse_plugin_cfg('dynamix',true));
+ $crypto = false;
+ $pools = pools_filter($disks);
+ $echo = [];
+
+ // sort unassigned devices on disk identification
+ if (count($devs)>1) array_multisort(array_column($devs,'sectors'),SORT_DESC,array_map('model',array_column($devs,'id')),SORT_NATURAL|SORT_FLAG_CASE,array_column($devs,'device'),$devs);
+
+ // merge device custom settings
+ if (file_exists($smartALL)) $var = array_merge($var, parse_ini_file($smartALL));
+ if (file_exists($smartONE)) {
+ $smarts = parse_ini_file($smartONE,true);
+ foreach ($smarts as $id => $smart) {
+ if (isset($disks)) {
+ foreach ($disks as $key => $disk) {
+ if ($disk['id'] == $id) $disks[$key] = array_merge($disks[$key], $smart);
+ }
+ }
+ if (isset($devs)) {
+ foreach ($devs as $key => $disk) {
+ if ($disk['id'] == $id) $devs[$key] = array_merge($devs[$key], $smart);
+ }
+ }
+ }
+ }
+
+ $sum = initSum();
+ $echo[0] = "array_devices\n";
+ $parity = parity_filter($disks);
+ $data = data_filter($disks);
+ foreach ($data as $disk) $crypto |= $disk['luksState']!=0 || vfs_luks($disk['fsType']);
+ if ($var['fsState']=='Stopped') {
+ foreach ($parity as $disk) $echo[0] .= array_offline($disk);
+ $echo[0] .= " |
";
+ foreach ($data as $disk) $echo[0] .= array_offline($disk);
+ $echo[0] .= "| "._('Slots').": | ".array_slots()." | |
";
+ } else {
+ foreach ($parity as $disk) if ($disk['status']!='DISK_NP_DSBL') $echo[0] .= array_online($disk);
+ foreach ($data as $disk) $echo[0] .= array_online($disk);
+ if ($display['total'] && $var['mdNumDisks']>1) $echo[0] .= show_totals(sprintf(_('Array of %s devices'),my_word($var['mdNumDisks'])),true,'array*');
+ }
+
+ $echo[1] = "boot_device\n";
+ $disk = &$disks['flash'];
+ $data = explode(' ',$diskio[$disk['device']] ?? '0 0');
+ $flash = &$sec['flash']; $share = "";
+ if ($var['shareSMBEnabled']=='yes' && $flash['export']=='e' && $flash['security']=='public')
+ $share = ""._('Flash device is set as public share')."
"._('Please change share SMB security')."
"._('Click on **FLASH** above this message')."";
+ $echo[1] .= "";
+ $echo[1] .= "| ".$share.device_info($disk,true)." | ";
+ $echo[1] .= "".device_desc($disk)." | ";
+ $echo[1] .= "* | ";
+ $echo[1] .= "".my_diskio($data[0])."".my_number($disk['numReads'])." | ";
+ $echo[1] .= "".my_diskio($data[1])."".my_number($disk['numWrites'])." | ";
+ $echo[1] .= "".my_number($disk['numErrors'])." | ";
+ $echo[1] .= fs_info($disk);
+ $echo[1] .= "
";
+
+ $sum = initSum();
+ $cache = cache_filter($disks); $n = 2;
+ foreach ($pools as $pool) {
+ $echo[$n] = "pool_device".($n-2)."\n";
+ $tmp = "/var/tmp/$pool.log.tmp";
+ foreach ($cache as $disk) if (prefix($disk['name'])==$pool) $crypto |= $disk['luksState']!=0 || vfs_luks($disk['fsType']);
+ if ($var['fsState']=='Stopped') {
+ $log = @(array)parse_ini_file($tmp);
+ $off = false;
+ foreach ($cache as $disk) if (prefix($disk['name'])==$pool) {
+ $echo[$n] .= array_offline($disk,$pool);
+ if (isset($log[$disk['name']])) $off |= ($log[$disk['name']]!=$disk['id']); else $log[$disk['name']] = $disk['id'];
+ }
+ $data = []; foreach ($log as $key => $value) $data[] = "$key=\"$value\"";
+ file_put_contents($tmp,implode("\n",$data));
+ $echo[$n] .= "| "._('Slots').": | ".cache_slots($off,$pool,$cache[$pool]['devicesSb'],$cache[$pool]['slots'])." | |
";
+ } else {
+ if ($cache[$pool]['devices']) {
+ foreach ($cache as $disk) if (prefix($disk['name'])==$pool) {
+ if (substr($cache[$pool]['fsStatus'],0,11)=='Unmountable' && empty($disk['fsStatus'])) $disk['fsStatus'] = $cache[$pool]['fsStatus'];
+ $echo[$n] .= array_online($disk);
+ }
+ if ($display['total'] && $cache[$pool]['devices']>1) $echo[$n] .= show_totals(sprintf(_('Pool of %s devices'),my_word($cache[$pool]['devices'])),false,"$pool*");
+ $sum = initSum();
+ }
+ @unlink($tmp);
+ }
+ $n++;
+ }
+
+ $echo[$n] = "open_devices\n";
+ foreach ($devs as $disk) {
+ $dev = $disk['device'];
+ $data = explode(' ',$diskio[$dev] ?? '0 0 0 0');
+ $disk['type'] = 'New';
+ $disk['color'] = $disk['spundown']=="0" ? 'blue-on' : 'blue-blink';
+ $echo[$n] .= "";
+ $echo[$n] .= "| ".device_info($disk,true)." | ";
+ $echo[$n] .= "".device_desc($disk)." | ";
+ $echo[$n] .= "".my_temp($disk['temp'])." | ";
+ $echo[$n] .= "".my_diskio($data[0])."".my_number($disk['numReads'])." | ";
+ $echo[$n] .= "".my_diskio($data[1])."".my_number($disk['numWrites'])." | ";
+ $echo[$n] .= "".my_number($disk['numErrors'])." | ";
+ if (file_exists("/tmp/preclear_stat_$dev")) {
+ $text = exec("cut -d'|' -f3 /tmp/preclear_stat_$dev|sed 's:\^n:\
:g'");
+ if (strpos($text,'Total time')===false) $text = _('Preclear in progress').'... '.$text;
+ $echo[$n] .= "$text | ";
+ } else
+ $echo[$n] .= " | ";
+ $echo[$n] .= "
";
+ }
+
+ publish('devices', implode("\0",$echo));
+ sleep(1);
+}
+?>