merge ronalds changes from upstream + some vm and dashboard fixes

This commit is contained in:
Eric Schultz
2016-01-12 19:59:16 -08:00
parent 7b894dc081
commit 8095de8759
92 changed files with 2466 additions and 1875 deletions
@@ -65,7 +65,7 @@ img.stopped{opacity:0.3;}
<th>Log</th>
</tr>
</thead>
<tbody>
<tbody id="docker_list">
<?
$all_containers = $docker->getDockerContainers();
if ( ! $all_containers) {
@@ -212,6 +212,19 @@ img.stopped{opacity:0.3;}
<script src="/webGui/javascript/context.js"></script>
<script src="/plugins/dynamix.docker.manager/javascript/docker.js"></script>
<script>
<?if ($display['resize']):?>
function resize(bind) {
var width = [];
var h = $('#docker_list').height();
var s = Math.max(window.innerHeight-300,370);
if (h>s || bind) {
$('#docker_list').height(s);
$('#docker_containers tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#docker_containers thead tr th').each(function(i){$(this).width(width[i]);});
if (!bind) $('#docker_containers thead,#docker_containers tbody').addClass('fixed');
}
}
<?endif;?>
$(function() {
if ($.cookie('docker_listview_mode') == 'advanced') {
$('.advanced').show();
@@ -239,6 +252,11 @@ $(function() {
$.cookie('docker_listview_mode', $('.advancedview').is(':checked') ? 'advanced' : 'basic', { expires: 3650 });
});
<?if ($display['resize']):?>
resize();
$(window).bind('resize',function(){resize(true);});
<?endif;?>
$('#docker_containers').tablesorter( {sortList: [[1,0]], headers: {0: {sorter: false }, 7: {sorter: false }}});
$('#docker_containers tr:even').addClass('odd');
+19 -2
View File
@@ -28,9 +28,26 @@ if ($tmp_stale) foreach (glob("/tmp/plugins/*.{plg,txt}", GLOB_NOSORT+GLOB_BRACE
<script src="/webGui/javascript/jquery.filetree.js"></script>
<script>
<?if ($display['resize']):?>
function resize(bind) {
var width = [];
var h = $('#plugin_list').height();
var s = Math.max(window.innerHeight-300,370);
if (h>s || bind) {
$('#plugin_list').height(s);
$('#plugin_table tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#plugin_table thead tr th').each(function(i){$(this).width(width[i]);});
if (!bind) $('#plugin_table thead,#plugin_table tbody').addClass('fixed');
}
}
<?endif;?>
$(function() {
$.get('/plugins/dynamix.plugin.manager/include/ShowPlugins.php',{stale:'<?=$tmp_stale?>'},function(data) {
if (data) $('#plugin_list').html(data);
<?if ($display['resize']):?>
resize();
$(window).bind('resize',function(){resize(true);});
<?endif;?>
$('#plugin_table').tablesorter({headers:{0:{sorter:false},5:{sorter:false}}});
$('.desc_readmore').readmore({maxHeight:58});
});
@@ -43,7 +60,7 @@ $(function() {
Click <strong><big>check for updates</big></strong> to check all plugins. This page might take some time to load depending on your internet connection and how many plugins need to be checked.
</blockquote>
<table class='tablesorter shift plugins' id='plugin_table'>
<table class='tablesorter plugins shift' id='plugin_table'>
<thead><tr><th></th><th>Plugin</th><th>Author</th><th>Version</th><th>Status</th><th></th></tr></thead>
<tbody id='plugin_list'><tr><td colspan='6'><br><i class="fa fa-spinner fa-spin icon"></i><em>Please wait, retrieving plugin information ...</em></td><tr></tbody>
<tbody id="plugin_list"><tr><td></td><td colspan='4'><br><i class="fa fa-spinner fa-spin icon"></i><em>Please wait, retrieving plugin information ...</em></td><td></td><tr></tbody>
</table>
Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

@@ -11,9 +11,10 @@
*/
?>
<?
require_once('webGui/include/Markdown.php');
require_once('plugins/dynamix.plugin.manager/include/PluginHelpers.php');
require_once 'webGui/include/Markdown.php';
require_once 'plugins/dynamix.plugin.manager/include/PluginHelpers.php';
$current = parse_ini_file('/etc/unraid-version');
foreach (glob("/var/log/plugins/*.plg", GLOB_NOSORT) as $plugin_link) {
// only consider symlinks
$plugin_file = @readlink($plugin_link);
@@ -50,9 +51,14 @@ foreach (glob("/var/log/plugins/*.plg", GLOB_NOSORT) as $plugin_link) {
if (file_exists($filename)) {
$latest = plugin("version", $filename);
if ($latest && strcmp($latest, $version) > 0) {
$version_info .= "<br><span class='red-text'>{$latest}</span>";
$status_info = make_link("update", basename($plugin_file));
$changes_file = $filename;
$unRAID = plugin("unRAID", $filename);
if (empty($unRAID) || strcmp($current['version'], $unRAID) >= 0) {
$version_info .= "<br><span class='red-text'>{$latest}</span>";
$status_info = make_link("update", basename($plugin_file));
$changes_file = $filename;
} else {
$status_info = "up-to-date";
}
} else {
$status_info = "up-to-date";
}
@@ -70,7 +76,7 @@ foreach (glob("/var/log/plugins/*.plg", GLOB_NOSORT) as $plugin_link) {
$action = strpos($plugin_file, "/usr/local/emhttp/plugins") !== 0 ? make_link("remove", basename($plugin_file)) : "built-in";
// write plugin information
echo "<tr>";
echo "<td style='vertical-align:top'><p>{$link}</p></td>";
echo "<td style='vertical-align:top;width:64px'><p style='text-align:center'>{$link}</p></td>";
echo "<td><span class='desc_readmore' style='display:block'>{$desc}</span></td>";
echo "<td>{$author}</td>";
echo "<td>{$version_info}</td>";
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
// Copyright 2015, Lime Technology LLC.
// License: GPLv2 only
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
@@ -12,13 +12,12 @@
*/
?>
<?
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");
$current = parse_ini_file('/etc/unraid-version');
$notify = "/usr/local/emhttp/webGui/scripts/notify";
$plugin = "/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/plugin";
exec("wget -qO /dev/null 127.0.0.1:$(lsof -lbnPi4 -sTCP:LISTEN|grep -Pom1 '^emhttp .*TCP[^\d]+\K\d+')/update.htm?cmdStatus=apply");
$var = parse_ini_file("/var/local/emhttp/var.ini");
$unraid = parse_plugin_cfg("dynamix",true);
$server = strtoupper($var['NAME']);
@@ -30,12 +29,16 @@ foreach (glob("/tmp/plugins/*.plg", GLOB_NOSORT) as $file) {
$old = exec("$plugin version '/var/log/plugins/$plg'");
unset($new);
exec("$plugin version '$file'", $new, $error);
// Silently suppress bad download of PLG file
// silently suppress bad download of PLG file
if ($error) continue;
$new = $new[0];
if (strcmp($new, $old) > 0) {
$name = basename($file, '.plg');
exec("$notify -e 'Plugin - $name [$new]' -s 'Notice [$server] - Version update $new' -d 'A new version of $name is available' -i 'normal $output' -x");
exec("$plugin unRAID '$file'", $unRAID);
$unRAID = $unRAID[0];
if (empty($unRAID) || strcmp($current['version'], $unRAID) >= 0) {
$name = basename($file, '.plg');
exec("$notify -e 'Plugin - $name [$new]' -s 'Notice [$server] - Version update $new' -d 'A new version of $name is available' -i 'normal $output' -x");
}
}
}
exit(0);
+21 -4
View File
@@ -111,7 +111,7 @@ if ($action) {
<th class='header'>Log</th>
</tr>
</thead>
<tbody>";
<tbody id='kvm_list'>";
//Get domain variables for each domain
if ($libvirt_running == "yes")
$doms = $lv->get_domains();
@@ -252,7 +252,7 @@ if ($action) {
<th class='header'>Actions</th><th class='header'></th>
</tr>
</thead>
<tbody>";
<tbody id='domdisk_list'>";
/* Display domain disks */
$tmp = $lv->get_disk_stats($res);
if (!empty($tmp)) {
@@ -413,7 +413,7 @@ if ($action) {
echo "</th><th class='header'></th><th class='header'></th>
</tr>
</thead>
<tbody>";
<tbody id=''domsnap_list>";
$tmp = $lv->domain_snapshots_list($res);
if (!empty($tmp)) {
sort($tmp);
@@ -621,6 +621,19 @@ function addVMContext(name, uuid, template, state, vncurl, log){
context.attach('#vm-'+uuid, opts);
}
<?if ($display['resize']):?>
function resize(bind) {
var width = [];
var h = $('#kvm_list').height();
var s = Math.max(window.innerHeight-300,370);
if (h>s || bind) {
$('#kvm_list').height(s);
$('#kvm_table tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#kvm_table thead tr th').each(function(i){$(this).width(width[i]);});
if (!bind) $('#kvm_table thead,#kvm_table tbody').addClass('fixed');
}
}
<?endif;?>
$(function() {
$('.text').click(showInput);
@@ -638,8 +651,12 @@ $(function() {
}, "json");
});
<?if ($display['resize']):?>
resize();
$(window).bind('resize',function(){resize(true);});
<?endif;?>
context.init({ preventDoubleContext: false });
<?=implode("\n\t", $contextMenus);?>
});
</script>
+18 -9
View File
@@ -234,7 +234,8 @@ module.exports = function inflate_fast(strm, start) {
var wsize; /* window size or zero if not using window */
var whave; /* valid bytes in the window */
var wnext; /* window write index */
var window; /* allocated sliding window, if wsize != 0 */
// Use `s_window` instead `window`, avoid conflict with instrumentation tools
var s_window; /* allocated sliding window, if wsize != 0 */
var hold; /* local strm.hold */
var bits; /* local strm.bits */
var lcode; /* local strm.lencode */
@@ -268,7 +269,7 @@ module.exports = function inflate_fast(strm, start) {
wsize = state.wsize;
whave = state.whave;
wnext = state.wnext;
window = state.window;
s_window = state.window;
hold = state.hold;
bits = state.bits;
lcode = state.lencode;
@@ -386,13 +387,13 @@ module.exports = function inflate_fast(strm, start) {
//#endif
}
from = 0; // window index
from_source = window;
from_source = s_window;
if (wnext === 0) { /* very common case */
from += wsize - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = window[from++];
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
@@ -404,14 +405,14 @@ module.exports = function inflate_fast(strm, start) {
if (op < len) { /* some from end of window */
len -= op;
do {
output[_out++] = window[from++];
output[_out++] = s_window[from++];
} while (--op);
from = 0;
if (wnext < len) { /* some from start of window */
op = wnext;
len -= op;
do {
output[_out++] = window[from++];
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
@@ -423,7 +424,7 @@ module.exports = function inflate_fast(strm, start) {
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = window[from++];
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
@@ -2377,7 +2378,7 @@ var ZStream = require('./lib/zlib/zstream.js');
var Inflate = function () {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10 * 10;
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
@@ -2385,12 +2386,20 @@ var Inflate = function () {
};
Inflate.prototype = {
inflate: function (data, flush) {
inflate: function (data, flush, expected) {
this.strm.input = data;
this.strm.avail_in = this.strm.input.length;
this.strm.next_in = 0;
this.strm.next_out = 0;
// resize our output buffer if it's too small
// (we could just use multiple chunks, but that would cause an extra
// allocation each time to flatten the chunks)
if (expected > this.chunkSize) {
this.chunkSize = expected;
this.strm.output = new Uint8Array(this.chunkSize);
}
this.strm.avail_out = this.chunkSize;
zlib.inflate(this.strm, flush);
@@ -1,676 +0,0 @@
/*
* JSUnzip
*
* Copyright (c) 2011 by Erik Moller
* All Rights Reserved
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
var tinf;
function JSUnzip() {
this.getInt = function(offset, size) {
switch (size) {
case 4:
return (this.data.charCodeAt(offset + 3) & 0xff) << 24 |
(this.data.charCodeAt(offset + 2) & 0xff) << 16 |
(this.data.charCodeAt(offset + 1) & 0xff) << 8 |
(this.data.charCodeAt(offset + 0) & 0xff);
break;
case 2:
return (this.data.charCodeAt(offset + 1) & 0xff) << 8 |
(this.data.charCodeAt(offset + 0) & 0xff);
break;
default:
return this.data.charCodeAt(offset) & 0xff;
break;
}
};
this.getDOSDate = function(dosdate, dostime) {
var day = dosdate & 0x1f;
var month = ((dosdate >> 5) & 0xf) - 1;
var year = 1980 + ((dosdate >> 9) & 0x7f)
var second = (dostime & 0x1f) * 2;
var minute = (dostime >> 5) & 0x3f;
hour = (dostime >> 11) & 0x1f;
return new Date(year, month, day, hour, minute, second);
}
this.open = function(data) {
this.data = data;
this.files = [];
if (this.data.length < 22)
return { 'status' : false, 'error' : 'Invalid data' };
var endOfCentralDirectory = this.data.length - 22;
while (endOfCentralDirectory >= 0 && this.getInt(endOfCentralDirectory, 4) != 0x06054b50)
--endOfCentralDirectory;
if (endOfCentralDirectory < 0)
return { 'status' : false, 'error' : 'Invalid data' };
if (this.getInt(endOfCentralDirectory + 4, 2) != 0 || this.getInt(endOfCentralDirectory + 6, 2) != 0)
return { 'status' : false, 'error' : 'No multidisk support' };
var entriesInThisDisk = this.getInt(endOfCentralDirectory + 8, 2);
var centralDirectoryOffset = this.getInt(endOfCentralDirectory + 16, 4);
var globalCommentLength = this.getInt(endOfCentralDirectory + 20, 2);
this.comment = this.data.slice(endOfCentralDirectory + 22, endOfCentralDirectory + 22 + globalCommentLength);
var fileOffset = centralDirectoryOffset;
for (var i = 0; i < entriesInThisDisk; ++i) {
if (this.getInt(fileOffset + 0, 4) != 0x02014b50)
return { 'status' : false, 'error' : 'Invalid data' };
if (this.getInt(fileOffset + 6, 2) > 20)
return { 'status' : false, 'error' : 'Unsupported version' };
if (this.getInt(fileOffset + 8, 2) & 1)
return { 'status' : false, 'error' : 'Encryption not implemented' };
var compressionMethod = this.getInt(fileOffset + 10, 2);
if (compressionMethod != 0 && compressionMethod != 8)
return { 'status' : false, 'error' : 'Unsupported compression method' };
var lastModFileTime = this.getInt(fileOffset + 12, 2);
var lastModFileDate = this.getInt(fileOffset + 14, 2);
var lastModifiedDate = this.getDOSDate(lastModFileDate, lastModFileTime);
var crc = this.getInt(fileOffset + 16, 4);
// TODO: crc
var compressedSize = this.getInt(fileOffset + 20, 4);
var uncompressedSize = this.getInt(fileOffset + 24, 4);
var fileNameLength = this.getInt(fileOffset + 28, 2);
var extraFieldLength = this.getInt(fileOffset + 30, 2);
var fileCommentLength = this.getInt(fileOffset + 32, 2);
var relativeOffsetOfLocalHeader = this.getInt(fileOffset + 42, 4);
var fileName = this.data.slice(fileOffset + 46, fileOffset + 46 + fileNameLength);
var fileComment = this.data.slice(fileOffset + 46 + fileNameLength + extraFieldLength, fileOffset + 46 + fileNameLength + extraFieldLength + fileCommentLength);
if (this.getInt(relativeOffsetOfLocalHeader + 0, 4) != 0x04034b50)
return { 'status' : false, 'error' : 'Invalid data' };
var localFileNameLength = this.getInt(relativeOffsetOfLocalHeader + 26, 2);
var localExtraFieldLength = this.getInt(relativeOffsetOfLocalHeader + 28, 2);
var localFileContent = relativeOffsetOfLocalHeader + 30 + localFileNameLength + localExtraFieldLength;
this.files[fileName] =
{
'fileComment' : fileComment,
'compressionMethod' : compressionMethod,
'compressedSize' : compressedSize,
'uncompressedSize' : uncompressedSize,
'localFileContent' : localFileContent,
'lastModifiedDate' : lastModifiedDate
};
fileOffset += 46 + fileNameLength + extraFieldLength + fileCommentLength;
}
return { 'status' : true }
};
this.read = function(fileName) {
var fileInfo = this.files[fileName];
if (fileInfo) {
if (fileInfo.compressionMethod == 8) {
if (!tinf) {
tinf = new TINF();
tinf.init();
}
var result = tinf.uncompress(this.data, fileInfo.localFileContent);
if (result.status == tinf.OK)
return { 'status' : true, 'data' : result.data };
else
return { 'status' : false, 'error' : result.error };
} else {
return { 'status' : true, 'data' : this.data.slice(fileInfo.localFileContent, fileInfo.localFileContent + fileInfo.uncompressedSize) };
}
}
return { 'status' : false, 'error' : "File '" + fileName + "' doesn't exist in zip" };
};
};
/*
* tinflate - tiny inflate
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/*
* tinflate javascript port by Erik Moller in May 2011.
* emoller@opera.com
*
* read_bits() patched by mike@imidio.com to allow
* reading more then 8 bits (needed in some zlib streams)
*/
"use strict";
function TINF() {
this.OK = 0;
this.DATA_ERROR = (-3);
this.WINDOW_SIZE = 32768;
/* ------------------------------ *
* -- internal data structures -- *
* ------------------------------ */
this.TREE = function() {
this.table = new Array(16); /* table of code length counts */
this.trans = new Array(288); /* code -> symbol translation table */
};
this.DATA = function(that) {
this.source = '';
this.sourceIndex = 0;
this.tag = 0;
this.bitcount = 0;
this.dest = [];
this.history = [];
this.ltree = new that.TREE(); /* dynamic length/symbol tree */
this.dtree = new that.TREE(); /* dynamic distance tree */
};
/* --------------------------------------------------- *
* -- uninitialized global data (static structures) -- *
* --------------------------------------------------- */
this.sltree = new this.TREE(); /* fixed length/symbol tree */
this.sdtree = new this.TREE(); /* fixed distance tree */
/* extra bits and base tables for length codes */
this.length_bits = new Array(30);
this.length_base = new Array(30);
/* extra bits and base tables for distance codes */
this.dist_bits = new Array(30);
this.dist_base = new Array(30);
/* special ordering of code length codes */
this.clcidx = [
16, 17, 18, 0, 8, 7, 9, 6,
10, 5, 11, 4, 12, 3, 13, 2,
14, 1, 15
];
/* ----------------------- *
* -- utility functions -- *
* ----------------------- */
/* build extra bits and base tables */
this.build_bits_base = function(bits, base, delta, first)
{
var i, sum;
/* build bits table */
for (i = 0; i < delta; ++i) bits[i] = 0;
for (i = 0; i < 30 - delta; ++i) bits[i + delta] = Math.floor(i / delta);
/* build base table */
for (sum = first, i = 0; i < 30; ++i)
{
base[i] = sum;
sum += 1 << bits[i];
}
}
/* build the fixed huffman trees */
this.build_fixed_trees = function(lt, dt)
{
var i;
/* build fixed length tree */
for (i = 0; i < 7; ++i) lt.table[i] = 0;
lt.table[7] = 24;
lt.table[8] = 152;
lt.table[9] = 112;
for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i;
for (i = 0; i < 144; ++i) lt.trans[24 + i] = i;
for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i;
for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i;
/* build fixed distance tree */
for (i = 0; i < 5; ++i) dt.table[i] = 0;
dt.table[5] = 32;
for (i = 0; i < 32; ++i) dt.trans[i] = i;
}
/* given an array of code lengths, build a tree */
this.build_tree = function(t, lengths, loffset, num)
{
var offs = new Array(16);
var i, sum;
/* clear code length count table */
for (i = 0; i < 16; ++i) t.table[i] = 0;
/* scan symbol lengths, and sum code length counts */
for (i = 0; i < num; ++i) t.table[lengths[loffset + i]]++;
t.table[0] = 0;
/* compute offset table for distribution sort */
for (sum = 0, i = 0; i < 16; ++i)
{
offs[i] = sum;
sum += t.table[i];
}
/* create code->symbol translation table (symbols sorted by code) */
for (i = 0; i < num; ++i)
{
if (lengths[loffset + i]) t.trans[offs[lengths[loffset + i]]++] = i;
}
}
/* ---------------------- *
* -- decode functions -- *
* ---------------------- */
/* get one bit from source stream */
this.getbit = function(d)
{
var bit;
/* check if tag is empty */
if (!d.bitcount--)
{
/* load next tag */
d.tag = d.source[d.sourceIndex++] & 0xff;
d.bitcount = 7;
}
/* shift bit out of tag */
bit = d.tag & 0x01;
d.tag >>= 1;
return bit;
}
/* read a num bit value from a stream and add base */
function read_bits_direct(source, bitcount, tag, idx, num)
{
var val = 0;
while (bitcount < 24) {
tag = tag | (source[idx++] & 0xff) << bitcount;
bitcount += 8;
}
val = tag & (0xffff >> (16 - num));
tag >>= num;
bitcount -= num;
return [bitcount, tag, idx, val];
}
this.read_bits = function(d, num, base)
{
if (!num)
return base;
var ret = read_bits_direct(d.source, d.bitcount, d.tag, d.sourceIndex, num);
d.bitcount = ret[0];
d.tag = ret[1];
d.sourceIndex = ret[2];
return ret[3] + base;
}
/* given a data stream and a tree, decode a symbol */
this.decode_symbol = function(d, t)
{
while (d.bitcount < 16) {
d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount;
d.bitcount += 8;
}
var sum = 0, cur = 0, len = 0;
do {
cur = 2 * cur + ((d.tag & (1 << len)) >> len);
++len;
sum += t.table[len];
cur -= t.table[len];
} while (cur >= 0);
d.tag >>= len;
d.bitcount -= len;
return t.trans[sum + cur];
}
/* given a data stream, decode dynamic trees from it */
this.decode_trees = function(d, lt, dt)
{
var code_tree = new this.TREE();
var lengths = new Array(288+32);
var hlit, hdist, hclen;
var i, num, length;
/* get 5 bits HLIT (257-286) */
hlit = this.read_bits(d, 5, 257);
/* get 5 bits HDIST (1-32) */
hdist = this.read_bits(d, 5, 1);
/* get 4 bits HCLEN (4-19) */
hclen = this.read_bits(d, 4, 4);
for (i = 0; i < 19; ++i) lengths[i] = 0;
/* read code lengths for code length alphabet */
for (i = 0; i < hclen; ++i)
{
/* get 3 bits code length (0-7) */
var clen = this.read_bits(d, 3, 0);
lengths[this.clcidx[i]] = clen;
}
/* build code length tree */
this.build_tree(code_tree, lengths, 0, 19);
/* decode code lengths for the dynamic trees */
for (num = 0; num < hlit + hdist; )
{
var sym = this.decode_symbol(d, code_tree);
switch (sym)
{
case 16:
/* copy previous code length 3-6 times (read 2 bits) */
{
var prev = lengths[num - 1];
for (length = this.read_bits(d, 2, 3); length; --length)
{
lengths[num++] = prev;
}
}
break;
case 17:
/* repeat code length 0 for 3-10 times (read 3 bits) */
for (length = this.read_bits(d, 3, 3); length; --length)
{
lengths[num++] = 0;
}
break;
case 18:
/* repeat code length 0 for 11-138 times (read 7 bits) */
for (length = this.read_bits(d, 7, 11); length; --length)
{
lengths[num++] = 0;
}
break;
default:
/* values 0-15 represent the actual code lengths */
lengths[num++] = sym;
break;
}
}
/* build dynamic trees */
this.build_tree(lt, lengths, 0, hlit);
this.build_tree(dt, lengths, hlit, hdist);
}
/* ----------------------------- *
* -- block inflate functions -- *
* ----------------------------- */
/* given a stream and two trees, inflate a block of data */
this.inflate_block_data = function(d, lt, dt)
{
// js optimization.
var ddest = d.dest;
var ddestlength = ddest.length;
while (1)
{
var sym = this.decode_symbol(d, lt);
/* check for end of block */
if (sym == 256)
{
return this.OK;
}
if (sym < 256)
{
ddest[ddestlength++] = sym; // ? String.fromCharCode(sym);
d.history.push(sym);
} else {
var length, dist, offs;
var i;
sym -= 257;
/* possibly get more bits from length code */
length = this.read_bits(d, this.length_bits[sym], this.length_base[sym]);
dist = this.decode_symbol(d, dt);
/* possibly get more bits from distance code */
offs = d.history.length - this.read_bits(d, this.dist_bits[dist], this.dist_base[dist]);
if (offs < 0)
throw ("Invalid zlib offset " + offs);
/* copy match */
for (i = offs; i < offs + length; ++i) {
//ddest[ddestlength++] = ddest[i];
ddest[ddestlength++] = d.history[i];
d.history.push(d.history[i]);
}
}
}
}
/* inflate an uncompressed block of data */
this.inflate_uncompressed_block = function(d)
{
var length, invlength;
var i;
if (d.bitcount > 7) {
var overflow = Math.floor(d.bitcount / 8);
d.sourceIndex -= overflow;
d.bitcount = 0;
d.tag = 0;
}
/* get length */
length = d.source[d.sourceIndex+1];
length = 256*length + d.source[d.sourceIndex];
/* get one's complement of length */
invlength = d.source[d.sourceIndex+3];
invlength = 256*invlength + d.source[d.sourceIndex+2];
/* check length */
if (length != (~invlength & 0x0000ffff)) return this.DATA_ERROR;
d.sourceIndex += 4;
/* copy block */
for (i = length; i; --i) {
d.history.push(d.source[d.sourceIndex]);
d.dest[d.dest.length] = d.source[d.sourceIndex++];
}
/* make sure we start next block on a byte boundary */
d.bitcount = 0;
return this.OK;
}
/* inflate a block of data compressed with fixed huffman trees */
this.inflate_fixed_block = function(d)
{
/* decode block using fixed trees */
return this.inflate_block_data(d, this.sltree, this.sdtree);
}
/* inflate a block of data compressed with dynamic huffman trees */
this.inflate_dynamic_block = function(d)
{
/* decode trees from stream */
this.decode_trees(d, d.ltree, d.dtree);
/* decode block using decoded trees */
return this.inflate_block_data(d, d.ltree, d.dtree);
}
/* ---------------------- *
* -- public functions -- *
* ---------------------- */
/* initialize global (static) data */
this.init = function()
{
/* build fixed huffman trees */
this.build_fixed_trees(this.sltree, this.sdtree);
/* build extra bits and base tables */
this.build_bits_base(this.length_bits, this.length_base, 4, 3);
this.build_bits_base(this.dist_bits, this.dist_base, 2, 1);
/* fix a special case */
this.length_bits[28] = 0;
this.length_base[28] = 258;
this.reset();
}
this.reset = function()
{
this.d = new this.DATA(this);
delete this.header;
}
/* inflate stream from source to dest */
this.uncompress = function(source, offset)
{
var d = this.d;
var bfinal;
/* initialise data */
d.source = source;
d.sourceIndex = offset;
d.bitcount = 0;
d.dest = [];
// Skip zlib header at start of stream
if (typeof this.header == 'undefined') {
this.header = this.read_bits(d, 16, 0);
/* byte 0: 0x78, 7 = 32k window size, 8 = deflate */
/* byte 1: check bits for header and other flags */
}
var blocks = 0;
do {
var btype;
var res;
/* read final block flag */
bfinal = this.getbit(d);
/* read block type (2 bits) */
btype = this.read_bits(d, 2, 0);
/* decompress block */
switch (btype)
{
case 0:
/* decompress uncompressed block */
res = this.inflate_uncompressed_block(d);
break;
case 1:
/* decompress block with fixed huffman trees */
res = this.inflate_fixed_block(d);
break;
case 2:
/* decompress block with dynamic huffman trees */
res = this.inflate_dynamic_block(d);
break;
default:
return { 'status' : this.DATA_ERROR };
}
if (res != this.OK) return { 'status' : this.DATA_ERROR };
blocks++;
} while (!bfinal && d.sourceIndex < d.source.length);
d.history = d.history.slice(-this.WINDOW_SIZE);
return { 'status' : this.OK, 'data' : d.dest };
}
};
+14 -16
View File
@@ -374,8 +374,6 @@ var RFB;
}
for (i = 0; i < 4; i++) {
//this._FBU.zlibs[i] = new TINF();
//this._FBU.zlibs[i].init();
this._FBU.zlibs[i] = new inflator.Inflate();
}
},
@@ -739,6 +737,7 @@ var RFB;
// an RFB state change and a UI interface issue
this._updateState('password', "Password Required");
this._onPasswordRequired(this);
return false;
}
if (this._sock.rQwait("auth challenge", 16)) { return false; }
@@ -921,18 +920,17 @@ var RFB;
var totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16;
if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + name_length)) { return false; }
var i;
for (i = 0; i < numServerMessages; i++) {
var srvMsg = this._sock.rQshiftStr(16);
}
// we don't actually do anything with the capability information that TIGHT sends,
// so we just skip the all of this.
for (i = 0; i < numClientMessages; i++) {
var clientMsg = this._sock.rQshiftStr(16);
}
// TIGHT server message capabilities
this._sock.rQskipBytes(16 * numServerMessages);
for (i = 0; i < numEncodings; i++) {
var encoding = this._sock.rQshiftStr(16);
}
// TIGHT client message capabilities
this._sock.rQskipBytes(16 * numClientMessages);
// TIGHT encoding capabilities
this._sock.rQskipBytes(16 * numEncodings);
}
// NB(directxman12): these are down here so that we don't run them multiple times
@@ -1688,7 +1686,7 @@ var RFB;
var resetStreams = 0;
var streamId = -1;
var decompress = function (data) {
var decompress = function (data, expected) {
for (var i = 0; i < 4; i++) {
if ((resetStreams >> i) & 1) {
this._FBU.zlibs[i].reset();
@@ -1697,7 +1695,7 @@ var RFB;
}
//var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true);
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
/*if (uncompressed.status !== 0) {
Util.Error("Invalid data in zlib stream");
}*/
@@ -1830,7 +1828,7 @@ var RFB;
if (raw) {
data = this._sock.rQshiftBytes(cl_data);
} else {
data = decompress(this._sock.rQshiftBytes(cl_data));
data = decompress(this._sock.rQshiftBytes(cl_data), rowSize * this._FBU.height);
}
// Convert indexed (palette based) image data to RGB
@@ -1879,7 +1877,7 @@ var RFB;
if (raw) {
data = this._sock.rQshiftBytes(cl_data);
} else {
data = decompress(this._sock.rQshiftBytes(cl_data));
data = decompress(this._sock.rQshiftBytes(cl_data), uncompressedSize);
}
this._display.blitRgbImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, data, 0, false);
+14 -6
View File
@@ -15,8 +15,6 @@ var UI;
(function () {
"use strict";
var resizeTimeout;
// Load supporting scripts
window.onscriptsload = function () { UI.load(); };
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
@@ -26,6 +24,7 @@ var UI;
UI = {
rfb_state : 'loaded',
resizeTimeout: null,
settingsOpen : false,
connSettingsOpen : false,
popupStatusTimeout: null,
@@ -97,8 +96,9 @@ var UI;
UI.initSetting('view_only', false);
UI.initSetting('path', 'websockify');
UI.initSetting('repeaterID', '');
UI.initSetting('token', '');
var autoconnect = WebUtil.getQueryVar('autoconnect', false);
var autoconnect = WebUtil.getConfigVar('autoconnect', false);
if (autoconnect === 'true' || autoconnect == '1') {
autoconnect = true;
UI.connect();
@@ -253,8 +253,8 @@ var UI;
// When the local window has been resized, wait until the size remains
// the same for 0.5 seconds before sending the request for changing
// the resolution of the session
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function(){
clearTimeout(UI.resizeTimeout);
UI.resizeTimeout = setTimeout(function(){
display.set_maxWidth(size.w);
display.set_maxHeight(size.h);
Util.Debug('Attempting setDesktopSize(' +
@@ -358,7 +358,7 @@ var UI;
// Initial page load read/initialization of settings
initSetting: function(name, defVal) {
// Check Query string followed by cookie
var val = WebUtil.getQueryVar(name);
var val = WebUtil.getConfigVar(name);
if (val === null) {
val = WebUtil.readSetting(name, defVal);
}
@@ -519,6 +519,7 @@ var UI;
UI.connSettingsOpen = false;
UI.saveSetting('host');
UI.saveSetting('port');
UI.saveSetting('token');
//UI.saveSetting('password');
} else {
$D('noVNC_controls').style.display = "block";
@@ -812,7 +813,14 @@ var UI;
var host = $D('noVNC_host').value;
var port = $D('noVNC_port').value;
var password = $D('noVNC_password').value;
var token = $D('noVNC_token').value;
var path = $D('noVNC_path').value;
//if token is in path then ignore the new token variable
if (token) {
path = WebUtil.injectParamIfMissing(path, "token", token);
}
if ((!host) || (!port)) {
throw new Error("Must set host and port");
}
+47 -17
View File
@@ -66,6 +66,12 @@ function Websock() {
(function () {
"use strict";
// this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future.
var ENABLE_COPYWITHIN = false;
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var typedArrayToString = (function () {
// This is only for PhantomJS, which doesn't like apply-ing
@@ -340,9 +346,49 @@ function Websock() {
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
_expand_compact_rQ: function (min_fit) {
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
if (resizeNeeded) {
if (!min_fit) {
// just double the size if we need to do compaction
this._rQbufferSize *= 2;
} else {
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
}
}
// we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
}
}
if (resizeNeeded) {
var old_rQbuffer = this._rQ.buffer;
this._rQmax = this._rQbufferSize / 8;
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
} else {
if (ENABLE_COPYWITHIN) {
this._rQ.copyWithin(0, this._rQi);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
}
}
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
},
_decode_message: function (data) {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) {
this._expand_compact_rQ(u8.length);
}
this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length;
},
@@ -357,23 +403,7 @@ function Websock() {
this._rQlen = 0;
this._rQi = 0;
} else if (this._rQlen > this._rQmax) {
if (this._rQlen - this._rQi > 0.5 * this._rQbufferSize) {
var old_rQbuffer = this._rQ.buffer;
this._rQbufferSize *= 2;
this._rQmax = this._rQbufferSize / 8;
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
} else {
if (this._rQ.copyWithin) {
// Firefox only, ATM
this._rQ.copyWithin(0, this._rQi);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
}
}
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
this._expand_compact_rQ();
}
} else {
Util.Debug("Ignoring empty message");
@@ -90,6 +90,29 @@ WebUtil.getQueryVar = function (name, defVal) {
}
};
// Read a hash fragment variable
WebUtil.getHashVar = function (name, defVal) {
"use strict";
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
// Read a variable from the fragment or the query string
// Fragment takes precedence
WebUtil.getConfigVar = function (name, defVal) {
"use strict";
var val = WebUtil.getHashVar(name);
if (val === null) {
val = WebUtil.getQueryVar(name, defVal);
}
return val;
};
/*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
@@ -237,3 +260,27 @@ WebUtil.selectStylesheet = function (sheet) {
}
return sheet;
};
WebUtil.injectParamIfMissing = function (path, param, value) {
// force pretend that we're dealing with a relative path
// (assume that we wanted an extra if we pass one in)
path = "/" + path;
var elem = document.createElement('a');
elem.href = path;
var param_eq = encodeURIComponent(param) + "=";
var query;
if (elem.search) {
query = elem.search.slice(1).split('&');
} else {
query = [];
}
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
query.push(param_eq + encodeURIComponent(value));
elem.search = "?" + query.join("&");
}
return elem.pathname.slice(1) + elem.search + elem.hash;
};
+3
View File
@@ -11,6 +11,8 @@
Connect parameters are provided in query string:
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
or the fragment:
http://example.com/#host=HOST&port=PORT&encrypt=1&true_color=1
-->
<title>noVNC</title>
@@ -197,6 +199,7 @@
<li><label><strong>Host: </strong><input id="noVNC_host" /></label></li>
<li><label><strong>Port: </strong><input id="noVNC_port" /></label></li>
<li><label><strong>Password: </strong><input id="noVNC_password" type="password" /></label></li>
<li><label><strong>Token: </strong><input id="noVNC_token"/></label></li>
<li><input id="noVNC_connect_button" type="button" value="Connect"></li>
</ul>
</div>
+3 -3
View File
@@ -17,10 +17,10 @@ Title="Array Devices"
var timer = null;
function array_status() {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'array',width:window.innerWidth,timer:timer},function(data) {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'array'},function(data) {
if (data) $('#array_devices').html(data);
<?if (($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)) && $var['fsState']=='Started'):?>
<?if ($display['tabs']==0):?>
<?if ($tabbed):?>
if ($('#tab1').is(':checked')) timer = setTimeout(array_status,<?=abs($display['refresh'])?>);
<?else:?>
setTimeout(array_status,<?=abs($display['refresh'])?>);
@@ -40,7 +40,7 @@ array_status();
<tbody id="array_devices">
<?
foreach ($disks as $disk):
if ($disk['type']=='Parity' or $disk['type']=='data')
if ($disk['type']=='Parity' or $disk['type']=='Data')
echo "<tr><td colspan='11'>&nbsp;</td></tr>";
endforeach;
if ($display['total']) echo "<tr class='tr_last'><td colspan='11'>&nbsp;</td></tr>";
+42 -33
View File
@@ -14,21 +14,41 @@ Title="Array Operation"
*/
?>
<?
function maintenance_mode() {
echo "<tr>";
echo "<td></td>";
echo "<td><input type='checkbox' name='startMode' value='Maintenance'>Maintenance mode</td>";
echo "<td><strong>Maintenance mode</strong> - if checked, Start array but do not mount disks.</td>";
echo "</tr>";
}
function status_indicator() {
global $var;
$ball = "/webGui/images/{$var['mdColor']}.png";
switch ($var['mdColor']) {
case 'green-on': $help = 'Started, array protected'; break;
case 'green-blink': $help = 'Stopped'; break;
case 'yellow-on': $help = 'Started, array unprotected'; break;
case 'yellow-blink': $help = 'Stopped'; break;
}
return "<a class='info nohand' onclick='return false'><img src='$ball' class='icon'><span>$help</span></a>";
}
?>
<script>
function tab0() {
$.removeCookie('one',{path:'/'});
$.cookie('tab','tab0',{path:'/'});
}
function parity_status() {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'parity',timer:timer},function(data) {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'parity'},function(data) {
if (data) {$.each(data.split(';'),function(k,v) {if ($('#line'+k).length>0) $('#line'+k).html(v);});}
<?if ($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)):?>
<?if ($display['tabs']==0):?>
<?if ($tabbed):?>
if ($('#tab'+$('input[name$="tabs"]').length).is(':checked')) timer = setTimeout(parity_status,<?=abs($display['refresh'])?>);
<?else:?>
setTimeout(parity_status,<?=abs($display['refresh'])?>);
<?endif;?>
if (!data && $('#cancelButton').length>0 && $('#cancelButton').val()=='Cancel'){
if (!data && $('#cancelButton').length>0 && $('#cancelButton').val()=='Cancel') {
$('#cancelButton').prop('type','button').val('Done').bind({click:function(){refresh();}});
$('#cancelText').html('');
$('#line4').html('completed');
@@ -39,7 +59,7 @@ function parity_status() {
parity_status();
<?if ($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)):?>
<?if ($display['tabs']==0):?>
<?if ($tabbed):?>
$('#tab'+$('input[name$="tabs"]').length).bind({click:function() {clearTimeout(timer); parity_status(); tab0();}});
<?endif;?>
<?if (substr($var['fsState'],-3)=='ing'):?>
@@ -68,26 +88,6 @@ $('#tab'+$('input[name$="tabs"]').length).bind({click:function() {tab0();}});
$('.tabs').append("<span class='status vhshift'><input type='button' value='Refresh' onclick='refresh()'></span>");
<?endif;?>
</script>
<?
function maintenance_mode() {
echo "<tr>";
echo "<td></td>";
echo "<td><input type='checkbox' name='startMode' value='Maintenance'>Maintenance mode</td>";
echo "<td><strong>Maintenance mode</strong> - if checked, Start array but do not mount disks.</td>";
echo "</tr>";
}
function status_indicator() {
global $var;
$ball = "/webGui/images/{$var['mdColor']}.png";
switch ($var['mdColor']) {
case 'green-on': $help = 'Started, array protected'; break;
case 'green-blink': $help = 'Stopped'; break;
case 'yellow-on': $help = 'Started, array unprotected'; break;
case 'yellow-blink': $help = 'Stopped'; break;
}
return "<a class='info nohand' onclick='return false'><img src='$ball' class='icon'><span>$help</span></a>";
}
?>
<form name="arrayOps" method="POST" action="/update.htm" target="progressFrame">
<input type="hidden" name="startState" value="<?=$var['mdState']?>">
<table class="array_status">
@@ -143,9 +143,9 @@ function status_indicator() {
if ($var['sbSyncExit']!=0):?>
<tr>
<td></td>
<td></td>
<td><input type="button" value="History" onclick="openBox('/webGui/include/ParityHistory.php','Parity Check History',490,430,false)"></td>
<td>Last check incomplete on <strong><?=my_time($var['sbSynced2']).day_count($var['sbSynced2'])?></strong>, finding <strong><?=$var['sbSyncErrs']?></strong> error<?=$var['sbSyncErrs']==1?'':'s'?>.
<br><small>Error code: <?=my_error($sbSyncExit)?></small></td>
<br><i class="fa fa-dot-circle-o icon"></i><small>Error code: <?=my_error($var['sbSyncExit'])?></small></td>
</tr>
<? elseif ($var['sbSynced']==0):?>
<tr>
@@ -156,15 +156,24 @@ function status_indicator() {
<? elseif ($var['sbSynced2']==0):?>
<tr>
<td></td>
<td></td>
<td>Last checked <strong><?=my_time($var['sbSynced']).day_count($var['sbSynced'])?></strong>, finding <strong><?=$var['sbSyncErrs']?></strong> error<?=$var['sbSyncErrs']==1?'':'s'?>.</td>
</tr>
<? else:?>
<td><input type="button" value="History" onclick="openBox('/webGui/include/ParityHistory.php','Parity Check History',490,430,false)"></td>
<? list($entry,$duration,$speed,$status) = explode('|', read_parity_log($var['sbSynced']));
if ($status==0):?>
<td>Last checked on <strong><?=my_time($var['sbSynced']).day_count($var['sbSynced'])?></strong>, finding <strong><?=$var['sbSyncErrs']?></strong> error<?=$var['sbSyncErrs']==1?'':'s'?>.
<br><i class="fa fa-clock-o icon"></i><small>Duration: <?=my_check($duration,$speed)?></small>
<? else:?>
<td>Last check incomplete on <strong><?=my_time($var['sbSynced']).day_count($var['sbSynced'])?></strong>, finding <strong><?=$var['sbSyncErrs']?></strong> error<?=$var['sbSyncErrs']==1?'':'s'?>.
<br><i class="fa fa-dot-circle-o icon"></i><small>Error code: <?=$status?></small>
<? endif;?>
</td></tr>
<? else:
$duration = $var['sbSynced2']-$var['sbSynced'];
$speed = $duration?my_scale($var['mdResyncSize']*1024/$duration,$unit,1)." $unit/sec":'';?>
<tr>
<td></td>
<td></td>
<td><input type="button" value="History" onclick="openBox('/webGui/include/ParityHistory.php','Parity Check History',490,430,false)"></td>
<td>Last check completed on <strong><?=my_time($var['sbSynced2']).day_count($var['sbSynced2'])?></strong>, finding <strong><?=$var['sbSyncErrs']?></strong> error<?=$var['sbSyncErrs']==1?'':'s'?>.
<br><small>Duration: <?=my_check($var['sbSynced2']-$var['sbSynced'])?></small></td>
<br><i class="fa fa-clock-o icon"></i><small>Duration: <?=my_check($duration,$speed)?></small></td>
</tr>
<? endif;
endif;
@@ -311,7 +320,7 @@ function status_indicator() {
maintenance_mode();
break;
case "NEW_ARRAY":
if (strstr($disks['parity']['status'],"DISK_NP") && strstr($disks['parity2']['status'],"DISK_NP")):?>
if (strpos($disks['parity']['status'],"DISK_NP")===0 && (empty($disks['parity2']) || strpos($disks['parity2']['status'],"DISK_NP")===0)):?>
<tr>
<td><?=status_indicator()?><strong>Stopped</strong>. Configuration valid.</td>
<td><input type="submit" name="cmdStart" value="Start"></td>
+2 -2
View File
@@ -15,10 +15,10 @@ Title="Boot Device"
?>
<script>
function boot_status() {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'flash',width:window.innerWidth,timer:timer},function(data) {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'flash'},function(data) {
if (data) $('#boot_device').html(data);
<?if (($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)) && $var['fsState']=='Started'):?>
<?if ($display['tabs']==0):?>
<?if ($tabbed):?>
if ($('#tab3').is(':checked')) timer = setTimeout(boot_status,<?=abs($display['refresh'])?>);
<?else:?>
setTimeout(boot_status,<?=abs($display['refresh'])?>);
+2 -2
View File
@@ -16,10 +16,10 @@ Cond="($var['fsState']=='Stopped' || $var['cacheSbNumDisks'])"
?>
<script>
function cache_status() {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'cache',width:window.innerWidth,timer:timer},function(data) {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'cache'},function(data) {
if (data) $('#cache_device').html(data);
<?if (($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)) && $var['fsState']=='Started'):?>
<?if ($display['tabs']==0):?>
<?if ($tabbed):?>
if ($('#tab2').is(':checked')) timer = setTimeout(cache_status,<?=abs($display['refresh'])?>);
<?else:?>
setTimeout(cache_status,<?=abs($display['refresh'])?>);
+105 -44
View File
@@ -34,34 +34,64 @@ $memory_maximum = exec("dmidecode -t 16 | awk -F: '/^\tMaximum Capacity: [0-9]+
// If maximum < installed then roundup maximum to the next power of 2 size of installed. E.g. 6 -> 8 or 12 -> 16
if ($memory_maximum*1024 < $memory_installed) $memory_maximum = pow(2,ceil(log($memory_installed/1024)/log(2)));
function init_row($label) {
echo "<tr><td>$label</td>".str_repeat("<td></td>",25)."</tr>";
function init_row($label,$slots) {
echo "<tr><td>$label</td>".str_repeat("<td></td>",$slots)."</tr>";
}
function parity_status() {
global $var,$disks;
if ($disks['parity']['status']=='DISK_NP') {
echo "<tr><td colspan='2'><span class='red p0'><strong>Parity disk not present</strong></span></td></tr>";
return;
}
if ($var['mdNumInvalid']==0) {
echo "<tr><td colspan='2'><span class='green p0'><strong>Parity is valid</strong></span></td></tr>";
if ($var['sbSynced']==0) {
echo "<tr><td><em>Parity has not been checked yet.<em></td><td id='parity'></td></tr>";
} else {
unset($time);
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) {
echo "<tr><td>Last checked on <strong>".my_time($var['sbSynced']).day_count($var['sbSynced'])."</strong>, finding <strong>{$var['sbSyncErrs']}</strong> error".($var['sbSyncErrs']==1?'.':'s.');
echo "<br><em>Duration: ".my_check($time[0])."</em></td><td id='parity'></td></tr>";
} else {
echo "<tr><td>Last check incomplete on <strong>".my_time($var['sbSynced']).day_count($var['sbSynced'])."</strong>, finding <strong>{$var['sbSyncErrs']}</strong> error".($var['sbSyncErrs']==1?'.':'s.');
echo "<br><em>Error code: ".my_error($time[1])."</em></td><td id='parity'></td></tr>";
$parity_num_slots = 0;
$parity_disabled = [];
$parity_invalid = [];
foreach ($disks as $disk) {
if ($disk['type']=='Parity') {
$parity_num_slots++;
if (strpos($disk['status'],"DISK_NP")===0) {
$parity_disabled[] = trim('Parity ' . filter_var($disk['name'],FILTER_SANITIZE_NUMBER_INT));
}
if (strpos($disk['status'],"DISK_INVALID")===0) {
$parity_invalid[] = trim('Parity ' . filter_var($disk['name'],FILTER_SANITIZE_NUMBER_INT));
}
}
}
if ($parity_num_slots == count($parity_disabled)) {
echo "<tr style='vertical-align:top'><td><span class='red p0'><strong>Parity disk".($parity_num_slots>1?'s':'')." not present</strong></span></td><td id='parity'></td></tr>";
return;
}
if ($parity_num_slots > count($parity_invalid)) {
if (count($parity_invalid)==0) {
echo "<tr><td colspan='2'><span class='green p0'><strong>Parity is valid</strong></span></td></tr>";
} else {
echo "<tr><td colspan='2'><span class='orange p0'><strong>Degraded parity: </strong></span><span class='red p0'><strong>".implode(' and ',$parity_invalid)." ".(count($parity_invalid)>1)?'are':'is'." invalid</strong></span></td></tr>";
}
if ($var['sbSyncExit']!=0) {
echo "<tr><td>Last check incomplete on <strong>".my_time($var['sbSynced2']).day_count($var['sbSynced2'])."</strong>, finding <strong>{$var['sbSyncErrs']}</strong> error".($var['sbSyncErrs']==1?'.':'s.');
echo "<br><i class='fa fa-dot-circle-o icon'></i><em>Error code: ".my_error($var['sbSyncExit'])."</em></td><td id='parity'></td></tr>";
return;
}
if ($var['sbSynced']==0) {
echo "<tr><td><em>Parity has not been checked yet.<em></td><td id='parity'></td></tr>";
return;
}
if ($var['sbSynced2']==0) {
list($entry,$duration,$speed,$status) = explode('|', read_parity_log($var['sbSynced']));
if ($status==0) {
echo "<tr><td>Last checked on <strong>".my_time($var['sbSynced']).day_count($var['sbSynced'])."</strong>, finding <strong>{$var['sbSyncErrs']}</strong> error".($var['sbSyncErrs']==1?'.':'s.');
echo "<br><i class='fa fa-clock-o icon'></i><em>Duration: ".my_check($duration,$speed)."</em></td><td id='parity'></td></tr>";
} else {
echo "<tr><td>Last check incomplete on <strong>".my_time($var['sbSynced']).day_count($var['sbSynced'])."</strong>, finding <strong>{$var['sbSyncErrs']}</strong> error".($var['sbSyncErrs']==1?'.':'s.');
echo "<br><i class='fa fa-dot-circle-o icon'></i><em>Error code: $status</em></td><td id='parity'></td></tr>";
}
} else {
$status = 0;
$duration = $var['sbSynced2']-$var['sbSynced'];
$speed = $duration?my_scale($var['mdResyncSize']*1024/$duration,$unit,1)." $unit/sec":'';
echo "<tr><td>Last checked on <strong>".my_time($var['sbSynced2']).day_count($var['sbSynced2'])."</strong>, finding <strong>{$var['sbSyncErrs']}</strong> error".($var['sbSyncErrs']==1?'.':'s.');
echo "<br><i class='fa fa-clock-o icon'></i><em>Duration: ".my_check($duration,$speed)."</em></td><td id='parity'></td></tr>";
}
} else {
if ($var['mdInvalidDisk']==0) {
if (empty($var['mdInvalidDisk'])) {
echo "<tr><td colspan='2'><span class='red p0'><strong>Parity is invalid</strong></span></td></tr>";
} else {
echo "<tr><td colspan='2'><span class='red p0'><strong>Data is invalid</strong></span></td></tr>";
@@ -77,6 +107,8 @@ function export_settings($protocol,$share) {
return '<em>'.ucfirst($share['security']).'</em>';
}
?>
<link type="text/css" rel="stylesheet" href="/webGui/styles/context.standalone.css">
<script src="/webGui/javascript/context.js"></script>
<script>
var timer2,timer30;
@@ -131,13 +163,25 @@ function update30() {
if (data) $('#dash1').html(data);
clearTimeout(timer2);
timer2 = setTimeout(heatAlarm,2000);
$('[id^="smart-"]').each(function() {
var opts = [];
var id = '#'+$(this).attr('id');
var page = $(this).attr('name');
var view = $(this).attr('class');
var disk = id.substr(id.indexOf('-')+1);
opts.push({text:'Attributes',icon:'fa-sitemap',action:function(e){e.preventDefault();attributes(page,disk);}});
opts.push({text:'Capabilities',icon:'fa-user',action:function(e){e.preventDefault();capabilities(page,disk);}});
opts.push({text:'Identity',icon:'fa-home',action:function(e){e.preventDefault();identity(page,disk);}});
if (view == 'alert') opts.push({text:'Acknowledge',icon:'fa-check-square-o',action:function(e){e.preventDefault();acknowledge(disk);}});
context.attach(id,opts);
});
<?if ($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)):?>
timer30 = setTimeout(update30,<?=max(abs($display['refresh']),30000)?>);
<?endif;?>
});
}
function update60() {
<?if ($var['mdNumInvalid']==0 && $var['mdResync']>0):?>
<?if ((strstr($var['mdResyncAction'],"clear") || strstr($var['mdResyncAction'],"check")) && $var['mdResync']>0):?>
$.post('<?=$url?>',{cmd:'parity'},function(data) {
if (data) $('#parity').html(data);
<?if ($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)):?>
@@ -152,6 +196,24 @@ function heatAlarm() {
}
timer2 = setTimeout(heatAlarm,2000);
}
function attributes(page,disk) {
var tab = page=='New' ? 'tab2' : 'tab3';
$.cookie('one',tab,{path:'/'});
location.replace('/Dashboard/'+page+'?name='+disk);
}
function capabilities(page,disk) {
var tab = page=='New' ? 'tab3' : 'tab4';
$.cookie('one',tab,{path:'/'});
location.replace('/Dashboard/'+page+'?name='+disk);
}
function identity(page,disk) {
var tab = page=='New' ? 'tab4' : 'tab5';
$.cookie('one',tab,{path:'/'});
location.replace('/Dashboard/'+page+'?name='+disk);
}
function acknowledge(disk) {
$.post('/webGui/include/Acknowledge.php',{disk:disk},function(data){clearTimeout(timer30);update30();});
}
$(function() {
if ($.cookie('shareMode')!=null) { $('select[name="enter_share"] option')[$.cookie('shareMode')].selected = true; $('select[name="enter_share"]').change(); }
if ($.cookie('viewMode')!=null) { $('select[name="enter_view"] option')[$.cookie('viewMode')].selected = true; $('select[name="enter_view"]').change(); }
@@ -161,38 +223,37 @@ $(function() {
<?if ($display['refresh']==0 || ($display['refresh']<0 && $var['mdResync']>0)):?>
$('.tabs').append("<span class='status vhshift'><input type='button' value='Refresh' onclick='refresh()'></span>");
<?endif;?>
context.init({preventDoubleContext:false});
});
</script>
<table class='share_status fixed'>
<thead><tr>
<?
$row0 = array_fill(0,25,"<td>-</td>"); $i = 1;
foreach ($disks as $disk):
switch ($disk['type']):
case 'Parity':
if ($disk['status']!='DISK_NP') $row0[0] = '<td>Parity<br>0</td>';
break;
case 'Data':
case 'Cache':
if ($disk['status']!='DISK_NP') $row0[$i++] = "<td>{$disk['type']}<br>{$disk['idx']}</td>";
break;
endswitch;
endforeach;
foreach ($devs as $dev):
$slots = max(count($disks)+count($devs)-1, 25);
$row0 = array_fill(0,$slots,"<td>-</td>"); $i = 0;
foreach ($disks as $disk)
if ($disk['type']=='Parity' && $disk['status']!='DISK_NP')
$row0[$i++] = "<td>Parity<br>&nbsp;".filter_var($disk['name'],FILTER_SANITIZE_NUMBER_INT)."&nbsp;</td>";
foreach ($disks as $disk)
if ($disk['type']=='Data' && $disk['status']!='DISK_NP')
$row0[$i++] = "<td>Disk<br>&nbsp;".filter_var($disk['name'],FILTER_SANITIZE_NUMBER_INT)."&nbsp;</td>";
foreach ($disks as $disk)
if ($disk['type']=='Cache' && $disk['status']!='DISK_NP')
$row0[$i++] = "<td>Cache<br>&nbsp;".filter_var($disk['name'],FILTER_SANITIZE_NUMBER_INT)."&nbsp;</td>";
foreach ($devs as $dev)
$row0[$i++] = "<td>{$dev['device']}</td>";
endforeach;
echo "<td>Array Status</td>".implode('',$row0);
?>
</tr></thead>
<tbody id='dash1'>
<?init_row('Active')?>
<?init_row('Inactive')?>
<?init_row('Unassigned')?>
<?init_row('Faulty')?>
<?init_row('Heat alarm')?>
<?init_row('SMART status')?>
<?init_row('Utilization')?>
<?init_row('Active',$slots)?>
<?init_row('Inactive',$slots)?>
<?init_row('Unassigned',$slots)?>
<?init_row('Faulty',$slots)?>
<?init_row('Heat alarm',$slots)?>
<?init_row('SMART status',$slots)?>
<?init_row('Utilization',$slots)?>
</tbody>
</table>
<table class='share_status table'>
+5 -5
View File
@@ -44,7 +44,7 @@ div.Panel:hover{overflow:visible;z-index:10;}
<?
$allContainers = array();
if ((pgrep('docker')!==false) && (empty($display['dashapps']) || $display['dashapps'] == 'icons' || $display['dashapps'] == 'docker')) {
require_once('/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php');
require_once '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php';
$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerTemplates = new DockerTemplates();
@@ -57,8 +57,8 @@ if ((pgrep('docker')!==false) && (empty($display['dashapps']) || $display['dasha
$allVMs = array();
if ((pgrep('libvirtd')!==false) && (empty($display['dashapps']) || $display['dashapps'] == 'icons' || $display['dashapps'] == 'vms')) {
require_once('/usr/local/emhttp/plugins/dynamix.vm.manager/classes/libvirt.php');
require_once('/usr/local/emhttp/plugins/dynamix.vm.manager/classes/libvirt_helpers.php');
require_once '/usr/local/emhttp/plugins/dynamix.vm.manager/classes/libvirt.php';
require_once '/usr/local/emhttp/plugins/dynamix.vm.manager/classes/libvirt_helpers.php';
$doms = $lv->get_domains();
if (is_array($doms)) {
@@ -266,7 +266,7 @@ function addVMContext(name, uuid, template, state, vncurl, log){
if (state == "shutoff") {
opts.push({text: "Edit", icon: "fa-pencil", href: path+'/UpdateVM?uuid='+uuid });
opts.push({text: "Edit XML", icon: "fa-code", href: path+'/UpdateVM?template=XML_Expert&amp;uuid='+uuid });
opts.push({text: "Edit XML", icon: "fa-code", href: path+'/UpdateVM?template=Custom&amp;uuid='+uuid });
opts.push({divider: true});
@@ -284,7 +284,7 @@ function addVMContext(name, uuid, template, state, vncurl, log){
} else {
opts.push({text: "View XML", icon: "fa-code", href: path+'/UpdateVM?template=XML_Expert&amp;uuid='+uuid });
opts.push({text: "View XML", icon: "fa-code", href: path+'/UpdateVM?template=Custom&amp;uuid='+uuid });
}
+2 -3
View File
@@ -1,4 +1,4 @@
Menu="Device"
Menu="Device New"
Title="Attributes"
Cond="strpos($disks[$name]['status'],'_NP')===false"
---
@@ -14,10 +14,9 @@ Cond="strpos($disks[$name]['status'],'_NP')===false"
* all copies or substantial portions of the Software.
*/
?>
<script>
$(function() {
$.post("/webGui/include/SmartInfo.php",{cmd:'attributes',port:'<?=isset($disks[$name]['device'])?$disks[$name]['device']:$name?>'}, function(data) {
$.post("/webGui/include/SmartInfo.php",{cmd:'attributes',port:'<?=isset($disks[$name]['device'])?$disks[$name]['device']:$name?>',name:'<?=$name?>'}, function(data) {
$('#disk_attributes').html(data);
});
});
+2 -3
View File
@@ -1,4 +1,4 @@
Menu="Device"
Menu="Device New"
Title="Capabilities"
Cond="strpos($disks[$name]['status'],'_NP')===false"
---
@@ -14,10 +14,9 @@ Cond="strpos($disks[$name]['status'],'_NP')===false"
* all copies or substantial portions of the Software.
*/
?>
<script>
$(function() {
$.post("/webGui/include/SmartInfo.php",{cmd:'capabilities',port:'<?=isset($disks[$name]['device'])?$disks[$name]['device']:$name?>'}, function(data) {
$.post("/webGui/include/SmartInfo.php",{cmd:'capabilities',port:'<?=isset($disks[$name]['device'])?$disks[$name]['device']:$name?>',name:'<?=$name?>'}, function(data) {
$('#disk_capabilities').html(data);
});
});
+2 -3
View File
@@ -1,4 +1,4 @@
Menu="Device"
Menu="Device New"
Title="Identity"
Cond="strpos($disks[$name]['status'],'_NP')===false"
---
@@ -14,10 +14,9 @@ Cond="strpos($disks[$name]['status'],'_NP')===false"
* all copies or substantial portions of the Software.
*/
?>
<script>
$(function() {
$.post("/webGui/include/SmartInfo.php",{cmd:'identify',port:'<?=isset($disks[$name]['device'])?$disks[$name]['device']:$name?>'}, function(data) {
$.post("/webGui/include/SmartInfo.php",{cmd:'identify',port:'<?=isset($disks[$name]['device'])?$disks[$name]['device']:$name?>',name:'<?=$name?>'}, function(data) {
$('#disk_identify').html(data);
});
});
+140 -148
View File
@@ -14,127 +14,72 @@ Png="devicesettings.png"
* all copies or substantial portions of the Software.
*/
?>
<?
$date = date('Ymd-Hi');
if (isset($disks[$name])) {
$disk = $disks[$name];
$dev = $disk['device'];
$file = "{$disk['id']}-$date.txt";
} else {
$disk = [];
$dev = $name;
$file = exec("ls -l /dev/disk/by-id/[au]*|grep '$name\$'|grep -Po '[ata|usb]-\K\S+'")."-$date.txt";
}
$on = exec("hdparm -C /dev/$dev|grep -Pom1 'active'");
$zip = str_replace(' ','_',strtolower($var['NAME']))."-smart-$date.zip";
$refs = array(); $n = 0;
$slot = strpos($disk['status'],'_NP')===false;
require_once "$docroot/webGui/include/Preselect.php";
$disk = &$disks[$name];
$events = isset($disk['smEvents']) ? explode('|',$disk['smEvents']) : (isset($var['smEvents']) ? explode('|',$var['smEvents']) : $numbers);
foreach ($disks as $ref) {
if ($ref['name']=='flash' || strpos($ref['status'],'_NP')) continue;
$refs[] = $ref['name'];
if ($ref['name']==$name) $i = $n;
$n++;
function displayTemp($temp) {
global $display;
return $display['unit']=='F' ? round(9/5*$temp)+32 : $temp;
}
$end = count($refs)-1;
$prev = $i>0 ? $refs[$i-1] : $refs[$end];
$next = $i<$end ? $refs[$i+1] : $refs[0];
?>
<script>
var ctrl = "<span class='status vhshift'><a href='/Main/Device?name=<?=$prev?>' title='previous device'><button type='button' style='margin-right:4px'><i class='fa fa-chevron-left'></i></button></a><a href='/Main/Device?name=<?=$next?>' title='next device'><button type='button'><i class='fa fa-chevron-right'></i></button></a></span>";
function cleanUp() {
if (document.hasFocus()) {
$('input[value="Downloading..."]').val('Download').prop('disabled',false);
$.post('/webGui/include/Download.php',{cmd:'delete',file:'<?=$file?>'});
$.post('/webGui/include/Download.php',{cmd:'delete',file:'<?=$zip?>'});
} else {
setTimeout(cleanUp,4000);
}
}
function saveSMART() {
$('input[value="Download"]').val('Downloading...').prop('disabled',true);
$.post('/webGui/include/SmartInfo.php',{cmd:'save',port:'<?=$dev?>',file:'<?=$file?>'}, function() {
$.post('/webGui/include/Download.php',{cmd:'save',source:'<?=$file?>',file:'<?=$zip?>'},function(zip) {
location = zip;
setTimeout(cleanUp,4000);
});
});
}
function testUpdate(init) {
$.post('/webGui/include/SmartInfo.php',{cmd:'update',port:'<?=$dev?>',name:'<?=$disk['name']?>'},function(data) {
$('#test_result').html(data);
if (data.indexOf('%')>=0) {
if (!init) timers.tester = setTimeout(testUpdate, 3000);
} else {
$('#short_test').val('Start');
$('#long_test').val('Start');
<?if ($on):?>
$('#short_test').removeAttr('disabled');
$('#long_test').removeAttr('disabled');
function doDispatch(form) {
var fields = {};
<?if ($display['unit']=='F'):?>
if (form.<?=$name?>_hotTemp.value>0) form.<?=$name?>_hotTemp.value = Math.round((form.<?=$name?>_hotTemp.value-32)*5/9);
if (form.<?=$name?>_maxTemp.value>0) form.<?=$name?>_maxTemp.value = Math.round((form.<?=$name?>_maxTemp.value-32)*5/9);
<?endif;?>
$.removeCookie('tester.<?=$dev?>',{path:'/'});
}
});
fields['#cfg'] = "/boot/config/smart-one.cfg";
$(form).find('input[name^="<?=$name?>_"]').each(function(){fields[$(this).attr('name')] = $(this).val(); $(this).prop('disabled',true);});
$.post('/webGui/include/Dispatcher.php',fields);
}
function selftestLog() {
$('#selftest').toggle('slow');
var text = $('#toggle_log').val()=='Show' ? 'Hide' : 'Show';
$('#toggle_log').val(text);
}
function errorLog() {
$('#errorlog').toggle('slow');
var text = $('#toggle_error').val()=='Show' ? 'Hide' : 'Show';
$('#toggle_error').val(text);
}
function startShortTest() {
if ($('#short_test').val()=='Start') {
$('#short_test').val('Stop');
$('#long_test').attr('disabled','disabled');
$.cookie('tester.<?=$dev?>','short',{path:'/'});
$.post('/webGui/include/SmartInfo.php',{cmd:'short',port:'<?=$dev?>'},function() {timers.tester = setTimeout(testUpdate,0);});
} else {
clearTimeout(timers.tester);
$.removeCookie('tester.<?=$dev?>',{path:'/'});
$.post('/webGui/include/SmartInfo.php',{cmd:'stop',port:'<?=$dev?>'}, function() {setTimeout(testUpdate,0);});
function prepareDeviceInfo(form) {
var events = [];
for (var i=0; i < <?=count($preselect)?>; i++) {
if (form.elements['at'+i].checked) events.push(form.elements['at'+i].value);
form.elements['at'+i].disabled = true;
}
var custom = form.smCustom.value.trim();
var custom = custom.length ? custom.split(',') : [];
for (var i=0; i < custom.length; i++) events.push(custom[i].trim());
form.smEvents.value = events.join('|');
}
function startLongTest() {
if ($('#long_test').val()=='Start') {
$('#long_test').val('Stop');
$('#short_test').attr('disabled','disabled');
$.cookie('tester.<?=$dev?>','long',{path:'/'});
$.post('/webGui/include/SmartInfo.php',{cmd:'long',port:'<?=$dev?>'},function(){timers.tester = setTimeout(testUpdate,0);});
function setGlue(form) {
var data = [{glue:'' ,more:0},
{glue:'' ,more:0},
{glue:'' ,more:0},
{glue:',',more:1,min1:0,max1:127},
{glue:'/',more:2,min1:1,max1:128,min2:1,max2:8},
{glue:'/',more:3,min1:1,max1:4,min2:1,max2:128,min3:1,max3:4},
{glue:'' ,more:0},
{glue:',',more:1,min1:0,max1:127}
];
var n = form.smType.selectedIndex > 0 ? form.smType.selectedIndex-1 : <?=isset($var['smIndex'])?$var['smIndex']:0?>;
var x = data[n]['more'];
for (var i=1; i <= x; i++) $('input[name="smPort'+i+'"]').attr('placeholder',data[n]['min'+i]+'..'+data[n]['max'+i]).show();
for (var i=x+1; i <= 3; i++) $('input[name="smPort'+i+'"]').val('').hide();
if (x > 0) {
$('#devtext').show();
$('input[name="smDevice"]').show();
$('#helptext').show();
} else {
clearTimeout(timers.tester);
$.removeCookie('tester.<?=$dev?>',{path:'/'});
$.post('/webGui/include/SmartInfo.php',{cmd:'stop',port:'<?=$dev?>'}, function() {setTimeout(testUpdate,0);});
$('#devtext').hide();
$('input[name="smDevice"]').val('').hide();
$('#helptext').hide();
}
}
$(function() {
<?if ($tabbed):?>
$('.tabs').append(ctrl);
<?else:?>
$('div[id=title]:not(".nocontrol")').each(function(){$(this).append(ctrl);});
<?endif;?>
<?if ($slot):?>
var tester = $.cookie('tester.<?=$dev?>');
testUpdate(tester===undefined);
if (tester !== undefined) $('#'+tester+'_test').val('Stop').removeAttr('disabled');
<?if ($on):?>
$('#toggle_log').removeAttr('disabled');
$('#toggle_error').removeAttr('disabled');
$.post('/webGui/include/SmartInfo.php',{cmd:'selftest',port:'<?=$dev?>'},function(data) { $('#selftest').html(data); });
$.post('/webGui/include/SmartInfo.php',{cmd:'errorlog',port:'<?=$dev?>'},function(data) { $('#errorlog').html(data); });
<?endif;?>
var form = document.smart_settings;
<?if (!isset($disk['smType'])):?>
form.smType.selectedIndex = 0;
<?endif;?>
setGlue(form);
});
</script>
<form markdown="1" method="POST" action="/update.htm" target="progressFrame">
<form markdown="1" method="POST" action="/update.htm" target="progressFrame" onsubmit="doDispatch(this)">
Name:
: <big><?=my_disk($name)?></big>
@@ -188,7 +133,7 @@ File system type:
<?endif;?>
Spin down delay:</td>
Spin down delay:
: <select name="diskSpindownDelay.<?=$disk['idx']?>" size="1">
<?=mk_option($disk['spindownDelay'], "-1", "Use default")?>
<?=mk_option($disk['spindownDelay'], "0", "Never")?>
@@ -204,7 +149,30 @@ Spin down delay:</td>
<?=mk_option($disk['spindownDelay'], "7", "7 hours")?>
<?=mk_option($disk['spindownDelay'], "8", "8 hours")?>
<?=mk_option($disk['spindownDelay'], "9", "9 hours")?>
</select>
</select><span id="smart_selftest" class='orange-text'></span>
Warning disk utilization level (%):
: <input type="number" min="0" max="99" name="<?=$name?>_warning" class="narrow" value="<?=!empty($disk['warning'])?$disk['warning']:''?>" placeholder="<?=$display['warning']?>">
> *Warning disk utilization* sets the warning threshold for this hard disk utilization. Exceeding this threshold will result in a warning notification.
>
> When the warning level is set equal or greater than the critical level, there will be only critical notifications (warnings are not existing).
Critical disk utilization level (%):
: <input type="number" min="0" max="100" name="<?=$name?>_critical" class="narrow" value="<?=!empty($disk['critical'])?$disk['critical']:''?>" placeholder="<?=$display['critical']?>")>
> *Critical disk utilization* sets the critical threshold for this hard disk utilization. Exceeding this threshold will result in an alert notification.
Warning disk temperature threshold (&deg;<?=$display['unit']?>):
: <input type="number" min="1" max="300" name="<?=$name?>_hotTemp" class="narrow" value="<?=!empty($disk['hotTemp'])?displayTemp($disk['hotTemp']):''?>" placeholder="<?=displayTemp($display['hot'])?>">
> *Warning disk temperature* sets the warning threshold for this hard disk temperature. Exceeding this threshold will result in a warning notification.
Critical disk temperature threshold (&deg;<?=$display['unit']?>):
: <input type="number" min="1" max="300" name="<?=$name?>_maxTemp" class="narrow" value="<?=!empty($disk['maxTemp'])?displayTemp($disk['maxTemp']):''?>" placeholder="<?=displayTemp($display['max'])?>">
> *Critical disk temperature* sets the critical threshold for this hard disk temperature. Exceeding this threshold will result in an alert notification.
<?if (($var['spinupGroups']=="yes")&&($disk['name']!="cache")):?>
@@ -215,10 +183,9 @@ Spinup group(s):
&nbsp;
: <input type="submit" name="changeDisk" value="Apply"><input type="button" value="Done" onclick="done()">
</form>
<?if ($name=="cache" && $var['SYS_CACHE_SLOTS']>1):?>
<?if ($name=="cache" && $var['SYS_CACHE_SLOTS']>1):?>
<div id="title" class="nocontrol"><span class="left"><img src="/plugins/dynamix/icons/poolinformation.png" class="icon">Pool Information</span></div>
btrfs filesystem show:
@@ -421,53 +388,78 @@ xfs_repair status:
</form>
<?endif;?>
<div id="title" class="nocontrol"><span class="left"><img src="/plugins/dynamix/icons/self-test.png" class="icon">Self-Test</span></div>
<div markdown="1">
Download SMART report:
: <input type='button' value='Download' onclick='saveSMART()'<?if (!$slot):?> disabled<?endif;?>>
<div id="title"><span class="left"><img src="/webGui/icons/smartchecks.png" class="icon">SMART Settings</span></div>
SMART self-test history:
: <input type="button" value="Show" id="toggle_log" onclick="selftestLog()" disabled>
<form markdown="1" name="smart_settings" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareDeviceInfo(this)">
<input type="hidden" name="#file" value="/boot/config/smart-one.cfg">
<input type="hidden" name="#include" value="webGui/include/update.smart.php">
<input type="hidden" name="#section" value="<?=$name?>">
<input type="hidden" name="smEvents" value="">
<input type="hidden" name="smGlue" value="<?=$var['smGlue']?>">
SMART notification value:
: <select name="smSelect" size="1">
<?=mk_option($disk['smSelect'], "-1", "Use default")?>
<?=mk_option($disk['smSelect'], "0", "Raw")?>
<?=mk_option($disk['smSelect'], "1", "Normalized")?>
</select>
> 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.
<pre id="selftest" style="display:none"></pre>
SMART error log:
: <input type="button" value="Show" id="toggle_error" onclick="errorLog()" disabled>
> 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.
<pre id="errorlog" style="display:none"></pre>
SMART short self-test:
: <input type='button' value='Start' id='short_test' onclick="startShortTest()" disabled>
> Starts a *short* SMART self-test, the estimated duration can be viewed under the *Capabilities* section. This is usually a few minutes.
> SMART notifications are generated on either an increasing RAW value of the attribute, or a decreasing NORMALIZED value which reaches a predefined threshold set by the manufacturer.
>
> 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.
> Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings).
SMART extended self-test:
: <input type='button' value='Start' id='long_test' onclick="startLongTest()" disabled>
SMART notification tolerance level:
: <select name="smLevel" size="1">
<?=mk_option($disk['smLevel'], "-1", "Use default")?>
<?=mk_option($disk['smLevel'], "1.00", "Absolute")?>
<?=mk_option($disk['smLevel'], "1.05", "5%")?>
<?=mk_option($disk['smLevel'], "1.10", "10%")?>
<?=mk_option($disk['smLevel'], "1.15", "15%")?>
<?=mk_option($disk['smLevel'], "1.20", "20%")?>
<?=mk_option($disk['smLevel'], "1.25", "25%")?>
<?=mk_option($disk['smLevel'], "1.50", "50%")?>
</select>
> Starts an *extended* SMART self-test, the estimated duration can be viewed under the *Capabilities* section. This is usually several hours.
> A tolerance level may be given to prevent that small changes result in a notification. Setting a too high tolerance level may result in critical changes without a notification.
>
> 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.
> Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings).
SMART controller type:
: <select name="smType" size="1" onchange="setGlue(this.form)">
<?=mk_option($disk['smType'], "-1", "Use default")?>
<?=mk_option($disk['smType'], "", "Automatic")?>
<?=mk_option($disk['smType'], "-d ata", "ATA")?>
<?=mk_option($disk['smType'], "-d scsi", "SCSI")?>
<?=mk_option($disk['smType'], "-d 3ware", "3Ware")?>
<?=mk_option($disk['smType'], "-d areca", "Areca")?>
<?=mk_option($disk['smType'], "-d hpt", "HighPoint")?>
<?=mk_option($disk['smType'], "-d marvell", "Marvell")?>
<?=mk_option($disk['smType'], "-d megaraid", "MegaRAID")?>
</select>
<input type="text" name="smPort1" maxlength="3" value="<?=$disk['smPort1']?>" style="display:none;width:32px">
<input type="text" name="smPort2" maxlength="3" value="<?=$disk['smPort2']?>" style="display:none;width:32px">
<input type="text" name="smPort3" maxlength="3" value="<?=$disk['smPort3']?>" style="display:none;width:32px"><span id="devtext" style="display:none;font-weight:bold">/dev/</span>
<input type="text" name="smDevice" value="<?=isset($disk['smDevice'])?$disk['smDevice']:''?>" style="display:none;width:32px" placeholder="<?=$disk['device']?>"><span id="helptext" style="display:none;font-style:italic">enter disk index and device name as applicable to your controller</span>
> By default automatic controller selection is done by smartctl to read the SMART information. Certain controllers however need specific settings for smartctl to work.
> Use this setting to select your controller type and fill-in the specific disk index and device name for your situation. Use the manufacturer's documentation to find the relevant information.
>
> This feature is only available when the disk is in active mode.
> Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings).
Last SMART test result:
<?if ($slot):?>
: <span id="test_result"></span>
<?else:?>
: ---
<?endif;?>
SMART attribute notifications:
: <input type="text" name="smCustom" value="<?=isset($disk['smCustom'])?$disk['smCustom']:$var['smCustom']?>" class="narrow">Custom attributes (use comma to separate numbers)
> 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.
<?for ($x = 0; $x < count($preselect); $x++):?>
&nbsp;
: <input type="checkbox" name="at<?=$x?>" value="<?=$preselect[$x]['code']?>"<?=in_array($preselect[$x]['code'],$events)?' checked':''?>><span style="display:inline-block;width:136px"><?=$preselect[$x]['code']?></span><?=$preselect[$x]['text']?>
<?endfor;?>
</div>
> The user can enable or disable notifications for the given SMART attributes. It is recommended to keep the default, which is ALL selected attributes,
> when certain attributes are not present on your hard disk or do not provide the correct information, these may be excluded.
> In addition custom SMART attributes can be entered to generate notifications. Be careful in this selection,
> it may cause an avalance of notifcations if inappropriate SMART attributes are chosen.
>
> Each disk may have its own specific setting overruling the 'default' setting (see global SMART settings under Disk Settings).
<input type="submit" name="#default" value="Default">
: <input type="submit" name="#apply" value="Apply"><input type="button" value="Done" onclick="done()">
</form>
+14 -70
View File
@@ -14,80 +14,24 @@ Cond="$var['fsState']=='Started' && $var['shareDisk']!='no'"
* all copies or substantial portions of the Software.
*/
?>
<script>
function displayDisks(all) {
$('#disk_list').html("<tr><td colspan='8' style='text-align:center'><i class='fa fa-spinner fa-spin icon'></i>Please wait... computing disk shares!</td></tr>");
$.get('/webGui/include/DiskList.php',{compute:all,path:'<?=$path?>',prev:'<?=$prev?>',scale:'<?=$display['scale']?>',number:'<?=$display['number']?>'},function(data){
$('#disk_list').html(data);
$('#compute-disks').prop('disabled',!data || data.indexOf('colspan=')!=-1);
});
}
$(function() {
displayDisks('no');
});
</script>
<?
// 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 '<em>'.ucfirst($share['security']).'</em>';
}
// Share size per disk
$preserve = $path==$prev;
$ssz2 = array();
foreach (glob("state/*.ssz2", GLOB_NOSORT) as $entry) {
if ($preserve) {
$ssz2[basename($entry, ".ssz2")] = parse_ini_file($entry);
} else {
unlink($entry);
}
}
?>
<table class="share_status share <?=$display['view']?>">
<thead><tr><td style="width:15%">Name</td><td style="width:33%">Comment</td><td>SMB</td><td>NFS</td><td>AFP</td><td>Size</td><td>Free</td><td>View</td></tr></thead>
<tbody>
<?
foreach ($disks as $disk):
if ($disk['type']=='Flash') continue;
if ($disk['fsColor']=='grey-off') continue;
if ($disk['exportable']=='no') continue;
$num_exportable++;
$name = $disk['name'];
$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;
}
?><tr>
<td><a class='info nohand' onclick='return false'><img src='<?=$ball?>' class='icon'><span style="left:18px"><?=$help?></span></a><a href="<?=$path?>/Disk?name=<?=$name?>" onclick="$.cookie('one','tab1',{path:'/'})"><?=$name?></a></td>
<td><?=$disk['comment']?></td>
<td><?=disk_share_settings($var['shareSMBEnabled'], $sec[$name])?></td>
<td><?=disk_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])?></td>
<td><?=disk_share_settings($var['shareAFPEnabled'], $sec_afp[$name])?></td>
<?if (array_key_exists($name, $ssz2)):?>
<td><?=my_scale(($disk['fsSize'])*1024, $unit).' '.$unit?></td>
<td><?=my_scale($disk['fsFree']*1024, $unit).' '.$unit?></td>
<td><a href="<?=$path?>/Browse?dir=/mnt/<?=$name?>"><img src="/webGui/images/explore.png" title="Browse /mnt/<?=$name?>"></a></td>
</tr>
<?foreach ($ssz2[$name] as $share_name => $share_size):
if ($share_name!="total"):
?> <tr class="share_status_size">
<td><?=$share_name?>:</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><?=my_scale($share_size*1024, $unit).' '.$unit?></td>
<td><?=my_scale($disk['fsFree']*1024, $unit).' '.$unit?></td>
<td></td>
</tr>
<? endif;
endforeach;
else:
$cmd="/webGui/scripts/disk_size&arg1=$name&arg2=/var/local/emhttp/$name.ssz2";
?><td><a href="/update.htm?cmd=<?=$cmd?>" target="progressFrame" onclick="$(this).text('Please wait...')">Compute...</a></td>
<td><?=my_scale($disk['fsFree']*1024, $unit).' '.$unit?></td>
<td><a href="<?=$path?>/Browse?dir=/mnt/<?=$name?>"><img src="/webGui/images/explore.png" title="Browse /mnt/<?=$name?>"></a></td>
</tr>
<?endif;
endforeach;
?>
</tbody>
<tbody id="disk_list"></tbody>
</table>
<?if (!isset($num_exportable)):?>
<div style="font-style:italic;font-size:14px;opacity:0.5;padding:5px;">There are no exportable disk shares</div>
<?endif;?>
<p><input id="compute-disks" type="button" value="Compute All" onclick="$(this).prop('disabled',true);displayDisks('yes')"></p>
> **Colored Status Indicator** the significance of the color indicator at the beginning of each line in *Disk Shares* is as follows:
>
+127 -1
View File
@@ -14,7 +14,43 @@ Icon="disk-settings.png"
* all copies or substantial portions of the Software.
*/
?>
<form markdown="1" method="POST" action="/update.htm" target="progressFrame">
<?
require_once "$docroot/webGui/include/Preselect.php";
$events = isset($var['smEvents']) ? explode('|', $var['smEvents']) : $numbers;
function displayTemp($temp) {
global $display;
return $display['unit']=='F' ? round(9/5*$temp)+32 : $temp;
}
?>
<script>
function doDispatch(form) {
var fields = {};
<?if ($display['unit']=='F'):?>
form.display_hot.value = Math.round((form.display_hot.value-32)*5/9);
form.display_max.value = Math.round((form.display_max.value-32)*5/9);
<?endif;?>
fields['#cfg'] = "/boot/config/plugins/dynamix/dynamix.cfg";
$(form).find('input[name^="display_"]').each(function(){fields[$(this).attr('name')] = $(this).val(); $(this).prop('disabled',true);});
$.post('/webGui/include/Dispatcher.php',fields);
}
function prepareDiskSettings(form) {
var events = [];
for (var i=0; i < <?=count($preselect)?>; i++) {
if (form.elements['at'+i].checked) events.push(form.elements['at'+i].value);
form.elements['at'+i].disabled = true;
}
var custom = form.smCustom.value.trim();
var custom = custom.length ? custom.split(',') : [];
for (var i=0; i < custom.length; i++) events.push(custom[i].trim());
form.smEvents.value = events.join('|');
}
function setIndex(form) {
form.smIndex.value = form.smType.selectedIndex;
}
</script>
<form markdown="1" method="POST" action="/update.htm" target="progressFrame" onsubmit="doDispatch(this)">
Enable auto start:
: <select name="startArray" size="1">
<?=mk_option($var['startArray'], "no", "No")?>
@@ -135,6 +171,96 @@ Tunable (md_write_method):
>
> *Auto* selects `read/modify/write`.
Default warning disk utilization level (%):
: <input type="number" min="0" max="99" name="display_warning" class="narrow" value="<?=$display['warning']?>">
> *Warning disk utilization* sets the default warning threshold for all hard disks utilization. Exceeding this threshold will result in a warning notification.
>
> When the warning level is set equal or greater than the critical level, there will be only critical notifications (warnings are not existing).
Default critical disk utilization level (%):
: <input type="number" min="0" max="100" name="display_critical" class="narrow" value="<?=$display['critical']?>">
> *Critical disk utilization* sets the default critical threshold for all hard disks utilization. Exceeding this threshold will result in an alert notification.
Default warning disk temperature threshold (&deg;<?=$display['unit']?>):
: <input type="number" min="1" max="300" name="display_hot" class="narrow" value="<?=displayTemp($display['hot'])?>">
> *Warning disk temperature* sets the default warning threshold for all hard disks temperature. Exceeding this threshold will result in a warning notification.
Default critical disk temperature threshold (&deg;<?=$display['unit']?>):
: <input type="number" min="1" max="300" name="display_max" class="narrow" value="<?=displayTemp($display['max'])?>">
> *Critical disk temperature* sets the default critical threshold for all hard disks temperature. Exceeding this threshold will result in an alert notification.
&nbsp;
: <input type="submit" name="changeDisk" value="Apply"><input type="button" value="Done" onclick="done()">
</form>
<div id="title"><span class="left"><img src="/webGui/icons/smartchecks.png" class="icon">Global SMART Settings</span></div>
<form markdown="1" name="smart_settings" method="POST" action="/update.php" target="progressFrame" onsubmit="prepareDiskSettings(this)">
<input type="hidden" name="#file" value="/boot/config/smart-all.cfg">
<input type="hidden" name="#include" value="webGui/include/update.smart.php">
<input type="hidden" name="smEvents" value="">
<input type="hidden" name="smIndex" value="0">
Default SMART notification value:
: <select name="smSelect" size="1">
<?=mk_option($var['smSelect'], "0", "Raw")?>
<?=mk_option($var['smSelect'], "1", "Normalized")?>
</select>
> SMART notifications are generated on either an increasing RAW value of the attribute, or a decreasing NORMALIZED value which reaches a predefined threshold set by the manufacturer.
>
> This section is used to set the global settings for all disks. It is possible to adjust settings for individual disks.
Default SMART notification tolerance level:
: <select name="smLevel" size="1">
<?=mk_option($var['smLevel'], "1.00", "Absolute")?>
<?=mk_option($var['smLevel'], "1.05", "5%")?>
<?=mk_option($var['smLevel'], "1.10", "10%")?>
<?=mk_option($var['smLevel'], "1.15", "15%")?>
<?=mk_option($var['smLevel'], "1.20", "20%")?>
<?=mk_option($var['smLevel'], "1.25", "25%")?>
<?=mk_option($var['smLevel'], "1.50", "50%")?>
</select>
> A tolerance level may be given to prevent that small changes result in a notification. Setting a too high tolerance level may result in critical changes without a notification.
>
> This section is used to set the global settings for all disks. It is possible to adjust settings for individual disks.
Default SMART controller type:
: <select name="smType" size="1" onchange="setIndex(this.form)">
<?=mk_option($var['smType'], "", "Automatic")?>
<?=mk_option($var['smType'], "-d ata", "ATA")?>
<?=mk_option($var['smType'], "-d scsi", "SCSI")?>
<?=mk_option($var['smType'], "-d 3ware", "3Ware")?>
<?=mk_option($var['smType'], "-d areca", "Areca")?>
<?=mk_option($var['smType'], "-d hpt", "HighPoint")?>
<?=mk_option($var['smType'], "-d marvell", "Marvell")?>
<?=mk_option($var['smType'], "-d megaraid", "MegaRAID")?>
</select>
> By default automatic controller selection is done by smartctl to read the SMART information. Certain controllers however need specific settings for smartctl to work.
> Use this setting to select your controller type and fill-in the specific disk index and device name for your situation. Use the manufacturer's documentation to find the relevant information.
>
> This section is used to set the global settings for all disks. It is possible to adjust settings for individual disks.
Default SMART attribute notifications:
: <input type="text" name="smCustom" value="<?=$var['smCustom']?>" class="narrow">Custom attributes (use comma to separate numbers)
<?for ($x = 0; $x < count($preselect); $x++):?>
&nbsp;
: <input type="checkbox" name="at<?=$x?>" value="<?=$preselect[$x]['code']?>"<?=in_array($preselect[$x]['code'],$events)?' checked':''?>><span style="display:inline-block;width:136px"><?=$preselect[$x]['code']?></span><?=$preselect[$x]['text']?>
<?endfor;?>
> The user can enable or disable notifications for the given SMART attributes. It is recommended to keep the default, which is ALL selected attributes,
> when certain attributes are not present on your hard disk or do not provide the correct information, these may be excluded.
> In addition custom SMART attributes can be entered to generate notifications. Be careful in this selection,
> it may cause an avalance of notifcations if inappropriate SMART attributes are chosen.
>
> This section is used to set the global settings for all disks. It is possible to adjust settings for individual disks.
<input type="submit" name="#default" value="Default">
: <input type="submit" name="#apply" value="Apply"><input type="button" value="Done" onclick="done()">
</form>
+36 -89
View File
@@ -37,11 +37,9 @@ function restore() {
$('select[name="banner"]').trigger('change');
filename = 'reset';
}
function forceReload() {
window.onload = parent.location.reload(true);
}
function upload(remove) {
// gets invoked when the user clicks APPLY, at this point image will be saved or deleted
if (remove || filename=='reset') {
@@ -51,7 +49,20 @@ function upload(remove) {
window.onload = forceReload();
}
}
function presetTime(form) {
var system = form.date.selectedIndex==0;
if (system) form.time.selectedIndex = 0;
form.time.disabled = system;
}
function presetBanner(form) {
if (form.banner.selectedIndex == 0) $('#custom').hide(); else $('#custom').show();
}
function presetRefresh(form) {
for (var i=0,item; item=form.refresh.options[i]; i++) item.value *= -1;
}
function presetPassive(index) {
if (index==0) $('#passive').hide(); else $('#passive').show();
}
$(function() {
// appending a time dummy will make the browser think/realize the image has changed
$('#image').attr('src', $('#image').attr('src')+'?t='+new Date().getTime());
@@ -106,73 +117,15 @@ $(function() {
presetTime(document.display_settings);
presetBanner(document.display_settings);
});
function prepareDisplay(form) {
if (!form.critical.value | !form.warning.value) {
swal({title:"Missing value",text:"An empty percentage value is not allowed!",type:"error"});
return false;
}
if (form.warning.value>form.critical.value) {
swal({title:"Wrong value",text:"Warning percentage must be lower than Critical percentage!",type:"error"});
return false;
}
if (!form.number.value) form.number.value='.,';
if (form.unit.selectedIndex==1) {
form.hot.value = Math.round(5/9*(form.hot.value-32));
form.max.value = Math.round(5/9*(form.max.value-32));
}
return true;
}
function presetTime(form) {
var system = form.date.selectedIndex==0;
if (system) form.time.selectedIndex = 0;
form.time.disabled = system;
}
function presetBanner(form) {
if (form.banner.selectedIndex == 0) $('#custom').hide(); else $('#custom').show();
}
function presetTemp(form) {
var hot = $('#hot').html();
var max = $('#max').html();
if (form.unit.selectedIndex==0) {
form.hot.value = Math.round(5/9*(form.hot.value-32));
form.max.value = Math.round(5/9*(form.max.value-32));
$('#hot').html(hot.replace('F)','C)'));
$('#max').html(max.replace('F)','C)'));
} else {
form.hot.value = Math.round((9/5*form.hot.value)+32);
form.max.value = Math.round((9/5*form.max.value)+32);
$('#hot').html(hot.replace('C)','F)'));
$('#max').html(max.replace('C)','F)'));
}
}
function presetRefresh(form) {
for (var i=0,item; item=form.refresh.options[i]; i++) item.value *= -1;
}
function presetPassive(index) {
if (index==0) $('#passive').hide(); else $('#passive').show();
}
function checkTemp(form,what) {
if (form.hot.value>=form.max.value) {
var message = what ? 'Warning temperature must be lower than Critical temperature' : 'Critical temperature must be higher than Warning temperature';
swal({title:"Incorrect value",text:message,type:"warning"});
}
}
</script>
> The display settings below determine how items are displayed on screen. Use these settings to tweak the visual effects to your likings.
>
> You can experiment with these settings as desired, they only affect visual properties.
<form markdown="1" name="display_settings" method="POST" action="/update.php" target="progressFrame" onsubmit="return prepareDisplay(this)">
<input type="hidden" name="#file" value="dynamix/dynamix.cfg"/>
<input type="hidden" name="#section" value="display"/>
<form markdown="1" name="display_settings" method="POST" action="/update.php" target="progressFrame">
<input type="hidden" name="#file" value="dynamix/dynamix.cfg">
<input type="hidden" name="#section" value="display">
Date format:
: <select name="date" size="1" onchange="presetTime(this.form)">
<?=mk_option($display['date'], "%c", "System Setting")?>
@@ -193,8 +146,12 @@ Time format:
Number format:
: <select name="number" size="1">
<?=mk_option($display['number'], ".,", "American")?>
<?=mk_option($display['number'], ",.", "European")?>
<?=mk_option($display['number'], ".,", "[D] dot : [G] comma")?>
<?=mk_option($display['number'], ". ", "[D] dot : [G] space")?>
<?=mk_option($display['number'], ".", "[D] dot : [G] none")?>
<?=mk_option($display['number'], ",.", "[D] comma : [G] dot")?>
<?=mk_option($display['number'], ", ", "[D] comma : [G] space")?>
<?=mk_option($display['number'], ",", "[D] comma : [G] none")?>
</select>
Number scaling:
@@ -221,6 +178,18 @@ Page view:
<?=mk_option($display['tabs'], "1", "Non-tabbed")?>
</select>
Placement of Users menu:
: <select name="users" size="1">
<?=mk_option($display['users'], "Tasks:3", "Header menu")?>
<?=mk_option($display['users'], "UserPreferences", "Settings menu")?>
</select>
Listing height:
: <select name="resize" size="1">
<?=mk_option($display['resize'], "0", "Automatic")?>
<?=mk_option($display['resize'], "1", "Fixed")?>
</select>
Table view spacing:
: <select name="view" size="1">
<?=mk_option($display['view'], "", "Normal")?>
@@ -285,20 +254,8 @@ Used / Free columns:
<?=mk_option($display['text'], "21", "Bar (color) - Text")?>
</select>
Warning disk utilization level (%):
: <input type="number" min="0" max="99" name="warning" class="narrow" maxlength="2" value="<?=$display['warning']?>">
> *Warning disk utilization* sets the warning threshold for a hard disk utilization. Exceeding this threshold will result in a warning notification.
>
> When the warning level is set equal or greater than the critical level, there will be only critical notifications (warnings are not existing).
Critical disk utilization level (%):
: <input type="number" min="0" max="100" name="critical" class="narrow" maxlength="3" value="<?=$display['critical']?>">
> *Critical disk utilization* sets the critical threshold for a hard disk utilization. Exceeding this threshold will result in an alert notification.
Temperature unit:
: <select name="unit" size="1" onchange="presetTemp(this.form)">
: <select name="unit" size="1">
<?=mk_option($display['unit'], "C", "Celsius")?>
<?=mk_option($display['unit'], "F", "Fahrenheit")?>
</select>
@@ -307,16 +264,6 @@ Temperature unit:
>
> Make sure any newly entered values represent the selected temperature unit.
<span id="hot">Warning disk temperature threshold (&deg;<?=$display['unit']?>):</span>
: <input type="number" min="0" max="300" name="hot" class="narrow" maxlength="3" value="<?=$display['unit']=='C'?$display['hot']:round(9/5*$display['hot']+32)?>" onchange="checkTemp(this.form,true)">
> *Warning disk temperature* sets the warning threshold for a hard disk temperature. Exceeding this threshold will result in a warning notification.
<span id="max">Critical disk temperature threshold (&deg;<?=$display['unit']?>):</span>
: <input type="number" min="0" max="300" name="max" class="narrow" maxlength="3" value="<?=$display['unit']=='C'?$display['max']:round(9/5*$display['max']+32)?>" onchange="checkTemp(this.form,false)">
> *Critical disk temperature* sets the critical threshold for a hard disk temperature. Exceeding this threshold will result in an alert notification.
Page update frequency:
: <select name="refresh" size="1" onchange="presetPassive(this.selectedIndex)">
<?=mk_option($display['refresh'], "0", "Disabled")?>
+14 -4
View File
@@ -22,6 +22,7 @@ if (file_exists($ftp_userlist_file)) {
$ftp_userlist = str_replace("\n", " ", trim(file_get_contents($ftp_userlist_file)));
if ($ftp_userlist === false) $ftp_userlist = "";
}
$ftp_server = exec("lsof -i:21 -Pln|awk '/\(LISTEN\)/{print $2;exit}'") ? 1 : 0;
?>
<script>
$(function() {
@@ -32,11 +33,20 @@ $(function() {
<form markdown="1" method="POST" action="/update.php" target="progressFrame">
<input type="hidden" name="#command" value="/webGui/scripts/ftpusers">
FTP user(s):
: <input type="text" name="#arg[1]" size="40" maxlength="80" value="<?=$ftp_userlist;?>">
FTP server:
: <select name="#arg[1]" size="1">
<?=mk_option($ftp_server, "0", "Disabled")?>
<?=mk_option($ftp_server, "1", "Enabled")?>
</select>
> Enter the user names (separated by spaces) permitted to acces the server using [FTP](/Help).
> To disable FTP, clear this setting.
> Enable or disable the FTP server daemon. By default the FTP server is enabled.
> This setting is not saved, i.e. upon system reboot it will revert to its default setting.
FTP user(s):
: <input type="text" name="#arg[2]" size="40" maxlength="80" value="<?=$ftp_userlist;?>">
> Enter the user names (separated by spaces) permitted to access the server using FTP.
> To disallow any user access, clear this setting.
>
> **Note:** do not enter user name `root` since this may cause problems in the future.
+6 -1
View File
@@ -18,6 +18,11 @@ Icon="help.png"
function HelpButton() {
$('.inline_help').toggle('slow');
$('#nav-item.HelpButton').toggleClass('nav-button-active');
$.cookie('help')==null ? $.cookie('help','help',{path:'/'}) : $.removeCookie('help',{path:'/'});
if ($.cookie('help')==null) {
$.cookie('help','help',{path:'/'}); var help = 'on';
} else {
$.removeCookie('help',{path:'/'}); var help = 'off';
}
swal({title:'Help is '+help.toUpperCase(),imageUrl:'/webGui/images/help-'+help+'.png',timer:1500,animation:'slide-from-top',showConfirmButton:false});
}
</script>
+2 -2
View File
@@ -42,8 +42,8 @@ function presetMover(form) {
form.min.disabled = false;
form.day.disabled = mode!=2;
form.dotm.disabled = mode!=3;
form.day.value = form.day.disabled ? '*' : '0';
form.dotm.value = form.dotm.disabled ? '*' : '1';
form.day.value = form.day.disabled ? '*' : (form.day.value=='*' ? 0 : form.day.value);
form.dotm.value = form.dotm.disabled ? '*' : (form.dotm.value=='*' ? 1 : form.dotm.value);
if (mode==0) {$('#H1').hide(); $('#H2').show();} else {$('#H2').hide(); $('#H1').show();}
}
</script>
+1
View File
@@ -0,0 +1 @@
Type="xmenu"
-64
View File
@@ -16,23 +16,12 @@ Icon="notifications.png"
* all copies or substantial portions of the Software.
*/
?>
<?
$events = explode('|', $notify['events']);
$disabled = $notify['system'] ? '' : 'disabled';
?>
<link type="text/css" rel="stylesheet" href="/webGui/styles/jquery.switchbutton.css">
<style>
.advanced{display:none;}
.switch-button-label.off{color:inherit;}
</style>
<script src="/webGui/javascript/jquery.switchbutton.js"></script>
<script>
var this_tab = $('input[name$="tabs"]').length;
function prepareNotify(form) {
var events = [];
form.entity.value = form.normal1.checked | form.warning1.checked | form.alert1.checked;
form.normal.value = form.normal1.checked*1 + form.normal2.checked*2 + form.normal3.checked*4;
form.warning.value = form.warning1.checked*1 + form.warning2.checked*2 + form.warning3.checked*4;
@@ -58,12 +47,6 @@ function prepareNotify(form) {
form.report1.disabled = true;
form.report2.disabled = true;
form.report3.disabled = true;
for (var i=1; i<=5; i++) {
if (form.elements['a'+i].checked) events.push(form.elements['a'+i].value);
form.elements['a'+i].disabled = true;
}
if (form.custom.value>0) events.push(form.custom.value);
form.events.value = events.join('|');
}
function prepareCheckbox(index) {
if (index==0) $('.checkbox').attr('disabled','disabled'); else $('.checkbox').removeAttr('disabled');
@@ -88,27 +71,6 @@ function prepareReport(value) {
prepareTitle();
}
$(function(){
var last = $('input[name$="tabs"]').length;
var elementId = "basicAdvanced";
$('.tabs').append($('<span id="'+elementId+'" class="status" style="margin-top: 2px;display: none;"><input type="checkbox" class="advancedview"></span>'));
<?if ($tabbed):?>
if ($('#tab'+this_tab).is(':checked')) $('#'+elementId).show();
$('#tab'+this_tab).bind({click:function(){$('#'+elementId).show();}});
for (var x=1; x<=last; x++) if(x != this_tab) $('#tab'+x).bind({click:function(){$('#'+elementId).hide();}});
<?else:?>
$('#'+elementId).show();
<?endif;?>
if ($.cookie('notifcations_view_mode') == 'advanced') $('.advanced').show();
$('.advancedview').switchButton({
labels_placement: "left",
on_label: 'Advanced View',
off_label: 'Basic View',
checked: $.cookie('notifcations_view_mode') == 'advanced'
});
$('.advancedview').change(function() {
$('.advanced').finish().toggle('fast');
$.cookie('notifcations_view_mode', $(this).is(':checked') ? 'advanced' : 'basic', {expires:3650});
});
preparePlugin(document.notify_settings.version.value);
prepareDocker(document.notify_settings.docker_update.value);
prepareReport(document.notify_settings.status.value);
@@ -126,7 +88,6 @@ $(function(){
<input type="hidden" name="plugin">
<input type="hidden" name="docker_notify">
<input type="hidden" name="report">
<input type="hidden" name="events">
Date format:
: <select name="date" size="1">
<?=mk_option($notify['date'], "d-m-Y", "DD-MM-YYYY")?>
@@ -261,31 +222,6 @@ Notification entity:
>
> Choose for each classification how you want to be notified.
<div class="advanced" markdown="1">
SMART attribute notifications:
: <input type="checkbox" name="a1" value="5"<?=in_array(5,$events) ? ' checked' : ''?>><span style="display:inline-block;width:72px">5</span>Reallocated sectors count
&nbsp;
: <input type="checkbox" name="a2" value="187"<?=in_array(187,$events) ? ' checked' : ''?>><span style="display:inline-block;width:72px">187</span>Reported uncorrectable errors
&nbsp;
: <input type="checkbox" name="a3" value="188"<?=in_array(188,$events) ? ' checked' : ''?>><span style="display:inline-block;width:72px">188</span>Command timeout
&nbsp;
: <input type="checkbox" name="a4" value="197"<?=in_array(197,$events) ? ' checked' : ''?>><span style="display:inline-block;width:72px">197</span>Current pending sector count
&nbsp;
: <input type="checkbox" name="a5" value="198"<?=in_array(198,$events) ? ' checked' : ''?>><span style="display:inline-block;width:72px">198</span>Uncorrectable sector count
&nbsp;
: <input type="text" name="custom" value="<?=$notify['custom']?>" maxlength="3" style="width:25px;margin-right:57px">Custom attribute number</dd>
</div>
<div class="advanced" markdown="1">
> The user can enable or disable notifications for the given SMART attributes. It is recommended to keep the default, which is ALL selected attributes, but when certain attributes are not present on your hard disk, these may be excluded.
> In addition a custom SMART attribute number can be given to generate notifications. Be careful in the selection, this may cause an avalance of notifcations if an inappropriate SMART attribute is chosen.
</div>
<input type="submit" name="#default" value="Default">
: <input type="submit" name="#apply" value="Apply"><input type="button" value="Done" onclick="done()"></td>
</form>
+47 -7
View File
@@ -1,4 +1,4 @@
Menu="Notifications:4"
Menu="UNRAID-OS"
Title="Archived Notifications"
---
<?PHP
@@ -16,33 +16,73 @@ Title="Archived Notifications"
?>
<?
$path = "{$notify['path']}/archive/*.notify";
$files = count(glob($path, GLOB_NOSORT));
$files = count(glob("{$notify['path']}/archive/*.notify", GLOB_NOSORT));
?>
<script>
var rows = [];
Array.prototype.remove = function() {
var val, a = arguments, L = a.length, ax;
while (L && this.length) {
val = a[--L];
while ((ax = this.indexOf(val)) !== -1) this.splice(ax, 1);
}
return this;
};
<?if ($display['resize']):?>
function resize(bind) {
var width = [];
var h = $('#archive_list').height();
var s = Math.max(window.innerHeight-300,370);
if (h>s || bind) {
$('#archive_list').height(s);
$('#archive_table tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#archive_table thead tr th').each(function(i){$(this).width(width[i]);});
if (!bind) $('#archive_table thead,#archive_table tbody').addClass('fixed');
}
}
<?endif;?>
function archiveList(init) {
$.post('/webGui/include/NotificationsArchive.php',{log:'<?=$path?>',date:'<?=$notify['date']?>',time:'<?=$notify['time']?>'},function(data) {
$.get('/webGui/include/NotificationsArchive.php',function(data) {
if (data) $('#archive_list').html(data);
if (init) {
<?if ($display['resize']):?>
resize();
<?endif;?>
$('#archive_table').tablesorter({headers:{5:{sorter:false}}});
} else {
$('#archive_table').trigger('update',true);
}
$('#archive_list .expand-child td').hide();
for (var i = 0; i < rows.length; i++) $('#archive_list .expand-child.row'+rows[i]+' td').show();
<?if ($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)):?>
setTimeout(archiveList,<?=max(abs($display['refresh']),30000)?>);
<?endif;?>
});
}
function askConfirmation() {
swal({title:"Are you sure?",text:"This will delete all notification files!",type:"warning",showCancelButton:true},function(){$.get('/webGui/include/DeleteLogFile.php',{log:'<?=$path?>'},function(){archiveList();});});
swal({title:"Are you sure?",text:"This will delete all notification files!",type:"warning",showCancelButton:true},function(){$.post('/webGui/include/DeleteLogFile.php',{log:'*.notify'},function(){archiveList();});});
}
function openClose(row) {
var extra = '#archive_list .expand-child.row'+row+' td';
if ($(extra).is(":visible")) {
$(extra).hide();
rows.remove(row);
} else {
$(extra).show();
rows.push(row);
}
}
$(function(){
archiveList(true);
<?if ($display['resize']):?>
$(window).bind('resize',function(){resize(true);});
<?endif;?>
});
</script>
// Adjust the width of thead cells when window resizes
</script>
<table class="tablesorter left shift" id="archive_table">
<thead><tr><th>Time</th><th>Event</th><th>Subject</th><th>Description</th><th style='width:7%'>Importance</th><th style='width:4%;text-align:right'><?if ($files):?><a href="#" onclick="askConfirmation();return false" title="Delete all notifications"><i class="fa fa-trash"></i></a><?endif;?></th></tr></thead>
<thead><tr><th>Time</th><th>Event</th><th>Subject</th><th>Description</th><th>Importance</th><th style="text-align:right"><?if ($files):?><a href="#" onclick="askConfirmation();return false" title="Delete all notifications"><i class="fa fa-trash"></i></a><?endif;?></th></tr></thead>
<tbody id="archive_list"></tbody>
</table>
<input type="button" value="Done" onclick="done()">
+2 -2
View File
@@ -20,10 +20,10 @@ $tabX = '#tab'.($var['fsState']=='Stopped'||is_dir('/mnt/cache') ? '4' : '3');
?>
<script>
function open_status() {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'open',width:window.innerWidth,timer:timer},function(data) {
$.post('/webGui/include/DeviceList.php',{path:'<?=$path?>',device:'open'},function(data) {
if (data) $('#open_devices').html(data);
<?if (($display['refresh']>0 || ($display['refresh']<0 && $var['mdResync']==0)) && $var['fsState']=='Started'):?>
<?if ($display['tabs']==0):?>
<?if ($tabbed):?>
if ($('<?=$tabX?>').is(':checked')) timer = setTimeout(open_status,<?=abs($display['refresh'])?>);
<?else:?>
setTimeout(open_status,<?=abs($display['refresh'])?>);
+13 -3
View File
@@ -13,7 +13,17 @@ Title="Page Map"
* all copies or substantial portions of the Software.
*/
?>
<?if ($display['resize']):?>
<script>
function resize() {
$('div.up').height(Math.max(window.innerHeight-280,370)).show();
}
$(function() {
resize();
$(window).bind('resize',function(){resize();});
});
</script>
<?endif;?>
<?
function show_map($menu, $level) {
$pages = find_pages( $menu);
@@ -33,9 +43,9 @@ function show_map($menu, $level) {
}
echo "</ul>";
}
echo "<div class='up'>";
echo $display['resize'] ? "<div class='up' style='display:none'>" : "<div class='up'>";
show_map("Tasks", 1);
show_map("Buttons", 1);
echo "</div>";
?>
<input type="button" value="Done" onclick="done()">
<input type="button" value="Done" onclick="done()">
+13 -3
View File
@@ -13,8 +13,18 @@ Title="Processes"
* all copies or substantial portions of the Software.
*/
?>
<?if ($display['resize']):?>
<script>
function resize() {
$('pre.up').height(Math.max(window.innerHeight-280,370)).show();
}
$(function() {
resize();
$(window).bind('resize',function(){resize();});
});
</script>
<?endif;?>
<?
echo "<pre class='up'>".shell_exec('ps -ef')."</pre>";
echo ($display['resize'] ? "<pre class='up' style='display:none'>" : "<pre class='up'>").shell_exec('ps -ef')."</pre>";
?>
<input type="button" value="Done" onclick="done()">
<input type="button" value="Done" onclick="done()">
+38
View File
@@ -0,0 +1,38 @@
Menu="SMB:2"
Title="SMB Extras"
Cond="($var['shareSMBEnabled']!='no')"
Png="smbsettings.png"
---
<?PHP
/* Copyright 2016, Lime Technology
* Copyright 2016, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
> 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);
?>
<form markdown="1" method="POST" action="/update.php" target="progressFrame">
<input type="hidden" name="#include" value="/webGui/include/update.file.php">
<input type="hidden" name="#file" value="<?=$file;?>">
Samba extra configuration:
: <textarea spellcheck="false" cols="80" rows="22" maxlength="2048" name="text" style="font-family:bitstream;width:66%"><?=$text;?></textarea>
&nbsp;
: <input type="submit" value="Apply"/><input type="button" value="Done" onclick="done()">
> Click the **Apply** button to commit the current edits. Click **Reset** to
> undo any changes you make (before Saving). Click **Done** to exit this page.
</form>
+220
View File
@@ -0,0 +1,220 @@
Menu="Device:2 New:2"
Title="Self-Test"
Cond="strpos($disks[$name]['status'],'_NP')===false"
---
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$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:
: <input type='button' value='Download' onclick='saveSMART()'>
SMART self-test history:
: <input type="button" value="Show" id="toggle_log" onclick="selftestLog()" disabled>
> 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.
<pre id="selftest" style="display:none"></pre>
SMART error log:
: <input type="button" value="Show" id="toggle_error" onclick="errorLog()" disabled>
> 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.
<pre id="errorlog" style="display:none"></pre>
SMART short self-test:
: <input type='button' value='Start' id='short_test' onclick="startShortTest()" disabled>
> 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:
: <input type='button' value='Start' id='long_test' onclick="startLongTest()" disabled>
> 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:
: <span id="test_result"><i class='fa fa-spinner fa-spin'></i>&nbsp;<em>Please wait... retrieving S.M.A.R.T. information!</em></span>
> 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.
<script>
function cleanUp() {
if (document.hasFocus()) {
$('input[value="Downloading..."]').val('Download').prop('disabled',false);
$.post('/webGui/include/Download.php',{cmd:'delete',file:'<?=$file?>'});
$.post('/webGui/include/Download.php',{cmd:'delete',file:'<?=$zip?>'});
} else {
setTimeout(cleanUp,4000);
}
}
function saveSMART() {
$('input[value="Download"]').val('Downloading...').prop('disabled',true);
$.post('/webGui/include/SmartInfo.php',{cmd:'save',port:'<?=$dev?>',name:'<?=$name?>',file:'<?=$file?>'}, function() {
$.post('/webGui/include/Download.php',{cmd:'save',source:'<?=$file?>',file:'<?=$zip?>'},function(zip) {
location = zip;
setTimeout(cleanUp,4000);
});
});
}
function testUpdate(init) {
$.post('/webGui/include/SmartInfo.php',{cmd:'update',port:'<?=$dev?>',name:'<?=$name?>',type:'<?=$type?>'},function(data) {
$('#test_result').html(data);
if (data.indexOf('%')>=0) {
if ($('#smart_selftest').length) {
<?if ($spindown):?>
$('select[name="<?=$spindown?>"]').prop('disabled',true);
<?endif;?>
$('#smart_selftest').html('<i class="fa fa-camera"></i> S.M.A.R.T self-test in progress...');
}
if (!init) timers.testUpdate = setTimeout(testUpdate, 3000);
} else {
if ($('#smart_selftest').length) $('#smart_selftest').html('');
$('#short_test').val('Start');
$('#long_test').val('Start');
<?if ($live):?>
$('#short_test').removeAttr('disabled');
$('#long_test').removeAttr('disabled');
<?endif;?>
if (!init) {
$.removeCookie('test.<?=$dev?>',{path:'/'});
<?if ($spindown):?>
$('select[name="<?=$spindown?>"]').val(<?=spindownDelay()?>).prop('disabled',false);
$.post('/update.htm',{'<?=$spindown?>':<?=spindownDelay()?>,changeDisk:'apply'});
$.post('/webGui/include/SmartInfo.php',{cmd:'delete',file:'<?=$spindown?>'});
<?endif;?>
}
}
});
}
function selftestLog() {
$('#selftest').toggle('slow');
var text = $('#toggle_log').val()=='Show' ? 'Hide' : 'Show';
$('#toggle_log').val(text);
}
function errorLog() {
$('#errorlog').toggle('slow');
var text = $('#toggle_error').val()=='Show' ? 'Hide' : 'Show';
$('#toggle_error').val(text);
}
function startShortTest() {
if ($('#short_test').val()=='Start') {
$('#short_test').val('Stop');
$('#long_test').attr('disabled','disabled');
$.cookie('test.<?=$dev?>','short',{path:'/',expires:365});
$.post('/webGui/include/SmartInfo.php',{cmd:'short',port:'<?=$dev?>',name:'<?=$name?>'},function(){timers.testUpdate = setTimeout(testUpdate,0);});
<?if ($spindown):?>
$('select[name="<?=$spindown?>"]').val(0).prop('disabled',true);
$.post('/update.htm',{'<?=$spindown?>':0,changeDisk:'apply'});
<?endif;?>
} else {
clearTimeout(timers.testUpdate);
$.removeCookie('test.<?=$dev?>',{path:'/'});
$.post('/webGui/include/SmartInfo.php',{cmd:'stop',port:'<?=$dev?>',name:'<?=$name?>'},function(){setTimeout(testUpdate,0);});
<?if ($spindown):?>
$('select[name="<?=$spindown?>"]').val(<?=spindownDelay()?>).prop('disabled',false);
$.post('/update.htm',{'<?=$spindown?>':<?=spindownDelay()?>,changeDisk:'apply'});
$.post('/webGui/include/SmartInfo.php',{cmd:'delete',file:'<?=$spindown?>'});
<?endif;?>
}
}
function startLongTest() {
if ($('#long_test').val()=='Start') {
$('#long_test').val('Stop');
$('#short_test').attr('disabled','disabled');
$.cookie('test.<?=$dev?>','long',{path:'/',expires:365});
$.post('/webGui/include/SmartInfo.php',{cmd:'long',port:'<?=$dev?>',name:'<?=$name?>'},function(){timers.testUpdate = setTimeout(testUpdate,0);});
<?if ($spindown):?>
$('select[name="<?=$spindown?>"]').val(0).prop('disabled',true);
$.post('/update.htm',{'<?=$spindown?>':0,changeDisk:'apply'});
<?endif;?>
} else {
clearTimeout(timers.testUpdate);
$.removeCookie('test.<?=$dev?>',{path:'/'});
$.post('/webGui/include/SmartInfo.php',{cmd:'stop',port:'<?=$dev?>',name:'<?=$name?>'},function(){setTimeout(testUpdate,0);});
<?if ($spindown):?>
$('select[name="<?=$spindown?>"]').val(<?=spindownDelay()?>).prop('disabled',false);
$.post('/update.htm',{'<?=$spindown?>':<?=spindownDelay()?>,changeDisk:'apply'});
$.post('/webGui/include/SmartInfo.php',{cmd:'delete',file:'<?=$spindown?>'});
<?endif;?>
}
}
<?if (count($sheets)>1):?>
var ctrl = "<span class='status vhshift'><span class='waitmsg' style='display:none;font-size:small;font-style:italic;margin-right:8px'>please wait...</span><a href='/Main/<?=$type?>?name=<?=$prev?>' title='previous device'>";
ctrl += "<button type='button' style='margin-right:4px' onclick='this.disabled=true;$(\".waitmsg\").show();'><i class='fa fa-chevron-left'></i></button></a>";
ctrl += "<a href='/Main/<?=$type?>?name=<?=$next?>' title='next device'><button type='button' onclick='this.disabled=true;$(\".waitmsg\").show();'><i class='fa fa-chevron-right'></i></button></a></span>";
$(function() {
<?if ($tabbed):?>
$('.tabs').append(ctrl);
<?else:?>
$('div[id=title]:not(".nocontrol")').each(function(){$(this).append(ctrl);});
<?endif;?>
});
<?endif;?>
$(function() {
var smarttest = $.cookie('test.<?=$dev?>');
testUpdate(smarttest===undefined);
if (smarttest !== undefined) $('#'+smarttest+'_test').val('Stop').removeAttr('disabled');
<?if ($live):?>
$('#toggle_log').removeAttr('disabled');
$('#toggle_error').removeAttr('disabled');
$.post('/webGui/include/SmartInfo.php',{cmd:'selftest',port:'<?=$dev?>',name:'<?=$name?>'},function(data){$('#selftest').html(data);});
$.post('/webGui/include/SmartInfo.php',{cmd:'errorlog',port:'<?=$dev?>',name:'<?=$name?>'},function(data){$('#errorlog').html(data);});
<?endif;?>
});
</script>
+6 -13
View File
@@ -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) {
+18 -69
View File
@@ -14,83 +14,32 @@ Cond="$var['fsState']=="Started" && $var['shareUser']=='e'"
* all copies or substantial portions of the Software.
*/
?>
<script>
function displayShares(all) {
$('#share_list').html("<tr><td colspan='8' style='text-align:center'><i class='fa fa-spinner fa-spin icon'></i>Please wait... computing user shares!</td></tr>");
$.get('/webGui/include/ShareList.php',{compute:all,path:'<?=$path?>',prev:'<?=$prev?>',scale:'<?=$display['scale']?>',number:'<?=$display['number']?>'},function(data){
$('#share_list').html(data);
$('#compute-shares').prop('disabled',!data || data.indexOf('colspan=')!=-1);
});
}
$(function() {
displayShares('no');
});
</script>
<?
// 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 '<em>'.ucfirst($share['security']).'</em>';
}
// 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);
}
}
?>
<table class="share_status share <?=$display['view']?>">
<thead><tr><td style="width:15%">Name</td><td style="width:33%">Comment</td><td>SMB</td><td>NFS</td><td>AFP</td><td>Size</td><td>Free</td><td>View</td></tr></thead>
<tbody>
<?
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;
}
?><tr>
<td><a class='info nohand' onclick='return false'><img src='<?=$ball?>' class='icon'><span style="left:18px"><?=$help?></span></a><a href="<?=$path?>/Share?name=<?=urlencode($name)?>" onclick="$.cookie('one','tab1',{path:'/'})"><?=$share['name']?></a></td>
<td><?=$share['comment']?></td>
<td><?=user_share_settings($var['shareSMBEnabled'], $sec[$name])?></td>
<td><?=user_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])?></td>
<td><?=user_share_settings($var['shareAFPEnabled'], $sec_afp[$name])?></td>
<?if (array_key_exists("{$share['name']}", $ssz1)):?>
<td><?=my_scale($ssz1[$share['name']]['total']*1024, $unit).' '.$unit?></td>
<td><?=my_scale($share['free']*1024, $unit).' '.$unit?></td>
<td><a href="<?=$path?>/Browse?dir=/mnt/user/<?=urlencode($share['name'])?>"><img src="/webGui/images/explore.png" title="Browse /mnt/user/<?=urlencode($share['name'])?>"></a></td>
</tr>
<?foreach ($ssz1[$share['name']] as $disk_name => $disk_size):
if ($disk_name!="total"):
?> <tr class="share_status_size">
<td><?=my_disk($disk_name)?>:</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><?=my_scale($disk_size*1024, $unit).' '.$unit?></td>
<td><?=my_scale($disks[$disk_name]['fsFree']*1024, $unit).' '.$unit?></td>
<td></td>
</tr>
<? endif;
endforeach;
else:
$cmd="/webGui/scripts/share_size" . "&arg1=" . urlencode($name) . "&arg2=" . urlencode("/var/local/emhttp/$name.ssz1");
?><td><a href="/update.htm?cmd=<?=$cmd?>" target="progressFrame" onclick="$(this).text('Please wait...')">Compute...</a></td>
<td><?=my_scale($share['free']*1024, $unit).' '.$unit?></td>
<td><a href="<?=$path?>/Browse?dir=/mnt/user/<?=urlencode($share['name'])?>"><img src="/webGui/images/explore.png" title="Browse /mnt/user/<?=urlencode($share['name'])?>"></a></td>
</tr>
<?endif;
endforeach;
?>
</tbody>
<tbody id="share_list"></tbody>
</table>
<?if (!count($shares)):?>
<div style="font-style:italic;font-size:14px;opacity:0.5;padding:5px;">There are no user shares</div>
<?endif;?>
<form method="GET" action="<?=$path?>/Share">
<p><form name="share_form" method="GET" action="<?=$path?>/Share">
<input type="hidden" name="name" value="">
<input id="compute-shares" type="button" value="Compute All" onclick="$(this).prop('disabled',true);displayShares('yes')">
<?if ($var['startMode']=='Normal'):?>
<p><input type="submit" value="Add Share"<?=$var['shareUser']=='e'?'>':' disabled>User shares must be <strong>enabled</strong> to add shares.'?></p>
<input type="submit" value="Add Share"<?=$var['shareUser']=='e'?'>':' disabled>User shares must be <strong>enabled</strong> to add shares.'?>
<?else:?>
<p><input type="submit" value="Add Share" disabled></p>
<input type="submit" value="Add Share" disabled>
<?endif;?>
</form>
</form></p>
> **Colored Status Indicator** the significance of the color indicator at the beginning of each line in *User Shares* is as follows:
>
+5 -11
View File
@@ -90,12 +90,7 @@ Included disk(s):
</select>
> 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):
: <select id="s2" name="shareUserExclude" size="1" multiple="multiple" style="display:none">
@@ -104,12 +99,11 @@ Excluded disk(s):
<?endforeach;?>
</select>
> 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.
&nbsp;
: <input type="submit" name="changeShare" value="Apply" <?=$disabled?>><input type="button" value="Done" onclick="done()"><?if ($disabled):?>Array must be **Stopped** to change<?endif;?>
+1 -5
View File
@@ -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:
: <input type="text" name="AuthUser" value="<?=$ssmtp['AuthUser']?>">
Password:
: <input type="password" name="AuthPass" value="<?=$ssmtp['AuthPass']?>" onchange="passcheck(this.form)"><span id='authpass' style='display:none;color:red;font-weight:bold'>Warning: do not use the # character in your password!</span>
: <input type="password" name="AuthPass" value="<?=$ssmtp['AuthPass']?>">
> Enter the username and password to login to your email account. Be aware that the password is stored unencrypted in the email configuration file.
+47 -4
View File
@@ -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 "<pre class='up'>".shell_exec('cat /var/log/syslog')."</pre>";
?>
<script>
function cleanUp() {
@@ -26,7 +24,7 @@ function cleanUp() {
} else {
setTimeout(cleanUp,4000);
}
}
}
function syslog() {
$('input[value="Download"]').val('Downloading...').prop('disabled',true);
$.post('/webGui/include/Download.php',{cmd:'save',source:'/var/log/syslog',file:'<?=$zip?>'},function(zip) {
@@ -34,6 +32,51 @@ function syslog() {
setTimeout(cleanUp,4000);
});
}
$('.tabs').append("<span class='status vhshift'><input type='button' value='Download' onclick='syslog()'></span>");
function highlight(checked,line) {
var o = checked ? '-' : '';
var n = ($('span.text').css('display')=='none' && !checked) ? 'none' : '';
switch (line) {
case 'E': $('span.'+o+'error').css('display',n); $('span.error'+o).toggleClass('error -error error-'); break;
case 'W': $('span.'+o+'warn').css('display',n); $('span.warn'+o).toggleClass('warn -warn warn-'); break;
case 'S': $('span.'+o+'system').css('display',n); $('span.system'+o).toggleClass('system -system system-'); break;
case 'A': $('span.'+o+'array').css('display',n); $('span.array'+o).toggleClass('array -array array-'); break;
case 'L': $('span.'+o+'login').css('display',n); $('span.login'+o).toggleClass('login -login login-'); break;
case 'N': $('span.text,span[class^="-"]').css('display',checked ? 'none':''); break;
}
$('span.label').show();
}
function toggle(checked) {
highlight(checked,'E');
highlight(checked,'W');
highlight(checked,'S');
highlight(checked,'A');
highlight(checked,'L');
$('span.label input[type=checkbox]').not('.ctrl').prop('checked',checked);
}
<?if ($display['resize']):?>
function resize() {
$('pre.up').height(Math.max(window.innerHeight-280,370)).show();
}
$(function() {
resize();
$(window).bind('resize',function(){resize();});
});
<?endif;?>
$('.tabs').append("<span class='status'><span class='lite label'><label>Text<input type='checkbox' class='ctrl' onclick='highlight(!this.checked,\"N\")' checked></label></span><span class='error label'><label>Error<input type='checkbox' onclick='highlight(this.checked,\"E\")' checked></label></span><span class='warn label'><label>Warning<input type='checkbox' onclick='highlight(this.checked,\"W\")' checked></label></span><span class='system label'><label>System<input type='checkbox' onclick='highlight(this.checked,\"S\")' checked></label></span><span class='array label'><label>Array<input type='checkbox' onclick='highlight(this.checked,\"A\")' checked></label></span><span class='login label'><label>Login<input type='checkbox' onclick='highlight(this.checked,\"L\")' checked></label></span><span class='lite label'><input type='checkbox' class='ctrl' onclick='toggle(this.checked)' checked></span></span>");
</script>
<?
require_once 'webGui/include/ColorCoding.php';
echo $display['resize'] ? "<pre class='up' style='display:none'>" : "<pre class='up'>";
$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)."</span>";
}
}
echo "</pre>";
?>
<input type="button" value="Download" onclick="syslog()"><input type="button" value="Done" onclick="done()">
+3
View File
@@ -20,4 +20,7 @@ Title="Users"
<div style='clear:both'></div>
<form method="GET" action="<?=$path?>/UserAdd">
<input type="submit" value="Add User">
<?if (!empty($display['users']) && substr($display['users'],0,5)!='Tasks'):?>
<input type="button" value="Done" onclick="done()">
<?endif;?>
</form>
+4 -2
View File
@@ -1,2 +1,4 @@
Menu="Tasks:3"
Type="xmenu"
Menu="$display['users'] Tasks:3"
Type="xmenu"
Title="Users"
Icon="users.png"
+12 -2
View File
@@ -13,7 +13,17 @@ Title="Vars"
* all copies or substantial portions of the Software.
*/
?>
<?if ($display['resize']):?>
<script>
function resize() {
$('pre.up').height(Math.max(window.innerHeight-280,370)).show();
}
$(function() {
resize();
$(window).bind('resize',function(){resize();});
});
</script>
<?endif;?>
<?
foreach ($site as &$page) $page['text'] = '...';
$myPage['text'] = '...';
@@ -21,6 +31,6 @@ $pages['Vars']['text'] = '...';
$text = '...';
ksort($site);
ksort($GLOBALS);
echo ($display['resize'] ? "<pre class='up' style='display:none'>" : "<pre class='up'>").print_r($GLOBALS,true)."</pre>";
?>
<pre class='up'><?print_r($GLOBALS);?></pre>
<input type="button" value="Done" onclick="done()">
+2 -2
View File
@@ -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=""
+6 -13
View File
@@ -3,17 +3,16 @@
<!DOCTYPE PLUGIN [
<!ENTITY name "dynamix">
<!ENTITY author "Bergware">
<!ENTITY version "2015.09.20">
<!ENTITY version "2016.01.11"> <!-- Intended new version of Dynamix webGUI -->
<!ENTITY unRAID "6.2.0-b16"> <!-- Change to new unRAID version when issued together -->
<!ENTITY pluginURL "https://raw.github.com/limetech/&name;/master/plugins/&name;/&name;.plg">
]>
<PLUGIN name="&name;"
author="&author;"
version="&version;">
<!--
Prevent independent update for now
pluginURL="&pluginURL;">
-->
<PLUGIN name="&name;" author="&author;" version="&version;" unRAID="&unRAID;">
<CHANGES>
Dynamix webGui v&version;
@@ -24,16 +23,12 @@ Please refer to
on github.
</CHANGES>
<!--
Get from github as tarball.
-->
<!-- Get from github as tarball. -->
<FILE Name="/boot/config/plugins/&name;/&name;-&version;.tar.gz">
<URL>"https://github.com/limetech/&name;/archive/&version;.tar.gz"</URL>
</FILE>
<!--
The 'install' script.
-->
<!-- The 'install' script. -->
<FILE Run="/bin/bash">
<INLINE>
rm -r /tmp/plugins/&name;-&version; 2&gt;/dev/null
@@ -48,9 +43,7 @@ find /boot/config/plugins/&name; -type f -iname "*.tar.gz" ! -iname "&name;-&ver
</INLINE>
</FILE>
<!--
The 'remove' script.
-->
<!-- The 'remove' script. -->
<FILE Run="/bin/bash" Method="remove">
<INLINE>
rm -r /boot/config/plugins/&name;/&name;-&version;.tar.gz 2&gt;/dev/null
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

+27
View File
@@ -0,0 +1,27 @@
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$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";
?>
+35
View File
@@ -0,0 +1,35 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
// 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 ','\<errno=[^0]','host protected area','invalid signature','limiting speed to','duplicate (object|error)\b','power is back','gpt:partition_entry','no floppy controller']
],
['class' => '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']
]
];
?>
+18
View File
@@ -0,0 +1,18 @@
<?PHP
/* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
// 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));
?>
+83 -42
View File
@@ -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, "<a href=\"/Main/Device?name=$name\" onclick=\"$.cookie('one','tab2',{path:'/'})\" title=\"$smart reallocated sectors\"><img src=\"$path/$thumb.png\"></a>");
$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, "<span id='smart-$name' name='$page' class='$thumb'><img src=\"$path/$thumb.png\" onmouseover=\"this.style.cursor='pointer'\" title=\"Click to get context menu\"></span>");
}
function my_usage(&$source,$used) {
my_insert($source, $used ? "<div class='usage-disk all'><span style='width:$used'>$used</span></div>" : "-");
@@ -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,"<td></td>"); my_insert($row1[0],"Active");
$row2 = array_fill(0,26,"<td></td>"); my_insert($row2[0],"Inactive");
$row3 = array_fill(0,26,"<td></td>"); my_insert($row3[0],"Unassigned");
$row4 = array_fill(0,26,"<td></td>"); my_insert($row4[0],"Faulty");
$row5 = array_fill(0,26,"<td></td>"); my_insert($row5[0],"Heat alarm");
$row6 = array_fill(0,26,"<td></td>"); my_insert($row6[0],"SMART status");
$row7 = array_fill(0,26,"<td></td>"); 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,'<td></td>'); my_insert($row1[0],'Active');
$row2 = array_fill(0,$slots,'<td></td>'); my_insert($row2[0],'Inactive');
$row3 = array_fill(0,$slots,'<td></td>'); my_insert($row3[0],'Unassigned');
$row4 = array_fill(0,$slots,'<td></td>'); my_insert($row4[0],'Faulty');
$row5 = array_fill(0,$slots,'<td></td>'); my_insert($row5[0],'Heat alarm');
$row6 = array_fill(0,$slots,'<td></td>'); my_insert($row6[0],'SMART status');
$row7 = array_fill(0,$slots,'<td></td>'); 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],"<img src=$path/$state.png>");
break;
default:
my_insert($row4[$n],"<img src=$path/$state.png>");
if ($disk['type']=='Parity' && $disk['status']=='DISK_NP_DSBL')
my_insert($row3[$n],"<img src=$path/$state.png>"); //parity is really unassigned
else
my_insert($row4[$n],"<img src=$path/$state.png>");
break;}
$temp = $disk['temp'];
if ($temp>=$_POST['hot']) my_insert($row5[$n],"<span class='heat-img'><img src='$path/".($temp>=$_POST['max']?'max':'hot').".png'></span><span class='heat-text' style='display:none'>".my_temp($temp,$_POST['unit'])."</span>");
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++],"<img src=$path/$state.png>");
}
foreach ($devs as $dev) my_insert($row3[$i++],"<img src=$path/blue-on.png>");
echo "<tr>".implode('',$row1)."</tr>";
echo "<tr>".implode('',$row2)."</tr>";
echo "<tr>".implode('',$row3)."</tr>";
@@ -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\"<br>Drops: \"$5\"<br>Overruns: \"$6\"#Errors: \"$8\"<br>Drops: \"$9\"<br>Overruns: \"$10}'",$ports); break;
default: $ports = array();}
default: $ports = [];}
echo implode('#',$ports);
break;
case 'parity':
$var = parse_ini_file("state/var.ini");
echo "<span class='orange p0'><strong>".($var['mdNumInvalid']==0 ? 'Parity-Check' : ($var['mdInvalidDisk']==0 ? 'Parity-Sync' : 'Data-Rebuild'))." in progress... Completed: ".number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),0)." %.</strong></span>".
"<br><em>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))."</em>";
$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 "<span class='orange p0'><strong>".$mode." in progress... Completed: ".number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),0)." %.</strong></span>";
echo "<br><em>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))."</em>";
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;
@@ -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) {
+4 -2
View File
@@ -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));
?>
+158 -205
View File
@@ -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 = "<a href='update.htm?cmdSpin{$action}={$href}' title='Click to spin $action device' class='none' target='progressFrame' onclick=\"$.removeCookie('one',{path:'/'});\"><i class='fa fa-sort-$action spacing'></i></a>";
}
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 = "<a href='update.htm?$cmd' title='Click to spin $action device' class='none' target='progressFrame' onclick=\"$.removeCookie('one',{path:'/'});\"><i class='fa fa-sort-$action spacing'></i></a>";
} 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 = "<a class='info nohand' onclick='return false'><img src='$ball' class='icon'><span>${help}</span></a>";
$link = strpos($disk['status'], 'DISK_NP')===false ? "<a href='$path/$device?name=$href'>$name</a>" : $name;
return $ctrl.$status.$link;
$status = "$ctrl<a class='info nohand' onclick='return false'><img src='/webGui/images/{$disk['color']}.png' class='icon'><span>$help</span></a>";
$link = strpos($disk['status'], 'DISK_NP')===false ? "<a href='$path/$type?name=$name' title='".my_disk($name)." Settings'>".my_disk($name)."</a>" : 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 "<a href='$path/Browse?dir=$dir'><img src='/webGui/images/explore.png' title='Browse $dir'></a>";
}
}
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' ? "<a href=\"#\" title=\"Disk Log Information\" onclick=\"openBox('/webGui/scripts/disk_log&arg1={$disk['device']}','Disk Log Information',600,900,false);return false\"><i class=\"fa fa-hdd-o icon\"></i></a>" : "";
return "$log{$disk['id']} - $size $unit ({$disk['device']})";
}
function assignment($disk) {
global $var, $disks, $devs, $screen;
$out = "<form method='POST' name=\"{$disk['name']}Form\" action='/update.htm' target='progressFrame'>";
$out .= "<input type='hidden' name='changeDevice' value='Apply'>";
$out .= "<select style=\"max-width:none\" name=\"slotId.{$disk['idx']}\" onChange=\"{$disk['name']}Form.submit()\">";
$empty = ($disk['idSb']!="" ? "no device" : "unassigned");
if ($disk['id']!="") {
function assignment(&$disk) {
global $var, $devs;
$out = "<form method='POST' name=\"{$disk['name']}Form\" action='/update.htm' target='progressFrame'><input type='hidden' name='changeDevice' value='Apply'>";
$out .= "<select style=\"min-width:400px;max-width:400px\" name=\"slotId.{$disk['idx']}\" onChange=\"{$disk['name']}Form.submit()\">";
$empty = ($disk['idSb']!='' ? 'no device' : 'unassigned');
if ($disk['id']!='') {
$out .= "<option value=\"{$disk['id']}\" selected>".device_desc($disk)."</option>";
$out .= "<option value=''>$empty</option>";
} else {
} else
$out .= "<option value='' selected>$empty</option>";
}
foreach ($devs as $dev) {
if (!file_exists("{$screen}_{$dev['device']}")) {
$out .= "<option value=\"{$dev['id']}\">".device_desc($dev)."</option>";
}
}
return $out."</select></form>";
foreach ($devs as $dev) {$out .= "<option value=\"{$dev['id']}\">".device_desc($dev)."</option>";}
return "$out</select></form>";
}
function render_used_and_free($disk) {
function fs_info(&$disk) {
global $display;
if ($disk['type']=='Parity' || $disk['fsStatus']=='-') {
echo "<td colspan='4'></td>";
echo "<td colspan='5'></td>";
return;
} else if ($disk['fsStatus']=='Mounted') {
echo "<td>{$disk['fsType']}</td>";
echo "<td>".my_scale($disk['fsSize']*1024, $unit)." $unit</td>";
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 "<td>".my_scale($disk['fsUsed']*1024, $unit)." $unit</td>";
echo "<td>".my_scale($disk['fsSize']*1024,$unit)." $unit</td>";
if ($display['text']%10==0) {
echo "<td>".my_scale($disk['fsUsed']*1024,$unit)." $unit</td>";
} else {
$used = $disk['fsSize'] ? 100 - round(100*$disk['fsFree']/$disk['fsSize']) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:{$used}%' class='".usage_color($used,false)."'><span>".my_scale($disk['fsUsed']*1024, $unit)." $unit</span></span></div></td>";
$used = $disk['fsSize'] ? 100-round(100*$disk['fsFree']/$disk['fsSize']) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:$used%' class='".usage_color($disk,$used,false)."'><span>".my_scale($disk['fsUsed']*1024,$unit)." $unit</span></span></div></td>";
}
if ($text2) {
echo "<td>".my_scale($disk['fsFree']*1024, $unit)." $unit</td>";
if ($display['text']<10 ? $display['text']%10==0 : $display['text']%10!=0) {
echo "<td>".my_scale($disk['fsFree']*1024,$unit)." $unit</td>";
} else {
$free = $disk['fsSize'] ? round(100*$disk['fsFree']/$disk['fsSize']) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:{$free}%' class='".usage_color($free,true)."'><span>".my_scale($disk['fsFree']*1024, $unit)." $unit</span></span></div></td>";
echo "<td><div class='usage-disk'><span style='margin:0;width:$free%' class='".usage_color($disk,$free,true)."'><span>".my_scale($disk['fsFree']*1024,$unit)." $unit</span></span></div></td>";
}
} else {
} else
echo "<td colspan='2'></td><td>{$disk['fsStatus']}</td><td></td>";
}
echo "<td>".device_browse($disk)."</td>";
}
function array_offline($disk) {
function array_offline(&$disk) {
echo "<tr>";
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 "<td>".device_info($disk)."</td>";
echo "<td>".assignment($disk)."</td>";
echo "<td colspan='9'></td>";
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 "<td>".device_info($disk)."</td>";
echo "<td>".assignment($disk)."</td>";
echo "<td>".my_temp($disk['temp'])."</td>";
echo "<td colspan='8'></td>";
break;
case "DISK_NP_MISSING":
case 'DISK_NP_MISSING':
echo "<td>".device_info($disk)."<span class='diskinfo'><em>Missing</em></span></td>";
echo "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024, $unit)." $unit</em></td>";
echo "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024,$unit)." $unit</em></td>";
echo "<td colspan='9'></td>";
break;
case "DISK_WRONG":
case 'DISK_WRONG':
echo "<td>".device_info($disk)."<span class='diskinfo'><em>Wrong</em></span></td>";
echo "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024, $unit)." $unit</em></td>";
echo "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale($disk['sizeSb']*1024,$unit)." $unit</em></td>";
echo "<td>".my_temp($disk['temp'])."</td>";
echo "<td colspan='8'></td>";
break;
}
echo "</tr>";
}
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 "<tr>";
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 "<td>".device_info($disk)."</td>";
// echo "<td colspan='9'>Not installed</td>";
// echo "<td></td>";
break;
case "DISK_OK_NP":
case 'DISK_OK_NP':
case 'DISK_NP_DSBL':
echo "<td>".device_info($disk)."</td>";
echo "<td>Not Installed</td>";
echo "<td><em>Not installed</em></td>";
echo "<td colspan='4'></td>";
render_used_and_free($disk);
echo "<td>".device_browse($disk)."</td>";
break;
case "DISK_NP_DSBL":
echo "<td>".device_info($disk)."</td>";
if ($disk['type']=="Parity") {
echo "<td>Not installed</td>";
echo "<td colspan='9'></td>";
} else {
echo "<td><em>Not installed</em></td>";
echo "<td colspan='4'></td>";
render_used_and_free($disk);
echo "<td>".device_browse($disk)."</td>";
}
break;
case "DISK_DSBL":
echo "<td>".device_info($disk)."</td>";
echo "<td>".device_desc($disk)."</td>";
echo "<td>".my_temp($disk['temp'])."</td>";
echo "<td>".my_number($disk['numReads'])."</td>";
echo "<td>".my_number($disk['numWrites'])."</td>";
echo "<td>".my_number($disk['numErrors'])."</td>";
if ($disk['type']=="Parity") {
echo "<td colspan='5'></td>";
} else {
render_used_and_free($disk);
echo "<td>".device_browse($disk)."</td>";
}
fs_info($disk);
break;
case 'DISK_DSBL':
default:
echo "<td>".device_info($disk)."</td>";
echo "<td>".device_desc($disk)."</td>";
@@ -228,8 +179,7 @@ function array_online($disk) {
echo "<td>".my_number($disk['numReads'])."</td>";
echo "<td>".my_number($disk['numWrites'])."</td>";
echo "<td>".my_number($disk['numErrors'])."</td>";
render_used_and_free($disk);
echo "<td>".device_browse($disk)."</td>";
fs_info($disk);
break;
}
echo "</tr>";
@@ -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 "<tr class='tr_last'>";
echo "<td><img src='/webGui/images/sum.png' class='icon'>Total</td>";
echo "<td>$text</td>";
echo "<td>".($counts>0?my_temp(round($temps/$counts, 1)):'*')."</td>";
echo "<td>".my_number($reads)."</td>";
echo "<td>".my_number($writes)."</td>";
echo "<td>".my_number($errors)."</td>";
echo "<td>".($sum['count']>0 ? my_temp(round($sum['temp']/$sum['count'],1)) : '*')."</td>";
echo "<td>".my_number($sum['numReads'])."</td>";
echo "<td>".my_number($sum['numWrites'])."</td>";
echo "<td>".my_number($sum['numErrors'])."</td>";
echo "<td></td>";
if (strstr($text,"Array") && ($var['startMode'] == "Normal")) {
echo "<td>".my_scale($tot_size*1024, $unit)." $unit</td>";
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 "<td>".my_scale($tot_used*1024, $unit)." $unit</td>";
if (strstr($text,'Array') && ($var['startMode']=='Normal')) {
echo "<td>".my_scale($sum['fsSize']*1024,$unit)." $unit</td>";
if ($display['text']%10==0) {
echo "<td>".my_scale($sum['fsUsed']*1024,$unit)." $unit</td>";
} else {
$used = $tot_size ? 100 - round(100*$tot_free/$tot_size) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:{$used}%' class='".usage_color($used,false)."'><span>".my_scale($tot_used*1024, $unit)." $unit</span></span></div></td>";
$used = $sum['fsSize'] ? 100-round(100*$sum['fsFree']/$sum['fsSize']) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:$used%' class='".usage_color($display,$used,false)."'><span>".my_scale($sum['fsUsed']*1024,$unit)." $unit</span></span></div></td>";
}
if ($text2) {
echo "<td>".my_scale($tot_free*1024, $unit)." $unit</td>";
if ($display['text']<10 ? $display['text']%10==0 : $display['text']%10!=0) {
echo "<td>".my_scale($sum['fsFree']*1024,$unit)." $unit</td>";
} else {
$free = $tot_size ? round(100*$tot_free/$tot_size) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:{$free}%' class='".usage_color($free,true)."'><span>".my_scale($tot_free*1024, $unit)." $unit</span></span></div></td>";
$free = $sum['fsSize'] ? round(100*$sum['fsFree']/$sum['fsSize']) : 0;
echo "<td><div class='usage-disk'><span style='margin:0;width:$free%' class='".usage_color($display,$free,true)."'><span>".my_scale($sum['fsFree']*1024,$unit)." $unit</span></span></div></td>";
}
echo "<td></td>";
}
else
} else
echo "<td colspan=4></td>";
echo "</tr>";
}
@@ -295,13 +236,13 @@ function array_slots() {
global $var;
$min = max($var['sbNumDisks'], 3);
$max = $var['MAX_ARRAYSZ'];
$out = "";
$out .= "<form method='POST' action='/update.htm' target='progressFrame'>";
$noparity2 = ($var['regTy']=='Pro')?0:1;
$out = "<form method='POST' action='/update.htm' target='progressFrame'>";
$out .= "<input type='hidden' name='changeSlots' value='Apply'>";
$out .= "<select name='SYS_ARRAY_SLOTS' onChange='this.form.submit()'>";
$out .= "<select style='min-width:auto' name='SYS_ARRAY_SLOTS' onChange='this.form.submit()'>";
for ($n=$min; $n<=$max; $n++) {
$selected = ($n == $var['SYS_ARRAY_SLOTS'])? " selected" : "";
$out .= "<option value='$n'{$selected}>$n</option>";
$selected = ($n == $var['SYS_ARRAY_SLOTS'])? ' selected' : '';
$out .= "<option value='$n'{$selected}>".($n-$noparity2)."</option>";
}
$out .= "</select></form>";
return $out;
@@ -310,13 +251,12 @@ function cache_slots() {
global $var;
$min = $var['cacheSbNumDisks'];
$max = $var['MAX_CACHESZ'];
$out = "";
$out .= "<form method='POST' action='/update.htm' target='progressFrame'>";
$out = "<form method='POST' action='/update.htm' target='progressFrame'>";
$out .= "<input type='hidden' name='changeSlots' value='Apply'>";
$out .= "<select name='SYS_CACHE_SLOTS' onChange='this.form.submit()'>";
$out .= "<select style='min-width:auto' name='SYS_CACHE_SLOTS' onChange='this.form.submit()'>";
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 .= "<option value='$n'{$selected}>$option</option>";
}
$out .= "</select></form>";
@@ -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 "<tr>";
echo "<td>".device_info($disk)."</td>";
echo "<td>".device_desc($disk)."</td>";
@@ -344,33 +284,30 @@ case 'flash':
echo "<td>".my_number($disk['numReads'])."</td>";
echo "<td>".my_number($disk['numWrites'])."</td>";
echo "<td>".my_number($disk['numErrors'])."</td>";
render_used_and_free($disk);
echo "<td>".device_browse($disk)."</td>";
fs_info($disk);
echo "</tr>";
break;
case 'cache':
if ($var['fsState']=='Stopped') {
foreach ($disks as $disk) {if ($disk['type']=='Cache') array_offline($disk);}
echo "<tr class='tr_last'><td><img src='/webGui/images/sum.png' class='icon'>Slots:</td><td colspan='9'>".cache_slots()."</td><td></td></tr>";
echo "<tr><td colspan='10'></td><td></td></tr>";
} 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 "<tr>";
echo "<td>".device_info($dev)."</td>";
echo "<td>".device_desc($dev)."</td>";
echo "<td>".my_temp($dev['temp'])."</td>";
if (file_exists("/tmp/preclear_stat_{$dev['device']}")) {
$text = exec("cut -d'|' -f3 /tmp/preclear_stat_{$dev['device']} | sed 's:\^n:\<br\>:g'");
$text = exec("cut -d'|' -f3 /tmp/preclear_stat_{$dev['device']}|sed 's:\^n:\<br\>:g'");
if (strpos($text,'Total time')===false) $text = 'Preclear in progress... '.$text;
echo "<td colspan='8' style='text-align:right'><em>$text</em></td>";
} 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;
}
+114
View File
@@ -0,0 +1,114 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
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 '<em>'.ucfirst($share['security']).'</em>';
}
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 "<tr>";
echo "<td><a class='info nohand' onclick='return false'><img src='$ball' class='icon'><span style='left:18px'>$help</span></a><a href='$path/Disk?name=$name' onclick=\"$.cookie('one','tab1',{path:'/'})\">$name</a></td>";
echo "<td>{$disk['comment']}</td>";
echo "<td>".disk_share_settings($var['shareSMBEnabled'], $sec[$name])."</td>";
echo "<td>".disk_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])."</td>";
echo "<td>".disk_share_settings($var['shareAFPEnabled'], $sec_afp[$name])."</td>";
$cmd="/webGui/scripts/disk_size"."&arg1=".urlencode($name)."&arg2=ssz2";
if (array_key_exists($name, $ssz2)) {
echo "<td>".my_scale(($disk['fsSize'])*1024, $unit)." $unit</td>";
echo "<td>".my_scale($disk['fsFree']*1024, $unit)." $unit</td>";
echo "<td><a href='$path/Browse?dir=/mnt/$name'><img src='/webGui/images/explore.png' title='Browse /mnt/$name'></a></td>";
echo "</tr>";
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 "<tr class='share_status_size".($inside ? "'>" : " warning'>");
echo "<td>$sharename:</td>";
echo "<td>".($inside ? "" : "<em>Share is outside the list of designated disks</em>")."</td>";
echo "<td></td>";
echo "<td></td>";
echo "<td></td>";
echo "<td class='disk-$row-1'>".my_scale($sharesize*1024, $unit)." $unit</td>";
echo "<td class='disk-$row-2'>".my_scale($disk['fsFree']*1024, $unit)." $unit</td>";
echo "<td><a href='/update.htm?cmd=$cmd' target='progressFrame' title='Recompute...' onclick='$(\".disk-$row-1\").html(\"Please wait...\");$(\".disk-$row-2\").html(\"\");'><i class='fa fa-refresh icon'></i></a></td>";
echo "</tr>";
}
} else {
echo "<td><a href='/update.htm?cmd=$cmd' target='progressFrame' onclick=\"$(this).text('Please wait...')\">Compute...</a></td>";
echo "<td>".my_scale($disk['fsFree']*1024, $unit)." $unit</td>";
echo "<td><a href='$path/Browse?dir=/mnt/$name'><img src='/webGui/images/explore.png' title='Browse /mnt/$name'></a></td>";
echo "</tr>";
}
}
if ($row==0) {
echo "<tr><td colspan='8' style='text-align:center'><i class='fa fa-folder-open-o icon'></i>There are no exportable disk shares</td></tr>";
}
?>
+27
View File
@@ -0,0 +1,27 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$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);
?>
+33 -18
View File
@@ -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 $num<count($words) ? $words[$num] : $num;
}
function my_usage() {
global $disks,$var;
global $disks,$var,$display;
$arraysize=0;
$arrayfree=0;
foreach ($disks as $disk) {
@@ -69,33 +71,34 @@ function my_usage() {
}
if ($var['fsNumMounted']>0) {
$used = $arraysize ? 100-round(100*$arrayfree/$arraysize) : 0;
echo "<div class='usage-bar'><span style='width:{$used}%' class='".usage_color($used,false)."'><span>{$used}%</span></span></div>";
echo "<div class='usage-bar'><span style='width:{$used}%' class='".usage_color($display,$used,false)."'><span>{$used}%</span></span></div>";
} else {
echo "<div class='usage-bar'><span><center>".($var['fsState']=='Started'?'Maintenance':'off-line')."</center></span></div>";
}
}
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;
@@ -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 rowspan='3'><a href='#' onclick='openClose($row)'>"; $_td = "<i class='fa fa-anchor'></i></a></td>";
} else {
$td_ = "<td>"; $_td = "</td>";
}
$c = 0;
foreach ($fields as $field) {
if (!$field) continue;
$item = explode('=', $field);
if (!$c++) echo "<tr><td>".date("{$_POST['date']} {$_POST['time']}", $item[1])."</td>"; else echo "<td>{$item[1]}</td>";
if ($c==5) break;
$item = $field ? explode('=', $field, 2) : array("","-");
echo (!$c++) ? "<tr>$td_".date("{$dynamix['notify']['date']} {$dynamix['notify']['time']}", $item[1])."$_td" : "<td>{$item[1]}</td>";
}
echo "<td style='text-align:right'><a href='#' onclick='$.post(\"/webGui/include/DeleteLogFile.php\",{log:\"$archive\"},function(){archiveList();});return false' title='Delete notification'><i class='fa fa-trash-o'></i></a></td></tr>";
if ($extra) {
$item = explode('=', $field, 2);
echo "<tr class='expand-child row$row'><td colspan='5'>{$item[1]}</td></tr><tr class='expand-child row$row'><td colspan='5'></td></tr>";
$row++;
}
echo "<td style='text-align:right'><a href='#' onclick='$.get(\"/webGui/include/DeleteLogFile.php\",{log:\"$file\"},function(){archiveList();});return false' title='Delete notification'><i class='fa fa-trash-o'></i></a></td></tr>";
}
if (empty($files)) echo "<tr><td colspan='6' style='text-align:center'><em>No notifications available</em></td></tr>";
?>
+19 -1
View File
@@ -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[] = '';
+51
View File
@@ -0,0 +1,51 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
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);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<link type="text/css" rel="stylesheet" href="/webGui/styles/default-fonts.css">
<link type="text/css" rel="stylesheet" href="/webGui/styles/default-white.css">
</head>
<body>
<table class='share_status' style='margin-top:0'><thead><tr><td>Date</td><td>Duration</td><td>Speed</td><td>Status</td></tr></thead><tbody>
<?
$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 "<tr><td>$date</td><td>".my_duration($duration)."</td><td>$speed</td><td>".($status==0?'OK':$status)."</td></tr>"; $row++;}
}
fclose($handle);
}
if ($row==0) echo "<tr><td colspan='4' style='text-align:center'>No parity check history present!</td></tr>";
?>
</tbody></table>
<center><input type="button" value="Done" onclick="top.Shadowbox.close()"></center>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
// 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'];
?>
+114
View File
@@ -0,0 +1,114 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
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 "<tr><td colspan='8' style='text-align:center'>There are no user shares</td></tr>";
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 '<em>'.ucfirst($share['security']).'</em>';
}
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 "<tr>";
echo "<td><a class='info nohand' onclick='return false'><img src='$ball' class='icon'><span style='left:18px'>$help</span></a><a href='$path/Share?name=".urlencode($name)."' onclick=\"$.cookie('one','tab1',{path:'/'})\">$name</a></td>";
echo "<td>{$share['comment']}</td>";
echo "<td>".user_share_settings($var['shareSMBEnabled'], $sec[$name])."</td>";
echo "<td>".user_share_settings($var['shareNFSEnabled'], $sec_nfs[$name])."</td>";
echo "<td>".user_share_settings($var['shareAFPEnabled'], $sec_afp[$name])."</td>";
$cmd="/webGui/scripts/share_size"."&arg1=".urlencode($name)."&arg2=ssz1";
if (array_key_exists($name, $ssz1)) {
echo "<td>".my_scale($ssz1[$name]['disk.total']*1024, $unit)." $unit</td>";
echo "<td>".my_scale($share['free']*1024, $unit)." $unit</td>";
echo "<td><a href='$path/Browse?dir=/mnt/user/".urlencode($name)."'><img src='/webGui/images/explore.png' title='Browse /mnt/user/".urlencode($name)."'></a></td>";
echo "</tr>";
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 "<tr class='share_status_size".($inside ? "'>" : " warning'>");
echo "<td>".my_disk($diskname).":</td>";
echo "<td>".($inside ? "" : "<em>Share is outside the list of designated disks</em>")."</td>";
echo "<td></td>";
echo "<td></td>";
echo "<td></td>";
echo "<td class='share-$row-1'>".my_scale($disksize*1024, $unit)." $unit</td>";
echo "<td class='share-$row-2'>".my_scale($disks[$diskname]['fsFree']*1024, $unit)." $unit</td>";
echo "<td><a href='/update.htm?cmd=$cmd' target='progressFrame' title='Recompute...' onclick='$(\".share-$row-1\").html(\"Please wait...\");$(\".share-$row-2\").html(\"\");'><i class='fa fa-refresh icon'></i></a></td>";
echo "</tr>";
}
} else {
echo "<td><a href='/update.htm?cmd=$cmd' target='progressFrame' onclick=\"$(this).text('Please wait...')\">Compute...</a></td>";
echo "<td>".my_scale($share['free']*1024, $unit)." $unit</td>";
echo "<td><a href='$path/Browse?dir=/mnt/user/".urlencode($name)."'><img src='/webGui/images/explore.png' title='Browse /mnt/user/".urlencode($name)."'></a></td>";
echo "</tr>";
}
}
if ($row==0) {
echo "<tr><td colspan='8' style='text-align:center'><i class='fa fa-folder-open-o icon'></i>There are no exportable user shares</td></tr>";
}
+88 -41
View File
@@ -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 "<td>".ucfirst(implode(' ',$words))."</td>";
}
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 "<tr{$color}>";
if ($info[0] == 9 && is_numeric($info[9])) $info[9] .= duration($info[9]);
foreach ($info as $field) echo "<td>".str_replace('_',' ',$field)."</td>";
echo "</tr>";
if ($info[8]=='-') $info[8] = 'Never';
if ($info[0]==9 && is_numeric($info[9])) duration($info[9]);
echo "<tr{$color}>".implode('',array_map('normalize', $info))."</tr>";
$empty = false;
}
if ($empty) echo "<tr><td colspan='10' style='text-align:center'>Can not read attributes</td></tr>";
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 "<tr><td>{$row[0]}</td><td>{$row[1]}</td><td>{$row[2]}</td></tr>";
$row = ["","",""];
echo "<tr><td>${row[0]}</td><td>${row[1]}</td><td>${row[2]}</td></tr>";
$row = ['','',''];
$empty = false;
}
}
if ($empty) echo "<tr><td colspan='3' style='text-align:center'>Can not read capabilities</td></tr>";
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] = "<span class='green-text'>Passed</span>";
if ($info[1]=='FAILED') $info[1] = "<span class='red-text'>Failed</span>";
echo "<tr><td>".preg_replace("/ is$/","",$info[0]).":</td><td>$info[1]</td></tr>";
if (strpos($line,'VALID ARGUMENTS')!==false) break;
list($title,$info) = array_map('trim', explode(':', $line, 2));
if (in_array($info,$passed)) $info = "<span class='green-text'>Passed</span>";
if (in_array($info,$failed)) $info = "<span class='red-text'>Failed</span>";
echo "<tr>".normalize(preg_replace('/ is:$/',':',"$title:"),' ')."<td>$info</td></tr>";
$empty = false;
}
if ($empty) echo "<tr><td colspan='2' style='text-align:center'>Can not read identification</td></tr>";
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 "<a href='/update.htm?cmdSpinup={$_POST['name']}' class='info' target='progressFrame'><input type='button' value='Spin Up'></a><span class='orange-text'><big>Unavailable - disk must be spun up</big></span>";
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 "<a href='/update.htm?$cmd' class='info' target='progressFrame'><input type='button' value='Spin Up'></a><span class='orange-text'><big>Unavailable - disk must be spun up</big></span>";
break;
}
$progress = exec("smartctl -c /dev/$port|grep -Pom1 '\d+%'");
$progress = exec("smartctl -c $type /dev/$port|grep -Pom1 '\d+%'");
if ($progress) {
echo "<big><i class='fa fa-spinner fa-pulse'></i> ".(100-substr($progress,0,-1))."% complete</big>";
echo "<big><i class='fa fa-spinner fa-pulse'></i> self-test in progress, ".(100-substr($progress,0,-1))."% complete</big>";
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 "<big>No self-tests logged on this disk</big>";
break;
@@ -110,10 +157,10 @@ case "update":
echo "<span class='red-text'><big>Errors occurred - Check SMART report</big></span>";
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;
}
?>
+11 -1
View File
@@ -22,7 +22,17 @@ default:
echo '<span class="green"><strong>Array Started</strong></span>'; break;
}
if ($var['mdResync']) {
echo '&bullet;<span class="orange"><strong>'.($var['mdNumInvalid']==0 ? 'Parity-Check:' : ($var['mdInvalidDisk']==0 ? 'Parity-Sync:' : 'Data-Rebuild:')).' '.number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),1,$_POST['dot'],'').' %</strong></span>';
$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 '&bullet;<span class="orange"><strong>'.$mode.' '.number_format(($var['mdResyncPos']/($var['mdResync']/100+1)),1,$_POST['dot'],'').' %</strong></span>';
if ($_POST['mode']<0) echo '#stop';
}
?>
+1 -1
View File
@@ -11,7 +11,7 @@
*/
?>
<?
require_once "webGui/include/Wrappers.php";
require_once 'webGui/include/Wrappers.php';
$memory = '/tmp/memory.tmp';
if (isset($_POST['#apply'])) {
+26
View File
@@ -0,0 +1,26 @@
<?PHP
/* Copyright 2015, Lime Technology
* Copyright 2015, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
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;
}
?>
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
echo "<p style='text-align:center'><span class='error label'>Error</span><span class='warn label'>Warning</span><span class='system label'>System</span><span class='array label'>Array</span></p>\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)."</span>\n";
}
?>
+7 -9
View File
@@ -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"
+10 -4
View File
@@ -1,11 +1,17 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?php
// usage: ftpusers ['user1 user2 .. ']
// usage: ftpusers 0|1 ['user1 user2 .. ']
// 0 = disable, 1 = enable FTP server daemon
// the list of users must be within quotes, ie, as one argument
// if no user(s) specified, deletes the config file
$config_file = "/boot/config/vsftpd.user_list";
if ($argc > 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");
?>
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
[[ $1 == up ]] && cmd=-S0
[[ $1 == down ]] && cmd=-y
exec /usr/sbin/hdparm $cmd /dev/$2
+218 -114
View File
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
@@ -12,87 +12,95 @@
*/
?>
<?
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)."<br>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);
+4 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
/* Copyright 2012, Andrew Hamer-Adams, http://www.pixeleyes.co.nz.
* Copyright 2015, Bergware International.
@@ -13,7 +13,7 @@
*/
?>
<?
require_once('/usr/local/emhttp/webGui/include/Wrappers.php');
require_once '/usr/local/emhttp/webGui/include/Wrappers.php';
function usage() {
echo <<<EOT
@@ -187,8 +187,8 @@ case 'add':
$archive = "{$archive}/".safe_filename("{$event}-{$ticket}.notify");
if (file_exists($archive)) break;
$entity = $overrule===false ? $notify[$importance] : $overrule;
if (!$mailtest) file_put_contents($archive,"timestamp = $timestamp\nevent = $event\nsubject = $subject\ndescription = $description\nimportance = $importance\n");
if (($entity & 1)==1 && !$mailtest) file_put_contents($unread,"timestamp = $timestamp\nevent = $event\nsubject = $subject\ndescription = $description\nimportance = $importance\n");
if (!$mailtest) file_put_contents($archive,"timestamp=$timestamp\nevent=$event\nsubject=$subject\ndescription=$description\nimportance=$importance\n".($message ? "message=".str_replace('\n','<br>',$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('<br>','. ',$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;
+8 -9
View File
@@ -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"
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$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");
+46 -43
View File
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
@@ -12,51 +12,63 @@
*/
?>
<?
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);
?>
+28 -2
View File
@@ -1,3 +1,29 @@
#!/bin/bash
#!/usr/bin/php -q
<?PHP
/* Copyright 2015, Bergware International.
* Copyright 2015, Lime Technology
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
echo "<p style='text-align:center'><span class='error label'>Error</span><span class='warn label'>Warning</span><span class='system label'>System</span><span class='array label'>Array</span><span class='login label'>Login</span></p>\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)."</span>";
flush();
}
pclose($handle);
?>
+12 -2
View File
@@ -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;}
+12 -2
View File
@@ -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;}
+2 -1
View File
@@ -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;}
+2 -1
View File
@@ -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;}
+7 -4
View File
@@ -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';
?>