diff --git a/plugins/dynamix/ArrayOperation.page b/plugins/dynamix/ArrayOperation.page index 8cd9f4212..b85a491b4 100644 --- a/plugins/dynamix/ArrayOperation.page +++ b/plugins/dynamix/ArrayOperation.page @@ -15,7 +15,16 @@ Tag="snowflake-o" */ ?> "; @@ -41,6 +50,7 @@ td.gap{padding-left:26px!important}
@@ -305,11 +343,14 @@ toggle_diskio(true); Start will bring the array on-line. - Encryption status: + if (($keyfile && $wrong) || $missing || $encrypt || (!$keyfile && $crypto)):?> + Encryption status: Encryption input: - Passphrase:show passphrase + Passphrase:show passphrase + Retype passphrase: Keyfile: + permit reformat +
+ +
+ + + + +
Delete encryption keyfile
+
+
@@ -509,4 +559,36 @@ if (isset($display['sleep'])) @include $display['sleep']; > > Once you have assigned all of your hard drives, refer to the Array Status section below > and Start the array. + + +
+> #### Encryption input +> +> This keyfile is read during array Start and is used to encrypt/decrypt content of encrypted devices. +> +> With array Stopped, the keyfile may be deleted and the user can specify a new encryption key. Note that once a device +> is formatted with a particular key it may only be opened using that same key. Changing the encryption key requires +> encrypted devices to be reformatted **resulting in permanent loss of all existing data on those devices.** +> +> With array Started, the keyfile may be deleted to ensure there is no encryption key present on the server when +> the array is online. Note that plugins are installed and may execute before and during the array Start process. +> +> #### Passphrase +> +> Enter a passphrase of up to 512 characters. It is highly advisable to only use the 95 printable characters from the +> first 128 characters of the [ASCII table](https://en.wikipedia.org/wiki/ASCII), as they will always have the same binary +> representation. Other characters may have different encoding depending on system configuration and your passphrase will +> not work with a different encoding. If you want a longer passphrase or to include binary data, upload a keyfile instead. +> +> Please refer to the [cryptsetup FAQ](https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions#5-security-aspects) +> for what constitutes a *secure* passphrase. +> +> **Memorize** this passphrase. **IF LOST, ENCRYPTED CONTENT CANNOT BE RECOVERED!** +> +> #### Keyfile +> +> Select a local keyfile with a stored encryption key or a binary file. The maximum size of the keyfile is 8M (8388608 byte). +> +> **Backup** your local keyfile. **IF LOST, ENCRYPTED CONTENT CANNOT BE RECOVERED!** + diff --git a/plugins/dynamix/CacheDevices.page b/plugins/dynamix/CacheDevices.page index 689b1c472..40a4f9b92 100644 --- a/plugins/dynamix/CacheDevices.page +++ b/plugins/dynamix/CacheDevices.page @@ -1,6 +1,6 @@ Menu="Main:2" Title="Cache Devices" -Tag="upload" +Tag="bullseye" Cond="($var['fsState']=='Stopped' || $var['cacheSbNumDisks'])" --- + @@ -104,7 +151,7 @@ Spinup group(s): Spin down delay: -: @@ -143,14 +190,14 @@ File system status: File system type: -: > - - - + + + > Enter the desired file system type. Changing the file system type of a device will permit you to reformat @@ -160,10 +207,10 @@ File system type: 1):?> File system type: -: > - + @@ -469,7 +516,7 @@ xfs_repair status: SMART notification value: -: @@ -480,7 +527,7 @@ SMART notification value: > Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings). SMART notification tolerance level: -: @@ -496,22 +543,24 @@ SMART notification tolerance level: > Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings). SMART controller type: -: - + + + - - - - + + + /dev/ + enter disk index and device name as applicable to your controller > By default automatic controller selection is done by smartctl to read the SMART information. Certain controllers however need specific settings for smartctl to work. > Use this setting to select your controller type and fill-in the specific disk index and device name for your situation. Use the manufacturer's documentation to find the relevant information. @@ -519,12 +568,12 @@ SMART controller type: > Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings). SMART attribute notifications: -: Custom attributes (use comma to separate numbers) +: Custom attributes (use comma to separate numbers) - +   -: > - +: >Attribute = + > The user can enable or disable notifications for the given SMART attributes. It is recommended to keep the default, which is ALL selected attributes, > when certain attributes are not present on your hard disk or do not provide the correct information, these may be excluded. diff --git a/plugins/dynamix/DiskSettings.page b/plugins/dynamix/DiskSettings.page index 827bf5db2..79a721246 100644 --- a/plugins/dynamix/DiskSettings.page +++ b/plugins/dynamix/DiskSettings.page @@ -24,6 +24,13 @@ function displayTemp($temp) { return $display['unit']=='F' ? round(9/5*$temp)+32 : $temp; } ?> + - - - - - - - - -Encryption key: -: - -> Shows the path and name of the current keyfile, if present. -> -> This keyfile is read during array Start and is used to encrypt/decrypt content of encrypted devices. -> -> With array Stopped, the keyfile may be deleted and the user can specify a new encryption key. Note that once a device -> is formatted with a particular key it may only be opened using that same key. Changing the encryption key requires -> encrypted devices to be reformatted **resulting in permanent loss of all existing data on those devices.** -> -> With array Started, the keyfile may be deleted to ensure there is no encryption key present on the server when -> the array is online. Note that plugins are installed and may execute before and during the array Start process. - -Delete -: - - -Encryption key: -: Not present - -Encryption input: -: - -> Select manual input or file input of the encryption key. Note that the encryption key needs to be re-entered each time the server is rebooted. -> -> The array will **not** Start automatically when encrypted volumes are present. - -
-Passphrase: -: onkeyup="toggleApply(this.form)">show passphrase - -> Enter a passphrase of up to 512 characters. It is highly advisable to only use the 95 printable characters from the -> first 128 characters of the [ASCII table](https://en.wikipedia.org/wiki/ASCII), as they will always have the same binary -> representation. Other characters may have different encoding depending on system configuration and your passphrase will -> not work with a different encoding. If you want a longer passphrase or to include binary data, upload a keyfile instead. -> -> Please refer to the [cryptsetup FAQ](https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions#5-security-aspects) -> for what constitutes a *secure* passphrase. -> -> **Memorize** this passphrase. **IF LOST, ENCRYPTED CONTENT CANNOT BE RECOVERED!** - - -
- - -  -: permit reformat - -> To permit re-Format of encrypted devices, check this box and retype the passphrase. - - - -: Array must be Stopped to change - - diff --git a/plugins/dynamix/icons/encryptionsettings.png b/plugins/dynamix/icons/encryptionsettings.png deleted file mode 100644 index 201b3b2d4..000000000 Binary files a/plugins/dynamix/icons/encryptionsettings.png and /dev/null differ diff --git a/plugins/dynamix/images/encryption-settings.png b/plugins/dynamix/images/encryption-settings.png deleted file mode 100644 index 6c3882f83..000000000 Binary files a/plugins/dynamix/images/encryption-settings.png and /dev/null differ diff --git a/plugins/dynamix/include/Browse.php b/plugins/dynamix/include/Browse.php index 7dedf7de4..d2cc9d50a 100644 --- a/plugins/dynamix/include/Browse.php +++ b/plugins/dynamix/include/Browse.php @@ -54,11 +54,11 @@ foreach ($file as $row) { foreach ($rows as $row) $show |= strpos($disks[$tag.str_replace($tag,'',$row)]['fsType'],'luks:')!==false; if ($show) foreach ($rows as $row) { switch ($disks[$tag.str_replace($tag,'',$row)]['luksState']) { - case 0: $luks .= ""; break; - case 1: $luks .= ""; break; - case 2: $luks .= ""; break; - case 3: $luks .= ""; break; - default: $luks .= ""; break;} + case 0: $luks = "Not encrypted"; break; + case 1: $luks = "Encrypted and unlocked"; break; + case 2: $luks = "Locked: missing encryption key"; break; + case 3: $luks = "Locked: wrong encryption key"; break; + default: $luks = "Locked: unknown error"; break;} } $list[] = [ 'type' => $attr[0], diff --git a/plugins/dynamix/include/DashUpdate.php b/plugins/dynamix/include/DashUpdate.php index 59e8fe081..665323ba5 100644 --- a/plugins/dynamix/include/DashUpdate.php +++ b/plugins/dynamix/include/DashUpdate.php @@ -13,6 +13,11 @@ 7{print $1,$2,$4,$6,$9,$10}' ".escapeshellarg($file)." 2>/dev/null", $codes); + exec("awk 'NR>7{print $1,$2,$4,$6,$9,$10}' ".escapeshellarg($smart)." 2>/dev/null", $codes); foreach ($codes as $code) { - if (!$code) continue; + if (!$code || !is_numeric($code[0])) continue; list($id,$class,$value,$thres,$when,$raw) = explode(' ',$code); $fail = strpos($when,'FAILING_NOW')!==false; if (!$fail && !in_array($id,$events)) continue; @@ -52,7 +57,7 @@ function my_usage(&$source,$used) { my_insert($source, $used ? "
$used
" : "-"); } function my_temp($value,$unit) { - return ($unit=='C' ? $value : round(9/5*$value+32))." $unit"; + return ($unit=='F' ? round(9/5*$value+32) : $value)." $unit"; } function my_clock($time) { if (!$time) return 'less than a minute'; @@ -87,15 +92,15 @@ case 'disk': $row6 = array_fill(0,31,''); my_insert($row6[0],'Heat alarm'); $row7 = array_fill(0,31,''); my_insert($row7[0],'SMART status'); $row8 = array_fill(0,31,''); my_insert($row8[0],'Utilization'); - $funcRenderRow = function($n,$disk) use (&$row1,&$row2,&$row3,&$row4,&$row5,&$row6,&$row7,&$row8,$path) { + $diskRow = function($n,$disk) use (&$row1,&$row2,&$row3,&$row4,&$row5,&$row6,&$row7,&$row8,$path) { if ($n>0) { if (isset($disk['luksState'])) { switch ($disk['luksState']) { case 0: $luks = ""; break; - case 1: $luks = ""; break; - case 2: $luks = ""; break; - case 3: $luks = ""; break; - default: $luks = ""; break; + case 1: $luks = ""; break; + case 2: $luks = ""; break; + case 3: $luks = ""; break; + default: $luks = ""; break; } } else $luks = ""; my_insert($row1[$n],$luks); @@ -117,8 +122,8 @@ case 'disk': my_insert($row5[$n],""); break;} $temp = $disk['temp']; - $hot = strlen($disk['hotTemp']) ? $disk['hotTemp'] : $_POST['hot']; - $max = strlen($disk['maxTemp']) ? $disk['maxTemp'] : $_POST['max']; + $hot = $disk['hotTemp'] ?? $_POST['hot']; + $max = $disk['maxTemp'] ?? $_POST['max']; $heat = $temp>=$max && $max>0 ? 'max' : ($temp>=$hot && $hot>0 ? 'hot' : ''); if ($heat) my_insert($row6[$n],""); @@ -128,16 +133,27 @@ case 'disk': my_usage($row8[$n],($disk['type']!='Parity' && $disk['fsStatus']=='Mounted')?(($disk['fsSize'] ? round((1-$disk['fsFree']/$disk['fsSize'])*100):0).'%'):''); } }; - foreach ($disks as $disk) if ($disk['type']=='Parity') $funcRenderRow($i++,$disk); - foreach ($disks as $disk) if ($disk['type']=='Data') $funcRenderRow($i++,$disk); + $devRow = function($n,$disk) use (&$row4,&$row6,&$row7,$path) { + $hot = $_POST['hot']; + $max = $_POST['max']; + $name = $dev['device']; + $port = substr($name,-2)!='n1' ? $name : substr($name,0,-2); + $smart = "state/smart/$name"; + $state = exec("hdparm -C ".escapeshellarg("/dev/$port")."|grep -Po 'active|unknown'") ? 'blue-on' : 'blue-blink'; + if ($state=='blue-on') my_smart($row7[$n],$name,'New'); + $temp = file_exists($smart) ? exec("awk 'BEGIN{t=\"*\"} \$1==190||\$1==194{t=\$10;exit};\$1==\"Temperature:\"{t=\$2;exit} END{print t}' ".escapeshellarg($smart)) : '*'; + $heat = $temp>=$max && $max>0 ? 'max' : ($temp>=$hot && $hot>0 ? 'hot' : ''); + if ($heat) + my_insert($row6[$n],""); + else + if ($state=='blue-on' && $temp>0) my_insert($row6[$n],"".my_temp($temp,$_POST['unit']).""); + my_insert($row4[$n],""); + }; + foreach ($disks as $disk) if ($disk['type']=='Parity') $diskRow($i++,$disk); + foreach ($disks as $disk) if ($disk['type']=='Data') $diskRow($i++,$disk); if ($slots <= 30) { - foreach ($disks as $disk) if ($disk['type']=='Cache') $funcRenderRow($i++,$disk); - foreach ($devs as $dev) { - $device = $dev['device']; - $state = exec("hdparm -C ".escapeshellarg("/dev/$device")."|grep -Po active") ? 'blue-on' : 'blue-blink'; - if ($state=='blue-on') my_smart($row7[$i],$device,'New'); - my_insert($row4[$i++],""); - } + foreach ($disks as $disk) if ($disk['type']=='Cache') $diskRow($i++,$disk); + foreach ($devs as $dev) $devRow($i++,$dev); } echo "".implode('',$row1).""; echo "".implode('',$row2).""; @@ -157,13 +173,8 @@ case 'disk': $row6 = array_fill(0,31,''); my_insert($row6[0],'Heat alarm'); $row7 = array_fill(0,31,''); my_insert($row7[0],'SMART status'); $row8 = array_fill(0,31,''); my_insert($row8[0],'Utilization'); - foreach ($disks as $disk) if ($disk['type']=='Cache') $funcRenderRow($i++,$disk); - foreach ($devs as $dev) { - $device = $dev['device']; - $state = exec("hdparm -C ".escapeshellarg("/dev/$device")."|grep -Po active") ? 'blue-on' : 'blue-blink'; - if ($state=='blue-on') my_smart($row7[$i],$device,'New'); - my_insert($row4[$i++],""); - } + foreach ($disks as $disk) if ($disk['type']=='Cache') $diskRow($i++,$disk); + foreach ($devs as $dev) $devRow($i++,$dev); echo "".implode('',$row1).""; echo "".implode('',$row2).""; echo "".implode('',$row3).""; @@ -230,7 +241,7 @@ case 'shares': case 'smb': exec("lsof /mnt/user /mnt/disk* 2>/dev/null|awk '/^smbd/ && $0!~/\.AppleD(B|ouble)/ && $5==\"REG\"'|awk -F/ '{print $4}'",$lsof); $counts = array_count_values($lsof); $count = []; - foreach ($names as $name) $count[] = isset($counts[$name]) ? $counts[$name] : 0; + foreach ($names as $name) $count[] = $counts[$name] ?? 0; echo implode('#',$count); break; case 'afp': diff --git a/plugins/dynamix/include/DeviceList.php b/plugins/dynamix/include/DeviceList.php index 5aba6a776..bfe20b319 100644 --- a/plugins/dynamix/include/DeviceList.php +++ b/plugins/dynamix/include/DeviceList.php @@ -35,7 +35,7 @@ function in_parity_log($log,$timestamp) { return !empty($line); } function device_info(&$disk,$online) { - global $path, $var; + global $path, $var, $show; $name = $disk['name']; $fancyname = $disk['type']=='New' ? $name : my_disk($name); $type = $disk['type']=='Flash' || $disk['type']=='New' ? $disk['type'] : 'Device'; @@ -58,14 +58,14 @@ function device_info(&$disk,$online) { } $status = "$ctrl$help"; $link = (strcmp($disk['status'], 'DISK_NP')!=0 || $disk['name']=="cache") ? "".$fancyname."" : $fancyname; - switch ($disk['luksState']) { - case 0: $luks = ""; break; - case 1: $luks = ""; break; - case 2: $luks = ""; break; - case 3: $luks = ""; break; - default: $luks = ""; break; - } - return $status.$link.$luks; + if ($show && $online) switch ($disk['luksState']) { + case 0: $luks = ""; break; + case 1: $luks = "Encrypted and unlocked"; break; + case 2: $luks = "Locked: missing encryption key"; break; + case 3: $luks = "Locked: wrong encryption key"; break; + default: $luks = "Locked: unknown error"; break; + } else $luks = ''; + return $status.$luks.$link; } function device_browse(&$disk) { global $path; @@ -177,7 +177,7 @@ function array_offline(&$disk) { function array_online(&$disk) { global $sum, $diskio; $dev = $disk['device']; - $data = isset($diskio[$dev]) ? explode(' ',$diskio[$dev]) : []; + $data = explode(' ',$diskio[$dev] ?? ''); if (is_numeric($disk['temp'])) { $sum['count']++; $sum['temp'] += $disk['temp']; @@ -229,16 +229,17 @@ function my_clock($time) { $mins = $time%60; return plus($days,'day',($hour|$mins)==0).plus($hour,'hour',$mins==0).plus($mins,'minute',true); } -function read_disk($dev, $part) { +function read_disk($name, $part) { global $var; + $port = substr($name,-2)!='n1' ? $name : substr($name,0,-2); switch ($part) { case 'color': - return exec("hdparm -C ".escapeshellarg("/dev/$dev")."|grep -Po active") ? 'blue-on' : 'blue-blink'; + return exec("hdparm -C ".escapeshellarg("/dev/$port")."|grep -Po 'active|unknown'") ? 'blue-on' : 'blue-blink'; case 'temp': - $smart = "/var/local/emhttp/smart/$dev"; - if (!file_exists($smart) || (time()-filemtime($smart)>=$var['poll_attributes'])) exec("smartctl -n standby -A ".escapeshellarg("/dev/$dev")." >".escapeshellarg($smart)." &"); - $temp = exec("awk '\$1==190||\$1==194{print \$10;exit}' $smart"); - return $temp ?: '*'; + $smart = "/var/local/emhttp/smart/$name"; + $type = $var['smType'] ?? ''; + if (!file_exists($smart) || (time()-filemtime($smart)>=$var['poll_attributes'])) exec("smartctl -n standby -A $type ".escapeshellarg("/dev/$port")." >".escapeshellarg($smart)." &"); + return exec("awk 'BEGIN{t=\"*\"} \$1==190||\$1==194{t=\$10;exit};\$1==\"Temperature:\"{t=\$2;exit} END{print t}' ".escapeshellarg($smart)." 2>/dev/null"); } } function show_totals($text) { @@ -301,8 +302,10 @@ function cache_slots() { $out .= ""; return $out; } +$show = false; switch ($_POST['device']) { case 'array': + foreach ($disks as $disk) if ($disk['type']=='Data') $show |= strpos($disk['fsType'],'luks:')!==false; if ($var['fsState']=='Stopped') { foreach ($disks as $disk) {if ($disk['type']=='Parity') array_offline($disk);} echo ""; @@ -329,6 +332,7 @@ case 'flash': echo ""; break; case 'cache': + foreach ($disks as $disk) if ($disk['type']=='Cache') $show |= strpos($disk['fsType'],'luks:')!==false; if ($var['fsState']=='Stopped') { foreach ($disks as $disk) {if ($disk['type']=='Cache') array_offline($disk);} echo ""; diff --git a/plugins/dynamix/include/DiskList.php b/plugins/dynamix/include/DiskList.php index e8ad84803..3096c62f1 100644 --- a/plugins/dynamix/include/DiskList.php +++ b/plugins/dynamix/include/DiskList.php @@ -24,8 +24,8 @@ $compute = $_GET['compute']; $path = $_GET['path']; $fill = $_GET['fill']; -$display = []; -$display['scale'] = $_GET['scale']; +$display = []; +$display['scale'] = $_GET['scale']; $display['number'] = $_GET['number']; // Display export settings @@ -46,8 +46,12 @@ function shareInclude($name) { return !$include || substr($name,0,4)!='disk' || strpos("$include,", "$name,")!==false; } -// Compute all disk shares -if ($compute=='yes') foreach ($disks as $name => $disk) if ($disk['exportable']=='yes') exec("webGui/scripts/disk_size ".escapeshellarg($name)." ssz2"); +// Compute all disk shares & check encryption +$show = false; +foreach ($disks as $name => $disk) { + if ($compute=='yes' && $disk['exportable']=='yes') exec("webGui/scripts/disk_size ".escapeshellarg($name)." ssz2"); + if (strstr('Data,Cache',$disk['type'])) $show |= strpos($disk['fsType'],'luks:')!==false; +} // global shares include/exclude $myDisks = array_filter(array_diff(array_keys($disks), explode(',',$var['shareUserExclude'])), 'globalInclude'); @@ -62,23 +66,21 @@ else // Build table $row = 0; foreach ($disks as $name => $disk) { - if ($disk['type']=='Flash') continue; - if ($disk['fsColor']=='grey-off') continue; - if ($disk['exportable']=='no') continue; + if (!strstr('Data,Cache',$disk['type']) || $disk['fsColor']=='grey-off' || $disk['exportable']=='no') continue; $row++; $ball = "/webGui/images/{$disk['fsColor']}.png"; switch ($disk['fsColor']) { case 'green-on': $help = 'All files protected'; break; case 'yellow-on': $help = 'All files unprotected'; break; } - switch ($disk['luksState']) { - case 0: $luks = ""; break; - case 1: $luks = ""; break; - case 2: $luks = ""; break; - default: $luks = ""; break; - } + if ($show) switch ($disk['luksState']) { + case 0: $luks = ""; break; + case 1: $luks = "All files encrypted"; break; + case 2: $luks = "Some or all files unencrypted"; break; + default: $luks = "Unknown encryption state'"; break; + } else $luks = ""; echo ""; - echo ""; + echo ""; echo ""; echo ""; echo ""; diff --git a/plugins/dynamix/include/Helpers.php b/plugins/dynamix/include/Helpers.php index 470efb4f7..e0f2ef2f7 100644 --- a/plugins/dynamix/include/Helpers.php +++ b/plugins/dynamix/include/Helpers.php @@ -1,6 +1,6 @@ $share) exec("webGui/scripts/share_size ".escapeshellarg($name)." ssz1"); +// Compute all user shares & check encryption +$show = false; +foreach ($shares as $name => $share) { + if ($compute=='yes') exec("webGui/scripts/share_size ".escapeshellarg($name)." ssz1"); + $show |= $share['luksStatus']>0; +} // global shares include/exclude $myDisks = array_filter(array_diff(array_keys($disks), explode(',',$var['shareUserExclude'])), 'globalInclude'); @@ -75,14 +79,14 @@ foreach ($shares as $name => $share) { case 'green-on': $help = 'All files protected'; break; case 'yellow-on': $help = 'Some or all files unprotected'; break; } - switch ($share['luksStatus']) { - case 0: $luks = ""; break; - case 1: $luks = ""; break; - case 2: $luks = ""; break; - default: $luks = ""; break; - } + if ($show) switch ($share['luksStatus']) { + case 0: $luks = ""; break; + case 1: $luks = "All files encrypted"; break; + case 2: $luks = "Some or all files unencrypted"; break; + default: $luks = "Unknown encryption state'"; break; + } else $luks = ""; echo ""; - echo ""; + echo ""; echo ""; echo ""; echo ""; diff --git a/plugins/dynamix/include/SmartInfo.php b/plugins/dynamix/include/SmartInfo.php index 2e60c6a44..3a82e0d28 100644 --- a/plugins/dynamix/include/SmartInfo.php +++ b/plugins/dynamix/include/SmartInfo.php @@ -28,47 +28,36 @@ function duration(&$hrs) { function spindownDelay($port) { $disks = parse_ini_file('state/disks.ini',true); foreach ($disks as $disk) { - if ($disk['device']==$port) { file_put_contents("/var/tmp/diskSpindownDelay.{$disk['idx']}", $disk['spindownDelay']); break; } + $name = substr($disk['device'],-2)!='n1' ? $disk['device'] : substr($disk['device'],0,-2); + if ($name==$port) {file_put_contents("/var/tmp/diskSpindownDelay.{$disk['idx']}", $disk['spindownDelay']); break;} } } -function get(&$ref,$n,$d) { - global $var; - $val = $ref[$n] ?? -1; if ($val==-1) $val = $var[$n] ?? $d; - return $val; -} -function exist(&$ref) { - return isset($ref) && strlen($ref); -} function append(&$ref, &$info) { - if (isset($info)) $ref .= ($ref ? " " : "").$info; + if ($info) $ref .= ($ref ? " " : "").$info; } $disks = []; $var = []; require_once "$docroot/webGui/include/CustomMerge.php"; +require_once "$docroot/webGui/include/Wrappers.php"; +require_once "$docroot/webGui/include/Preselect.php"; $name = $_POST['name'] ?? ''; $port = $_POST['port'] ?? ''; if ($name) { $disk = &$disks[$name]; - $type = get($disk,'smType',''); - if ($type) { - $ports = []; - if (exist($disk['smDevice'])) $port = $disk['smDevice']; - if (exist($disk['smPort1'])) $ports[] = $disk['smPort1']; - if (exist($disk['smPort2'])) $ports[] = $disk['smPort2']; - if (exist($disk['smPort3'])) $ports[] = $disk['smPort3']; - if ($ports) $type .= ','.implode($disk['smGlue'] ?? ',',$ports); - } + $type = get_value($disk,'smType',''); + get_ctlr_options($type, $disk); +} else { + $disk = []; + $type = ''; } +$port = port_name($disk['smDevice'] ?? $port); switch ($_POST['cmd']) { case "attributes": - require_once "$docroot/webGui/include/Wrappers.php"; - require_once "$docroot/webGui/include/Preselect.php"; - $select = get($disk,'smSelect',0); - $level = get($disk,'smLevel',1); - $events = explode('|',$disk['smEvents'] ?? $var['smEvents'] ?? $numbers); - $temps = [190,194]; + $select = get_value($disk,'smSelect',0); + $level = get_value($disk,'smLevel',1); + $events = explode('|',get_value($disk,'smEvents',$numbers)); $unraid = parse_plugin_cfg('dynamix',true); - $max = $unraid['display']['max']; - $hot = $unraid['display']['hot']; + $max = $disk['maxTemp'] ?? $unraid['display']['max']; + $hot = $disk['hotTemp'] ?? $unraid['display']['hot']; exec("smartctl -A $type ".escapeshellarg("/dev/$port")."|awk 'NR>4'",$output); if (strpos($output[0], 'SMART Attributes Data Structure')===0) { $output = array_slice($output, 3); @@ -79,8 +68,8 @@ case "attributes": $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], $temps)) { - if ($info[9]>=$max) $color = " class='alert'"; elseif ($info[9]>=$hot) $color = " class='warn'"; + elseif (in_array($info[0], [190,194])) { + if ($info[9]>=$max && $max>0) $color = " class='alert'"; elseif ($info[9]>=$hot && $hot>0) $color = " class='warn'"; } if ($info[8]=='-') $info[8] = 'Never'; if ($info[0]==9 && is_numeric($info[9])) duration($info[9]); @@ -93,7 +82,19 @@ case "attributes": foreach ($output as $line) { if (strpos($line,':')===false) continue; list($name,$value) = explode(':', $line); - echo ""; + $name = ucfirst(strtolower($name)); + $value = trim($value); + $color = ''; + switch ($name) { + case 'Temperature': + $temp = strtok($value,' '); + if ($temp>=$max && $max>0) $color = " class='alert'"; elseif ($temp>=$hot && $hot>0) $color = " class='warn'"; + break; + case 'Power on hours': + if (is_numeric($value)) duration($value); + break; + } + echo ""; } } break; @@ -123,7 +124,7 @@ case "identify": exec("smartctl -H $type ".escapeshellarg("/dev/$port")."|grep -Pom1 '^SMART.*: [A-Z]+'|sed 's:self-assessment test result::'",$output); $empty = true; foreach ($output as $line) { - if (!strlen($line)) continue; + if (!$line) continue; if (strpos($line,'VALID ARGUMENTS')!==false) break; list($title,$info) = array_map('trim', explode(':', $line, 2)); if (in_array($info,$passed)) $info = "Passed"; diff --git a/plugins/dynamix/include/Wrappers.php b/plugins/dynamix/include/Wrappers.php index b73db61c2..ebd5d8381 100644 --- a/plugins/dynamix/include/Wrappers.php +++ b/plugins/dynamix/include/Wrappers.php @@ -1,6 +1,6 @@ =')) return $remote; } } + +function get_value(&$object,$name,$default) { + global $var; + $value = $object[$name] ?? -1; + return $value!==-1 ? $value : ($var[$name] ?? $default); +} + +function get_ctlr_options(&$type, &$disk) { + if (!$type) return; + $ports = []; + if (strlen($disk['smPort1'])) $ports[] = $disk['smPort1']; + if (strlen($disk['smPort2'])) $ports[] = $disk['smPort2']; + if (strlen($disk['smPort3'])) $ports[] = $disk['smPort3']; + $type .= ($ports ? ','.implode($disk['smGlue'] ?? ',',$ports) : ''); +} +function port_name($port) { + return substr($port,-2)!='n1' ? $port : substr($port,0,-2); +} ?> diff --git a/plugins/dynamix/javascript/dynamix.js b/plugins/dynamix/javascript/dynamix.js index 3374262d2..699325353 100644 --- a/plugins/dynamix/javascript/dynamix.js +++ b/plugins/dynamix/javascript/dynamix.js @@ -15,7 +15,7 @@ i&&i.element!==s.element[0]&&("touch"===a.tolerance?l=!(i.left>o||n>i.right||i.t },_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction"),i=this.element.find(this.options.items),s=i.filter(":ui-button");i.not(":ui-button").button(),s.button("refresh"),this.buttons=i.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}}),e.ui.button,e.widget("ui.dialog",{version:"1.11.4",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"Close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i,s=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(":focusable").focus().length)try{i=this.document[0].activeElement,i&&"body"!==i.nodeName.toLowerCase()&&e(i).blur()}catch(a){}this._hide(this.uiDialog,this.options.hide,function(){s._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,i){var s=!1,a=this.uiDialog.siblings(".ui-front:visible").map(function(){return+e(this).css("z-index")}).get(),n=Math.max.apply(null,a);return n>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",n+1),s=!0),s&&!i&&this._trigger("focus",t),s},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var e=this._focusedElement;e||(e=this.element.find("[autofocus]")),e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("
").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),void 0;if(t.keyCode===e.ui.keyCode.TAB&&!t.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),a=i.filter(":last");t.target!==a[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==s[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){a.focus()}),t.preventDefault()):(this._delay(function(){s.focus()}),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("
").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html(" "),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("
").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("
").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),void 0):(e.each(i,function(i,s){var a,n;s=e.isFunction(s)?{click:s,text:i}:s,s=e.extend({type:"button"},s),a=s.click,s.click=function(){a.apply(t.element[0],arguments)},n={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,e("",s).button(n).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,a){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,t(a))},drag:function(e,s){i._trigger("drag",e,t(s))},stop:function(a,n){var r=n.offset.left-i.document.scrollLeft(),o=n.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(o>=0?"+":"")+o,of:i.window},e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",a,t(n))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,s=this.options,a=s.resizable,n=this.uiDialog.css("position"),r="string"==typeof a?a:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:r,start:function(s,a){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,t(a))},resize:function(e,s){i._trigger("resize",e,t(s))},stop:function(a,n){var r=i.uiDialog.offset(),o=r.left-i.document.scrollLeft(),h=r.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(o>=0?"+":"")+o+" "+"top"+(h>=0?"+":"")+h,of:i.window},e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",a,t(n))}}).css("position",n)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._makeFocusTarget(),this._focusedElement=e(t.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var t=this._trackingInstances(),i=e.inArray(this,t);-1!==i&&t.splice(i,1)},_trackingInstances:function(){var e=this.document.data("ui-dialog-instances");return e||(e=[],this.document.data("ui-dialog-instances",e)),e},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(t){var i=this,s=!1,a={};e.each(t,function(e,t){i._setOption(e,t),e in i.sizeRelatedOptions&&(s=!0),e in i.resizableRelatedOptions&&(a[e]=t)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",a)},_setOption:function(e,t){var i,s,a=this.uiDialog;"dialogClass"===e&&a.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=a.is(":data(ui-draggable)"),i&&!t&&a.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(s=a.is(":data(ui-resizable)"),s&&!t&&a.resizable("destroy"),s&&"string"==typeof t&&a.resizable("option","handles",t),s||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),e=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),t=Math.max(0,s.minHeight-e),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-e):"none","auto"===s.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("
").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=!0;this._delay(function(){t=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(e){t||this._allowInteraction(e)||(e.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=e("
").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var e=this.document.data("ui-dialog-overlays")-1;e?this.document.data("ui-dialog-overlays",e):this.document.unbind("focusin").removeData("ui-dialog-overlays"),this.overlay.remove(),this.overlay=null}}}),e.widget("ui.menu",{version:"1.11.4",defaultElement:"
Slots:".cache_slots()."
$help$name$luks$help$luks$name{$disk['comment']}".disk_share_settings($var['shareSMBEnabled'], $sec[$name])."".disk_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])."
$help$name$luks$help$luks$name{$share['comment']}".user_share_settings($var['shareSMBEnabled'], $sec[$name])."".user_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])."
-".ucfirst(strtolower($name))."".trim($value)."
-$name$value