" : "
";
show_map("Tasks", 1);
show_map("Buttons", 1);
echo "
";
?>
-
\ No newline at end of file
+
diff --git a/plugins/dynamix/Processes.page b/plugins/dynamix/Processes.page
index 6d08c80e1..3e3789c53 100644
--- a/plugins/dynamix/Processes.page
+++ b/plugins/dynamix/Processes.page
@@ -13,8 +13,18 @@ Title="Processes"
* all copies or substantial portions of the Software.
*/
?>
-
+
+
+
-echo "
".shell_exec('ps -ef')."";
+echo ($display['resize'] ? "
" : "").shell_exec('ps -ef')."";
?>
-
\ No newline at end of file
+
diff --git a/plugins/dynamix/SMBExtras.page b/plugins/dynamix/SMBExtras.page
new file mode 100644
index 000000000..02a5a9c34
--- /dev/null
+++ b/plugins/dynamix/SMBExtras.page
@@ -0,0 +1,38 @@
+Menu="SMB:2"
+Title="SMB Extras"
+Cond="($var['shareSMBEnabled']!='no')"
+Png="smbsettings.png"
+---
+
+> Use this page to make changes to your `smb-extra.conf` file. Samba will need
+> to be restarted in order for changes to take effect.
+
+
+$file = "/boot/config/smb-extra.conf";
+$text = @file_get_contents($file);
+?>
+
\ No newline at end of file
diff --git a/plugins/dynamix/Selftest.page b/plugins/dynamix/Selftest.page
new file mode 100644
index 000000000..1f25a9918
--- /dev/null
+++ b/plugins/dynamix/Selftest.page
@@ -0,0 +1,220 @@
+Menu="Device:2 New:2"
+Title="Self-Test"
+Cond="strpos($disks[$name]['status'],'_NP')===false"
+---
+
+
+$date = date('Ymd-Hi');
+$sheets = []; $n = 0;
+$type = isset($disks[$name]) ? 'Device' : 'New';
+if ($type=='New') {
+ $dev = $name;
+ foreach ($devs as $sheet) {
+ $sheets[] = $sheet['device'];
+ if ($sheet['device']==$name) {$i = $n; $file = $sheet['id']."-$date.txt";}
+ $n++;
+ }
+} else {
+ $disk = $disks[$name];
+ $dev = $disk['device'];
+ $file = $disk['id']."-$date.txt";
+ foreach ($disks as $sheet) {
+ if ($sheet['name']=='flash' || strpos($sheet['status'],'_NP')) continue;
+ $sheets[] = $sheet['name'];
+ if ($sheet['name']==$name) { $i = $n; $spindown = "diskSpindownDelay.{$sheet['idx']}"; $delay = $sheet['spindownDelay']; }
+ $n++;
+ }
+}
+$live = exec("hdparm -C /dev/$dev|grep -Pom1 'active|unknown'");
+$end = count($sheets)-1;
+$prev = $i>0 ? $sheets[$i-1] : $sheets[$end];
+$next = $i<$end ? $sheets[$i+1] : $sheets[0];
+$zip = str_replace(' ','_',strtolower($var['NAME']))."-smart-$date.zip";
+
+function spindownDelay() {
+ global $spindown, $delay;
+ $tmp = "/var/tmp/$spindown";
+ return file_exists($tmp) ? file_get_contents($tmp) : $delay;
+}
+?>
+Download SMART report:
+:
+
+SMART self-test history:
+:
+
+> Press **Show** to view the self-test history as is kept on the disk itself.
+> This feature is only available when the disk is in active mode.
+
+
+
+SMART error log:
+:
+
+> Press **Show** to view the error report as is kept on the disk itself.
+> This feature is only available when the disk is in active mode.
+
+
+
+SMART short self-test:
+:
+
+> Starts a *short* SMART self-test, the estimated duration can be viewed under the *Capabilities* section. This is usually a few minutes.
+>
+> When the disk is spun down, it will abort any running self-test.
+> This feature is only available when the disk is in active mode.
+
+SMART extended self-test:
+:
+
+> Starts an *extended* SMART self-test, the estimated duration can be viewed under the *Capabilities* section. This is usually several hours.
+>
+> When the disk is spun down, it will abort any running self-test. It is advised to disable the spin down timer of the disk
+> to avoid interruption of this self-test.
+>
+> This feature is only available when the disk is in active mode.
+
+Last SMART test result:
+:
Please wait... retrieving S.M.A.R.T. information!
+
+> When no test is running it will show here the latest obtained self-test result (if available).
+> Otherwise a progress indicator (percentage value) is shown for a running test.
+
+
\ No newline at end of file
diff --git a/plugins/dynamix/ShareEdit.page b/plugins/dynamix/ShareEdit.page
index cc5598abb..3baf7d2b7 100644
--- a/plugins/dynamix/ShareEdit.page
+++ b/plugins/dynamix/ShareEdit.page
@@ -41,20 +41,13 @@ function shareEmpty($name) {
return (($files = @scandir('/mnt/user/'.$name)) && (count($files) <= 2));
}
-if ($var['shareUserInclude']) {
- $myDisks = explode(',',$var['shareUserInclude']);
-} else {
- $myDisks = array();
- foreach ($disks as $disk) $myDisks[] = $disk['name'];
+function globalInclude($name) {
+ global $var;
+ return substr($name,0,4)=='disk' && (!$var['shareUserInclude'] || strpos("{$var['shareUserInclude']},","$name,")!==false);
}
-if ($var['shareUserExclude']) {
- $exclude = explode(',',$var['shareUserExclude']);
- foreach ($exclude as $disk) {
- $index = array_search($disk,$myDisks);
- if ($index !== false) array_splice($myDisks,$index,1);
- }
-}
+// global shares include/exclude
+$myDisks = array_filter(array_diff(array_keys($disks), explode(',',$var['shareUserExclude'])), 'globalInclude');
?>
> A *Share*, also called a *User Share*, is simply the name of a top-level directory that exists on one or more of your
@@ -65,7 +58,7 @@ if ($var['shareUserExclude']) {
$(function() {
$("#s1").dropdownchecklist({emptyText:'All', width:300, explicitClose:'...close'});
$("#s2").dropdownchecklist({emptyText:'None', width:300, explicitClose:'...close'});
- setDiskList(document.share_edit.shareUseCache);
+ setDiskList(document.share_edit.shareUseCache.value);
presetSpace(document.share_edit.shareFloor);
});
function setDiskList(cache) {
diff --git a/plugins/dynamix/ShareList.page b/plugins/dynamix/ShareList.page
index 0a3150eb9..27392fbb6 100644
--- a/plugins/dynamix/ShareList.page
+++ b/plugins/dynamix/ShareList.page
@@ -14,83 +14,32 @@ Cond="$var['fsState']=="Started" && $var['shareUser']=='e'"
* all copies or substantial portions of the Software.
*/
?>
+
-
-// Display export settings
-function user_share_settings($protocol,$share) {
- if (empty($share)) return;
- if ($protocol!='yes' || $share['export']=='-') return "-";
- if ($share['export']=='e') return ucfirst($share['security']);
- return '
'.ucfirst($share['security']).'';
-}
-// Share size per disk
-$preserve = $path==$prev;
-$ssz1 = array();
-foreach (glob("state/*.ssz1", GLOB_NOSORT) as $entry) {
- if ($preserve) {
- $ssz1[basename($entry, ".ssz1")] = parse_ini_file($entry);
- } else {
- unlink($entry);
- }
-}
-?>
| Name | Comment | SMB | NFS | AFP | Size | Free | View |
-
-
-foreach ($shares as $name => $share):
- $ball = "/webGui/images/{$share['color']}.png";
- switch ($share['color']) {
- case 'green-on': $help = 'All files protected'; break;
- case 'yellow-on': $help = 'Some or all files unprotected'; break;
- }
-?>
- =$help?>=$share['name']?> |
- =$share['comment']?> |
- =user_share_settings($var['shareSMBEnabled'], $sec[$name])?> |
- =user_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])?> |
- =user_share_settings($var['shareAFPEnabled'], $sec_afp[$name])?> |
-
- =my_scale($ssz1[$share['name']]['total']*1024, $unit).' '.$unit?> |
- =my_scale($share['free']*1024, $unit).' '.$unit?> |
- ![Browse /mnt/user/<?=urlencode($share['name'])?>](/webGui/images/explore.png) |
-
- $disk_size):
- if ($disk_name!="total"):
-?>
- | =my_disk($disk_name)?>: |
- |
- |
- |
- |
- =my_scale($disk_size*1024, $unit).' '.$unit?> |
- =my_scale($disks[$disk_name]['fsFree']*1024, $unit).' '.$unit?> |
- |
-
- endif;
- endforeach;
- else:
- $cmd="/webGui/scripts/share_size" . "&arg1=" . urlencode($name) . "&arg2=" . urlencode("/var/local/emhttp/$name.ssz1");
-?>Compute... |
- =my_scale($share['free']*1024, $unit).' '.$unit?> |
- ![Browse /mnt/user/<?=urlencode($share['name'])?>](/webGui/images/explore.png) |
-
-
-
+
-
-
There are no user shares
-
-
+
> **Colored Status Indicator** the significance of the color indicator at the beginning of each line in *User Shares* is as follows:
>
diff --git a/plugins/dynamix/ShareSettings.page b/plugins/dynamix/ShareSettings.page
index d5b9b6cbf..7e6ceb75c 100644
--- a/plugins/dynamix/ShareSettings.page
+++ b/plugins/dynamix/ShareSettings.page
@@ -90,12 +90,7 @@ Included disk(s):
> This setting defines the set of array disks which are *included* in User Shares.
-> Set this field to *blank* in order to allow **all** array disks to be included.
->
-> To specify a set of disks name them like this:
-> * disk1,disk2,disk3 *disk names seperated by commas*
-> * disk1-3 *a range of disks*
-> * disk2,disk4-6,disk10 *another example*
+> Unchecking all disks will allow **all** array disks to be included.
Excluded disk(s):
:
-> This setting defines the set of array disk which are *excluded* from User Shares. Set this
-> field to *blank* in order to not exclude any disks; otherwise, set this field as above to define the set
-> of disks to exclude.
+> This setting defines the set of array disk which are *excluded* from User Shares.
+> Uncheck all disks in order to not exclude any disks
>
-> **Note:** Each separate User Share also includes its own set of Included and Excluded disks which represent
-> a subset of the Included/Excluded disks defined here.
+> **Note:** Each separate User Share also includes its own set of Included and Excluded
+> disks which represent a subset of the Included/Excluded disks defined here.
:
>
Array must be **Stopped** to change
diff --git a/plugins/dynamix/SmtpSettings.page b/plugins/dynamix/SmtpSettings.page
index 9ebee5c2e..9ce811f7d 100644
--- a/plugins/dynamix/SmtpSettings.page
+++ b/plugins/dynamix/SmtpSettings.page
@@ -27,7 +27,6 @@ $(function() {
mailtest();
$.get('/webGui/include/SMTPtest.php',function(data){clearTimeout(pid); $('#testresult').html(data)});
});
- passcheck(document.smtp_setup);
tls(document.smtp_setup);
pwd(document.smtp_setup);
});
@@ -39,9 +38,6 @@ function mailcheck(form) {
}
return true;
}
-function passcheck(form) {
- if (form.AuthPass.value.indexOf('#')<0) $('#authpass').hide(); else $('#authpass').show();
-}
function tls(form) {
form.TLSCert.disabled = form.UseTLSCert.selectedIndex==0;
}
@@ -180,7 +176,7 @@ Username:
:
Password:
-:
Warning: do not use the # character in your password!
+:
> Enter the username and password to login to your email account. Be aware that the password is stored unencrypted in the email configuration file.
diff --git a/plugins/dynamix/Syslog.page b/plugins/dynamix/Syslog.page
index 38f75d374..3055c6fbf 100644
--- a/plugins/dynamix/Syslog.page
+++ b/plugins/dynamix/Syslog.page
@@ -13,10 +13,8 @@ Title="System Log"
* all copies or substantial portions of the Software.
*/
?>
-
$zip = str_replace(' ','_',strtolower($var['NAME']))."-syslog-".date('Ymd-Hi').".zip";
-echo "
".shell_exec('cat /var/log/syslog')."";
?>
+
+require_once 'webGui/include/ColorCoding.php';
+
+echo $display['resize'] ? "
" : "";
+$logs = glob('/var/log/syslog*',GLOB_NOSORT);
+usort($logs, create_function('$a,$b', 'return filemtime($a)-filemtime($b);'));
+foreach ($logs as $log) {
+ foreach (file($log) as $line) {
+ $span = "span class='text'";
+ foreach ($match as $type) foreach ($type['text'] as $text) if (preg_match("/$text/i",$line)) {$span = "span class='{$type['class']}'"; break 2;}
+ echo "<$span>".htmlentities($line)."";
+ }
+}
+echo "";
+?>
diff --git a/plugins/dynamix/UserList.page b/plugins/dynamix/UserList.page
index cc32c92df..03db15117 100644
--- a/plugins/dynamix/UserList.page
+++ b/plugins/dynamix/UserList.page
@@ -20,4 +20,7 @@ Title="Users"
diff --git a/plugins/dynamix/Users.page b/plugins/dynamix/Users.page
index 5ca8184e2..628c0395f 100644
--- a/plugins/dynamix/Users.page
+++ b/plugins/dynamix/Users.page
@@ -1,2 +1,4 @@
-Menu="Tasks:3"
-Type="xmenu"
\ No newline at end of file
+Menu="$display['users'] Tasks:3"
+Type="xmenu"
+Title="Users"
+Icon="users.png"
\ No newline at end of file
diff --git a/plugins/dynamix/Vars.page b/plugins/dynamix/Vars.page
index 33755e1e5..063a61816 100644
--- a/plugins/dynamix/Vars.page
+++ b/plugins/dynamix/Vars.page
@@ -13,7 +13,17 @@ Title="Vars"
* all copies or substantial portions of the Software.
*/
?>
-
+
+
+
foreach ($site as &$page) $page['text'] = '...';
$myPage['text'] = '...';
@@ -21,6 +31,6 @@ $pages['Vars']['text'] = '...';
$text = '...';
ksort($site);
ksort($GLOBALS);
+echo ($display['resize'] ? "
" : "").print_r($GLOBALS,true)."
";
?>
-
diff --git a/plugins/dynamix/default.cfg b/plugins/dynamix/default.cfg
index f477ef241..b38f4041c 100644
--- a/plugins/dynamix/default.cfg
+++ b/plugins/dynamix/default.cfg
@@ -9,10 +9,12 @@ unit="C"
scale="-1"
align="right"
view=""
+resize="0"
total="1"
banner="image"
dashapps="icons"
tabs="1"
+users="Tasks:3"
usage="0"
text="1"
warning="70"
@@ -43,8 +45,6 @@ plugin="1"
report="1"
version=""
status=""
-events="5|187|188|197|198"
-custom=""
[ssmtp]
root=""
RcptTo=""
diff --git a/plugins/dynamix/dynamix.plg b/plugins/dynamix/dynamix.plg
index e35aff869..145677eb4 100644
--- a/plugins/dynamix/dynamix.plg
+++ b/plugins/dynamix/dynamix.plg
@@ -3,17 +3,16 @@
-
+
+
]>
-
+
Dynamix webGui v&version;
@@ -24,16 +23,12 @@ Please refer to
on github.
-
+
"https://github.com/limetech/&name;/archive/&version;.tar.gz"
-
+
rm -r /tmp/plugins/&name;-&version; 2>/dev/null
@@ -48,9 +43,7 @@ find /boot/config/plugins/&name; -type f -iname "*.tar.gz" ! -iname "&name;-&ver
-
+
rm -r /boot/config/plugins/&name;/&name;-&version;.tar.gz 2>/dev/null
diff --git a/plugins/dynamix/icons/smb.png b/plugins/dynamix/icons/smb.png
index d6c2f6aaf..bfaef37a1 100644
Binary files a/plugins/dynamix/icons/smb.png and b/plugins/dynamix/icons/smb.png differ
diff --git a/plugins/dynamix/icons/smbsecuritysettings.png b/plugins/dynamix/icons/smbsecuritysettings.png
index d6c2f6aaf..bfaef37a1 100644
Binary files a/plugins/dynamix/icons/smbsecuritysettings.png and b/plugins/dynamix/icons/smbsecuritysettings.png differ
diff --git a/plugins/dynamix/icons/smbsettings.png b/plugins/dynamix/icons/smbsettings.png
index d6c2f6aaf..bfaef37a1 100644
Binary files a/plugins/dynamix/icons/smbsettings.png and b/plugins/dynamix/icons/smbsettings.png differ
diff --git a/plugins/dynamix/images/alert.png b/plugins/dynamix/images/alert.png
new file mode 100644
index 000000000..52a2545e7
Binary files /dev/null and b/plugins/dynamix/images/alert.png differ
diff --git a/plugins/dynamix/images/help-off.png b/plugins/dynamix/images/help-off.png
new file mode 100644
index 000000000..d9f463750
Binary files /dev/null and b/plugins/dynamix/images/help-off.png differ
diff --git a/plugins/dynamix/images/help-on.png b/plugins/dynamix/images/help-on.png
new file mode 100644
index 000000000..4cefe96cf
Binary files /dev/null and b/plugins/dynamix/images/help-on.png differ
diff --git a/plugins/dynamix/images/users.png b/plugins/dynamix/images/users.png
new file mode 100644
index 000000000..2aeea5a6f
Binary files /dev/null and b/plugins/dynamix/images/users.png differ
diff --git a/plugins/dynamix/images/windows-logo.png b/plugins/dynamix/images/windows-logo.png
index a542c2a78..44c7c6bd8 100644
Binary files a/plugins/dynamix/images/windows-logo.png and b/plugins/dynamix/images/windows-logo.png differ
diff --git a/plugins/dynamix/include/Acknowledge.php b/plugins/dynamix/include/Acknowledge.php
new file mode 100644
index 000000000..55a3c2ace
--- /dev/null
+++ b/plugins/dynamix/include/Acknowledge.php
@@ -0,0 +1,27 @@
+
+
+$ram = "/var/local/emhttp/monitor.ini";
+$rom = "/boot/config/plugins/dynamix/monitor.ini";
+$saved = parse_ini_file($ram,true);
+$saved["smart"]["{$_POST['disk']}.ack"] = "true";
+
+$text = "";
+foreach ($saved as $item => $block) {
+ if ($block) $text .= "[$item]\n";
+ foreach ($block as $key => $value) $text .= "$key=\"$value\"\n";
+}
+file_put_contents($ram, $text);
+file_put_contents($rom, $text);
+echo "200 OK";
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/include/ColorCoding.php b/plugins/dynamix/include/ColorCoding.php
new file mode 100644
index 000000000..e96540158
--- /dev/null
+++ b/plugins/dynamix/include/ColorCoding.php
@@ -0,0 +1,35 @@
+
+
+// Color coding for syslog and disk log
+$match =
+[['class' => 'text',
+ 'text' => ['to the standard error','non[ -]fatal error','correct gpt errors','error handler\b','(kernel|logger): [|+ #-.]','logger: (naming|log)']
+ ],
+ ['class' => 'login',
+ 'text' => ['(accepted|failed) password','sshd\[\d+\]:']
+ ],
+ ['class' => 'warn',
+ 'text' => ['\b(warning|conflicts|kill|failed|checksum|spurious|replayed|preclear_disk)\b','acpi (error|exception)','\b(soft|hard) resetting ','\ 'error',
+ 'text' => ['\b(error|emask|tainted|killed|fsck\?|parity incorrect|invalid opcode|kernel bug|power failure)\b','\b(dma|ata\d+[.:]) disabled','nobody cared','unknown boot option','write protect is on','call trace','out[ _]of[ _]memory','hpa detected: current \d+']
+ ],
+ ['class' => 'system',
+ 'text' => ['\b(checksumming|controller|driver|version|highmem|lowmem|bogomips)\b','throttling rate','get value of subfeature','[mg]hz processor','cpu\d*: (intel|amd)','kernel: (processors|memory|smp|console):','\bmd: xor using','thermal zone','adding \d+k swap on','kernel command line:','_sse','found.*chip','\b(mouse|speaker|kbd port|aux port|ps\/2|keyboard)\b']
+ ],
+ ['class' => 'array',
+ 'text' => [': (mdcmd|md|super\.dat|unraid system|unregistered|running, size)\b','key detected, registered']
+ ]
+];
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/include/CustomMerge.php b/plugins/dynamix/include/CustomMerge.php
new file mode 100644
index 000000000..b8917d3e8
--- /dev/null
+++ b/plugins/dynamix/include/CustomMerge.php
@@ -0,0 +1,18 @@
+
+
+// Merge SMART settings
+$smartONE = '/boot/config/smart-one.cfg';
+$smartALL = '/boot/config/smart-all.cfg';
+if (file_exists($smartONE)) $disks = array_merge_recursive($disks, parse_ini_file($smartONE,true));
+if (file_exists($smartALL)) $var = array_merge($var, parse_ini_file($smartALL));
+?>
diff --git a/plugins/dynamix/include/DashUpdate.php b/plugins/dynamix/include/DashUpdate.php
index 53df296d7..e30d86513 100644
--- a/plugins/dynamix/include/DashUpdate.php
+++ b/plugins/dynamix/include/DashUpdate.php
@@ -11,20 +11,37 @@
*/
?>
-$path = '/webGui/images';
-
+function normalize($type,$count) {
+ $words = explode('_',$type);
+ foreach ($words as &$word) $word = $word==strtoupper($word) ? $word : preg_replace(['/^(ct|cnt)$/','/^blk$/'],['count','block'],strtolower($word));
+ return ucfirst(implode(' ',$words)).": ".str_replace('_',' ',strtolower($count))."\n";
+}
function my_insert(&$source,$string) {
$source = substr_replace($source,$string,4,0);
}
-function my_smart(&$source,$name) {
- global $path;
+function my_smart(&$source,$name,$page) {
+ global $var,$disks,$path,$failed,$numbers,$saved;
+ $disk = &$disks[$name];
+ $select = isset($disk['smSelect']) ? $disk['smSelect'] : -1; if ($select==-1) $select = isset($var['smSelect']) ? $var['smSelect'] : 0;
+ $level = isset($disk['smLevel']) ? $disk['smLevel'] : -1; if ($level==-1) $level = isset($var['smLevel']) ? $var['smLevel'] : 1;
+ $events = isset($disk['smEvents']) ? explode('|',$disk['smEvents']) : (isset($var['smEvents']) ? explode('|',$var['smEvents']) : $numbers);
$thumb = 'good';
- $saved = @parse_ini_file("/var/local/emhttp/monitor.ini",true);
- $last = isset($saved["smart"]["$name.5"]) ? $saved["smart"]["$name.5"] : 0;
- $smart = exec("grep -Pom1 '^ 5.+ \K\d+$' /var/local/emhttp/smart/$name");
- if (!$smart) $smart = 0;
- if (($last == 0 && $smart > 0) || ($last > 0 && $smart > $last)) $thumb = 'bad';
- my_insert($source, "
");
+ $file = "state/smart/$name";
+ if (file_exists("$file.ssa") && in_array(file_get_contents("$file.ssa"),$failed)) {
+ $thumb = 'bad';
+ } else {
+ if (empty($saved["smart"]["$name.ack"])) {
+ exec("awk 'NR>7{print $1,$2,$4,$6,$9,$10}' $file 2>/dev/null", $codes);
+ foreach ($codes as $code) {
+ if (!$code) continue;
+ list($id,$class,$value,$thres,$when,$raw) = explode(' ',$code);
+ $fail = strpos($when,'FAILING_NOW')!==false;
+ if (!$fail && !in_array($id,$events)) continue;
+ if ($fail || ($select ? $thres>0 && $value<=$thres*$level : $raw>0)) {$thumb = 'alert'; break;};
+ }
+ }
+ }
+ my_insert($source, "
");
}
function my_usage(&$source,$used) {
my_insert($source, $used ? "$used
" : "-");
@@ -48,33 +65,28 @@ function mhz($speed) {
function rpm($speed) {
return "$speed RPM";
}
+$path = '/webGui/images';
+$failed = ['FAILED','NOK'];
switch ($_POST['cmd']) {
case 'disk':
- $i = 2;
- $disks = parse_ini_file("state/disks.ini",true);
- $devs = parse_ini_file("state/devs.ini",true);
- $row1 = array_fill(0,26," | "); my_insert($row1[0],"Active");
- $row2 = array_fill(0,26," | "); my_insert($row2[0],"Inactive");
- $row3 = array_fill(0,26," | "); my_insert($row3[0],"Unassigned");
- $row4 = array_fill(0,26," | "); my_insert($row4[0],"Faulty");
- $row5 = array_fill(0,26," | "); my_insert($row5[0],"Heat alarm");
- $row6 = array_fill(0,26," | "); my_insert($row6[0],"SMART status");
- $row7 = array_fill(0,26," | "); my_insert($row7[0],"Utilization");
- foreach ($disks as $disk) {
- $state = $disk['color'];
- $n = 0;
- switch ($disk['type']) {
- case 'Parity':
- if ($disk['status']!='DISK_NP') $n = 1;
- break;
- case 'Data':
- if ($disk['status']!='DISK_NP') $n = $i++;
- break;
- case 'Cache':
- if ($disk['status']!='DISK_NP') $n = $i++;
- if ($disk['name']!='cache') $disk['fsStatus']=='-';
- break;}
+ $i = 1;
+ $var = [];
+ $disks = @parse_ini_file('state/disks.ini',true);
+ $devs = @parse_ini_file('state/devs.ini',true);
+ $saved = @parse_ini_file('state/monitor.ini',true);
+ require_once 'CustomMerge.php';
+ require_once 'Preselect.php';
+ $slots = max(count($disks)+count($devs)-1, 25)+1;
+ $row1 = array_fill(0,$slots,' | '); my_insert($row1[0],'Active');
+ $row2 = array_fill(0,$slots,' | '); my_insert($row2[0],'Inactive');
+ $row3 = array_fill(0,$slots,' | '); my_insert($row3[0],'Unassigned');
+ $row4 = array_fill(0,$slots,' | '); my_insert($row4[0],'Faulty');
+ $row5 = array_fill(0,$slots,' | '); my_insert($row5[0],'Heat alarm');
+ $row6 = array_fill(0,$slots,' | '); my_insert($row6[0],'SMART status');
+ $row7 = array_fill(0,$slots,' | '); my_insert($row7[0],'Utilization');
+ $funcRenderRow = function($n,$disk) use (&$row1,&$row2,&$row3,&$row4,&$row5,&$row6,&$row7,$path,$_POST) {
if ($n>0) {
+ $state = $disk['color'];
switch ($state) {
case 'grey-off':
break; //ignore
@@ -89,15 +101,34 @@ case 'disk':
my_insert($row3[$n],"
");
break;
default:
- my_insert($row4[$n],"
");
+ if ($disk['type']=='Parity' && $disk['status']=='DISK_NP_DSBL')
+ my_insert($row3[$n],"
"); //parity is really unassigned
+ else
+ my_insert($row4[$n],"
");
break;}
$temp = $disk['temp'];
if ($temp>=$_POST['hot']) my_insert($row5[$n],"
".my_temp($temp,$_POST['unit'])."");
- if ($disk['device'] && !strpos($state,'blink')) my_smart($row6[$n],$disk['name']);
- my_usage($row7[$n],($n>1 && $disk['fsStatus']=='Mounted')?(round((1-$disk['fsFree']/$disk['fsSize'])*100).'%'):'');
+ if ($disk['device'] && !strpos($state,'blink')) my_smart($row6[$n],$disk['name'],'Device');
+ my_usage($row7[$n],($disk['type']!='Parity' && $disk['fsStatus']=='Mounted')?(round((1-$disk['fsFree']/$disk['fsSize'])*100).'%'):'');
}
+ };
+ foreach ($disks as $disk)
+ if ($disk['type']=='Parity' && $disk['status']!='DISK_NP')
+ $funcRenderRow($i++,$disk);
+ foreach ($disks as $disk)
+ if ($disk['type']=='Data' && $disk['status']!='DISK_NP')
+ $funcRenderRow($i++,$disk);
+ foreach ($disks as $disk)
+ if ($disk['type']=='Cache' && $disk['status']!='DISK_NP') {
+ if ($disk['name']!='cache') $disk['fsStatus']=='-';
+ $funcRenderRow($i++,$disk);
+ }
+ foreach ($devs as $dev) {
+ $device = $dev['device'];
+ $state = exec("hdparm -C /dev/$device|grep -Po active") ? 'blue-on' : 'blue-blink';
+ if ($state=='blue-on') my_smart($row6[$i],$device,'New');
+ my_insert($row3[$i++],"
");
}
- foreach ($devs as $dev) my_insert($row3[$i++],"
");
echo "".implode('',$row1)."
";
echo "".implode('',$row2)."
";
echo "".implode('',$row3)."
";
@@ -139,20 +170,30 @@ case 'port':
break;
case 'port': exec("ifconfig -s|awk '/^(bond|eth|lo)/{print $3\"#\"$7}'",$ports); break;
case 'link': exec("ifconfig -s|awk '/^(bond|eth|lo)/{print \"Errors: \"$4\"
Drops: \"$5\"
Overruns: \"$6\"#Errors: \"$8\"
Drops: \"$9\"
Overruns: \"$10}'",$ports); break;
- default: $ports = array();}
+ default: $ports = [];}
echo implode('#',$ports);
break;
case 'parity':
$var = parse_ini_file("state/var.ini");
- echo "".($var['mdNumInvalid']==0 ? 'Parity-Check' : ($var['mdInvalidDisk']==0 ? 'Parity-Sync' : 'Data-Rebuild'))." in progress... Completed: ".number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),0)." %.".
- "
Elapsed time: ".my_clock(floor(($var['currTime']-$var['sbUpdated'])/60)).". Estimated finish: ".my_clock(round(((($var['mdResyncDt']*(($var['mdResync']-$var['mdResyncPos'])/($var['mdResyncDb']/100+1)))/100)/60),0))."";
+ $mode = '';
+ if (strstr($var['mdResyncAction'],"recon")) {
+ $mode = 'Parity-Sync / Data-Rebuild';
+ } elseif (strstr($var['mdResyncAction'],"clear")) {
+ $mode = 'Clearing';
+ } elseif ($var['mdResyncAction']=="check") {
+ $mode = 'Read-Check';
+ } elseif (strstr($var['mdResyncAction'],"check")) {
+ $mode = 'Parity-Check';
+ }
+ echo "".$mode." in progress... Completed: ".number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),0)." %.";
+ echo "
Elapsed time: ".my_clock(floor(($var['currTime']-$var['sbUpdated'])/60)).". Estimated finish: ".my_clock(round(((($var['mdResyncDt']*(($var['mdResync']-$var['mdResyncPos'])/($var['mdResyncDb']/100+1)))/100)/60),0))."";
break;
case 'shares':
$names = explode(',',$_POST['names']);
switch ($_POST['com']) {
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 = array();
+ $counts = array_count_values($lsof); $count = [];
foreach ($names as $name) $count[] = isset($counts[$name]) ? $counts[$name] : 0;
echo implode('#',$count);
break;
diff --git a/plugins/dynamix/include/DefaultPageLayout.php b/plugins/dynamix/include/DefaultPageLayout.php
index 2ed024127..21db3c9b3 100644
--- a/plugins/dynamix/include/DefaultPageLayout.php
+++ b/plugins/dynamix/include/DefaultPageLayout.php
@@ -40,6 +40,7 @@ Shadowbox.init({skipSetup:true});
// server uptime
var uptime = =strtok(exec("cat /proc/uptime"),' ')?>;
+var before = new Date();
// Page refresh timer
var update = =abs($display['refresh'])/1000?>;
@@ -64,11 +65,13 @@ function plus(value, label, last) {
return value>0 ? (value+' '+label+(value!=1?'s':'')+(last?'':', ')) : '';
}
function updateTime() {
+ var now = new Date();
days = parseInt(uptime/86400);
hour = parseInt(uptime/3600%24);
mins = parseInt(uptime/60%60);
$('#uptime').html(((days|hour|mins)?plus(days,'day',(hour|mins)==0)+plus(hour,'hour',mins==0)+plus(mins,'minute',true):'less than a minute'));
- uptime++;
+ uptime += Math.round((now.getTime() - before.getTime())/1000);
+ before = now;
setTimeout(updateTime,1000);
}
function refresh(top) {
diff --git a/plugins/dynamix/include/DeleteLogFile.php b/plugins/dynamix/include/DeleteLogFile.php
index 36f8f7582..7b03556bd 100644
--- a/plugins/dynamix/include/DeleteLogFile.php
+++ b/plugins/dynamix/include/DeleteLogFile.php
@@ -11,6 +11,8 @@
*/
?>
-$log = $_GET['log'];
-if (basename($log,'.notify')=='*') array_map('unlink',glob($log,GLOB_NOSORT)); else unlink($log);
+require_once 'Wrappers.php';
+
+$dynamix = parse_plugin_cfg('dynamix',true);
+if (strpos($_POST['log'],'*')===false) @unlink("{$dynamix['notify']['path']}/archive/{$_POST['log']}"); else array_map('unlink',glob("{$dynamix['notify']['path']}/archive/{$_POST['log']}",GLOB_NOSORT));
?>
diff --git a/plugins/dynamix/include/DeviceList.php b/plugins/dynamix/include/DeviceList.php
index 78342590b..7738d86fe 100644
--- a/plugins/dynamix/include/DeviceList.php
+++ b/plugins/dynamix/include/DeviceList.php
@@ -11,216 +11,167 @@
*/
?>
-require_once('Helpers.php');
+require_once 'Helpers.php';
-$path = $_POST['path'];
-$width = $_POST['width'];
-$var = parse_ini_file("state/var.ini");
-$devs = parse_ini_file("state/devs.ini",true);
-$disks = parse_ini_file("state/disks.ini",true);
-$screen = '/tmp/screen_buffer';
+$path = $_POST['path'];
+$var = parse_ini_file('state/var.ini');
+$devs = parse_ini_file('state/devs.ini',true);
+$disks = parse_ini_file('state/disks.ini',true);
+$sum = ['count'=>0, 'temp'=>0, 'fsSize'=>0, 'fsUsed'=>0, 'fsFree'=>0, 'numReads'=>0, 'numWrites'=>0, 'numErrors'=>0];
+extract(parse_plugin_cfg('dynamix',true));
-$temps=0; $counts=0; $tot_size=0; $tot_used=0; $tot_free=0; $reads=0; $writes=0; $errors=0;
+require_once 'CustomMerge.php';
-extract(parse_plugin_cfg("dynamix",true));
-
-function device_info($disk) {
- global $path, $width, $var, $display, $screen;
- $href = $disk['name'];
- if ($href != 'preclear') {
- $name = my_disk($href);
- $type = $disk['type'];
- } else {
- $name = $disk['device'];
- $type = 'Preclear';
- $href = "{$disk['device']}&file=$screen";
+function in_parity_log($log,$timestamp) {
+ if (file_exists($log)) {
+ $handle = fopen($log, 'r');
+ while (($line = fgets($handle)) !== false) {
+ if (strpos($line,$timestamp)!==false) break;
+ }
+ fclose($handle);
}
- if ($var['fsState']=='Started' && $type!='Flash' && $type!='Preclear' && strpos($disk['status'], 'DISK_NP')===false) {
- $action = strpos($disk['color'],'blink')===false ? "down" : "up";
- $ctrl = "";
- }
- else
- $ctrl = "";
- $ball = "/webGui/images/{$disk['color']}.png";
+ return !empty($line);
+}
+function device_info(&$disk) {
+ global $path, $var;
+ $name = $disk['name'];
+ $type = $disk['type']=='Flash' || $disk['type']=='New' ? $disk['type'] : 'Device';
+ $action = strpos($disk['color'],'blink')===false ? 'down' : 'up';
+ if ($var['fsState']=='Started' && $type!='Flash') {
+ $cmd = $type=='New' ? "cmd=/webGui/scripts/hd_parm&arg1=$action&arg2=$name" : "cmdSpin$action=$name";
+ $ctrl = "";
+ } else
+ $ctrl = '';
switch ($disk['color']) {
case 'green-on': $help = 'Normal operation, device is active'; break;
case 'green-blink': $help = 'Device is in standby mode (spun-down)'; break;
- case 'blue-on': $help = ($disk['name']=='preclear' ? 'Unassigned device' : 'New device'); break;
- case 'blue-blink': $help = ($disk['name']=='preclear' ? 'Unassigned device, in standby mode' : 'New device, in stadby mode (spun-down)'); break;
- case 'yellow-on': $help = ($disk['type']=='Parity' ? 'Parity is invalid' : 'Device contents emulated'); break;
- case 'yellow-blink': $help = 'Device contents emulated, in standby mode (spun-down)'; break;
- case 'red-on':
- case 'red-blink': $help = ($disk['type']=='Parity' ? 'Parity device is disabled' : 'Device is disabled, contents emulated'); break;
- case 'red-off': $help = ($disk['type']=='Parity' ? 'Parity device missing' : 'Device is missing (disabled), contents emulated'); break;
+ case 'blue-on': $help = 'New device'; break;
+ case 'blue-blink': $help = 'New device, in standby mode (spun-down)'; break;
+ case 'yellow-on': $help = $disk['type']=='Parity' ? 'Parity is invalid' : 'Device contents emulated'; break;
+ case 'yellow-blink': $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': $help = $disk['type']=='Parity' ? 'Parity device is disabled' : 'Device is disabled, contents emulated'; break;
+ case 'red-off': $help = $disk['type']=='Parity' ? 'Parity device is missing' : 'Device is missing (disabled), contents emulated'; break;
case 'grey-off': $help = 'Device not present'; break;
}
- switch ($type) {
- case 'Flash':
- $device = "Flash";
- break;
- default:
- $device = "Device";
- break;
- }
- $status = "
${help}";
- $link = strpos($disk['status'], 'DISK_NP')===false ? "$name" : $name;
- return $ctrl.$status.$link;
+ $status = "$ctrl
$help";
+ $link = strpos($disk['status'], 'DISK_NP')===false ? "".my_disk($name)."" : my_disk($name);
+ return $status.$link;
}
-function device_browse($disk) {
+function device_browse(&$disk) {
global $path;
if ($disk['fsStatus']=='Mounted') {
- $dir = $disk['name']=="flash" ? "/boot" : "/mnt/{$disk['name']}";
+ $dir = $disk['name']=='flash' ? "/boot" : "/mnt/{$disk['name']}";
return "
";
}
}
-function device_desc($disk) {
+function device_desc(&$disk) {
global $var;
- $size = my_scale($disk['size']*1024, $unit);
- return "{$disk['id']} - $size $unit ({$disk['device']})";
+ $size = my_scale($disk['size']*1024,$unit);
+ $log = $var['fsState']=='Started' ? "" : "";
+ return "$log{$disk['id']} - $size $unit ({$disk['device']})";
}
-function assignment($disk) {
- global $var, $disks, $devs, $screen;
- $out = "";
}
-function render_used_and_free($disk) {
+function fs_info(&$disk) {
global $display;
if ($disk['type']=='Parity' || $disk['fsStatus']=='-') {
- echo " | ";
+ echo " | ";
+ return;
} else if ($disk['fsStatus']=='Mounted') {
echo "{$disk['fsType']} | ";
- echo "".my_scale($disk['fsSize']*1024, $unit)." $unit | ";
- switch ($display['text']) {
- case 0:
- $text1 = true; $text2 = true; break;
- case 1: case 2:
- $text1 = false; $text2 = false; break;
- case 10: case 20:
- $text1 = true; $text2 = false; break;
- case 11: case 21:
- $text1 = false; $text2 = true; break;
- }
- if ($text1) {
- echo "".my_scale($disk['fsUsed']*1024, $unit)." $unit | ";
+ echo "".my_scale($disk['fsSize']*1024,$unit)." $unit | ";
+ if ($display['text']%10==0) {
+ echo "".my_scale($disk['fsUsed']*1024,$unit)." $unit | ";
} else {
- $used = $disk['fsSize'] ? 100 - round(100*$disk['fsFree']/$disk['fsSize']) : 0;
- echo "".my_scale($disk['fsUsed']*1024, $unit)." $unit | ";
+ $used = $disk['fsSize'] ? 100-round(100*$disk['fsFree']/$disk['fsSize']) : 0;
+ echo "".my_scale($disk['fsUsed']*1024,$unit)." $unit | ";
}
- if ($text2) {
- echo "".my_scale($disk['fsFree']*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 = $disk['fsSize'] ? round(100*$disk['fsFree']/$disk['fsSize']) : 0;
- echo "".my_scale($disk['fsFree']*1024, $unit)." $unit | ";
+ echo "".my_scale($disk['fsFree']*1024,$unit)." $unit | ";
}
- } else {
+ } else
echo " | {$disk['fsStatus']} | | ";
- }
+ echo "".device_browse($disk)." | ";
}
-function array_offline($disk) {
+function array_offline(&$disk) {
echo "";
switch ($disk['status']) {
- case "DISK_NP":
- case "DISK_OK_NP":
- case "DISK_NP_DSBL":
+ case 'DISK_NP':
+ case 'DISK_OK_NP':
+ case 'DISK_NP_DSBL':
echo "| ".device_info($disk)." | ";
echo "".assignment($disk)." | ";
echo " | ";
break;
- case "DISK_OK":
- case "DISK_INVALID":
- case "DISK_DSBL":
- case "DISK_DSBL_NEW":
- case "DISK_NEW":
+ case 'DISK_OK':
+ case 'DISK_INVALID':
+ case 'DISK_DSBL':
+ case 'DISK_DSBL_NEW':
+ case 'DISK_NEW':
echo "".device_info($disk)." | ";
echo "".assignment($disk)." | ";
echo "".my_temp($disk['temp'])." | ";
echo " | ";
break;
- case "DISK_NP_MISSING":
+ case 'DISK_NP_MISSING':
echo "".device_info($disk)."Missing | ";
- echo "".assignment($disk)."{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024, $unit)." $unit | ";
+ echo "".assignment($disk)."{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024,$unit)." $unit | ";
echo " | ";
break;
- case "DISK_WRONG":
+ case 'DISK_WRONG':
echo "".device_info($disk)."Wrong | ";
- echo "".assignment($disk)."{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024, $unit)." $unit | ";
+ echo "".assignment($disk)."{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024,$unit)." $unit | ";
echo "".my_temp($disk['temp'])." | ";
echo " | ";
break;
}
echo "
";
}
-function array_online($disk) {
- global $display, $temps, $counts, $tot_size, $tot_used, $tot_free, $reads, $writes, $errors;
+function array_online(&$disk) {
+ global $sum;
if (is_numeric($disk['temp'])) {
- $temps += $disk['temp'];
- $counts++;
+ $sum['count']++;
+ $sum['temp'] += $disk['temp'];
}
- $reads += $disk['numReads'];
- $writes += $disk['numWrites'];
- $errors += $disk['numErrors'];
- if (isset($disk['fsFree']) && $disk['type']!='Parity') {
- $disk['fsUsed'] = $disk['fsSize'] - $disk['fsFree'];
- $tot_size += $disk['fsSize'];
- $tot_free += $disk['fsFree'];
- $tot_used += $disk['fsUsed'];
+ $sum['numReads'] += $disk['numReads'];
+ $sum['numWrites'] += $disk['numWrites'];
+ $sum['numErrors'] += $disk['numErrors'];
+ if (isset($disk['fsFree'])) {
+ $disk['fsUsed'] = $disk['fsSize']-$disk['fsFree'];
+ $sum['fsSize'] += $disk['fsSize'];
+ $sum['fsUsed'] += $disk['fsUsed'];
+ $sum['fsFree'] += $disk['fsFree'];
}
echo "";
switch ($disk['status']) {
- case "DISK_NP":
-// Suppress empty slots to keep device list short
-// this actually should be configurable
+ case 'DISK_NP':
+// Suppress empty slots to keep device list short (make this configurable?)
// echo "| ".device_info($disk)." | ";
// echo "Not installed | ";
// echo " | ";
break;
- case "DISK_OK_NP":
+ case 'DISK_OK_NP':
+ case 'DISK_NP_DSBL':
echo "".device_info($disk)." | ";
- echo "Not Installed | ";
+ echo "Not installed | ";
echo " | ";
- render_used_and_free($disk);
- echo "".device_browse($disk)." | ";
- break;
- case "DISK_NP_DSBL":
- echo "".device_info($disk)." | ";
- if ($disk['type']=="Parity") {
- echo "Not installed | ";
- echo " | ";
- } else {
- echo "Not installed | ";
- echo " | ";
- render_used_and_free($disk);
- echo "".device_browse($disk)." | ";
- }
- break;
- case "DISK_DSBL":
- echo "".device_info($disk)." | ";
- echo "".device_desc($disk)." | ";
- echo "".my_temp($disk['temp'])." | ";
- echo "".my_number($disk['numReads'])." | ";
- echo "".my_number($disk['numWrites'])." | ";
- echo "".my_number($disk['numErrors'])." | ";
- if ($disk['type']=="Parity") {
- echo " | ";
- } else {
- render_used_and_free($disk);
- echo "".device_browse($disk)." | ";
- }
+ fs_info($disk);
break;
+ case 'DISK_DSBL':
default:
echo "".device_info($disk)." | ";
echo "".device_desc($disk)." | ";
@@ -228,8 +179,7 @@ function array_online($disk) {
echo "".my_number($disk['numReads'])." | ";
echo "".my_number($disk['numWrites'])." | ";
echo "".my_number($disk['numErrors'])." | ";
- render_used_and_free($disk);
- echo "".device_browse($disk)." | ";
+ fs_info($disk);
break;
}
echo "
";
@@ -241,53 +191,44 @@ 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($device, $item) {
+function read_disk(&$device, $item) {
global $var;
- $smart = "/var/local/emhttp/smart/$device";
- if (!file_exists($smart) || (time()-filemtime($smart)>=$var['poll_attributes'])) exec("smartctl -n standby -A /dev/$device > $smart");
- $temp = exec("awk '/Temperature/{print \$10;exit}' $smart");
switch ($item) {
- case 'color': return $temp ? 'blue-on' : 'blue-blink';
- case 'temp' : return $temp ? $temp : '*';
+ case 'color':
+ return exec("hdparm -C /dev/$device|grep -Po active") ? 'blue-on' : 'blue-blink';
+ case 'temp':
+ $smart = "/var/local/emhttp/smart/$device";
+ if (!file_exists($smart) || (time()-filemtime($smart)>=$var['poll_attributes'])) exec("smartctl -n standby -A /dev/$device > $smart");
+ $temp = exec("awk '\$1==190||\$1==194{print \$10;exit}' $smart");
+ return $temp ? $temp : '*';
}
}
function show_totals($text) {
- global $var, $display, $temps, $counts, $tot_size, $tot_used, $tot_free, $reads, $writes, $errors;
+ global $var, $display, $sum;
echo "";
echo " Total | ";
echo "$text | ";
- echo "".($counts>0?my_temp(round($temps/$counts, 1)):'*')." | ";
- echo "".my_number($reads)." | ";
- echo "".my_number($writes)." | ";
- echo "".my_number($errors)." | ";
+ echo "".($sum['count']>0 ? my_temp(round($sum['temp']/$sum['count'],1)) : '*')." | ";
+ echo "".my_number($sum['numReads'])." | ";
+ echo "".my_number($sum['numWrites'])." | ";
+ echo "".my_number($sum['numErrors'])." | ";
echo " | ";
- if (strstr($text,"Array") && ($var['startMode'] == "Normal")) {
- echo "".my_scale($tot_size*1024, $unit)." $unit | ";
- switch ($display['text']) {
- case 0:
- $text1 = true; $text2 = true; break;
- case 1: case 2:
- $text1 = false; $text2 = false; break;
- case 10: case 20:
- $text1 = true; $text2 = false; break;
- case 11: case 21:
- $text1 = false; $text2 = true; break;
- }
- if ($text1) {
- echo "".my_scale($tot_used*1024, $unit)." $unit | ";
+ if (strstr($text,'Array') && ($var['startMode']=='Normal')) {
+ echo "".my_scale($sum['fsSize']*1024,$unit)." $unit | ";
+ if ($display['text']%10==0) {
+ echo "".my_scale($sum['fsUsed']*1024,$unit)." $unit | ";
} else {
- $used = $tot_size ? 100 - round(100*$tot_free/$tot_size) : 0;
- echo "".my_scale($tot_used*1024, $unit)." $unit | ";
+ $used = $sum['fsSize'] ? 100-round(100*$sum['fsFree']/$sum['fsSize']) : 0;
+ echo "".my_scale($sum['fsUsed']*1024,$unit)." $unit | ";
}
- if ($text2) {
- echo "".my_scale($tot_free*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 = $tot_size ? round(100*$tot_free/$tot_size) : 0;
- echo "".my_scale($tot_free*1024, $unit)." $unit | ";
+ $free = $sum['fsSize'] ? round(100*$sum['fsFree']/$sum['fsSize']) : 0;
+ echo "".my_scale($sum['fsFree']*1024,$unit)." $unit | ";
}
echo " | ";
- }
- else
+ } else
echo " | ";
echo "
";
}
@@ -295,13 +236,13 @@ function array_slots() {
global $var;
$min = max($var['sbNumDisks'], 3);
$max = $var['MAX_ARRAYSZ'];
- $out = "";
- $out .= "";
+ $noparity2 = ($var['regTy']=='Pro')?0:1;
+ $out = "";
$out .= "";
- $out .= "";
return $out;
@@ -310,13 +251,12 @@ function cache_slots() {
global $var;
$min = $var['cacheSbNumDisks'];
$max = $var['MAX_CACHESZ'];
- $out = "";
- $out .= "";
+ $out = "";
$out .= "";
- $out .= "";
+ $out .= "";
for ($n=$min; $n<=$max; $n++) {
- $option = $n ? $n : "none";
- $selected = ($n == $var['SYS_CACHE_SLOTS'])? " selected" : "";
+ $option = $n ? $n : 'none';
+ $selected = ($n == $var['SYS_CACHE_SLOTS'])? ' selected' : '';
$out .= "";
}
$out .= "";
@@ -331,12 +271,12 @@ case 'array':
} else {
foreach ($disks as $disk) {if ($disk['type']=='Parity') array_online($disk);}
foreach ($disks as $disk) {if ($disk['type']=='Data') array_online($disk);}
- if ($display['total']) show_totals("Array of ".my_word($var['mdNumDisks'])." devices");
+ if ($display['total']) show_totals('Array of '.my_word($var['mdNumDisks']).' devices');
}
break;
case 'flash':
$disk = &$disks['flash'];
- $disk['fsUsed'] = $disk['fsSize'] - $disk['fsFree'];
+ $disk['fsUsed'] = $disk['fsSize']-$disk['fsFree'];
echo "";
echo "| ".device_info($disk)." | ";
echo "".device_desc($disk)." | ";
@@ -344,33 +284,30 @@ case 'flash':
echo "".my_number($disk['numReads'])." | ";
echo "".my_number($disk['numWrites'])." | ";
echo "".my_number($disk['numErrors'])." | ";
- render_used_and_free($disk);
- echo "".device_browse($disk)." | ";
+ fs_info($disk);
echo "
";
break;
case 'cache':
if ($var['fsState']=='Stopped') {
foreach ($disks as $disk) {if ($disk['type']=='Cache') array_offline($disk);}
echo " Slots: | ".cache_slots()." | |
";
- echo " | |
";
} else {
foreach ($disks as $disk) {if ($disk['type']=='Cache') array_online($disk);}
- if ($display['total'] && $var['cacheSbNumDisks']>1) show_totals("Pool of ".my_word($var['cacheNumDevices'])." devices");
+ if ($display['total'] && $var['cacheSbNumDisks']>1) show_totals('Pool of '.my_word($var['cacheNumDevices']).' devices');
}
break;
case 'open':
- $status = isset($confirm['preclear']) ? '' : '_NP';
foreach ($devs as $dev) {
- $dev['name'] = 'preclear';
- $dev['color'] = read_disk($dev['device'], 'color');
- $dev['temp'] = read_disk($dev['device'], 'temp');
- $dev['status'] = $status;
+ $dev['name'] = $dev['device'];
+ $dev['type'] = 'New';
+ $dev['color'] = read_disk($dev['device'],'color');
+ $dev['temp'] = read_disk($dev['device'],'temp');
echo "";
echo "| ".device_info($dev)." | ";
echo "".device_desc($dev)." | ";
echo "".my_temp($dev['temp'])." | ";
if (file_exists("/tmp/preclear_stat_{$dev['device']}")) {
- $text = exec("cut -d'|' -f3 /tmp/preclear_stat_{$dev['device']} | sed 's:\^n:\
:g'");
+ $text = exec("cut -d'|' -f3 /tmp/preclear_stat_{$dev['device']}|sed 's:\^n:\
:g'");
if (strpos($text,'Total time')===false) $text = 'Preclear in progress... '.$text;
echo "$text | ";
} else
@@ -381,13 +318,29 @@ case 'open':
case 'parity':
$data = array();
if ($var['mdResync']>0) {
- $data[] = my_scale($var['mdResync']*1024, $unit)." $unit";
+ $data[] = my_scale($var['mdResync']*1024,$unit)." $unit";
$data[] = my_clock(floor(($var['currTime']-$var['sbUpdated'])/60));
- $data[] = my_scale($var['mdResyncPos']*1024, $unit)." $unit (".number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),1,substr($display['number'],0,1),'')." %)";
- $data[] = my_scale($var['mdResyncDb']/$var['mdResyncDt']*1024, $unit, 1)." $unit/sec";
+ $data[] = my_scale($var['mdResyncPos']*1024,$unit)." $unit (".number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),1,substr($display['number'],0,1),'')." %)";
+ $data[] = my_scale($var['mdResyncDb']/$var['mdResyncDt']*1024,$unit, 1)." $unit/sec";
$data[] = my_clock(round(((($var['mdResyncDt']*(($var['mdResync']-$var['mdResyncPos'])/($var['mdResyncDb']/100+1)))/100)/60),0));
$data[] = $var['sbSyncErrs'];
echo implode(';',$data);
+ } else {
+ if ($var['sbSynced']==0) break;
+ $log = '/boot/config/parity-checks.log';
+ $timestamp = str_replace(['.0','.'],[' ',' '],date('M.d H:i:s',$var['sbSynced']));
+ if (in_parity_log($log,$timestamp)) break;
+ exec("grep -Po '^$timestamp .*(sync done. \Ktime=\d+|sync completion \Kstatus: -?\d+)' /var/log/syslog", $rows);
+ $duration = 0; $speed = 0; $status = 0;
+ foreach ($rows as $row) {
+ if (strpos($row,'time=')!==false) {
+ $duration = substr($row,5);
+ } elseif (strpos($row,'status:')!==false) {
+ $status = substr($row,8);
+ }
+ }
+ if ($duration>0) $speed = isset($disks['parity']['sizeSb']) ? my_scale($disks['parity']['sizeSb']*1024/$duration,$unit,1)." $unit/s" : "Unknown";
+ file_put_contents($log,"$timestamp|$duration|$speed|$status\n",FILE_APPEND);
}
break;
}
diff --git a/plugins/dynamix/include/DiskList.php b/plugins/dynamix/include/DiskList.php
new file mode 100644
index 000000000..2505dc411
--- /dev/null
+++ b/plugins/dynamix/include/DiskList.php
@@ -0,0 +1,114 @@
+
+
+require_once 'Helpers.php';
+
+$shares = parse_ini_file('state/shares.ini',true);
+$disks = parse_ini_file('state/disks.ini',true);
+$var = parse_ini_file('state/var.ini');
+$sec = parse_ini_file('state/sec.ini',true);
+$sec_nfs = parse_ini_file('state/sec_nfs.ini',true);
+$sec_afp = parse_ini_file('state/sec_afp.ini',true);
+$compute = $_GET['compute'];
+$path = $_GET['path'];
+$prev = $_GET['prev'];
+
+$display = [];
+$display['scale'] = $_GET['scale'];
+$display['number'] = $_GET['number'];
+
+// Display export settings
+function disk_share_settings($protocol,$share) {
+ if (empty($share)) return;
+ if ($protocol!='yes' || $share['export']=='-') return "-";
+ if ($share['export']=='e') return ucfirst($share['security']);
+ return ''.ucfirst($share['security']).'';
+}
+
+function globalInclude($name) {
+ global $var;
+ return substr($name,0,4)!='disk' || !$var['shareUserInclude'] || strpos("{$var['shareUserInclude']},","$name,")!==false;
+}
+
+function shareInclude($name) {
+ global $include;
+ 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 \"$name\" \"ssz2\"");
+
+// global shares include/exclude
+$myDisks = array_filter(array_diff(array_keys($disks), explode(',',$var['shareUserExclude'])), 'globalInclude');
+
+// Share size per disk
+$preserve = ($path==$prev || $compute=='yes');
+$ssz2 = array();
+foreach (glob("state/*.ssz2", GLOB_NOSORT) as $entry) {
+ if ($preserve) {
+ $ssz2[basename($entry, ".ssz2")] = parse_ini_file($entry);
+ } else {
+ unlink($entry);
+ }
+}
+
+// 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;
+ $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;
+ }
+ echo "
";
+ echo " $help$name | ";
+ echo "{$disk['comment']} | ";
+ echo "".disk_share_settings($var['shareSMBEnabled'], $sec[$name])." | ";
+ echo "".disk_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])." | ";
+ echo "".disk_share_settings($var['shareAFPEnabled'], $sec_afp[$name])." | ";
+ $cmd="/webGui/scripts/disk_size"."&arg1=".urlencode($name)."&arg2=ssz2";
+ if (array_key_exists($name, $ssz2)) {
+ echo "".my_scale(($disk['fsSize'])*1024, $unit)." $unit | ";
+ echo "".my_scale($disk['fsFree']*1024, $unit)." $unit | ";
+ echo " | ";
+ echo "
";
+ foreach ($ssz2[$name] as $sharename => $sharesize) {
+ if ($sharename=='share.total') continue;
+ $include = $shares[$sharename]['include'];
+ $inside = in_array($disk['name'], array_filter(array_diff($myDisks, explode(',',$shares[$sharename]['exclude'])), 'shareInclude'));
+ echo "" : " warning'>");
+ echo "| $sharename: | ";
+ echo "".($inside ? "" : "Share is outside the list of designated disks")." | ";
+ echo " | ";
+ echo " | ";
+ echo " | ";
+ echo "".my_scale($sharesize*1024, $unit)." $unit | ";
+ echo "".my_scale($disk['fsFree']*1024, $unit)." $unit | ";
+ echo " | ";
+ echo "
";
+ }
+ } else {
+ echo "Compute... | ";
+ echo "".my_scale($disk['fsFree']*1024, $unit)." $unit | ";
+ echo " | ";
+ echo "";
+ }
+}
+if ($row==0) {
+ echo "| There are no exportable disk shares |
";
+}
+?>
diff --git a/plugins/dynamix/include/Dispatcher.php b/plugins/dynamix/include/Dispatcher.php
new file mode 100644
index 000000000..9af85f01e
--- /dev/null
+++ b/plugins/dynamix/include/Dispatcher.php
@@ -0,0 +1,27 @@
+
+
+$keys = parse_ini_file($_POST['#cfg'], true);
+$text = "";
+
+foreach ($_POST as $field => $value) {
+ if ($field[0] == '#') continue;
+ list($section,$key) = explode('_', $field, 2);
+ $keys[$section][$key] = $value;
+}
+foreach ($keys as $section => $block) {
+ $text .= "[$section]\n";
+ foreach ($block as $key => $value) $text .= "$key=\"$value\"\n";
+}
+file_put_contents($_POST['#cfg'], $text);
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/include/Helpers.php b/plugins/dynamix/include/Helpers.php
index 4e7fe9a55..9edf1f4a6 100644
--- a/plugins/dynamix/include/Helpers.php
+++ b/plugins/dynamix/include/Helpers.php
@@ -11,26 +11,28 @@
*/
?>
-require_once('Wrappers.php');
+require_once 'Wrappers.php';
// Helper functions
-function my_scale($value, &$unit, $precision = NULL) {
+function my_scale($value, &$unit, $decimals = NULL) {
global $display;
$scale = $display['scale'];
$number = $display['number'];
$dot = substr($number,0,1);
$comma = substr($number,1,1);
$units = array('B','KB','MB','GB','TB','PB');
- if ($scale==0 && !$precision) {
+ if ($scale==0 && $decimals==NULL) {
+ $decimals = 0;
$unit = '';
- return number_format($value, 0, $dot, ($value>=10000 ? $comma : ''));
} else {
$base = $value ? floor(log($value, 1000)) : 0;
if ($scale>0 && $base>$scale) $base = $scale;
+ $value /= pow(1000, $base);
+ if ($decimals==NULL) $decimals = $value>=100 ? 0 : ($value>=10 ? 1 : (round($value*100)%100==0 ? 0 : 2));
+ if ($scale<0 && round($value,$decimals)==1000) { $value = 1; $base++; }
$unit = $units[$base];
- $value = round($value/pow(1000, $base), $precision ? $precision : 2);
- return number_format($value, $precision ? $precision : (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)), $dot, ($value>=10000 ? $comma : ''));
}
+ return number_format($value, $decimals, $number[0], $value>=10000 ? $number[1] : '');
}
function my_number($value) {
global $display;
@@ -58,7 +60,7 @@ function my_word($num) {
return $num0) {
$used = $arraysize ? 100-round(100*$arrayfree/$arraysize) : 0;
- echo "{$used}%
";
+ echo "{$used}%
";
} else {
echo "".($var['fsState']=='Started'?'Maintenance':'off-line')."
";
}
}
-function usage_color($limit,$free) {
+function usage_color(&$disk,$limit,$free) {
global $display;
if ($display['text']==1 || intval($display['text']/10)==1) return '';
+ $critical = !empty($disk['critical']) ? $disk['critical'] : $display['critical'];
+ $warning = !empty($disk['warning']) ? $disk['warning'] : $display['warning'];
if (!$free) {
- if ($limit>=$display['critical']) return 'redbar';
- if ($limit>=$display['warning']) return 'orangebar';
+ if ($limit>=$critical) return 'redbar';
+ if ($limit>=$warning) return 'orangebar';
return 'greenbar';
} else {
- if ($limit<=100-$display['critical']) return 'redbar';
- if ($limit<=100-$display['warning']) return 'orangebar';
+ if ($limit<=100-$critical) return 'redbar';
+ if ($limit<=100-$warning) return 'orangebar';
return 'greenbar';
}
}
-function my_check($time) {
- global $var;
- if (!$time) return "unavailable (system reboot or log rotation)";
+function my_check($time,$speed) {
+ if (!$time) return 'unavailable (no parity-check entries logged)';
$days = floor($time/86400);
$hmss = $time-$days*86400;
$hour = floor($hmss/3600);
$mins = $hmss/60%60;
$secs = $hmss%60;
- return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true).". Average speed: ".my_scale($var['mdResyncSize']*1024/$time,$unit,1)." $unit/sec";
+ return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true).". Average speed: $speed";
}
function my_error($code) {
switch ($code) {
@@ -140,6 +143,18 @@ function day_count($time) {
function plus($val, $word, $last) {
return $val>0 ? (($val || $last) ? ($val.' '.$word.($val!=1?'s':'').($last ?'':', ')) : '') : '';
}
+function read_parity_log($epoch) {
+ $log = '/boot/config/parity-checks.log';
+ if (file_exists($log)) {
+ $timestamp = str_replace(['.0','.'],[' ',' '],date('M.d H:i:s',$epoch));
+ $handle = fopen($log, 'r');
+ while (($line = fgets($handle)) !== false) {
+ if (strpos($line,$timestamp)!==false) break;
+ }
+ fclose($handle);
+ }
+ return $line ? $line : '0|0|0|0';
+}
function urlencode_path($path) {
return str_replace("%2F", "/", urlencode($path));
}
@@ -209,7 +224,7 @@ function transpose_user_path($path) {
if (!empty($realdisk)) {
// there may be several disks participating in this path (e.g. disk1,2,3) so
// only return the first disk and replace 'user' with say 'cache' or 'disk1'
- $path = str_replace('/mnt/user/', '/mnt/'.strtok($realdisk.',', ',').'/', $dir);
+ $path = str_replace('/mnt/user/', '/mnt/'.strtok($realdisk.',', ',').'/', $path);
}
}
return $path;
diff --git a/plugins/dynamix/include/NotificationsArchive.php b/plugins/dynamix/include/NotificationsArchive.php
index 5b2886d4a..62c8f6143 100644
--- a/plugins/dynamix/include/NotificationsArchive.php
+++ b/plugins/dynamix/include/NotificationsArchive.php
@@ -11,18 +11,33 @@
*/
?>
-$files = glob($_POST['log'], GLOB_NOSORT);
+require_once 'Wrappers.php';
+
+$dynamix = parse_plugin_cfg('dynamix',true);
+$files = glob("{$dynamix['notify']['path']}/archive/*.notify", GLOB_NOSORT);
usort($files, create_function('$a,$b', 'return filemtime($b)-filemtime($a);'));
+$row = 1;
foreach ($files as $file) {
- $fields = preg_split('/\n/', file_get_contents($file));
+ $fields = explode(PHP_EOL, file_get_contents($file));
+ $archive = basename($file);
+ if ($extra = count($fields)>6) {
+ $td_ = ""; $_td = " | ";
+ } else {
+ $td_ = ""; $_td = " | ";
+ }
$c = 0;
foreach ($fields as $field) {
- if (!$field) continue;
- $item = explode('=', $field);
- if (!$c++) echo "| ".date("{$_POST['date']} {$_POST['time']}", $item[1])." | "; else echo "{$item[1]} | ";
+ if ($c==5) break;
+ $item = $field ? explode('=', $field, 2) : array("","-");
+ echo (!$c++) ? "
$td_".date("{$dynamix['notify']['date']} {$dynamix['notify']['time']}", $item[1])."$_td" : "| {$item[1]} | ";
+ }
+ echo " |
";
+ if ($extra) {
+ $item = explode('=', $field, 2);
+ echo "| {$item[1]} |
|
";
+ $row++;
}
- echo " | ";
}
if (empty($files)) echo "| No notifications available |
";
?>
diff --git a/plugins/dynamix/include/PageBuilder.php b/plugins/dynamix/include/PageBuilder.php
index 857f3b81c..ed50bbd52 100644
--- a/plugins/dynamix/include/PageBuilder.php
+++ b/plugins/dynamix/include/PageBuilder.php
@@ -11,7 +11,21 @@
*/
?>
-require_once('Markdown.php');
+require_once 'Markdown.php';
+
+function get_ini_key($key,$default) {
+ $x = strpos($key, '[');
+ $var = $x>0 ? substr($key,1,$x-1) : substr($key,1);
+ global $$var;
+ eval("\$var=$key;");
+ return $var ? $var : $default;
+}
+
+function get_file_key($file,$default) {
+ list($key, $default) = explode('=',$default,2);
+ $var = @parse_ini_file($file);
+ return isset($var[$key]) ? $var[$key] : $default;
+}
function build_pages($pattern) {
global $site;
@@ -32,6 +46,10 @@ function find_pages($item) {
foreach ($site as $page) {
if (empty($page['Menu'])) continue;
$menu = strtok($page['Menu'], ' ');
+ switch ($menu[0]) {
+ case '$': $menu = get_ini_key($menu,strtok(' ')); break;
+ case '/': $menu = get_file_key($menu,strtok(' ')); break;
+ }
while ($menu !== false) {
$add = explode(':', $menu);
$add[] = '';
diff --git a/plugins/dynamix/include/ParityHistory.php b/plugins/dynamix/include/ParityHistory.php
new file mode 100644
index 000000000..60a7ed0d5
--- /dev/null
+++ b/plugins/dynamix/include/ParityHistory.php
@@ -0,0 +1,51 @@
+
+
+function plus($val, $word, $last) {
+ return $val>0 ? (($val||$last)?($val.' '.$word.($last?'':', ')):'') : '';
+}
+function my_duration($time) {
+ if (!$time) return 'Unavailable';
+ $days = floor($time/86400);
+ $hmss = $time-$days*86400;
+ $hour = floor($hmss/3600);
+ $mins = $hmss/60%60;
+ $secs = $hmss%60;
+ return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hr',($mins|$secs)==0).plus($mins,'min',$secs==0).plus($secs,'sec',true);
+}
+?>
+
+
+
+
+
+
+
+| Date | Duration | Speed | Status |
+
+$log = '/boot/config/parity-checks.log'; $row = 0;
+if (file_exists($log)) {
+ $handle = fopen($log, 'r');
+ while (($line = fgets($handle)) !== false) {
+ list($date,$duration,$speed,$status) = explode('|',$line);
+ if ($speed==0) $speed = 'Unavailable';
+ if ($duration>0||$status<>0) {echo "| $date | ".my_duration($duration)." | $speed | ".($status==0?'OK':$status)." |
"; $row++;}
+ }
+ fclose($handle);
+}
+if ($row==0) echo "| No parity check history present! |
";
+?>
+
+
+
+
\ No newline at end of file
diff --git a/plugins/dynamix/include/Preselect.php b/plugins/dynamix/include/Preselect.php
new file mode 100644
index 000000000..bd5405005
--- /dev/null
+++ b/plugins/dynamix/include/Preselect.php
@@ -0,0 +1,23 @@
+
+
+// Preselected SMART codes for notifications
+$numbers = [];
+$preselect = [['code' => 5, 'set' => true, 'text' => 'Reallocated sectors count'],
+ ['code' => 187, 'set' => true, 'text' => 'Reported uncorrectable errors'],
+ ['code' => 188, 'set' => false,'text' => 'Command time-out'],
+ ['code' => 197, 'set' => true, 'text' => 'Current pending sector count'],
+ ['code' => 198, 'set' => true, 'text' => 'Uncorrectable sector count']];
+
+for ($x = 0; $x < count($preselect); $x++) if ($preselect[$x]['set']) $numbers[] = $preselect[$x]['code'];
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/include/ShareList.php b/plugins/dynamix/include/ShareList.php
new file mode 100644
index 000000000..259788061
--- /dev/null
+++ b/plugins/dynamix/include/ShareList.php
@@ -0,0 +1,114 @@
+
+
+require_once 'Helpers.php';
+
+$shares = parse_ini_file('state/shares.ini',true);
+$disks = parse_ini_file('state/disks.ini',true);
+$var = parse_ini_file('state/var.ini');
+$sec = parse_ini_file('state/sec.ini',true);
+$sec_nfs = parse_ini_file('state/sec_nfs.ini',true);
+$sec_afp = parse_ini_file('state/sec_afp.ini',true);
+$compute = $_GET['compute'];
+$path = $_GET['path'];
+$prev = $_GET['prev'];
+
+$display = [];
+$display['scale'] = $_GET['scale'];
+$display['number'] = $_GET['number'];
+
+if (!$shares) {
+ echo "| There are no user shares |
";
+ exit;
+}
+
+// Display export settings
+function user_share_settings($protocol,$share) {
+ if (empty($share)) return;
+ if ($protocol!='yes' || $share['export']=='-') return "-";
+ if ($share['export']=='e') return ucfirst($share['security']);
+ return ''.ucfirst($share['security']).'';
+}
+
+function globalInclude($name) {
+ global $var;
+ return substr($name,0,4)!='disk' || !$var['shareUserInclude'] || strpos("{$var['shareUserInclude']},","$name,")!==false;
+}
+
+function shareInclude($name) {
+ global $include;
+ return !$include || substr($name,0,4)!='disk' || strpos("$include,", "$name,")!==false;
+}
+
+// Compute all user shares
+if ($compute=='yes') foreach ($shares as $name => $share) exec("webGui/scripts/share_size \"$name\" \"ssz1\"");
+
+// global shares include/exclude
+$myDisks = array_filter(array_diff(array_keys($disks), explode(',',$var['shareUserExclude'])), 'globalInclude');
+
+// Share size per disk
+$preserve = ($path==$prev || $compute=='yes');
+$ssz1 = array();
+foreach (glob("state/*.ssz1", GLOB_NOSORT) as $entry) {
+ if ($preserve)
+ $ssz1[basename($entry, ".ssz1")] = parse_ini_file($entry);
+ else
+ unlink($entry);
+}
+
+// Build table
+$row = 0;
+foreach ($shares as $name => $share) {
+ $row++;
+ $ball = "/webGui/images/{$share['color']}.png";
+ switch ($share['color']) {
+ case 'green-on': $help = 'All files protected'; break;
+ case 'yellow-on': $help = 'Some or all files unprotected'; break;
+ }
+ echo "";
+ echo " $help$name | ";
+ echo "{$share['comment']} | ";
+ echo "".user_share_settings($var['shareSMBEnabled'], $sec[$name])." | ";
+ echo "".user_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])." | ";
+ echo "".user_share_settings($var['shareAFPEnabled'], $sec_afp[$name])." | ";
+ $cmd="/webGui/scripts/share_size"."&arg1=".urlencode($name)."&arg2=ssz1";
+ if (array_key_exists($name, $ssz1)) {
+ echo "".my_scale($ssz1[$name]['disk.total']*1024, $unit)." $unit | ";
+ echo "".my_scale($share['free']*1024, $unit)." $unit | ";
+ echo " | ";
+ echo "
";
+ foreach ($ssz1[$name] as $diskname => $disksize) {
+ if ($diskname=='disk.total') continue;
+ $include = $share['include'];
+ $inside = in_array($diskname, array_filter(array_diff($myDisks, explode(',',$share['exclude'])), 'shareInclude'));
+ echo "" : " warning'>");
+ echo "| ".my_disk($diskname).": | ";
+ echo "".($inside ? "" : "Share is outside the list of designated disks")." | ";
+ echo " | ";
+ echo " | ";
+ echo " | ";
+ echo "".my_scale($disksize*1024, $unit)." $unit | ";
+ echo "".my_scale($disks[$diskname]['fsFree']*1024, $unit)." $unit | ";
+ echo " | ";
+ echo "
";
+ }
+ } else {
+ echo "Compute... | ";
+ echo "".my_scale($share['free']*1024, $unit)." $unit | ";
+ echo " | ";
+ echo "";
+ }
+}
+if ($row==0) {
+ echo "| There are no exportable user shares |
";
+}
diff --git a/plugins/dynamix/include/SmartInfo.php b/plugins/dynamix/include/SmartInfo.php
index f63663357..f9c0ed764 100644
--- a/plugins/dynamix/include/SmartInfo.php
+++ b/plugins/dynamix/include/SmartInfo.php
@@ -11,90 +11,137 @@
*/
?>
-require_once('Wrappers.php');
-
-function duration($h) {
+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 duration(&$hrs) {
$time = ceil(time()/3600)*3600;
$now = new DateTime("@$time");
- $poh = new DateTime("@".($time-$h*3600));
+ $poh = new DateTime("@".($time-$hrs*3600));
$age = date_diff($poh,$now);
- return " (".($age->y?"{$age->y}y, ":"").($age->m?"{$age->m}m, ":"").($age->d?"{$age->d}d, ":"")."{$age->h}h)";
+ $hrs = "$hrs (".($age->y?"{$age->y}y, ":"").($age->m?"{$age->m}m, ":"").($age->d?"{$age->d}d, ":"")."{$age->h}h)";
+}
+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; }
+ }
+}
+$disks = []; $var = [];
+require_once 'CustomMerge.php';
+$name = isset($_POST['name']) ? $_POST['name'] : '';
+$port = isset($_POST['port']) ? $_POST['port'] : '';
+if ($name) {
+ $disk = &$disks[$name];
+ $type = isset($disk['smType']) ? $disk['smType'] : -1; if ($type==-1) $type = isset($var['smType']) ? $var['smType'] : '';
+ if ($type) {
+ $ports = [];
+ if (isset($disk['smDevice']) && strlen($disk['smDevice'])) $port = $disk['smDevice'];
+ if (isset($disk['smPort1']) && strlen($disk['smPort1'])) $ports[] = $disk['smPort1'];
+ if (isset($disk['smPort2']) && strlen($disk['smPort2'])) $ports[] = $disk['smPort2'];
+ if (isset($disk['smPort3']) && strlen($disk['smPort3'])) $ports[] = $disk['smPort3'];
+ if ($ports) {
+ $glue = isset($disk['smGlue']) ? $disk['smGlue'] : ',';
+ $type .= ','.implode($glue,$ports);
+ }
+ }
}
-
-$port = $_POST['port'];
-
switch ($_POST['cmd']) {
case "attributes":
- $unraid = parse_plugin_cfg("dynamix",true);
- $events = explode('|', $unraid['notify']['events']);
- $temps = array(190,194);
+ require_once 'Wrappers.php';
+ require_once 'Preselect.php';
+ $select = isset($disk['smSelect']) ? $disk['smSelect'] : -1; if ($select==-1) $select = isset($var['smSelect']) ? $var['smSelect'] : 0;
+ $level = isset($disk['smLevel']) ? $disk['smLevel'] : -1; if ($level==-1) $level = isset($var['smLevel']) ? $var['smLevel'] : 1;
+ $events = isset($disk['smEvents']) ? explode('|',$disk['smEvents']) : (isset($var['smEvents']) ? explode('|',$var['smEvents']) : $numbers);
+ $temps = [190,194];
+ $unraid = parse_plugin_cfg('dynamix',true);
$max = $unraid['display']['max'];
$hot = $unraid['display']['hot'];
- exec("smartctl -A /dev/$port|awk 'NR>7'",$output);
+ exec("smartctl -A $type /dev/$port|awk 'NR>7'",$output);
+ $empty = true;
foreach ($output as $line) {
if (!$line) continue;
$info = explode(' ', trim(preg_replace('/\s+/',' ',$line)), 10);
$color = "";
- if (array_search($info[0], $events)!==false && $info[9]>0) $color = " class='orange-text'";
- else if (array_search($info[0], $temps)!==false) {
- if ($info[9]>=$max) $color = " class='red-text'"; else if ($info[9]>=$hot) $color = " class='orange-text'";
+ $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'";
+ else if (in_array($info[0], $temps)) {
+ if ($info[9]>=$max) $color = " class='alert'"; else if ($info[9]>=$hot) $color = " class='warn'";
}
- echo "";
- if ($info[0] == 9 && is_numeric($info[9])) $info[9] .= duration($info[9]);
- foreach ($info as $field) echo "| ".str_replace('_',' ',$field)." | ";
- echo "
";
+ if ($info[8]=='-') $info[8] = 'Never';
+ if ($info[0]==9 && is_numeric($info[9])) duration($info[9]);
+ echo "".implode('',array_map('normalize', $info))."
";
+ $empty = false;
}
+ if ($empty) echo "| Can not read attributes |
";
break;
case "capabilities":
- exec("smartctl -c /dev/$port|awk 'NR>5'",$output);
- $row = ["","",""];
+ exec("smartctl -c $type /dev/$port|awk 'NR>5'",$output);
+ $row = ['','',''];
+ $empty = true;
foreach ($output as $line) {
if (!$line) continue;
- $line = preg_replace('/^_/','__',preg_replace(array('/__+/','/_ +_/'),'_',str_replace(array(chr(9),')','('),'_',$line)));
+ $line = preg_replace('/^_/','__',preg_replace(['/__+/','/_ +_/'],'_',str_replace([chr(9),')','('],'_',$line)));
$info = array_map('trim', explode('_', preg_replace('/_( +)_ /','__',$line), 3));
if (isset($info[0])) $row[0] .= ($row[0] ? " " : "").$info[0];
if (isset($info[1])) $row[1] .= ($row[1] ? " " : "").$info[1];
if (isset($info[2])) $row[2] .= ($row[2] ? " " : "").$info[2];
if (substr($row[2],-1)=='.') {
- echo "| {$row[0]} | {$row[1]} | {$row[2]} |
";
- $row = ["","",""];
+ echo "| ${row[0]} | ${row[1]} | ${row[2]} |
";
+ $row = ['','',''];
+ $empty = false;
}
}
+ if ($empty) echo "| Can not read capabilities |
";
break;
case "identify":
- exec("smartctl -i /dev/$port|awk 'NR>4'",$output);
- exec("smartctl -H /dev/$port|grep 'result'|sed 's:self-assessment test result::'",$output);
+ $passed = ['PASSED','OK'];
+ $failed = ['FAILED','NOK'];
+ exec("smartctl -i $type /dev/$port|awk 'NR>4'",$output);
+ exec("smartctl -H $type /dev/$port|grep -Pom1 '^SMART.*: [A-Z]+'|sed 's:self-assessment test result::'",$output);
+ $empty = true;
foreach ($output as $line) {
if (!strlen($line)) continue;
- $info = array_map('trim', explode(':', $line, 2));
- if ($info[1]=='PASSED') $info[1] = "Passed";
- if ($info[1]=='FAILED') $info[1] = "Failed";
- echo "| ".preg_replace("/ is$/","",$info[0]).": | $info[1] |
";
+ if (strpos($line,'VALID ARGUMENTS')!==false) break;
+ list($title,$info) = array_map('trim', explode(':', $line, 2));
+ 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) echo "| Can not read identification |
";
break;
case "save":
- exec("smartctl -a /dev/$port >{$_SERVER['DOCUMENT_ROOT']}/{$_POST['file']}");
+ exec("smartctl -a $type /dev/$port >{$_SERVER['DOCUMENT_ROOT']}/{$_POST['file']}");
+ break;
+case "delete":
+ @unlink("/var/tmp/{$_POST['file']}");
break;
case "short":
- exec("smartctl -t short /dev/$port");
+ spindownDelay($port);
+ exec("smartctl -t short $type /dev/$port");
break;
case "long":
- exec("smartctl -t long /dev/$port");
+ spindownDelay($port);
+ exec("smartctl -t long $type /dev/$port");
break;
case "stop":
- exec("smartctl -X /dev/$port");
+ exec("smartctl -X $type /dev/$port");
break;
case "update":
- if (!exec("hdparm -C /dev/$port|grep -om1 active")) {
- echo "Unavailable - disk must be spun up";
+ if (!exec("hdparm -C /dev/$port|grep -Pom1 'active|unknown'")) {
+ $cmd = $_POST['type']=='New' ? "cmd=/webGui/scripts/hd_parm&arg1=up&arg2=$name" : "cmdSpinup=$name";
+ echo "Unavailable - disk must be spun up";
break;
}
- $progress = exec("smartctl -c /dev/$port|grep -Pom1 '\d+%'");
+ $progress = exec("smartctl -c $type /dev/$port|grep -Pom1 '\d+%'");
if ($progress) {
- echo " ".(100-substr($progress,0,-1))."% complete";
+ echo " self-test in progress, ".(100-substr($progress,0,-1))."% complete";
break;
}
- $result = trim(exec("smartctl -l selftest /dev/$port|grep -m1 '^# 1'|cut -c26-55"));
+ $result = trim(exec("smartctl -l selftest $type /dev/$port|grep -m1 '^# 1'|cut -c26-55"));
if (!$result) {
echo "No self-tests logged on this disk";
break;
@@ -110,10 +157,10 @@ case "update":
echo "Errors occurred - Check SMART report";
break;
case "selftest":
- echo shell_exec("smartctl -l selftest /dev/$port|awk 'NR>5'");
+ echo shell_exec("smartctl -l selftest $type /dev/$port|awk 'NR>5'");
break;
case "errorlog":
- echo shell_exec("smartctl -l error /dev/$port|awk 'NR>5'");
+ echo shell_exec("smartctl -l error $type /dev/$port|awk 'NR>5'");
break;
}
?>
diff --git a/plugins/dynamix/include/Watchdog.php b/plugins/dynamix/include/Watchdog.php
index e25a447a9..192a790ff 100644
--- a/plugins/dynamix/include/Watchdog.php
+++ b/plugins/dynamix/include/Watchdog.php
@@ -22,7 +22,17 @@ default:
echo 'Array Started'; break;
}
if ($var['mdResync']) {
- echo '•'.($var['mdNumInvalid']==0 ? 'Parity-Check:' : ($var['mdInvalidDisk']==0 ? 'Parity-Sync:' : 'Data-Rebuild:')).' '.number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),1,$_POST['dot'],'').' %';
+ $mode = '';
+ if (strstr($var['mdResyncAction'],"recon")) {
+ $mode = 'Parity-Sync / Data-Rebuild';
+ } elseif (strstr($var['mdResyncAction'],"clear")) {
+ $mode = 'Clearing';
+ } elseif ($var['mdResyncAction']=="check") {
+ $mode = 'Read-Check';
+ } elseif (strstr($var['mdResyncAction'],"check")) {
+ $mode = 'Parity-Check';
+ }
+ echo '•'.$mode.' '.number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),1,$_POST['dot'],'').' %';
if ($_POST['mode']<0) echo '#stop';
}
?>
\ No newline at end of file
diff --git a/plugins/dynamix/include/update.parity.php b/plugins/dynamix/include/update.parity.php
index 51c3973c1..a296f9271 100644
--- a/plugins/dynamix/include/update.parity.php
+++ b/plugins/dynamix/include/update.parity.php
@@ -11,7 +11,7 @@
*/
?>
-require_once "webGui/include/Wrappers.php";
+require_once 'webGui/include/Wrappers.php';
$memory = '/tmp/memory.tmp';
if (isset($_POST['#apply'])) {
diff --git a/plugins/dynamix/include/update.smart.php b/plugins/dynamix/include/update.smart.php
new file mode 100644
index 000000000..be7c9130b
--- /dev/null
+++ b/plugins/dynamix/include/update.smart.php
@@ -0,0 +1,26 @@
+
+
+if (isset($_POST['#default'])) {
+ $text = '';
+ if (isset($_POST['#section'])) {
+ unset($keys[$_POST['#section']]);
+ foreach ($keys as $section => $block) {
+ $text .= "[$section]\n";
+ foreach ($block as $key => $value) $text .= "$key=\"$value\"\n";
+ }
+ }
+ if ($text) file_put_contents($file, $text); else @unlink($file);
+ $save = false;
+}
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/scripts/diagnostics b/plugins/dynamix/scripts/diagnostics
index 26c929b38..0a4e79ef5 100755
--- a/plugins/dynamix/scripts/diagnostics
+++ b/plugins/dynamix/scripts/diagnostics
@@ -1,4 +1,4 @@
-#!/usr/bin/php
+#!/usr/bin/php -q
+
+echo "ErrorWarningSystemArray
\n";
+
+require_once '/usr/local/emhttp/webGui/include/ColorCoding.php';
+
+$ata = exec("ls -n /sys/block/{$argv[1]}|grep -Po 'ata\d+'");
+$dev = $ata ? "${argv[1]}|${ata}[.:]" : $argv[1];
+
+exec("grep -P '$dev' /var/log/syslog", $lines);
+
+foreach ($lines as $line) {
+ if (strpos($line,'disk_log')!==false) continue;
+ $span = "span";
+ foreach ($match as $type) foreach ($type['text'] as $text) if (preg_match("/$text/i",$line)) {$span = "span class='{$type['class']}'"; break 2;}
+ echo "<$span>".htmlentities($line)."\n";
+}
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/scripts/disk_size b/plugins/dynamix/scripts/disk_size
index ed1af7613..a6385dc01 100755
--- a/plugins/dynamix/scripts/disk_size
+++ b/plugins/dynamix/scripts/disk_size
@@ -7,26 +7,24 @@
# Since this uses the 'du' command, could take awhile.
disk="$1"
-output="$2"
+output="/var/local/emhttp/$disk.$2"
total=0;
echo "Computing share usage for $disk..."
-
rm -f "$output"
function check {
folder="/mnt/$2/$1"
- if [[ -e $folder ]] ; then
+ if [[ -e "$folder" ]] ; then
echo "calculating $1 usage..."
size=$(du -s "$folder"|cut -f1)
- echo "$1=$size" >> "$output"
+ echo "$1=$size" >>"$output"
total=$(($total + $size))
fi
}
-
-for share in /mnt/user/*; do
- [[ -d $share ]] && check $(basename "$share") $disk
+shares=$(ls -vd /mnt/user/*)
+for share in $shares; do
+ [[ -d $share ]] && check "$(basename "$share")" "$disk"
done
-echo "total=$total" >>"$output"
-
+echo "share.total=$total" >>"$output"
echo "total disk usage: $total"
diff --git a/plugins/dynamix/scripts/ftpusers b/plugins/dynamix/scripts/ftpusers
index 1ad0115dd..82a517b44 100755
--- a/plugins/dynamix/scripts/ftpusers
+++ b/plugins/dynamix/scripts/ftpusers
@@ -1,11 +1,17 @@
-#!/usr/bin/php
+#!/usr/bin/php -q
1)
- file_put_contents($config_file, implode("\n", explode(' ', $argv[1]))."\n");
+if (trim($argv[2]))
+ file_put_contents($config_file, implode("\n", explode(' ', trim($argv[2])))."\n");
else
@unlink($config_file);
+
+$state = $argv[1] ? "'s/^#\(ftp.*vsftpd\)\$/\\1/'" : "'s/^\(ftp.*vsftpd\)\$/#\\1/'";
+exec("sed -i $state /etc/inetd.conf");
+exec("killall -HUP inetd");
?>
diff --git a/plugins/dynamix/scripts/hd_parm b/plugins/dynamix/scripts/hd_parm
new file mode 100755
index 000000000..c2c512c8f
--- /dev/null
+++ b/plugins/dynamix/scripts/hd_parm
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+[[ $1 == up ]] && cmd=-S0
+[[ $1 == down ]] && cmd=-y
+exec /usr/sbin/hdparm $cmd /dev/$2
diff --git a/plugins/dynamix/scripts/monitor b/plugins/dynamix/scripts/monitor
index 9c62a55f6..137e99e75 100755
--- a/plugins/dynamix/scripts/monitor
+++ b/plugins/dynamix/scripts/monitor
@@ -1,4 +1,4 @@
-#!/usr/bin/php
+#!/usr/bin/php -q
-require_once('/usr/local/emhttp/webGui/include/Wrappers.php');
+exec("wget -qO /dev/null 127.0.0.1:$(lsof -lbnPi4 -sTCP:LISTEN|grep -Pom1 '^emhttp.*:\K[\d]+')/update.htm?cmdStatus=apply");
+
+$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
+$var = parse_ini_file("/var/local/emhttp/var.ini");
+$devs = parse_ini_file("/var/local/emhttp/devs.ini",true);
+
+require_once '/usr/local/emhttp/webGui/include/Wrappers.php';
+require_once '/usr/local/emhttp/webGui/include/Preselect.php';
+require_once '/usr/local/emhttp/webGui/include/CustomMerge.php';
$notify = "/usr/local/emhttp/webGui/scripts/notify";
+$ram = "/var/local/emhttp/monitor.ini";
+$rom = "/boot/config/plugins/dynamix/monitor.ini";
+$saved = @parse_ini_file($ram,true);
+$unraid = parse_plugin_cfg("dynamix",true);
+$high1 = $unraid['display']['critical'];
+$high2 = $unraid['display']['warning'];
+$server = strtoupper($var['NAME']);
+$errors = [];
-$ram = "/var/local/emhttp/monitor.ini";
-$rom = "/boot/config/plugins/dynamix/monitor.ini";
-
-function plus($val, $word, $last) {
- return $val>0 ? (($val || $last) ? ($val.' '.$word.($val!=1?'s':'').($last ?'':', ')) : '') : '';
+function plus($val,$word,$last) {
+ return $val>0 ? (($val||$last) ? ($val.' '.$word.($val!=1?'s':'').($last ?'':', ')) : '') : '';
}
-
function my_temp($value) {
global $unraid;
$unit = $unraid['display']['unit'];
return ($unit=='F' ? round(9/5*$value+32) : str_replace('.', $unraid['display']['number'][0], $value))." $unit";
}
-
function my_disk($name) {
return ucfirst(preg_replace('/^(disk|cache)([0-9]+)/','$1 $2',$name));
}
-
-function my_scale($value, &$unit, $precision = NULL) {
+function my_scale($value,&$unit,$precision = NULL) {
global $unraid;
$scale = $unraid['display']['scale'];
$number = $unraid['display']['number'];
- $units = array('B','KB','MB','GB','TB','PB');
- if ($scale==0 && !$precision) {
+ $units = ['B','KB','MB','GB','TB','PB'];
+ if ($scale==0 && $precision===NULL) {
$unit = '';
return number_format($value, 0, $number[0], ($value>=10000 ? $number[1] : ''));
} else {
$base = $value ? floor(log($value, 1000)) : 0;
if ($scale>0 && $base>$scale) $base = $scale;
+ $value = round($value/pow(1000, $base), $precision===NULL ? 2 : $precision);
+ if ($value>=1000 && $scale<0) { $value = 1; $base++; }
$unit = $units[$base];
- $value = round($value/pow(1000, $base), $precision ? $precision : 2);
- return number_format($value, $precision ? $precision : (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)), $number[0], ($value>=10000 ? $number[1] : ''));
+ return number_format($value, $precision===NULL ? (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)) : $precision, $number[0], ($value>=10000 ? $number[1] : ''));
}
}
-
-function my_check($time) {
- global $var;
- if (!$time) return "unavailable (system reboot or log rotation)";
+function my_check($time,$speed) {
+ if (!$time) return 'unavailable (no parity-check entries logged)';
$days = floor($time/86400);
$hmss = $time-$days*86400;
$hour = floor($hmss/3600);
$mins = $hmss/60%60;
$secs = $hmss%60;
- return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true)."
Average speed: ".my_scale($var['mdResyncSize']*1024/$time,$unit,1)." $unit/sec";
+ return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true).". Average speed: $speed";
}
-
-function my_error($code) {
- switch ($code) {
- case -4:
- return "user abort";
- default:
- return $code;
+function read_write_parity_log($epoch) {
+ global $disks;
+ $log = '/boot/config/parity-checks.log';
+ $timestamp = str_replace(['.0','.'],[' ',' '],date('M.d H:i:s',$epoch));
+ if (file_exists($log)) {
+ $handle = fopen($log, 'r');
+ while (($line = fgets($handle)) !== false) {
+ if (strpos($line,$timestamp)!==false) break;
+ }
+ fclose($handle);
}
+ if (empty($line)) {
+ exec("grep -Po '^$timestamp .*(sync done. \Ktime=\d+|sync completion \Kstatus: -?\d+)' /var/log/syslog", $rows);
+ $duration = 0; $speed = 0; $status = 0;
+ foreach ($rows as $row) {
+ if (strpos($row,'time=')!==false) {
+ $duration = substr($row,5);
+ } elseif (strpos($row,'status:')!==false) {
+ $status = substr($row,8);
+ }
+ }
+ if ($duration>0) $speed = isset($disks['parity']['sizeSb']) ? my_scale($disks['parity']['sizeSb']*1024/$duration,$unit,1)." $unit/s" : "Unknown";
+ $line = "$timestamp|$duration|$speed|$status";
+ file_put_contents($log,"$line\n",FILE_APPEND);
+ }
+ return $line;
}
-
-exec("wget -qO /dev/null 127.0.0.1:$(lsof -lbnPi4 -sTCP:LISTEN|grep -Pom1 '^emhttp.*:\K[\d]+')/update.htm?cmdStatus=apply");
-
-$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
-$var = parse_ini_file("/var/local/emhttp/var.ini");
-$saved = @parse_ini_file($ram,true);
-$unraid = parse_plugin_cfg("dynamix",true);
-$events = $unraid['notify']['events'];
-$max = $unraid['display']['max'];
-$hot = $unraid['display']['hot'];
-$high1 = $unraid['display']['critical'];
-$high2 = $unraid['display']['warning'];
-$server = strtoupper($var['NAME']);
-$errors = array();
-
-foreach ($disks as $disk) {
- $name = $disk['name'];
- if ($name=='flash' || substr($disk['status'],-3)=='_NP') continue;
- $temp = $disk['temp'];
- $info = "{$disk['id']} ({$disk['device']})";
- if ($info==" ()") $info = 'No device identification present';
- $text = my_disk($name).($name=='cache'||$name=='parity'?' disk':'');
-
-// process temperature notifications. Give messages only when changes occur!
+function check_temp($name,$temp,$text,$info) {
+ global $notify,$disks,$saved,$unraid,$server;
+ $disk = &$disks[$name];
+ $hot = !empty($disk['hotTemp']) ? $disk['hotTemp'] : $unraid['display']['hot'];
+ $max = !empty($disk['maxTemp']) ? $disk['maxTemp'] : $unraid['display']['max'];
$warn = $temp>=$max ? 'alert' : ($temp>=$hot ? 'warning' : '');
$item = 'temp';
$last = isset($saved[$item][$name]) ? $saved[$item][$name] : 0;
@@ -107,15 +115,122 @@ foreach ($disks as $disk) {
unset($saved[$item][$name]);
}
}
+}
+function check_smart($name,$port,$text,$info) {
+ global $var,$disks,$notify,$saved,$server,$numbers;
+ $disk = &$disks[$name];
+ $select = isset($disk['smSelect']) ? $disk['smSelect'] : -1; if ($select==-1) $select = isset($all['smSelect']) ? $var['smSelect'] : 0;
+ $level = isset($disk['smLevel']) ? $disk['smLevel'] : -1; if ($level==-1) $level = isset($var['smLevel']) ? $var['smLevel'] : 1;
+ $events = isset($disk['smEvents']) ? explode('|',$disk['smEvents']) : (isset($var['smEvents']) ? explode('|',$var['smEvents']) : $numbers);
+ $type = isset($disk['smType']) ? $disk['smType'] : -1; if ($type==-1) $type = isset($var['smType']) ? $var['smType'] : '';
+ if ($type) {
+ $ports = [];
+ if (!empty($disk['smDevice'])) $port = $disk['smDevice'];
+ if (!empty($disk['smPort1'])) $ports[] = $disk['smPort1'];
+ if (!empty($disk['smPort2'])) $ports[] = $disk['smPort2'];
+ if (!empty($disk['smPort3'])) $ports[] = $disk['smPort3'];
+ if ($ports) {
+ $glue = isset($disk['smGlue']) ? $disk['smGlue'] : ',';
+ $type .= ','.implode($glue,$ports);
+ }
+ }
+ $file = "/var/local/emhttp/smart/$name";
+ exec("awk 'NR>7{print $1,$2,$4,$6,$9,$10}' $file 2>/dev/null", $codes);
+ $item = 'smart';
+ foreach ($codes as $code) {
+ if (!$code) continue;
+ list($id,$class,$value,$thres,$when,$raw) = explode(' ',$code);
+ $fail = strpos($when,'FAILING_NOW')!==false;
+ if (!$fail && !in_array($id,$events)) continue;
+ $word = str_replace(['_',' (-)'],[' ',''],strtolower("$class ($when)"));
+ $ack = "$name.ack";
+ switch ($select) {
+ case 0:
+ $attr = "$name.$id";
+ $last = isset($saved[$item][$attr]) ? $saved[$item][$attr]*$level : 0;
+ if ($raw>0 || $fail) {
+ if ($raw>$last) {
+ exec("$notify -e \"unRAID $text SMART health [$id]\" -s \"Warning [$server] - $word is $raw\" -d \"$info\" -i \"warning\"");
+ $saved[$item][$attr] = $raw;
+ unset($saved[$item][$ack]);
+ }
+ } else {
+ if ($last>0) {
+ exec("$notify -e \"unRAID $text SMART message [$id]\" -s \"Notice [$server] - $word returned to normal value\" -d \"$info\"");
+ unset($saved[$item][$attr]);
+ unset($saved[$item][$ack]);
+ }
+ }
+ break;
+ case 1:
+ $attr = "$name.${id}n";
+ $last = isset($saved[$item][$attr]) ? $saved[$item][$attr] : 255;
+ if (($thres>0 && $value<=$thres*$level) || $fail) {
+ if ($value*($value>$thres?$level:1)<$last) {
+ exec("$notify -e \"unRAID $text SMART health [$id]\" -s \"Warning [$server] - $word is $value\" -d \"$info\" -i \"warning\"");
+ $saved[$item][$attr] = $value;
+ unset($saved[$item][$ack]);
+ }
+ } else {
+ if ($last<255) {
+ exec("$notify -e \"unRAID $text SMART message [$id]\" -s \"Notice [$server] - $word returned to normal value\" -d \"$info\"");
+ unset($saved[$item][$attr]);
+ unset($saved[$item][$ack]);
+ }
+ }
+ break;
+ }
+ }
+ $file .= '.ssa';
+ if (!file_exists($file) || (time()-filemtime($file)>=$var['poll_attributes'])) exec("smartctl -n standby -H $type /dev/$port|grep -Pom1 '^SMART.*: \K[A-Z]+'|tr -d '\n' > $file");
+}
+function check_usage($name,$used,$text,$info) {
+ global $notify,$disks,$saved,$unraid,$server;
+ if ($used == -1) return;
+ $disk = &$disks[$name];
+ $warning = !empty($disk['warning']) ? $disk['warning'] : $unraid['display']['warning'];
+ $critical = !empty($disk['critical']) ? $disk['critical'] : $unraid['display']['critical'];
+ $warn = $used>=$critical ? 'alert' : ($used>=$warning ? 'warning' : '');
+ $item = 'used';
+ $last = isset($saved[$item][$name]) ? $saved[$item][$name] : 0;
+ if ($warn) {
+ if ($used>$last) {
+ exec("$notify -e \"unRAID $text disk utilization\" -s \"".ucfirst($warn)." [$server] - $text is ".($warn=='alert'?'low on space':'high on usage')." (${used}%)\" -d \"$info\" -i \"$warn\"");
+ $saved[$item][$name] = $used;
+ }
+ } else {
+ if ($last) {
+ exec("$notify -e \"unRAID $text message\" -s \"Notice [$server] - $text returned to normal utilization level\" -d \"$info\"");
+ unset($saved[$item][$name]);
+ }
+ }
+}
-// process disk operation notifications. Give messages only when changes occur!
+// check once a week for smart database updates
+$smartDB = '/usr/share/smartmontools/drivedb.h.lastcheck';
+$interval = 3600*24*7; // one week
+if (!file_exists($smartDB) || (time()-filemtime($smartDB)>=$interval)) exec('/usr/sbin/update-smart-drivedb 1>/dev/null 2>&1');
+
+// check array devices
+foreach ($disks as $disk) {
+ $name = $disk['name'];
+ if ($name=='flash' || substr($disk['status'],-3)=='_NP') continue;
+ $text = my_disk($name).($name=='cache'||$name=='parity'?' disk':'');
+ $info = !empty($disk['id']) ? "{$disk['id']} ({$disk['device']})" : "No device identification ({$disk['device']})";
+// process disk temperature notifications
+ check_temp($name,$disk['temp'],$text,$info);
+// process disk SMART notifications
+ check_smart($name,$disk['device'],$text,$info);
+// process disk usage notifications
+ check_usage($name,($disk['fsSize']?100-round(100*$disk['fsFree']/$disk['fsSize']):-1),$text,$info);
+// process disk operation notifications
$warn = strtok($disk['color'],'-');
$item = 'disk';
$last = isset($saved[$item][$name]) ? $saved[$item][$name] : "";
switch ($warn) {
case 'red':
if ($warn!=$last) {
- $status = strtolower(str_replace(array('NP_','_'),array('',' '),$disk['status']));
+ $status = strtolower(str_replace(['NP_','_'],['',' '],$disk['status']));
exec("$notify -e \"unRAID $text error\" -s \"Alert [$server] - $text in error state ($status)\" -d \"$info\" -i \"alert\"");
$saved[$item][$name] = $warn;
}
@@ -133,41 +248,31 @@ foreach ($disks as $disk) {
unset($saved[$item][$name]);
}
break;}
-
-// Count errors
-if ($disk['numErrors']>0) $errors[] = "$text - $info (errors {$disk['numErrors']})";
-
-// process disk SMART notifications. Give messages only when changes occur!
- unset($codes);
- exec("awk '$1~/^($events)$/{print $1,$10,$2,$9}' /var/local/emhttp/smart/{$disk['name']}", $codes);
- $item = 'smart';
- foreach ($codes as $code) {
- if (!$code) continue;
- $warn = explode(' ',$code);
- $attr = "$name.{$warn[0]}";
- $word = str_replace(array('_',' (-) '),array(' ',''),strtolower($warn[2]).' ('.strtolower($warn[3]).') ');
- $last = isset($saved[$item][$attr]) ? $saved[$item][$attr] : 0;
- if ($warn[1]>0) {
- if ($warn[1]>$last) {
- exec("$notify -e \"unRAID $text SMART health [{$warn[0]}]\" -s \"Warning [$server] - $word is {$warn[1]}\" -d \"$info\" -i \"warning\"");
- $saved[$item][$attr] = $warn[1];
- }
- } else {
- if ($last) {
- exec("$notify -e \"unRAID $text SMART message [{$warn[0]}]\" -s \"Notice [$server] - $word returned to normal value\" -d \"$info\"");
- unset($saved[$item][$attr]);
- }
- }
- }
+// count disk errors
+ if ($disk['numErrors']>0) $errors[] = "$text - $info (errors {$disk['numErrors']})";
}
+// check unassigned devices
+foreach ($devs as $dev) {
+ $name = $dev['device'];
+ if (empty($name)) continue;
+ $smart = "/var/local/emhttp/smart/$name";
+ if (!file_exists($smart) || (time()-filemtime($smart)>=$var['poll_attributes'])) exec("smartctl -n standby -A /dev/$name > $smart");
+ $temp = exec("awk '\$1==190||\$1==194{print \$10;exit}' $smart");
+ $text = "device $name";
+ $info = !empty($dev['id']) ? "{$dev['id']} ($name)": "No device identification ($name)";
+// process disk temperature notifications
+ check_temp($name,$temp,$text,$info);
+// process disk SMART notifications
+ check_smart($name,$dev['device'],$text,$info);
+}
+
+// report array read errors
$item = 'array';
$name = 'errors';
$last = isset($saved[$item][$name]) ? $saved[$item][$name] : 0;
$warn = count($errors);
$info = "Array has $warn disk".($warn==1 ? "" : "s")." with read errors";
-
-// report array read errors. Give messages on disk changes.
if ($warn>0) {
if ($warn<>$last) {
$message = implode('\n', $errors);
@@ -181,10 +286,9 @@ if ($warn>0) {
}
}
+// process parity check, parity sync and data-rebuild notifications
$name = 'parity';
$last = isset($saved[$item][$name]) ? $saved[$item][$name] : '';
-
-// process parity check, parity sync and data-rebuild notifications. Give messages on start/stop conditions.
if ($var['mdResync']>0) {
if (!$last) {
$last = $var['mdNumInvalid']==0 ? 'Parity check' : ($var['mdInvalidDisk']==0 ? 'Parity sync:' : 'Data rebuild:');
@@ -194,10 +298,9 @@ if ($var['mdResync']>0) {
}
} else {
if ($last) {
- exec("awk '/sync completion/ {gsub(\"(time=|sec)\",\"\",x);print x;print \$NF};{x=\$NF}' /var/log/syslog|tail -2", $time);
- if (!count($time)) $time = array_fill(0,2,0);
- $info = $time[1]==0 ? "Duration: ".my_check($time[0]) : "Error code: ".my_error($time[1]);
- $level = ($time[1]==0 && $var['sbSyncErrs']==0) ? "normal" : "warning";
+ list($entry,$duration,$speed,$status) = explode('|', read_write_parity_log($var['sbSynced']));
+ $info = $status==0 ? "Duration: ".my_check($duration, $speed) : "Error code: $status";
+ $level = ($status==0 && $var['sbSyncErrs']==0) ? "normal" : "warning";
exec("$notify -e \"unRAID $last\" -s \"Notice [$server] - $last finished ({$var['sbSyncErrs']} errors)\" -d \"$info\" -i \"$level\"");
unset($saved[$item][$name]);
}
@@ -208,7 +311,6 @@ $name = 'flash';
$last = isset($saved[$item][$name]) ? $saved[$item][$name] : '';
$warn = exec("grep -Pom1 '/boot \S+ \K\S{2}' /proc/mounts");
$info = "{$disks['flash']['id']} ({$disks['flash']['device']})";
-
if ($warn!="rw") {
if ($warn!=$last) {
exec("$notify -e \"USB flash drive failure\" -s \"Alert [$server] - USB drive is not read-write\" -d \"$info\" -i \"alert\"");
@@ -222,43 +324,45 @@ if ($warn!="rw") {
}
// check docker image disk utilization
-$item = 'system';
-$name = 'docker';
-$last = isset($saved[$item][$name]) ? $saved[$item][$name] : '';
-$warn = str_replace("%", "", exec("df /var/lib/docker|grep -Pom1 '\d+%'"));
+system('mountpoint -q /var/lib/docker', $retval);
+if ($retval===0) {
+ $item = 'system';
+ $name = 'docker';
+ $last = isset($saved[$item][$name]) ? $saved[$item][$name] : '';
+ $warn = str_replace("%", "", exec("df /var/lib/docker|grep -Pom1 '\d+%'"));
+ if (file_exists("/boot/config/docker.cfg")) {
+ $cfg = parse_ini_file("/boot/config/docker.cfg");
+ $info = "Docker utilization of image file {$cfg['DOCKER_IMAGE_FILE']}";
+ } else
+ $info = "Docker image file not specified";
-if (file_exists("/boot/config/docker.cfg")) {
- $cfg = parse_ini_file("/boot/config/docker.cfg");
- $info = "Docker utilization of image file {$cfg['DOCKER_IMAGE_FILE']}";
-} else
- $info = "Docker image file not specified";
-
-if ($warn>=$high1) {
- if ($warn>$last) {
- exec("$notify -e \"Docker critical image disk utilization\" -s \"Alert [$server] - Docker image disk utilization of ${warn}%\" -d \"$info\" -i \"alert\"");
- $saved[$item][$name] = $warn;
- }
-} else if ($warn>=$high2) {
- if ($warn>$last) {
- exec("$notify -e \"Docker high image disk utilization\" -s \"Warning [$server] - Docker image disk utilization of ${warn}%\" -d \"$info\" -i \"warning\"");
- $saved[$item][$name] = $warn;
- }
-} else {
- if ($last) {
- exec("$notify -e \"Docker image disk utilization\" -s \"Notice [$server] - Docker image disk utilization returned to normal level\" -d \"$info\"");
- unset($saved[$item][$name]);
+ if ($warn>=$high1) {
+ if ($warn>$last) {
+ exec("$notify -e \"Docker critical image disk utilization\" -s \"Alert [$server] - Docker image disk utilization of ${warn}%\" -d \"$info\" -i \"alert\"");
+ $saved[$item][$name] = $warn;
+ }
+ } else if ($warn>=$high2) {
+ if ($warn>$last) {
+ exec("$notify -e \"Docker high image disk utilization\" -s \"Warning [$server] - Docker image disk utilization of ${warn}%\" -d \"$info\" -i \"warning\"");
+ $saved[$item][$name] = $warn;
+ }
+ } else {
+ if ($last) {
+ exec("$notify -e \"Docker image disk utilization\" -s \"Notice [$server] - Docker image disk utilization returned to normal level\" -d \"$info\"");
+ unset($saved[$item][$name]);
+ }
}
}
// save new status
if ($saved) {
- $text = "";
+ $text = '';
foreach ($saved as $item => $block) {
if ($block) $text .= "[$item]\n";
foreach ($block as $key => $value) $text .= "$key=\"$value\"\n";
}
if ($text) {
- file_put_contents($ram, $text);
+ if ($text != @file_get_contents($ram)) file_put_contents($ram, $text);
if (!file_exists($rom) || exec("diff -q $ram $rom")) file_put_contents($rom, $text);
} else {
@unlink($ram);
diff --git a/plugins/dynamix/scripts/notify b/plugins/dynamix/scripts/notify
index 081281234..ac6d469da 100755
--- a/plugins/dynamix/scripts/notify
+++ b/plugins/dynamix/scripts/notify
@@ -1,4 +1,4 @@
-#!/usr/bin/php
+#!/usr/bin/php -q
-require_once('/usr/local/emhttp/webGui/include/Wrappers.php');
+require_once '/usr/local/emhttp/webGui/include/Wrappers.php';
function usage() {
echo <<',$message)."\n" : ""));
+ if (($entity & 1)==1 && !$mailtest) file_put_contents($unread,"timestamp=$timestamp\nevent=$event\nsubject=$subject\ndescription=$description\nimportance=$importance\n");
if (($entity & 2)==2 || $mailtest) if (!generate_email($event, $subject, str_replace('
','. ',$description), $importance, $message)) exit(1);
if (($entity & 4)==4 && !$mailtest) { if (is_array($agents)) {foreach ($agents as $agent) {exec("TIMESTAMP='$timestamp' EVENT='$event' SUBJECT='$subject' DESCRIPTION='$description' IMPORTANCE='$importance' CONTENT='$message' ".$agent);};}};
break;
diff --git a/plugins/dynamix/scripts/share_size b/plugins/dynamix/scripts/share_size
index c0c08a07b..ec524b2f7 100755
--- a/plugins/dynamix/scripts/share_size
+++ b/plugins/dynamix/scripts/share_size
@@ -7,26 +7,25 @@
# uses the 'du' command, could take awhile.
share="$1"
-output="$2"
+output="/var/local/emhttp/$share.$2"
total=0;
echo "Computing disk usage for $share..."
-
rm -f "$output"
function check {
- if [[ -e $1/$2 ]] ; then
+ if [[ -e "$1/$2" ]] ; then
echo "calculating $1 usage..."
size=$(du -s "$1/$2"|cut -f1)
- echo "$(basename $1)=$size" >> "$output"
+ echo "$(basename "$1")=$size" >>"$output"
total=$(($total + $size))
fi
}
-check /mnt/cache "$share"
-for disk in /mnt/disk* ; do
- check $disk "$share"
+check "/mnt/cache" "$share"
+disks=$(ls -vd /mnt/disk*)
+for disk in $disks ; do
+ check "$disk" "$share"
done
-echo "total=$total" >>"$output"
-
+echo "disk.total=$total" >>"$output"
echo "total disk usage: $total"
diff --git a/plugins/dynamix/scripts/spindowndelay b/plugins/dynamix/scripts/spindowndelay
new file mode 100755
index 000000000..7f6c9c30d
--- /dev/null
+++ b/plugins/dynamix/scripts/spindowndelay
@@ -0,0 +1,41 @@
+#!/usr/bin/php -q
+
+
+$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
+$port = $argv[1];
+$timer = $argv[2];
+
+// search for disk and fill variables
+foreach ($disks as $disk) {
+ if ($disk['device']==$port) {
+ $idx = $disk['idx'];
+ $delay = $disk['spindownDelay'];
+ break;
+ }
+}
+// disk not found, quit without action
+if (!isset($idx)) exit(0);
+
+$file = "/var/tmp/diskSpindownDelay.$idx";
+if ($timer=='') {
+// restore 'old' spindown time
+ $delay = file_exists($file) ? file_get_contents($file) : -1;
+ @unlink($file);
+} else {
+// save 'old' and set 'new' spindown time
+ file_put_contents($file, $delay);
+ $delay = $timer;
+}
+// update spindown time of selected disk
+exec("wget -qO /dev/null 127.0.0.1:$(lsof -lbnPi4 -sTCP:LISTEN|grep -Pom1 '^emhttp.*:\K[\d]+')/update.htm?diskSpindownDelay.$idx=$delay&changeDisk=apply");
diff --git a/plugins/dynamix/scripts/statuscheck b/plugins/dynamix/scripts/statuscheck
index a375e3412..4c056a9ee 100755
--- a/plugins/dynamix/scripts/statuscheck
+++ b/plugins/dynamix/scripts/statuscheck
@@ -1,4 +1,4 @@
-#!/usr/bin/php
+#!/usr/bin/php -q
-require_once('/usr/local/emhttp/webGui/include/Wrappers.php');
+require_once '/usr/local/emhttp/webGui/include/Wrappers.php';
+exec("wget -qO /dev/null 127.0.0.1:$(lsof -lbnPi4 -sTCP:LISTEN|grep -Pom1 '^emhttp.*:\K[\d]+')/update.htm?cmdStatus=apply");
+
+$notify = "/usr/local/emhttp/webGui/scripts/notify";
+$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
+$var = parse_ini_file("/var/local/emhttp/var.ini");
+$unraid = parse_plugin_cfg("dynamix",true);
+$output = $unraid['notify']['report'];
+$hot = $unraid['display']['hot'];
+$max = $unraid['display']['max'];
+$server = strtoupper($var['NAME']);
+$data = array();
+$parity = false;
+$cache = false;
+$error0 = 0;
+$error1 = 0;
+$error2 = 0;
+$error3 = 0;
function plus($val, $word, $last) {
return $val>0 ? (($val || $last) ? ($val.' '.$word.($val!=1?'s':'').($last ?'':', ')) : '') : '';
}
-
function my_temp($value) {
global $unraid;
if ($value=='*') return ' - standby';
$unit = $unraid['display']['unit'];
return ' - active '.($unit=='F' ? round(9/5*$value+32) : str_replace('.', $unraid['display']['number'][0], $value)).' '.$unit;
}
-
function my_disk($name) {
return ucfirst(preg_replace('/^(disk|cache)([0-9]+)/','$1 $2',$name));
}
-
function my_scale($value, &$unit, $precision = NULL) {
global $unraid;
$scale = $unraid['display']['scale'];
$number = $unraid['display']['number'];
$units = array('B','KB','MB','GB','TB','PB');
- if ($scale==0 && !$precision) {
+ if ($scale==0 && $precision===NULL) {
$unit = '';
return number_format($value, 0, $number[0], ($value>=10000 ? $number[1] : ''));
} else {
$base = $value ? floor(log($value, 1000)) : 0;
if ($scale>0 && $base>$scale) $base = $scale;
+ $value = round($value/pow(1000, $base), $precision===NULL ? 2 : $precision);
+ if ($value>=1000 && $scale<0) { $value = 1; $base++; }
$unit = $units[$base];
- $value = round($value/pow(1000, $base), $precision ? $precision : 2);
- return number_format($value, $precision ? $precision : (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)), $number[0], ($value>=10000 ? $number[1] : ''));
+ return number_format($value, $precision===NULL ? (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)) : $precision, $number[0], ($value>=10000 ? $number[1] : ''));
}
}
-
-function my_check($time) {
- global $var;
- if (!$time) return "unavailable (system reboot or log rotation)";
+function my_check($time,$speed) {
+ if (!$time) return 'unavailable (no parity-check entries logged)';
$days = floor($time/86400);
$hmss = $time-$days*86400;
$hour = floor($hmss/3600);
$mins = $hmss/60%60;
$secs = $hmss%60;
- return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true)."Average speed: ".my_scale($var['mdResyncSize']*1024/$time,$unit,1)." $unit/sec";
+ return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true).". Average speed: $speed";
}
-
function my_time($time) {
global $unraid;
$date = strftime($unraid['display']['date'].($unraid['display']['date']!='%c' ? ", {$unraid['display']['time']}" : ""), $time);
@@ -74,7 +86,6 @@ function my_time($time) {
return "$date ($days days ago)";
}
}
-
function my_clock($time) {
if (!$time) return 'less than a minute';
$days = floor($time/1440);
@@ -82,27 +93,20 @@ 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_parity_log($epoch) {
+ $log = '/boot/config/parity-checks.log';
+ if (file_exists($log)) {
+ $timestamp = str_replace(['.0','.'],[' ',' '],date('M.d H:i:s',$epoch));
+ $handle = fopen($log, 'r');
+ while (($line = fgets($handle)) !== false) {
+ if (strpos($line,$timestamp)!==false) break;
+ }
+ fclose($handle);
+ }
+ return $line ? $line : '0|0|0|0';
+}
-exec("wget -qO /dev/null 127.0.0.1:$(lsof -lbnPi4 -sTCP:LISTEN|grep -Pom1 '^emhttp.*:\K[\d]+')/update.htm?cmdStatus=apply");
-
-$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
-$var = parse_ini_file("/var/local/emhttp/var.ini");
-$unraid = parse_plugin_cfg("dynamix",true);
-
-$output = $unraid['notify']['report'];
-$hot = $unraid['display']['hot'];
-$max = $unraid['display']['max'];
-$server = strtoupper($var['NAME']);
-
-$data = array();
-$parity = false;
-$cache = false;
-$error0 = 0;
-$error1 = 0;
-$error2 = 0;
-$error3 = 0;
-
-// Generate report of individual disks
+// generate report of array devices
foreach ($disks as $disk) {
$name = $disk['name'];
if ($name=='flash' || substr($disk['status'],-3)=='_NP') continue;
@@ -132,9 +136,9 @@ foreach ($disks as $disk) {
$data[] = my_disk($name)." - $info".my_temp($temp).$fail.$status;
}
$size = count($data);
-$data[] = "";
-// Generate parity report
+// generate parity report
+$data[] = "";
$mdResync = $var['mdResync'];
if ($mdResync>0) {
$mdResyncPos = $var['mdResyncPos'];
@@ -154,14 +158,13 @@ if ($mdResync>0) {
} else {
$data[] = $var['mdNumInvalid']==0 ? 'Parity is valid' : ($var['mdInvalidDisk']==0 ? 'Parity is invalid' : 'Data is invalid');
$sbSyncErrs = $var['sbSyncErrs'];
- exec("awk '/sync completion/ {gsub(\"(time=|sec)\",\"\",x);print x;print \$NF};{x=\$NF}' /var/log/syslog|tail -2", $time);
- if (!count($time)) $time = array_fill(0,2,0);
- if ($time[1]==0) {
+ list($entry,$duration,$speed,$status) = explode('|', read_parity_log($sbSynced));
+ if ($status==0) {
$data[] = "Last checked on ".my_time($sbSynced).", finding $sbSyncErrs error".($sbSyncErrs==1?'.':'s.');
- $data[] = "Duration: ".my_check($time[0]);
+ $data[] = "Duration: ".my_check($duration,$speed);
} else {
$data[] = "Last check incomplete on ".my_time($sbSynced).", finding $sbSyncErrs error".($sbSyncErrs==1?'.':'s.');
- $data[] = "Error code: {$time[1]}";
+ $data[] = "Error code: $status";
}
}
}
@@ -171,7 +174,7 @@ $warn = ($error0 || $error3) ? "alert" : (($error1 || $error2) ? "warning" : "no
$stat = $warn=="normal" ? "[PASS]" : "[FAIL]";
$info = "Array has $size disk".($size==1 ? "" : "s").($parity ? " ({$word}parity".($cache ? " & cache)" : ")") : ($cache ? " ({$word}cache)" : ""));
$message = implode('\n', $data);
-exec("/usr/local/emhttp/webGui/scripts/notify -s \"Notice [$server] - array health report $stat\" -d \"$info\" -m \"$message\" -i \"$warn $output\"");
+exec("$notify -s \"Notice [$server] - array health report $stat\" -d \"$info\" -m \"$message\" -i \"$warn $output\"");
exit(0);
?>
diff --git a/plugins/dynamix/scripts/tail_log b/plugins/dynamix/scripts/tail_log
index 454817e3f..11ba7d4a6 100755
--- a/plugins/dynamix/scripts/tail_log
+++ b/plugins/dynamix/scripts/tail_log
@@ -1,3 +1,29 @@
-#!/bin/bash
+#!/usr/bin/php -q
+
+
+echo "ErrorWarningSystemArrayLogin
\n";
-exec /usr/bin/tail -n 42 -f "/var/log/$1"
+require_once '/usr/local/emhttp/webGui/include/ColorCoding.php';
+
+$handle = popen("/usr/bin/tail -n 40 -f /var/log/{$argv[1]} 2>&1", 'r');
+while (!feof($handle)) {
+ $line = fgets($handle);
+ if (strpos($line,'tail_log')!==false) continue;
+ $span = "span";
+ foreach ($match as $type) foreach ($type['text'] as $text) if (preg_match("/$text/i",$line)) {$span = "span class='{$type['class']}'"; break 2;}
+ echo "<$span>".htmlentities($line)."";
+ flush();
+}
+pclose($handle);
+?>
\ No newline at end of file
diff --git a/plugins/dynamix/styles/default-black.css b/plugins/dynamix/styles/default-black.css
index 29ee33a4a..4d0c57b97 100644
--- a/plugins/dynamix/styles/default-black.css
+++ b/plugins/dynamix/styles/default-black.css
@@ -167,7 +167,17 @@ table.share_status.m36 tr>td+td+td{width:14%;}
table.share_status.m36 tr>td+td+td+td{width:8%;text-align:center;}
table.share_status.m0{width:30%;}
table.share_status.m0 tr>td+td+td{width:12%;text-align:center;}
+tr.alert,t{color:#F0000C;background-color:#FF9E9E;}
+tr.warn{color:#E68A00;background-color:#FEEFB3;}
+tr.past{color:#D63301;background-color:#FFDDD1;}
[name=arrayOps]{margin-top:12px;}
+span.error{color:#F0000C;background-color:#FF9E9E;display:block;width:100%;}
+span.warn{color:#E68A00;background-color:#FEEFB3;display:block;width:100%;}
+span.system{color:#00529B;background-color:#BDE5F8;display:block;width:100%;}
+span.array{color:#4F8A10;background-color:#DFF2BF;display:block;width:100%;}
+span.login{color:#D63301;background-color:#FFDDD1;display:block;width:100%;}
+span.lite{background-color:#0F0F0F;}
+span.label{font-size:small;padding:2px 0 2px 6px;margin-right:6px;border-radius:4px;display:inline;width:auto;vertical-align:middle;}
span.heat-text{font-size:10px;color:#CC0000;}
span.cpu-speed{display:block;color:#3B5998;}
span.status{float:right;font-size:14px;margin-top:0px;padding-right:8px;}
@@ -191,9 +201,9 @@ div.Panel{text-align:center;float:left;margin:0 30px 30px 12px;height:80px;}
div.Panel .PanelText{padding-top:10px;}
div.user-list{float:left;padding:10px;margin-right:10px;margin-bottom:24px;border:1px solid #202020;border-radius:5px;line-height:16px;background:-webkit-radial-gradient(#303030,#101010);background:linear-gradient(#303030,#101010);}
div.user-list img{margin-bottom:12px;}
-div.up{margin-top:-20px;}
+div.up{margin-top:-20px;border:1px solid #202020;padding:4px 6px;overflow:auto;}
pre.up{margin-top:-20px;}
-pre{border:1px solid #202020;font-family:bitstream;font-size:11px;padding:6px 12px;overflow:auto;}
+pre{border:1px solid #202020;font-family:bitstream;font-size:11px;padding:4px 6px;overflow:auto;}
iframe#progressFrame{position:fixed;bottom:32px;left:0;margin:0;padding:8px 8px 0 8px;width:100%;height:12px;line-height:12px;border-style:none;overflow:hidden;font-family:bitstream;font-size:10px;color:#A0A0A0;white-space:nowrap;z-index:-10;}
dl{margin:0;padding-left:12px;line-height:26px;}
dt{clear:left;float:left;width:33%;font-weight:bold;}
diff --git a/plugins/dynamix/styles/default-white.css b/plugins/dynamix/styles/default-white.css
index fe1bcf85c..539e751a2 100644
--- a/plugins/dynamix/styles/default-white.css
+++ b/plugins/dynamix/styles/default-white.css
@@ -165,7 +165,17 @@ table.share_status.m36 tr>td+td+td{width:14%;}
table.share_status.m36 tr>td+td+td+td{width:8%;text-align:center;}
table.share_status.m0{width:30%;}
table.share_status.m0 tr>td+td+td{width:12%;text-align:center;}
+tr.alert,t{color:#F0000C;background-color:#FF9E9E;}
+tr.warn{color:#E68A00;background-color:#FEEFB3;}
+tr.past{color:#D63301;background-color:#FFDDD1;}
[name=arrayOps]{margin-top:12px;}
+span.error{color:#F0000C;background-color:#FF9E9E;display:block;width:100%;}
+span.warn{color:#E68A00;background-color:#FEEFB3;display:block;width:100%;}
+span.system{color:#00529B;background-color:#BDE5F8;display:block;width:100%;}
+span.array{color:#4F8A10;background-color:#DFF2BF;display:block;width:100%;}
+span.login{color:#D63301;background-color:#FFDDD1;display:block;width:100%;}
+span.lite{background-color:#E0E0E0;}
+span.label{font-size:small;padding:2px 0 2px 6px;margin-right:6px;border-radius:4px;display:inline;width:auto;vertical-align:middle;}
span.heat-text{font-size:10px;color:#CC0000;}
span.cpu-speed{display:block;color:#3B5998;}
span.status{float:right;font-size:14px;margin-top:0px;padding-right:8px;}
@@ -189,9 +199,9 @@ div.Panel{text-align:center;float:left;margin:0 30px 30px 12px;height:80px;}
div.Panel .PanelText{padding-top:10px;}
div.user-list{float:left;padding:10px;margin-right:10px;margin-bottom:24px;border:1px solid #D0D0D0;border-radius:5px;line-height:16px;background:-webkit-radial-gradient(#B0B0B0,#F0F0F0);background:linear-gradient(#B0B0B0,#F0F0F0);}
div.user-list img{margin-bottom:12px;}
-div.up{margin-top:-20px;}
+div.up{margin-top:-20px;border:1px solid #D0D0D0;padding:4px 6px;overflow:auto;}
pre.up{margin-top:-20px;}
-pre{border:1px solid #D0D0D0;font-family:bitstream;font-size:11px;padding:6px 12px;overflow:auto;}
+pre{border:1px solid #D0D0D0;font-family:bitstream;font-size:11px;padding:4px 6px;overflow:auto;}
iframe#progressFrame{position:fixed;bottom:32px;left:0;margin:0;padding:8px 8px 0 8px;width:100%;height:12px;line-height:12px;border-style:none;overflow:hidden;font-family:bitstream;font-size:10px;color:#505050;white-space:nowrap;z-index:-10;}
dl{margin:0;padding-left:12px;line-height:26px;}
dt{clear:left;float:left;width:33%;font-weight:bold;}
diff --git a/plugins/dynamix/styles/dynamix-black.css b/plugins/dynamix/styles/dynamix-black.css
index 7b62e5727..39006628e 100644
--- a/plugins/dynamix/styles/dynamix-black.css
+++ b/plugins/dynamix/styles/dynamix-black.css
@@ -67,7 +67,8 @@ table.tablesorter.plugins td:first-child{width:64px;text-align:center;}
table.tablesorter.plugins tr>td+td+td{width:9%;white-space:nowrap;}
table.tablesorter.plugins td:last-child{width:96px;padding:0;}
table.tablesorter.shift{margin-top:-21px;}
-table.tablesorter.shift2{margin-top:-33px;}
+table.tablesorter thead.fixed{display:table;width:100%;}
+table.tablesorter tbody.fixed{display:block;overflow:auto;}
span.select ul{display:none;list-style-type:none;margin:0 0 2px 0;padding:0;}
span.select ul.unused{display:block;}
span.select ul li.nosort{font-weight:bold;}
diff --git a/plugins/dynamix/styles/dynamix-white.css b/plugins/dynamix/styles/dynamix-white.css
index b42f0425b..20ccaa091 100644
--- a/plugins/dynamix/styles/dynamix-white.css
+++ b/plugins/dynamix/styles/dynamix-white.css
@@ -67,7 +67,8 @@ table.tablesorter.plugins td:first-child{width:64px;text-align:center;}
table.tablesorter.plugins tr>td+td+td{width:9%;white-space:nowrap;}
table.tablesorter.plugins td:last-child{width:96px;padding:0;}
table.tablesorter.shift{margin-top:-21px;}
-table.tablesorter.shift2{margin-top:-33px;}
+table.tablesorter thead.fixed{display:table;width:100%;}
+table.tablesorter tbody.fixed{display:block;overflow:auto;}
span.select ul{display:none;list-style-type:none;margin:0 0 2px 0;padding:0;}
span.select ul.unused{display:block;}
span.select ul li.nosort{font-weight:bold;}
diff --git a/plugins/dynamix/template.php b/plugins/dynamix/template.php
index de30fc7ab..45ef6cbc6 100644
--- a/plugins/dynamix/template.php
+++ b/plugins/dynamix/template.php
@@ -11,8 +11,8 @@
*/
?>
-require_once('include/Helpers.php');
-require_once('include/PageBuilder.php');
+require_once 'include/Helpers.php';
+require_once 'include/PageBuilder.php';
// Extract the 'querystring'
// variables provided by emhttp:
@@ -39,7 +39,10 @@ $shares = parse_ini_file('state/shares.ini',true);
$sec_nfs = parse_ini_file('state/sec_nfs.ini',true);
$sec_afp = parse_ini_file('state/sec_afp.ini',true);
-$site = array();
+// Merge SMART settings
+require_once 'include/CustomMerge.php';
+
+$site = [];
$base = 'dynamix';
// Build the webGui pages first
build_pages("$base/*.page");
@@ -50,5 +53,5 @@ $myPage = $site[basename($path)];
$pageroot = "{$docroot}/".dirname($myPage['file']);
// Giddyup
-require_once('include/DefaultPageLayout.php');
+require_once 'include/DefaultPageLayout.php';
?>