Compare commits
180 Commits
feat/css3-
...
7.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
|
12828eec63 | ||
|
15f4138c87 | ||
|
fd6e4f1ba1 | ||
|
72a47035ef | ||
|
d57bf205fa | ||
|
16a8e7092d | ||
|
00b1f77742 | ||
|
506270e413 | ||
|
303c76d7da | ||
|
73ea1bb7b3 | ||
|
9d4ca6a2c9 | ||
49b82d0eb8 | |||
|
106f155ecc | ||
|
53704b58aa | ||
|
da9add3637 | ||
|
db77c13552 | ||
|
be22c0e1f8 | ||
|
cd9d20eaf3 | ||
|
c4afbba9bc | ||
|
16089cd927 | ||
|
1ede8e621b | ||
|
64ead9a127 | ||
|
ff80906d11 | ||
|
8981e8bb15 | ||
|
83675005d2 | ||
|
4b7f2bfcee | ||
|
72ff3c52c0 | ||
|
69e11713e5 | ||
|
67cf2db493 | ||
|
631479d27d | ||
|
28b3d2ae71 | ||
|
ec1689dc68 | ||
|
3615992dc4 | ||
d9f83cc76b | |||
|
7e6ad9512d | ||
|
d7b4dfd44b | ||
|
c62ef28fc3 | ||
|
07fa790411 | ||
|
fb19a99ad4 | ||
d0dcf6c314 | |||
0d925a2471 | |||
|
2b4eb1abad | ||
|
251881d850 | ||
|
24fce7582c | ||
|
b56f3e529c | ||
|
130e3fcd44 | ||
|
c1b2bb7435 | ||
|
840e19d322 | ||
|
2ae85fdd31 | ||
|
0db0032648 | ||
|
8d9e2a04c0 | ||
|
31f81349a8 | ||
|
3de8e05432 | ||
|
1c019c8f08 | ||
|
8d76d6f1cc | ||
|
3e17f35e19 | ||
|
6f51589547 | ||
|
b18e734381 | ||
|
b5a8223ffe | ||
|
936adea879 | ||
|
1572378824 | ||
|
0cf3585a0d | ||
|
3f103f2089 | ||
|
ed308c3a69 | ||
|
985d077af5 | ||
|
86b8b170d1 | ||
|
d351e51f58 | ||
|
53c1788580 | ||
|
0870461731 | ||
|
043d2baaf7 | ||
|
32bb9bb6d9 | ||
|
2ff8b77c9d | ||
|
654db74167 | ||
fb2b66b5b0 | |||
663665a61b | |||
|
c9333ea955 | ||
|
bad23e7647 | ||
17909f889e | |||
8cabad6f0d | |||
|
566113f86c | ||
|
21e640184e | ||
|
202196f7d3 | ||
9c2aa45751 | |||
|
06735f7a3e | ||
|
e7f7ef43c0 | ||
|
c5b692e805 | ||
|
6a91f270e3 | ||
|
6d3a7a3298 | ||
f81118ffe3 | |||
f9107ebe11 | |||
76f58d2995 | |||
b4722f57aa | |||
3314860d31 | |||
9a6b62ae98 | |||
e0b1612633 | |||
1e41ac637d | |||
858a3aa999 | |||
79c484e2e5 | |||
49793ff602 | |||
342619a567 | |||
|
27047c8832 | ||
|
b850940f19 | ||
|
de3334b0d2 | ||
|
373bb9f2ee | ||
|
833194705c | ||
|
392cc77a20 | ||
|
d272bf78ac | ||
|
9430366eaf | ||
|
71dc414592 | ||
|
2b41f9a8d2 | ||
|
70c01ec454 | ||
|
7840ae6d3c | ||
|
5b009dfb39 | ||
|
5565c02f74 | ||
|
793289bc7f | ||
|
7d92761860 | ||
|
1021adc33b | ||
|
c9374f7911 | ||
|
73a17a0306 | ||
|
0729386af9 | ||
|
4b8ec6e5a3 | ||
|
ea2fa8a8db | ||
|
d7e474257c | ||
|
4b3e8f2d46 | ||
|
54a6e3dd13 | ||
|
e20f37d936 | ||
|
4c1c566e78 | ||
|
a6c50b208a | ||
|
9a23761dc8 | ||
|
359334b85a | ||
|
f5ed6964dd | ||
|
882ee7f911 | ||
|
234a749b7f | ||
|
a9891f557e | ||
|
04640a5708 | ||
|
fac92be4d4 | ||
|
817ed5c1c1 | ||
|
a4abe0fa55 | ||
|
6920542d6c | ||
|
36f9e7402c | ||
|
cfa547972e | ||
|
84b5baf402 | ||
|
1b162e2c21 | ||
|
75f0c2e4f6 | ||
|
67369622fa | ||
|
17fdfb6f50 | ||
|
9e80fb13c7 | ||
|
1d6c3f4375 | ||
58c31f4ef6 | |||
|
84b767ddcd | ||
|
32d3f88b44 | ||
|
6156a2582f | ||
|
8a55228b8e | ||
|
fe7ab1fc43 | ||
30492ed2f2 | |||
da1ef5c0e0 | |||
93054c2091 | |||
|
f2abfaf292 | ||
|
5a42314c60 | ||
|
bbed6047e3 | ||
|
a7f6ac7389 | ||
|
f7748f7619 | ||
|
d4968e1b19 | ||
|
fb680469ac | ||
|
eaabbec7e0 | ||
|
693a0260af | ||
|
130c9c6373 | ||
73c264e9fe | |||
667741129a | |||
|
42fe45595f | ||
|
ed9b8322d4 | ||
|
cfb2daa531 | ||
|
694d35b412 | ||
|
ffabb996fc | ||
|
4a4444229e | ||
|
083cd984ea | ||
|
3dda97319d | ||
|
064cac1110 | ||
|
3d1b53d0ea | ||
|
61e99a390e |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -68,3 +68,8 @@ emhttp/plugins/dynamix.my.servers/unraid-components/index.html
|
||||
|
||||
# development scripts
|
||||
.dev-scripts/
|
||||
emhttp/plugins/node_modules/
|
||||
emhttp/plugins/.prettierignore
|
||||
emhttp/plugins/.prettierrc
|
||||
emhttp/plugins/package-lock.json
|
||||
emhttp/plugins/package.json
|
@@ -1 +0,0 @@
|
||||
/usr/libexec/unraid/firefox-119.0.r20231019122658-x86_64.AppImage
|
@@ -1 +0,0 @@
|
||||
/usr/libexec/unraid/unraidwold
|
@@ -1038,6 +1038,15 @@ You must specify a folder for Docker. The system will automatically create this
|
||||
It is recommended to create this folder under a share which resides on the Cache pool (setting: cache=Only). For best performance SSD devices are preferred.
|
||||
:end
|
||||
|
||||
:docker_storage_driver_help:
|
||||
overlay2 (default): Will use overlay2 as the storage driver for Docker, regardless of the underlying filesystem.
|
||||
|
||||
native: The native storage driver for your underlying filesystem will be used (XFS: overlay2 | ZFS: zfs | BTRFS: btrfs).
|
||||
|
||||
ATTENTION: Changing the storage type from an existing Docker installation is not possible, you have to delete your Docker directory first, change the storage type and then reinstall your containers.
|
||||
It is recommended to take a screenshot from your Docker containers before changing the storage type. After deleting and changing the storage type, reinstall the containers by clicking Add Container on the Docker page and select one by one from the drop down to reinstall them with your previous settings).
|
||||
:end
|
||||
|
||||
:docker_appdata_location_help:
|
||||
You can specify a folder to automatically generate and store subfolders containing configuration files for each Docker app (via the /config mapped volume).
|
||||
|
||||
@@ -1119,6 +1128,10 @@ This is the active Docker version.
|
||||
This is the location of the Docker image.
|
||||
:end
|
||||
|
||||
:docker_storage_driver_active_help:
|
||||
This is the storage driver for Docker.
|
||||
:end
|
||||
|
||||
:docker_appdata_location_active_help:
|
||||
This is the storage location for Docker containers.
|
||||
:end
|
||||
@@ -2415,7 +2428,7 @@ Note to Cloudflare users: the Cloudflare proxy is designed for http traffic, it
|
||||
|
||||
:wg_local_server_uses_nat_help:
|
||||
When NAT is enabled, the server uses its own LAN address when forwarding traffic from the tunnel to other devices in the LAN network.
|
||||
Use this setting when no router modifications are desired, but this approach doesn't work with Docker containers using custom IP addressess.
|
||||
Use this setting when no router modifications are desired, but this approach doesn't work with Docker containers using custom IP addresses.
|
||||
|
||||
When NAT is disabled, the server uses the WireGuard tunnel address when forwarding traffic.
|
||||
In this case it is required that the default gateway (router) has a static route configured to refer tunnel address back to the server.
|
||||
|
@@ -14,10 +14,15 @@ Tag="battery-3"
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
?>
|
||||
<script>
|
||||
function getUPSstatus() {
|
||||
$.post('/plugins/dynamix.apcupsd/include/UPSstatus.php',{level:<?=$cfg['BATTERYLEVEL']?>,runtime:<?=$cfg['MINUTES']?>},function(data) {
|
||||
var batteryLevel = "<?= _var($cfg,'BATTERYLEVEL',0) ?>";
|
||||
var batteryRuntime = "<?= _var($cfg,'MINUTES',0) ?>";
|
||||
|
||||
$.post('/plugins/dynamix.apcupsd/include/UPSstatus.php',{level:batteryLevel,runtime:batteryRuntime},function(data) {
|
||||
data = data.split('\n');
|
||||
$('#ups_summary').html(data[0]);
|
||||
$('#ups_status').html(data[1]);
|
||||
|
@@ -31,8 +31,8 @@ $cpus = cpu_list();
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/styles/style-$theme.css")?>">
|
||||
|
||||
<table id="docker_containers" class="tablesorter shift">
|
||||
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Port Mappings)_ <small>(_(App to Host)_)</small></th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
|
||||
<tbody id="docker_list"><tr><td colspan='8'></td></tr></tbody>
|
||||
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Container IP)_</th><th>_(Container Port)_</th><th>_(LAN IP:Port)_</th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
|
||||
<tbody id="docker_list"><tr><td colspan='9'></td></tr></tbody>
|
||||
</table>
|
||||
<input type="button" onclick="addContainer()" value="_(Add Container)_" style="display:none">
|
||||
<input type="button" onclick="startAll()" value="_(Start All)_" style="display:none">
|
||||
@@ -183,3 +183,4 @@ window.onunload = function(){
|
||||
dockerload.stop();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -103,6 +103,9 @@ function base_net($route) {
|
||||
return substr(explode('/',$route)[0],0,-2);
|
||||
}
|
||||
$bgcolor = strstr('white,azure',$display['theme']) ? '#f2f2f2' : '#1c1c1c';
|
||||
|
||||
//Check if docker.cfg not exists
|
||||
$no_dockercfg = !is_file('/boot/config/docker.cfg');
|
||||
?>
|
||||
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.filetree.css')?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.switchbutton.css')?>">
|
||||
@@ -188,6 +191,23 @@ _(Docker directory)_:
|
||||
:docker_vdisk_directory_help:
|
||||
|
||||
</div>
|
||||
|
||||
<div markdown="1" id="backingfs_type" style="display:none">
|
||||
_(Docker storage driver)_:
|
||||
: <select id="DOCKER_BACKINGFS" name="DOCKER_BACKINGFS" onchange="updateBackingFS(this.value)">
|
||||
<?=mk_option(_var($dockercfg,'DOCKER_BACKINGFS'), 'native', _('native'))?>
|
||||
<?=mk_option(_var($dockercfg,'DOCKER_BACKINGFS'), 'overlay2', _('overlay2'))?>
|
||||
</select>
|
||||
<?if ($var['fsState'] != "Started"):?>
|
||||
<span id="WARNING_BACKINGFS" style="display:none;"><i class="fa fa-warning icon warning"></i>_(Only modify if this is a new installation since this can lead to unwanted behaviour!)_</span>
|
||||
<?elseif (is_dir(_var($dockercfg,'DOCKER_IMAGE_FILE'))):?>
|
||||
<span id="WARNING_BACKINGFS" style="display:none;"><i class="fa fa-warning icon warning"></i>_(Switching the Storage Driver requires to delete and rebuild the Docker directory manually!)_</span>
|
||||
<?endif;?>
|
||||
|
||||
:docker_storage_driver_help:
|
||||
|
||||
</div>
|
||||
|
||||
_(Default appdata storage location)_:
|
||||
: <input type="text" id="DOCKER_APP_CONFIG_PATH" name="DOCKER_APP_CONFIG_PATH" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_APP_CONFIG_PATH')?>" placeholder="_(e.g.)_ /mnt/user/appdata/" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickfolders="true" pattern="^[^\\]*/$">
|
||||
<?if ($var['fsState'] != "Started"):?>
|
||||
@@ -418,6 +438,7 @@ _(IPv6 custom network on interface)_ <?=$network?> (_(optional)_):
|
||||
</div>
|
||||
<?else: /* DOCKER STARTED */?>
|
||||
|
||||
|
||||
_(Docker version)_:
|
||||
: <?$arrInfo = $DockerClient->getInfo(); echo $arrInfo['Version']?>
|
||||
|
||||
@@ -433,6 +454,11 @@ _(Docker directory)_:
|
||||
|
||||
:docker_vdisk_location_active_help:
|
||||
|
||||
_(Docker storage driver)_:
|
||||
: <?=_var($dockercfg,'DOCKER_BACKINGFS')?>
|
||||
|
||||
:docker_storage_driver_active_help:
|
||||
|
||||
_(Default appdata storage location)_:
|
||||
: <?=_var($dockercfg,'DOCKER_APP_CONFIG_PATH')?>
|
||||
|
||||
@@ -592,6 +618,9 @@ function prepareDocker(form) {
|
||||
$(form).find('input[name="DOCKER_IMAGE_FILE"]').val($('#DOCKER_IMAGE_TYPE').val()=='folder' ? $("#DOCKER_IMAGE_FILE2").val() : $("#DOCKER_IMAGE_FILE1").val());
|
||||
$("#DOCKER_IMAGE_FILE1").prop('disabled',true);
|
||||
$("#DOCKER_IMAGE_FILE2").prop('disabled',true);
|
||||
<?if ($no_dockercfg):?>
|
||||
$(form).find('input[name="DOCKER_BACKINGFS"]').val($('#DOCKER_IMAGE_TYPE').val()=='folder' ? $("#DOCKER_BACKINGFS").val() : $("#DOCKER_BACKINGFS").val('native'));
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
$(form).find('input:hidden[name^="DOCKER_DHCP_"]').each(function(){
|
||||
var id = '#'+$(this).attr('name')+'_';
|
||||
@@ -860,6 +889,7 @@ function btrfsScrub(path) {
|
||||
function updateLocation(val) {
|
||||
var content1 = $("#DOCKER_IMAGE_FILE1");
|
||||
var content2 = $("#DOCKER_IMAGE_FILE2");
|
||||
var dropdown = $("#DOCKER_BACKINGFS");
|
||||
switch (val) {
|
||||
case 'xfs':
|
||||
var path = content2.val().split('/');
|
||||
@@ -867,15 +897,18 @@ function updateLocation(val) {
|
||||
content1.val((path.join('/') + '/docker-xfs.img'));
|
||||
$('#vdisk_file').show('slow');
|
||||
$('#vdisk_dir').hide('slow');
|
||||
$('#backingfs_type').hide();
|
||||
content1.prop('disabled',false).trigger('change');
|
||||
content2.prop('disabled',true);
|
||||
dropdown.val('native');
|
||||
break;
|
||||
case 'folder':
|
||||
var path = content2.val().split('/');
|
||||
if (path[path.length-1]=='') path.splice(-2,2); else path.splice(-1,1);
|
||||
content2.val(path.join('/') + '/docker/');
|
||||
content2.val(path.join('/'));
|
||||
$('#vdisk_file').hide('slow');
|
||||
$('#vdisk_dir').show('slow');
|
||||
$('#backingfs_type').show('slow');
|
||||
content1.prop('disabled',true);
|
||||
content2.prop('disabled',false).trigger('change');
|
||||
break;
|
||||
@@ -885,11 +918,23 @@ function updateLocation(val) {
|
||||
content1.val((path.join('/') + '/docker.img'));
|
||||
$('#vdisk_file').show('slow');
|
||||
$('#vdisk_dir').hide('slow');
|
||||
$('#backingfs_type').hide();
|
||||
content1.prop('disabled',false).trigger('change');
|
||||
content2.prop('disabled',true);
|
||||
dropdown.val('native');
|
||||
break;
|
||||
}
|
||||
}
|
||||
function updateBackingFS(val) {
|
||||
var backingfs = "<?= _var($dockercfg,'DOCKER_BACKINGFS') ?>";
|
||||
var warning = document.getElementById("WARNING_BACKINGFS");
|
||||
var checkbox = $(".deleteCheckbox");
|
||||
if (val !== backingfs) {
|
||||
warning.style.display = "inline";
|
||||
} else {
|
||||
warning.style.display = "none";
|
||||
}
|
||||
}
|
||||
function checkbox_state(value) {
|
||||
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'exist',name:value},function(state){state==0 ? $('.deleteLabel').fadeIn() : $('.deleteLabel').fadeOut();});
|
||||
}
|
||||
@@ -897,6 +942,7 @@ $(function() {
|
||||
<?if ($DockerStopped):?>
|
||||
if ($('#DOCKER_IMAGE_TYPE').val()=='folder') {
|
||||
$('#vdisk_dir').show();
|
||||
$('#backingfs_type').show();
|
||||
checkbox_state($("#DOCKER_IMAGE_FILE2").val());
|
||||
$("#DOCKER_IMAGE_FILE2").prop('disabled',false);
|
||||
} else {
|
||||
|
@@ -9,3 +9,4 @@ DOCKER_ALLOW_ACCESS=""
|
||||
DOCKER_TIMEOUT=10
|
||||
DOCKER_READMORE="yes"
|
||||
DOCKER_PID_LIMIT=""
|
||||
DOCKER_BACKINGFS="overlay2"
|
||||
|
@@ -49,11 +49,11 @@ $port = file_exists('/sys/class/net/br0') ? 'BR0' : (file_exists('/sys/class/net
|
||||
// Docker configuration file - guaranteed to exist
|
||||
$docker_cfgfile = '/boot/config/docker.cfg';
|
||||
if (file_exists($docker_cfgfile)) {
|
||||
exec("grep -Pom2 '_SUBNET_|_{$port}(_[0-9]+)?=' $docker_cfgfile",$cfg);
|
||||
if (isset($cfg[0]) && $cfg[0]=='_SUBNET_' && empty($cfg[1])) {
|
||||
# interface has changed, update configuration
|
||||
exec("sed -ri 's/_(BR0|BOND0|ETH0)(_[0-9]+)?=/_{$port}\\2=/' $docker_cfgfile");
|
||||
}
|
||||
exec("grep -Pom2 '_SUBNET_|_{$port}(_[0-9]+)?=' $docker_cfgfile",$cfg);
|
||||
if (isset($cfg[0]) && $cfg[0]=='_SUBNET_' && empty($cfg[1])) {
|
||||
# interface has changed, update configuration
|
||||
exec("sed -ri 's/_(BR0|BOND0|ETH0)(_[0-9]+)?=/_{$port}\\2=/' $docker_cfgfile");
|
||||
}
|
||||
}
|
||||
|
||||
$defaults = (array)@parse_ini_file("$docroot/plugins/dynamix.docker.manager/default.cfg");
|
||||
@@ -255,7 +255,7 @@ class DockerTemplates {
|
||||
$doc = new DOMDocument();
|
||||
$doc->load($file['path']);
|
||||
if ($name) {
|
||||
if ($doc->getElementsByTagName('Name')->item(0)->nodeValue !== $name) continue;
|
||||
if (@$doc->getElementsByTagName('Name')->item(0)->nodeValue !== $name) continue;
|
||||
}
|
||||
$TemplateRepository = DockerUtil::ensureImageTag($doc->getElementsByTagName('Repository')->item(0)->nodeValue??'');
|
||||
if ($TemplateRepository && $TemplateRepository==$Repository) {
|
||||
@@ -293,7 +293,7 @@ class DockerTemplates {
|
||||
}
|
||||
|
||||
public function getAllInfo($reload=false,$com=true,$communityApplications=false) {
|
||||
global $dockerManPaths, $host;
|
||||
global $driver, $dockerManPaths, $host;
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerUpdate = new DockerUpdate();
|
||||
//$DockerUpdate->verbose = $this->verbose;
|
||||
@@ -307,9 +307,8 @@ class DockerTemplates {
|
||||
$tmp['paused'] = $ct['Paused'];
|
||||
$tmp['autostart'] = in_array($name,$autoStart);
|
||||
$tmp['cpuset'] = $ct['CPUset'];
|
||||
$tmp['url'] = $tmp['url'] ?? '';
|
||||
$tmp['url'] = $ct['Url'] ?? $tmp['url'] ?? '';
|
||||
// read docker label for WebUI & Icon
|
||||
if (isset($ct['Url']) && !$tmp['url']) $tmp['url'] = $ct['Url'];
|
||||
if (isset($ct['Icon'])) $tmp['icon'] = $ct['Icon'];
|
||||
if (isset($ct['Shell'])) $tmp['shell'] = $ct['Shell'];
|
||||
if (!$communityApplications) {
|
||||
@@ -322,8 +321,18 @@ class DockerTemplates {
|
||||
// non-templated webui, user specified
|
||||
$tmp['url'] = $webui;
|
||||
} else {
|
||||
$ip = ($ct['NetworkMode']=='host'||_var($port,'NAT')) ? $host : _var($port,'IP');
|
||||
if ($ct['NetworkMode']=='host') {
|
||||
$ip = $host;
|
||||
} elseif (isset($driver[$ct['NetworkMode']]) && ($driver[$ct['NetworkMode']] == 'ipvlan' || $driver[$ct['NetworkMode']] == 'macvlan')) {
|
||||
$ip = reset($ct['Networks'])['IPAddress'];
|
||||
} elseif (!is_null(_var($port,'PublicPort'))) {
|
||||
$ip = $host;
|
||||
} else {
|
||||
$ip = _var($port,'IP');
|
||||
}
|
||||
$tmp['url'] = $ip ? (strpos($tmp['url'],$ip)!==false ? $tmp['url'] : $this->getControlURL($ct, $ip, $tmp['url'])) : $tmp['url'];
|
||||
if (strpos($ct['NetworkMode'], 'container:') === 0)
|
||||
$tmp['url'] = '';
|
||||
}
|
||||
if ( ($tmp['shell'] ?? false) == false )
|
||||
$tmp['shell'] = $this->getTemplateValue($image, 'Shell');
|
||||
@@ -338,8 +347,12 @@ class DockerTemplates {
|
||||
$tmp['updated'] = var_export($DockerUpdate->getUpdateStatus($image),true);
|
||||
}
|
||||
if (!$com) $tmp['updated'] = 'undef';
|
||||
if (empty($tmp['template']) || $reload) $tmp['template'] = $this->getUserTemplate($name);
|
||||
if ($reload) $DockerUpdate->updateUserTemplate($name);
|
||||
if ($ct['Manager'] !== 'dockerman')
|
||||
$tmp['template'] = null;
|
||||
else if (empty($tmp['template']) || $reload) {
|
||||
$tmp['template'] = $this->getUserTemplate($name);
|
||||
if ($reload) $DockerUpdate->updateUserTemplate($name);
|
||||
}
|
||||
//$this->debug("\n$name");
|
||||
//foreach ($tmp as $c => $d) $this->debug(sprintf(' %-10s: %s', $c, $d));
|
||||
}
|
||||
@@ -897,7 +910,7 @@ class DockerClient {
|
||||
}
|
||||
|
||||
public function getDockerContainers() {
|
||||
global $driver;
|
||||
global $driver, $host;
|
||||
// Return cached values
|
||||
if (is_array($this::$containersCache)) return $this::$containersCache;
|
||||
$this::$containersCache = [];
|
||||
@@ -922,23 +935,48 @@ class DockerClient {
|
||||
$c['Icon'] = $info['Config']['Labels']['net.unraid.docker.icon'] ?? false;
|
||||
$c['Url'] = $info['Config']['Labels']['net.unraid.docker.webui'] ?? false;
|
||||
$c['Shell'] = $info['Config']['Labels']['net.unraid.docker.shell'] ?? false;
|
||||
$c['Manager'] = $info['Config']['Labels']['net.unraid.docker.managed'] ?? false;
|
||||
$c['Ports'] = [];
|
||||
$c['Networks'] = [];
|
||||
if ($id) $c['NetworkMode'] = $net.str_replace('/',':',DockerUtil::ctMap($id)?:'/???');
|
||||
if (isset($driver[$c['NetworkMode']])) {
|
||||
if ($driver[$c['NetworkMode']]=='bridge') {
|
||||
$ports = &$info['HostConfig']['PortBindings'];
|
||||
$nat = true;
|
||||
} elseif ($driver[$c['NetworkMode']]=='host') {
|
||||
$c['Ports']['host'] = ['host' => ''];
|
||||
} elseif ($driver[$c['NetworkMode']]=='ipvlan' || $driver[$c['NetworkMode']]=='macvlan') {
|
||||
$i = $ct['NetworkSettings']['Networks'][$c['NetworkMode']]['IPAddress'];
|
||||
$c['Ports']['vlan'] = ["$i" => $i];
|
||||
} else {
|
||||
$ports = &$info['Config']['ExposedPorts'];
|
||||
$nat = false;
|
||||
}
|
||||
$ip = $ct['NetworkSettings']['Networks'][$c['NetworkMode']]['IPAddress'];
|
||||
} else if (!$id) {
|
||||
$c['NetworkMode'] = DockerUtil::ctMap($c['NetworkMode']);
|
||||
$ports = &$info['Config']['ExposedPorts'];
|
||||
}
|
||||
foreach($ct['NetworkSettings']['Networks'] as $netName => $netVals) {
|
||||
$i = $c['NetworkMode']=='host' ? $host : $netVals['IPAddress'];
|
||||
$c['Networks'][$netName] = [ 'IPAddress' => $i ];
|
||||
if ($driver[$netName]=='ipvlan' || $driver[$netName]=='macvlan') {
|
||||
if (!isset($c['Ports']['vlan'])) $c['Ports']['vlan'] = [];
|
||||
$c['Ports']['vlan']["$i"] = $i;
|
||||
}
|
||||
}
|
||||
$ip = $c['NetworkMode']=='host' ? $host : $ct['NetworkSettings']['Networks'][$c['NetworkMode']]['IPAddress'] ?? null;
|
||||
$c['Networks'][$c['NetworkMode']] = [ 'IPAddress' => $ip ];
|
||||
$ports = (isset($ports) && is_array($ports)) ? $ports : [];
|
||||
foreach ($ports as $port => $value) {
|
||||
if (!isset($info['HostConfig']['PortBindings'][$port])) {
|
||||
continue;
|
||||
}
|
||||
[$PrivatePort, $Type] = array_pad(explode('/', $port),2,'');
|
||||
$c['Ports'][] = ['IP' => $ip, 'PrivatePort' => $PrivatePort, 'PublicPort' => $nat ? $value[0]['HostPort'] : $PrivatePort, 'NAT' => $nat, 'Type' => $Type];
|
||||
$PublicPort = $info['HostConfig']['PortBindings']["$port"][0]['HostPort'] ?: null;
|
||||
$nat = ($driver[$c['NetworkMode']]=='bridge');
|
||||
if (array_key_exists($PrivatePort, $c['Ports']) && $Type != $c['Ports'][$PrivatePort]['Type'])
|
||||
$Type = $c['Ports'][$PrivatePort]['Type'] . '/' . $Type;
|
||||
$c['Ports'][$PrivatePort] = ['IP' => $ip, 'PrivatePort' => $PrivatePort, 'PublicPort' => $PublicPort, 'NAT' => $nat, 'Type' => $Type, 'Driver' => $driver[$c['NetworkMode']]];
|
||||
}
|
||||
ksort($c['Ports']);
|
||||
$this::$containersCache[] = $c;
|
||||
}
|
||||
array_multisort(array_column($this::$containersCache,'Name'), SORT_NATURAL|SORT_FLAG_CASE, $this::$containersCache);
|
||||
|
@@ -87,11 +87,30 @@ foreach ($containers as $ct) {
|
||||
$icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png';
|
||||
$image = substr($icon,-4)=='.png' ? "<img src='$icon?".filemtime("$docroot{$info['icon']}")."' class='img' onerror=this.src='/plugins/dynamix.docker.manager/images/question.png';>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
|
||||
$wait = var_split($autostart[array_search($name,$names)]??'',1);
|
||||
$ports = [];
|
||||
foreach ($ct['Ports'] as $port) {
|
||||
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : _var($port,'IP')) : $null;
|
||||
$extern = $running ? (_var($port,'NAT') ? $host : $intern) : $null;
|
||||
$ports[] = sprintf('%s:%s/%s<i class="fa fa-arrows-h" style="margin:0 6px"></i>%s:%s', $intern, _var($port,'PrivatePort'), strtoupper(_var($port,'Type')), $extern, _var($port,'PublicPort'));
|
||||
$networks = [];
|
||||
$network_ips = [];
|
||||
$ports_internal = [];
|
||||
$ports_external = [];
|
||||
if (isset($ct['Ports']['vlan'])) {
|
||||
foreach ($ct['Ports']['vlan'] as $i)
|
||||
$ports_external[] = sprintf('%s', $i);
|
||||
$ports_internal[0] = sprintf('%s', 'all');
|
||||
}
|
||||
foreach($ct['Networks'] as $netName => $netVals) {
|
||||
$networks[] = $netName;
|
||||
$network_ips[] = $running ? $netVals['IPAddress'] : null;
|
||||
|
||||
if (isset($ct['Networks']['host'])) {
|
||||
$ports_external[] = sprintf('%s', $netVals['IPAddress']);
|
||||
$ports_internal[0] = sprintf('%s', 'all');
|
||||
} else if (!isset($ct['Ports']['vlan']) || strpos($ct['NetworkMode'], 'container:') != 0) {
|
||||
foreach ($ct['Ports'] as $port) {
|
||||
if (_var($port,'PublicPort') && _var($port,'Driver') == 'bridge')
|
||||
$ports_external[] = sprintf('%s:%s', $host, strtoupper(_var($port,'PublicPort')));
|
||||
if ((!isset($ct['Networks']['host'])) || (!isset($ct['Networks']['vlan'])))
|
||||
$ports_internal[] = sprintf('%s:%s', _var($port,'PrivatePort'), strtoupper(_var($port,'Type')));
|
||||
}
|
||||
}
|
||||
}
|
||||
$paths = [];
|
||||
$ct['Volumes'] = is_array($ct['Volumes']) ? $ct['Volumes'] : [];
|
||||
@@ -141,8 +160,10 @@ foreach ($containers as $ct) {
|
||||
break;
|
||||
}
|
||||
echo "<div class='advanced'><i class='fa fa-info-circle fa-fw'></i> ".compress(_($version),12,0)."</div></td>";
|
||||
echo "<td>{$ct['NetworkMode']}</td>";
|
||||
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports)."</span></td>";
|
||||
echo "<td style='white-space:nowrap'><span class='docker_readmore'> ".implode('<br>',$networks)."</span></td>";
|
||||
echo "<td style='white-space:nowrap'><span class='docker_readmore'> ".implode('<br>',$network_ips)."</span></td>";
|
||||
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports_internal)."</span></td>";
|
||||
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports_external)."</span></td>";
|
||||
echo "<td style='word-break:break-all'><span class='docker_readmore'>".implode('<br>',$paths)."</span></td>";
|
||||
echo "<td class='advanced'><span class='cpu-$id'>0%</span><div class='usage-disk mm'><span id='cpu-$id' style='width:0'></span><span></span></div>";
|
||||
echo "<br><span class='mem-$id'>0 / 0</span></td>";
|
||||
@@ -162,3 +183,4 @@ foreach ($images as $image) {
|
||||
}
|
||||
echo "\0".implode($docker)."\0".(pgrep('rc.docker')!==false ? 1:0);
|
||||
?>
|
||||
|
||||
|
@@ -12,11 +12,6 @@
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
/* Read the docker configuration file. */
|
||||
$cfgfile = "/boot/config/docker.cfg";
|
||||
$config_ini = @parse_ini_file($cfgfile, true, INI_SCANNER_RAW);
|
||||
$cfg = ($config_ini !== false) ? $config_ini : [];
|
||||
|
||||
function addRoute($ct) {
|
||||
// add static route(s) for remote WireGuard access
|
||||
[$pid,$net] = array_pad(explode(' ',exec("docker inspect --format='{{.State.Pid}} {{.NetworkSettings.Networks}}' $ct")),2,'');
|
||||
@@ -242,7 +237,7 @@ function xmlSecurity(&$template) {
|
||||
}
|
||||
|
||||
function xmlToCommand($xml, $create_paths=false) {
|
||||
global $docroot, $var, $cfg, $driver;
|
||||
global $docroot, $var, $driver;
|
||||
$xml = xmlToVar($xml);
|
||||
$cmdName = strlen($xml['Name']) ? '--name='.escapeshellarg($xml['Name']) : '';
|
||||
$cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : '';
|
||||
@@ -307,12 +302,17 @@ function xmlToCommand($xml, $create_paths=false) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the docker configuration file. */
|
||||
$cfgfile = "/boot/config/docker.cfg";
|
||||
$config_ini = @parse_ini_file($cfgfile, true, INI_SCANNER_RAW);
|
||||
$docker_cfg = ($config_ini !== false) ? $config_ini : [];
|
||||
|
||||
// Add pid limit if user has not specified it as an extra parameter
|
||||
$pidsLimit = preg_match('/--pids-limit (\d+)/', $xml['ExtraParams'], $matches) ? $matches[1] : null;
|
||||
if ($pidsLimit === null) {
|
||||
$pid_limit = "--pids-limit ";
|
||||
if (($cfg['DOCKER_PID_LIMIT']??'') != "") {
|
||||
$pid_limit .= $cfg['DOCKER_PID_LIMIT'];
|
||||
if (($docker_cfg['DOCKER_PID_LIMIT']??'') != "") {
|
||||
$pid_limit .= $docker_cfg['DOCKER_PID_LIMIT'];
|
||||
} else {
|
||||
$pid_limit .= "2048";
|
||||
}
|
||||
@@ -515,9 +515,15 @@ function getAllocations() {
|
||||
$nat = $ip = false;
|
||||
$list['Name'] = $ct['Name'];
|
||||
foreach ($ct['Ports'] as $tmp) {
|
||||
$nat = $tmp['NAT'];
|
||||
$ip = $tmp['IP'];
|
||||
$port[] = $tmp['PublicPort'];
|
||||
if (isset($tmp['NAT'])) {
|
||||
$nat = $tmp['NAT'];
|
||||
}
|
||||
if (isset($tmp['IP'])) {
|
||||
$ip = $tmp['IP'];
|
||||
}
|
||||
if (isset($tmp['PublicPort'])) {
|
||||
$port[] = $tmp['PublicPort'];
|
||||
}
|
||||
}
|
||||
sort($port);
|
||||
$ip = $ct['NetworkMode']=='host'||$nat ? $host : ($ip ?: DockerUtil::myIP($ct['Name']) ?: '0.0.0.0');
|
||||
|
28
emhttp/plugins/dynamix.docker.manager/system/Docker
Executable file
28
emhttp/plugins/dynamix.docker.manager/system/Docker
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Get active containers
|
||||
ACTIVE_CONTAINERS="$(docker ps -q --no-trunc 2>/dev/null)"
|
||||
|
||||
# Exit if no containers are active and return zero
|
||||
if [ -z "${ACTIVE_CONTAINERS}" ]; then
|
||||
echo "0"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Get all relevant memory entries from containers
|
||||
for container in ${ACTIVE_CONTAINERS} ; do
|
||||
CONT_MEMORY="$(cat /sys/fs/cgroup/docker/${container}/memory.stat 2>/dev/null | grep -Ew "anon|kernel|kernel_stack|pagetables|sec_pagetables|percpu|sock|vmalloc|shmem" | awk '{print $2}')"
|
||||
# Add up memory values
|
||||
for value in ${CONT_MEMORY} ; do
|
||||
if [[ ${value} =~ ^[0-9]+$ ]]; then
|
||||
((MEMORY_USAGE += value))
|
||||
fi
|
||||
done
|
||||
unset CONT_MEMORY
|
||||
done
|
||||
|
||||
# Check if value is a integer and return the value otherwiese return zero
|
||||
if [[ ${MEMORY_USAGE} =~ ^[0-9]+$ ]]; then
|
||||
echo "${MEMORY_USAGE}"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
".nuxt/nuxt-custom-elements/entries/unraid-components.client.mjs": {
|
||||
"file": "_nuxt/unraid-components.client-CNGeANhD.js",
|
||||
"file": "_nuxt/unraid-components.client-CQOXNcK4.js",
|
||||
"name": "unraid-components.client",
|
||||
"src": ".nuxt/nuxt-custom-elements/entries/unraid-components.client.mjs",
|
||||
"isEntry": true,
|
||||
@@ -8,5 +8,5 @@
|
||||
"_nuxt/unraid-components-p_3YF86n.css"
|
||||
]
|
||||
},
|
||||
"ts": 1716926465
|
||||
"ts": 1723595088
|
||||
}
|
@@ -489,6 +489,7 @@ $(function() {
|
||||
<input type="button" onclick="stopAll()" value="_(Stop All)_" style="display:none">
|
||||
|
||||
<div id="dialogWindow"></div>
|
||||
<div id="iframe-popup"></div>
|
||||
|
||||
<div id="templateISO" class="template">
|
||||
<dl>
|
||||
|
@@ -24,36 +24,74 @@ require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
$hardware = !empty(shell_exec("/etc/rc.d/rc.libvirt test"));
|
||||
if (!$hardware) {
|
||||
echo "<p class='notice'>"._('Your hardware does not have Intel VT-x or AMD-V capability').". "._('This is required to create VMs in KVM').". "._('Please disable the VM function').". ";
|
||||
echo "<a href='https://docs.unraid.net/unraid-os/manual/vm-management#determining-hvmiommu-hardware-support' target='_blank'> "._('Click here to see the Unraid Wiki for more information')."</a></p>";
|
||||
echo "<a href='https://docs.unraid.net/go/determining-hvmiommu-hardware-support/' target='_blank'> "._('View the Docs for more information')."</a></p>";
|
||||
}
|
||||
|
||||
function scan($area, $text) {
|
||||
return strpos($area,$text)!==false;
|
||||
}
|
||||
function detect(&$syslinux, $key) {
|
||||
$size = count($syslinux);
|
||||
$menu = $i = 0;
|
||||
$value = '';
|
||||
// find the default section
|
||||
while ($i < $size) {
|
||||
if (scan($syslinux[$i],'label ')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($syslinux[$n],'label ') && $n < $size) {
|
||||
if (scan($syslinux[$n],'menu default')) $menu = 1;
|
||||
if (scan($syslinux[$n],'append')) foreach (explode(' ',$syslinux[$n]) as $cmd) if (scan($cmd,$key)) {$value = explode('=',$cmd)[1]; break;}
|
||||
$n++;
|
||||
function detect(&$bootcfg, $bootenv, $key) {
|
||||
if ($bootenv === 'syslinux') {
|
||||
$size = count($bootcfg);
|
||||
$menu = $i = 0;
|
||||
$value = '';
|
||||
// find the default section
|
||||
while ($i < $size) {
|
||||
if (scan($bootcfg[$i],'label ')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($bootcfg[$n],'label ') && $n < $size) {
|
||||
if (scan($bootcfg[$n],'menu default')) $menu = 1;
|
||||
if (scan($bootcfg[$n],'append')) foreach (explode(' ',$bootcfg[$n]) as $cmd) if (scan($cmd,$key)) {$value = explode('=',$cmd)[1]; break;}
|
||||
$n++;
|
||||
}
|
||||
if ($menu) break; else $i = $n - 1;
|
||||
}
|
||||
if ($menu) break; else $i = $n - 1;
|
||||
$i++;
|
||||
}
|
||||
$i++;
|
||||
} elseif ($bootenv === 'grub') {
|
||||
$menu_entries = [];
|
||||
// find the current boot entry
|
||||
foreach ($bootcfg as $line) {
|
||||
if (preg_match('/set default=(\d+)/', $line, $match)) {
|
||||
$bootentry = (int)$match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// split boot entries
|
||||
foreach ($bootcfg as $line) {
|
||||
if (strpos($line, 'menuentry ') === 0) {
|
||||
$in_menuentry = true;
|
||||
$current_entry = $line . "\n";
|
||||
} elseif ($in_menuentry) {
|
||||
$current_entry .= $line . "\n";
|
||||
if (trim($line) === "}") {
|
||||
$menu_entries[] = $current_entry;
|
||||
$in_menuentry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// search in selected menuentry
|
||||
$menuentry = explode("\n", $menu_entries[$bootentry]);
|
||||
foreach (explode(' ', $menu_entries[$bootentry]) as $cmd) {
|
||||
if (scan($cmd,$key)) {
|
||||
$value = explode('=',$cmd)[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
return trim($value);
|
||||
}
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$bootcfg = file('/boot/syslinux/syslinux.cfg',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$bootenv = 'syslinux';
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
$bootcfg = file('/boot/grub/grub.cfg',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$bootenv = 'grub';
|
||||
}
|
||||
$syslinux = file('/boot/syslinux/syslinux.cfg',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$arrValidNetworks = getValidNetworks();
|
||||
$pcie_acs_override = detect($syslinux, 'pcie_acs_override');
|
||||
$vfio_allow_unsafe = detect($syslinux, 'allow_unsafe_interrupts');
|
||||
$pcie_acs_override = detect($bootcfg, $bootenv, 'pcie_acs_override');
|
||||
$vfio_allow_unsafe = detect($bootcfg, $bootenv, 'allow_unsafe_interrupts');
|
||||
$bgcolor = strstr('white,azure',$display['theme']) ? '#f2f2f2' : '#1c1c1c';
|
||||
$started = $var['fsState']=='Started';
|
||||
$libvirt_up = $libvirt_running=='yes';
|
||||
@@ -321,7 +359,7 @@ $(function(){
|
||||
<?if ($safemode):?>
|
||||
if (run) $("#settingsForm").submit();
|
||||
<?else:?>
|
||||
if (run) $.post("/plugins/dynamix.vm.manager/include/VMajax.php", {action:'syslinux',pcie:$('#pcie_acs_override').val(),vfio:$('#vfio_allow_unsafe').val()}, function(data){
|
||||
if (run) $.post("/plugins/dynamix.vm.manager/include/VMajax.php", {action:'cmdlineoverride',pcie:$('#pcie_acs_override').val(),vfio:$('#vfio_allow_unsafe').val()}, function(data){
|
||||
$("#settingsForm").submit();
|
||||
});
|
||||
<?endif;?>
|
||||
|
@@ -19,7 +19,7 @@ Cond="exec(\"grep -o '^USAGE=.Y' /boot/config/domain.cfg 2>/dev/null\") && is_fi
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
?>
|
||||
<table id="vmstats" class="tablesorter four shift">
|
||||
<thead class='child'><tr><th class="th1">_(Name)_</th><th class="th2">_(Guest CPU)_</th><th>_(Host CPU)_</th><th>_(Memory)_</th><th>_(Disk IO)_</th><th>_(Network IO)_</th></tr></thead>
|
||||
<thead class='child'><tr><th class="th1">_(Name)_</th><th class="th2">_(Guest CPU)_</th><th>_(Host CPU)_</th><th>_(Memory inuse/Current/Maximum)_</th><th>_(Disk IO)_</th><th>_(Network IO)_</th></tr></thead>
|
||||
<tbody id ="vmstatsbody" class='child'>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@@ -33,30 +33,60 @@ function scan($line, $text) {
|
||||
return stripos($line,$text)!==false;
|
||||
}
|
||||
|
||||
function embed(&$syslinux, $key, $value) {
|
||||
$size = count($syslinux);
|
||||
$make = false;
|
||||
$new = strlen($value) ? "$key=$value" : false;
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
// find sections and exclude safemode
|
||||
if (scan($syslinux[$i],'label ') && !scan($syslinux[$i],'safe mode') && !scan($syslinux[$i],'safemode')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($syslinux[$n],'label ') && $n < $size) {
|
||||
if (scan($syslinux[$n],'append ')) {
|
||||
$cmd = preg_split('/\s+/',trim($syslinux[$n]));
|
||||
// replace the existing setting
|
||||
for ($c = 1; $c < count($cmd); $c++) if (scan($cmd[$c],$key)) {$make |= ($cmd[$c]!=$new); $cmd[$c] = $new; break;}
|
||||
// or insert the new setting
|
||||
if ($c==count($cmd) && $new) {array_splice($cmd,-1,0,$new); $make = true;}
|
||||
$syslinux[$n] = ' '.str_replace(' ',' ',implode(' ',$cmd));
|
||||
function embed(&$bootcfg, $env, $key, $value) {
|
||||
if ($env === 'syslinux') {
|
||||
$size = count($bootcfg);
|
||||
$make = false;
|
||||
$new = strlen($value) ? "$key=$value" : false;
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
// find sections and exclude safemode
|
||||
if (scan($bootcfg[$i],'label ') && !scan($bootcfg[$i],'safe mode') && !scan($bootcfg[$i],'safemode')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($bootcfg[$n],'label ') && $n < $size) {
|
||||
if (scan($bootcfg[$n],'append ')) {
|
||||
$cmd = preg_split('/\s+/',trim($bootcfg[$n]));
|
||||
// replace the existing setting
|
||||
for ($c = 1; $c < count($cmd); $c++) if (scan($cmd[$c],$key)) {$make |= ($cmd[$c]!=$new); $cmd[$c] = $new; break;}
|
||||
// or insert the new setting
|
||||
if ($c==count($cmd) && $new) {array_splice($cmd,-1,0,$new); $make = true;}
|
||||
$bootcfg[$n] = ' '.str_replace(' ',' ',implode(' ',$cmd));
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
$n++;
|
||||
$i = $n - 1;
|
||||
}
|
||||
$i = $n - 1;
|
||||
$i++;
|
||||
}
|
||||
} elseif ($env === 'grub') {
|
||||
$size = count($bootcfg);
|
||||
$make = false;
|
||||
$new = strlen($value) ? "$key=$value" : false;
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
// find sections and exclude safemode/memtest
|
||||
if (scan($bootcfg[$i],'menuentry ') && !scan($bootcfg[$i],'safe mode') && !scan($bootcfg[$i],'safemode') && !scan($bootcfg[$i],'memtest')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($bootcfg[$n],'menuentry ') && $n < $size) {
|
||||
if (scan($bootcfg[$n],'linux ')) {
|
||||
$cmd = preg_split('/\s+/',trim($bootcfg[$n]));
|
||||
// replace the existing setting
|
||||
for ($c = 1; $c < count($cmd); $c++) if (scan($cmd[$c],$key)) {$make |= ($cmd[$c]!=$new); $cmd[$c] = $new; break;}
|
||||
// or insert the new setting
|
||||
if ($c == count($cmd) && $new) {
|
||||
$cmd[] = $new;
|
||||
$make = true;
|
||||
}
|
||||
$bootcfg[$n] = ' ' . str_replace(' ', ' ', implode(' ', $cmd));
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
$i = $n - 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return $make;
|
||||
}
|
||||
@@ -422,6 +452,61 @@ case 'snap-desc':
|
||||
: ['error' => $lv->get_last_error()];
|
||||
break;
|
||||
|
||||
case 'get_storage_fstype':
|
||||
$fstype = get_storage_fstype(unscript(_var($_REQUEST,'storage')));
|
||||
$arrResponse = ['fstype' => $fstype , 'success' => true] ;
|
||||
break;
|
||||
|
||||
case 'vm-removal':
|
||||
requireLibvirt();
|
||||
$arrResponse = ($data = getvmsnapshots($domName))
|
||||
? ['success' => true]
|
||||
: ['error' => $lv->get_last_error()];
|
||||
$datartn = $disksrtn = "";
|
||||
foreach($data as $snap=>$snapdetail) {
|
||||
$snapshotdatetime = date("Y-m-d H:i:s",$snapdetail["creationtime"]) ;
|
||||
$datartn .= "$snap $snapshotdatetime\n" ;
|
||||
}
|
||||
$disks = $lv->get_disk_stats($domName);
|
||||
|
||||
foreach($disks as $diskid=>$diskdetail) {
|
||||
if ($diskid == 0) $pathinfo = pathinfo($diskdetail['file']);
|
||||
}
|
||||
|
||||
$list = glob($pathinfo['dirname']."/*");
|
||||
$uuid = $lv->domain_get_uuid($domName);
|
||||
|
||||
$list2 = glob("/etc/libvirt/qemu/nvram/*$uuid*");
|
||||
$listnew = array();
|
||||
$list=array_merge($list,$list2);
|
||||
foreach($list as $key => $listent)
|
||||
{
|
||||
$pathinfo = pathinfo($listent);
|
||||
$listnew[] = "{$pathinfo['basename']} ({$pathinfo['dirname']})";
|
||||
}
|
||||
sort($listnew,SORT_NATURAL);
|
||||
$listcount = count($listnew);
|
||||
$snapcount = count($data);
|
||||
$disksrtn=implode("\n",$listnew);
|
||||
|
||||
|
||||
|
||||
if (strpos($dirname,'/mnt/user/')===0) {
|
||||
$realdisk = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($dirname)." 2>/dev/null"));
|
||||
if (!empty($realdisk)) {
|
||||
$dirname = str_replace('/mnt/user/', "/mnt/$realdisk/", $dirname);
|
||||
}
|
||||
}
|
||||
$fstype = trim(shell_exec(" stat -f -c '%T' $dirname"));
|
||||
$html = '<table class="snapshot">
|
||||
<tr><td>'._('VM Being removed').':</td><td><span id="VMBeingRemoved">'.$domName.'</span></td></tr>
|
||||
<tr><td>'._('Remove all files').':</td><td><input type="checkbox" id="All" checked value="" ></td></tr>
|
||||
<tr><td>'._('Files being removed').':</td><td><textarea id="textfiles" class="xml" rows="'.$listcount.'" style="white-space: pre; overflow: auto; width:600px" disabled>'.$disksrtn.'</textarea></td></tr>
|
||||
<tr><td>'._('Snapshots being removed').':</td><td><textarea id="textsnaps" rows="'.$snapsount.'" cols="80" disabled>'.$datartn.'</textarea></td></tr>
|
||||
</table>';
|
||||
$arrResponse = ['html' => $html , 'success' => true] ;
|
||||
break;
|
||||
|
||||
case 'disk-create':
|
||||
$disk = $_REQUEST['disk'];
|
||||
$driver = $_REQUEST['driver'];
|
||||
@@ -536,26 +621,39 @@ case 'hot-detach-usb':
|
||||
//TODO
|
||||
break;
|
||||
|
||||
case 'syslinux':
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
$syslinux = file($cfg, FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$m1 = embed($syslinux, 'pcie_acs_override', $_REQUEST['pcie']);
|
||||
$m2 = embed($syslinux, 'vfio_iommu_type1.allow_unsafe_interrupts', $_REQUEST['vfio']);
|
||||
if ($m1||$m2) file_put_contents($cfg, implode("\n",$syslinux)."\n");
|
||||
case 'cmdlineoverride':
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
$env = 'syslinux';
|
||||
$bootcfg = file($cfg, FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
$cfg = '/boot/grub/grub.cfg';
|
||||
$env = 'grub';
|
||||
$bootcfg = file($cfg, FILE_IGNORE_NEW_LINES);
|
||||
}
|
||||
$m1 = embed($bootcfg, $env, 'pcie_acs_override', $_REQUEST['pcie']);
|
||||
$m2 = embed($bootcfg, $env, 'vfio_iommu_type1.allow_unsafe_interrupts', $_REQUEST['vfio']);
|
||||
if ($m1||$m2) file_put_contents($cfg, implode("\n",$bootcfg)."\n");
|
||||
$arrResponse = ['success' => true, 'modified' => $m1|$m2];
|
||||
break;
|
||||
|
||||
case 'reboot':
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
$syslinux = file($cfg, FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
$env = 'syslinux';
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
$cfg = '/boot/grub/grub.cfg';
|
||||
$env = 'grub';
|
||||
}
|
||||
$bootcfg = file($cfg, FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$cmdline = explode(' ',file_get_contents('/proc/cmdline'));
|
||||
$pcie = $vfio = '';
|
||||
foreach ($cmdline as $cmd) {
|
||||
if (scan($cmd,'pcie_acs_override')) $pcie = explode('=',$cmd)[1];
|
||||
if (scan($cmd,'allow_unsafe_interrupts')) $vfio = explode('=',$cmd)[1];
|
||||
}
|
||||
$m1 = embed($syslinux, 'pcie_acs_override', $pcie);
|
||||
$m2 = embed($syslinux, 'vfio_iommu_type1.allow_unsafe_interrupts', $vfio);
|
||||
$m1 = embed($bootcfg, $env, 'pcie_acs_override', $pcie);
|
||||
$m2 = embed($bootcfg, $env, 'vfio_iommu_type1.allow_unsafe_interrupts', $vfio);
|
||||
$arrResponse = ['success' => true, 'modified' => $m1|$m2];
|
||||
break;
|
||||
|
||||
|
@@ -113,7 +113,7 @@ if (strpos($strSelectedTemplate,"User-") !== false) {
|
||||
<table>
|
||||
<tr>
|
||||
<td>_(Icon)_:</td>
|
||||
<td>
|
||||
<td class="template_img_parent">
|
||||
<input type="hidden" name="template[icon]" id="template_icon" value="<?=htmlspecialchars($arrLoad['icon'])?>" />
|
||||
<img id="template_img" src="<?=htmlspecialchars($strIconURL)?>" width="48" height="48" title="_(Change Icon)_..."/>
|
||||
<div id="template_img_chooser_outer">
|
||||
@@ -189,13 +189,13 @@ $(function() {
|
||||
});
|
||||
|
||||
$('.advancedview').switchButton({
|
||||
labels_placement: "left",
|
||||
labels_placement: "right",
|
||||
on_label: "_(XML View)_",
|
||||
off_label: "_(Form View)_",
|
||||
checked: isVMXMLMode()
|
||||
});
|
||||
$('.inlineview').switchButton({
|
||||
labels_placement: "left",
|
||||
labels_placement: "right",
|
||||
off_label: "_(Hide inline xml)_",
|
||||
on_label: "_(Show Inline XML)_",
|
||||
checked: isinlineXMLMode()
|
||||
|
@@ -267,6 +267,9 @@
|
||||
if (!empty($disk['serial'])) {
|
||||
$arrReturn['serial'] = $disk['serial'];
|
||||
}
|
||||
if (!empty($disk['discard'])) {
|
||||
$arrReturn['discard'] = $disk['discard'];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -699,8 +702,10 @@
|
||||
if ($strDevType == 'file' || $strDevType == 'block') {
|
||||
$strSourceType = ($strDevType == 'file' ? 'file' : 'dev');
|
||||
|
||||
if (isset($disk['discard'])) $strDevUnmap = " discard=\"{$disk['discard']}\" "; else $strDevUnmap = " discard=\"ignore\" ";
|
||||
|
||||
$diskstr .= "<disk type='" . $strDevType . "' device='disk'>
|
||||
<driver name='qemu' type='" . $disk['driver'] . "' cache='writeback'/>
|
||||
<driver name='qemu' type='" . $disk['driver'] . "' cache='writeback'".$strDevUnmap."/>
|
||||
<source " . $strSourceType . "='" . htmlspecialchars($disk['image'], ENT_QUOTES | ENT_XML1) . "'/>
|
||||
<target bus='" . $disk['bus'] . "' dev='" . $disk['dev'] . "' $rotation_rate />
|
||||
$bootorder
|
||||
@@ -905,10 +910,17 @@
|
||||
}
|
||||
|
||||
if ($gpu['multi'] == "on"){
|
||||
$newgpu_bus = dechex(hexdec($gpu_bus) + 0x20) ;
|
||||
$newgpu_bus= 0x10;
|
||||
if (!isset($multibus[$newgpu_bus])) {
|
||||
$multibus[$newgpu_bus] = 0x10;
|
||||
} else {
|
||||
#Get next bus
|
||||
$newgpu_bus = end($multibus) + 0x01;
|
||||
$multibus[$newgpu_bus] = $newgpu_bus;
|
||||
}
|
||||
if ($machine_type == "pc") $newgpu_slot = "0x01" ; else $newgpu_slot = "0x00" ;
|
||||
$strSpecialAddress = "<address type='pci' domain='0x0000' bus='0x$newgpu_bus' slot='$newgpu_slot' function='0x".$gpu_function."' multifunction='on' />" ;
|
||||
$multidevices[$gpu_bus] = "0x$gpu_bus" ;
|
||||
$strSpecialAddress = "<address type='pci' domain='0x0000' bus='$newgpu_bus' slot='$newgpu_slot' function='0x".$gpu_function."' multifunction='on' />" ;
|
||||
$multidevices[$gpu_bus] = $newgpu_bus ;
|
||||
}
|
||||
|
||||
|
||||
@@ -926,9 +938,9 @@
|
||||
}
|
||||
}
|
||||
$audiodevs_used=[];
|
||||
$strSpecialAddressAudio = "" ;
|
||||
if (!empty($audios)) {
|
||||
foreach ($audios as $i => $audio) {
|
||||
$strSpecialAddressAudio = "" ;
|
||||
// Skip duplicate audio devices
|
||||
if (empty($audio['id']) || in_array($audio['id'], $audiodevs_used)) {
|
||||
continue;
|
||||
@@ -937,9 +949,9 @@
|
||||
[$audio_bus, $audio_slot, $audio_function] = my_explode(":", str_replace('.', ':', $audio['id']), 3);
|
||||
if ($audio_function != 0) {
|
||||
if (isset($multidevices[$audio_bus])) {
|
||||
$newaudio_bus = dechex(hexdec($audio_bus) + 0x20) ;
|
||||
$newaudio_bus = $multidevices[$audio_bus] ;
|
||||
if ($machine_type == "pc") $newaudio_slot = "0x01" ; else $newaudio_slot = "0x00" ;
|
||||
$strSpecialAddressAudio = "<address type='pci' domain='0x0000' bus='0x$newaudio_bus' slot='$newaudio_slot' function='0x".$audio_function."' />" ;
|
||||
$strSpecialAddressAudio = "<address type='pci' domain='0x0000' bus='$newaudio_bus' slot='$newaudio_slot' function='0x".$audio_function."' />" ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -956,9 +968,9 @@
|
||||
}
|
||||
|
||||
$pcidevs_used=[];
|
||||
$strSpecialAddressOther = "" ;
|
||||
if (!empty($pcis)) {
|
||||
foreach ($pcis as $i => $pci_id) {
|
||||
$strSpecialAddressOther = "" ;
|
||||
// Skip duplicate other pci devices
|
||||
if (empty($pci_id) || in_array($pci_id, $pcidevs_used)) {
|
||||
continue;
|
||||
@@ -968,9 +980,9 @@
|
||||
|
||||
if ($pci_function != 0) {
|
||||
if (isset($multidevices[$pci_bus])) {
|
||||
$newpci_bus = dechex(hexdec($pci_bus) + 0x20) ;
|
||||
$newpci_bus = $multidevices[$pci_bus];
|
||||
if ($machine_type == "pc") $newpci_slot = "0x01" ; else $newpci_slot = "0x00" ;
|
||||
$strSpecialAddressOther = "<address type='pci' domain='0x0000' bus='0x$newpci_bus' slot='$newpci_slot' function='0x".$pci_function."' />" ;
|
||||
$strSpecialAddressOther = "<address type='pci' domain='0x0000' bus='$newpci_bus' slot='$newpci_slot' function='0x".$pci_function."' />" ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1339,6 +1351,7 @@
|
||||
if ($tmp) {
|
||||
$tmp['bus'] = $disk->target->attributes()->bus->__toString();
|
||||
$tmp["boot order"] = $disk->boot->attributes()->order ?? "";
|
||||
$tmp["discard"] = $disk->driver->attributes()->discard ?? "ignore";
|
||||
$tmp["rotation"] = $disk->target->attributes()->rotation_rate ?? "0";
|
||||
$tmp['serial'] = $disk->serial ;
|
||||
|
||||
@@ -1369,7 +1382,8 @@
|
||||
'bus' => $disk->target->attributes()->bus->__toString(),
|
||||
'boot order' => $disk->boot->attributes()->order ,
|
||||
'rotation' => $disk->target->attributes()->rotation_rate ?? "0",
|
||||
'serial' => $disk->serial
|
||||
'serial' => $disk->serial,
|
||||
'discard' => $disk->driver->attributes()->discard ?? "ignore"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1989,7 +2003,10 @@
|
||||
if (is_file($disk)) unlink($disk);
|
||||
if (is_file($cfg)) unlink($cfg);
|
||||
if (is_file($xml)) unlink($xml);
|
||||
if (is_dir($dir) && $this->is_dir_empty($dir)) rmdir($dir);
|
||||
if (is_dir($dir) && $this->is_dir_empty($dir)) {
|
||||
$error = my_rmdir($dir);
|
||||
qemu_log("$domain","delete empty $dir $error");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -1091,6 +1091,15 @@ private static $encoding = 'UTF-8';
|
||||
return $arrValidDiskBuses;
|
||||
}
|
||||
|
||||
function getValidDiskDiscard() {
|
||||
$arrValidDiskDiscard = [
|
||||
'ignore' => 'Ignore(No Trim)',
|
||||
'unmap' => 'Unmap(Trim)',
|
||||
];
|
||||
|
||||
return $arrValidDiskDiscard;
|
||||
}
|
||||
|
||||
function getValidCdromBuses() {
|
||||
$arrValidCdromBuses = [
|
||||
'scsi' => 'SCSI',
|
||||
@@ -1316,6 +1325,7 @@ private static $encoding = 'UTF-8';
|
||||
'driver' => $disk['type'],
|
||||
'dev' => $disk['device'],
|
||||
'bus' => $disk['bus'],
|
||||
'discard' => $disk['discard'],
|
||||
'boot' => $disk['boot order'],
|
||||
'rotation' => $disk['rotation'],
|
||||
'serial' => $disk['serial'],
|
||||
@@ -1330,6 +1340,7 @@ private static $encoding = 'UTF-8';
|
||||
'dev' => 'hda',
|
||||
'select' => '',
|
||||
'bus' => 'virtio',
|
||||
'discard' => 'ignore',
|
||||
'rotation' => "0"
|
||||
];
|
||||
}
|
||||
@@ -1457,7 +1468,12 @@ private static $encoding = 'UTF-8';
|
||||
// preserve existing disk driver settings
|
||||
foreach ($new['devices']['disk'] as $key => $disk) {
|
||||
$source = $disk['source']['@attributes']['file'];
|
||||
foreach ($old['devices']['disk'] as $k => $d) if ($source==$d['source']['@attributes']['file']) $new['devices']['disk'][$key]['driver']['@attributes'] = $d['driver']['@attributes'];
|
||||
if (isset($disk['driver']['@attributes']['discard'])) $discard = $disk['driver']['@attributes']['discard']; else $discard = null;
|
||||
foreach ($old['devices']['disk'] as $k => $d)
|
||||
if ($source==$d['source']['@attributes']['file']) {
|
||||
if (isset($discard)) $d['driver']['@attributes']['discard'] = $discard;
|
||||
$new['devices']['disk'][$key]['driver']['@attributes'] = $d['driver']['@attributes'];
|
||||
}
|
||||
}
|
||||
// settings not in the GUI, but maybe customized
|
||||
unset($old['clock']);
|
||||
@@ -2661,8 +2677,8 @@ function get_vm_usage_stats($collectcpustats = true,$collectdiskstats = true,$co
|
||||
# Memory Metrics
|
||||
if ($state == 1 && $collectmemstats) {
|
||||
$currentmem = $data["balloon.current"];
|
||||
$unusedmem = $data["balloon.unused"];
|
||||
$meminuse = $currentmem - $unusedmem;
|
||||
$maximummem = $data["balloon.maximum"];
|
||||
$meminuse = min($data["balloon.rss"],$data["balloon.current"]);
|
||||
} else $currentmem = $meminuse = 0;
|
||||
|
||||
# Disk
|
||||
@@ -2697,7 +2713,8 @@ function get_vm_usage_stats($collectcpustats = true,$collectdiskstats = true,$co
|
||||
"cpuguest" => $cpuGuestPercent,
|
||||
"timestamp" => $timestamp,
|
||||
"mem" => $meminuse,
|
||||
"maxmem" => $currentmem,
|
||||
"curmem" => $currentmem,
|
||||
"maxmem" => $maximummem,
|
||||
"rxrate" => $rxrate,
|
||||
"rxp" => $rx,
|
||||
"txrate" => $txrate,
|
||||
@@ -2822,4 +2839,26 @@ function get_vm_ip($dom) {
|
||||
return $myIP;
|
||||
}
|
||||
|
||||
function check_zfs_name($zfsname, $storage="default") {
|
||||
global $lv,$domain_cfg;
|
||||
if ($storage == "default") $storage = $domain_cfg['DOMAINDIR']; else $storage = "/mnt/$storage/";
|
||||
$storage=transpose_user_path($storage);
|
||||
$fstype = trim(shell_exec(" stat -f -c '%T' $storage"));
|
||||
#Check if ZFS.
|
||||
$allowed_chars = "/^[A-Za-z0-9][A-Za-z0-9\-_.: ]*$/";
|
||||
if ($fstype == "zfs" && !preg_match($allowed_chars, $zfsname)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function get_storage_fstype($storage="default") {
|
||||
global $domain_cfg;
|
||||
if ($storage == "default") $storage = $domain_cfg['DOMAINDIR']; else $storage = "/mnt/$storage/";
|
||||
$storage=transpose_user_path($storage);
|
||||
$fstype = trim(shell_exec(" stat -f -c '%T' $storage"));
|
||||
return $fstype;
|
||||
}
|
||||
|
||||
?>
|
||||
|
@@ -75,9 +75,11 @@ while (true) {
|
||||
if ($vmdata['state'] == 1) {
|
||||
$running++;
|
||||
$echodata .= "<tr><td>$vm</td>" ;
|
||||
$echodata .= "<td class='advanced'><span class='cpug-".$vm."'>".$vmdata['cpuguest']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuguest']."%;'></span><span></span></div></td>";
|
||||
$echodata .= "<td class='advanced'><span class='cpuh-".$vm."'>".$vmdata['cpuhost']."%</span><div class='usage-disk mm'><span id='cpuh-".$vm."' style='width:".$vmdata['cpuhost']."%;'></span><span></span></div></td><td>";
|
||||
$echodata .= my_scale($vmdata['mem']*1024,$unit)."$unit / ".my_scale($vmdata['maxmem']*1024,$unit)."$unit</td><td>";
|
||||
$echodata .= "<td class='advanced'><span class='cpug-".$vm."'>".$vmdata['cpuguest']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuguest']."%;'> </span><span></span></div></td>";
|
||||
$echodata .= "<td class='advanced'><span class='cpuh-".$vm."'>".$vmdata['cpuhost']."%</span><div class='usage-disk mm'><span id='cpuh-".$vm."' style='width:".$vmdata['cpuhost']."%;'> </span><span></span></div></td><td>";
|
||||
$echodata .= my_scale($vmdata['mem']*1024,$unit)."$unit / ".my_scale($vmdata['curmem']*1024,$unit)."$unit";
|
||||
if ($vmdata['curmem'] === $vmdata['maxmem']) $echodata .= " </td><td>";
|
||||
else $echodata .= " / " .my_scale($vmdata['maxmem']*1024,$unit)."$unit </td><td>";
|
||||
$echodata .= _("Read").": ".my_scale($vmdata['rdrate'],$unit)."$unit/s<br>"._("Write").": ".my_scale($vmdata['wrrate'],$unit)."$unit/s</td><td>";
|
||||
$echodata .= _("RX").": ".my_scale($vmdata['rxrate'],$unit)."$unit/s<br>"._("TX").": ".my_scale($vmdata['txrate'],$unit)."$unit/s</td></tr>";
|
||||
}
|
||||
|
@@ -23,14 +23,15 @@ span.advancedview_panel{display:none;line-height:16px;margin-top:1px}
|
||||
.basic{display:none}
|
||||
.advanced{/*Empty placeholder*/}
|
||||
.switch-button-label.off{color:inherit}
|
||||
.template_img_parent{position:relative}
|
||||
#template_img{cursor:pointer}
|
||||
#template_img:hover{opacity:0.5}
|
||||
#template_img:hover i{opacity:1.0}
|
||||
.template_img_chooser_inner{display:inline-block;width:80px;margin-bottom:15px;margin-right:10px;text-align:center;}
|
||||
.template_img_chooser_inner img{width:48px;height:48px}
|
||||
.template_img_chooser_inner p{text-align:center;line-height:8px;}
|
||||
#template_img_chooser{width:560px;height:300px;overflow-y:scroll;position:relative}
|
||||
#template_img_chooser div:hover{background-color:#eee;cursor:pointer;}
|
||||
#template_img_chooser{width:560px;height:300px;overflow-y:scroll;position:relative;display:grid;grid-template-columns: repeat(6, minmax(0, 1fr));}
|
||||
#template_img_chooser div:hover{color:#ff8c2f;cursor:pointer;}
|
||||
#form_content{display:none}
|
||||
#vmform .four{overflow:hidden}
|
||||
#vmform .four label{float:left;display:table-cell;width:15%;}
|
||||
|
@@ -23,14 +23,15 @@ span.advancedview_panel{display:none;line-height:16px;margin-top:1px}
|
||||
.basic{display:none}
|
||||
.advanced{/*Empty placeholder*/}
|
||||
.switch-button-label.off{color:inherit}
|
||||
.template_img_parent{position:relative}
|
||||
#template_img{cursor:pointer}
|
||||
#template_img:hover{opacity:0.5}
|
||||
#template_img:hover i{opacity:1.0}
|
||||
.template_img_chooser_inner{display:inline-block;width:80px;margin-bottom:15px;margin-right:10px;text-align:center;}
|
||||
.template_img_chooser_inner img{width:48px;height:48px}
|
||||
.template_img_chooser_inner p{text-align:center;line-height:8px;}
|
||||
#template_img_chooser{width:560px;height:300px;overflow-y:scroll;position:relative}
|
||||
#template_img_chooser div:hover{background-color:#eee;cursor:pointer;}
|
||||
#template_img_chooser{width:560px;height:300px;overflow-y:scroll;position:relative;display:grid;grid-template-columns: repeat(6, minmax(0, 1fr));}
|
||||
#template_img_chooser div:hover{color:#ff8c2f;cursor:pointer;}
|
||||
#form_content{display:none}
|
||||
#vmform .four{overflow:hidden}
|
||||
#vmform .four label{float:left;display:table-cell;width:15%;}
|
||||
|
@@ -31,6 +31,7 @@
|
||||
$arrValidUSBDevices = getValidUSBDevices();
|
||||
$arrValidDiskDrivers = getValidDiskDrivers();
|
||||
$arrValidDiskBuses = getValidDiskBuses();
|
||||
$arrValidDiskDiscard = getValidDiskDiscard();
|
||||
$arrValidCdromBuses = getValidCdromBuses();
|
||||
$arrValidVNCModels = getValidVNCModels();
|
||||
$arrValidProtocols = getValidVMRCProtocols();
|
||||
@@ -88,7 +89,8 @@
|
||||
'select' => $domain_cfg['VMSTORAGEMODE'],
|
||||
'bus' => 'virtio' ,
|
||||
'boot' => 1,
|
||||
'serial' => 'vdisk1'
|
||||
'serial' => 'vdisk1',
|
||||
'discard' => 'unmap'
|
||||
]
|
||||
],
|
||||
'gpu' => [
|
||||
@@ -334,9 +336,10 @@
|
||||
<input type="hidden" name="domain[memoryBacking]" id="domain_memorybacking" value="<?=htmlspecialchars($arrConfig['domain']['memoryBacking'])?>">
|
||||
|
||||
<table>
|
||||
<tr><td></td><td><span hidden id="zfs-name" class="orange-text"><i class="fa fa-warning"></i> _(Name contains invalid characters or does not start with an alphanumberic for a ZFS storage location<br>Only these special characters are valid Underscore (_) Hyphen (-) Colon (:) Period (.))_</span></td></tr>
|
||||
<tr>
|
||||
<td>_(Name)_:</td>
|
||||
<td><input type="text" name="domain[name]" id="domain_name" class="textTemplate" title="_(Name of virtual machine)_" placeholder="_(e.g.)_ _(My Workstation)_" value="<?=htmlspecialchars($arrConfig['domain']['name'])?>" required /></td>
|
||||
<td><input type="text" name="domain[name]" id="domain_name" oninput="checkName(this.value)" class="textTemplate" title="_(Name of virtual machine)_" placeholder="_(e.g.)_ _(My Workstation)_" value="<?=htmlspecialchars($arrConfig['domain']['name'])?>" required /></td>
|
||||
<td><textarea class="xml" id="xmlname" rows=1 disabled ><?=htmlspecialchars($xml2['name'])."\n".htmlspecialchars($xml2['uuid'])."\n".htmlspecialchars($xml2['metadata'])?></textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -377,7 +380,7 @@
|
||||
<tr>
|
||||
<?if (!$boolNew) $disablestorage = " disabled "; else $disablestorage = "";?>
|
||||
<td>_(Override Storage Location)_:</td><td>
|
||||
<select <?=$disablestorage?> name="template[storage]" class="disk_select narrow" id="storage_location" title="_(Location of virtual machine files)_">
|
||||
<select <?=$disablestorage?> name="template[storage]" onchange="get_storage_fstype(this)" class="disk_select narrow" id="storage_location" title="_(Location of virtual machine files)_">
|
||||
<?
|
||||
$default_storage=htmlspecialchars($arrConfig['template']['storage']);
|
||||
echo mk_option($default_storage, 'default', _('Default'));
|
||||
@@ -847,6 +850,10 @@
|
||||
</select>
|
||||
_(Boot Order)_:
|
||||
<input type="number" size="5" maxlength="5" id="disk[<?=$i?>][boot]" class="narrow bootorder" style="width: 50px;" name="disk[<?=$i?>][boot]" title="_(Boot order)_" value="<?=$arrDisk['boot']?>" >
|
||||
_(Discard)_:
|
||||
<select name="disk[<?=$i?>][discard]" class="disk_driver narrow" title="_(Set discard option)_">
|
||||
<?mk_dropdown_options($arrValidDiskDiscard, $arrDisk['discard']);?>
|
||||
</select>
|
||||
<? if ($arrDisk['bus'] == "virtio" || $arrDisk['bus'] == "usb") $ssddisabled = "hidden "; else $ssddisabled = " ";?>
|
||||
<span id="disk[<?=$i?>][rotatetext]" <?=$ssddisabled?>>_(SSD)_:</span>
|
||||
<input type="checkbox" id="disk[<?=$i?>][rotation]" class="narrow rotation" onchange="updateSSDCheck(this)"style="width: 50px;" name="disk[<?=$i?>][rotation]" <?=$ssddisabled ?> <?=$arrDisk['rotation'] ? "checked ":"";?> title="_(Set SDD flag)_" value="<?=$arrDisk['rotation']?>" >
|
||||
@@ -897,6 +904,11 @@
|
||||
Specify the order the devices are used for booting.
|
||||
</p>
|
||||
|
||||
<p class="advanced">
|
||||
<b>vDisk Discard</b><br>
|
||||
Specify if unmap(Trim) requests are sent to underlaying filesystem.
|
||||
</p>
|
||||
|
||||
<p class="advanced">
|
||||
<b>vDisk SSD Flag</b><br>
|
||||
Specify the vdisk shows as SSD within the guest, only supported on SCSI, SATA and IDE bus types.
|
||||
@@ -1001,6 +1013,10 @@
|
||||
|
||||
_(Boot Order)_:
|
||||
<input type="number" size="5" maxlength="5" id="disk[{{INDEX}}][boot]" class="narrow bootorder" style="width: 50px;" name="disk[{{INDEX}}][boot]" title="_(Boot order)_" value="" >
|
||||
_(Discard)_:
|
||||
<select name="disk[{{INDEX}}][discard]" class="disk_driver narrow" title="_(Set discard option)_">
|
||||
<?mk_dropdown_options($arrValidDiskDiscard, "unmap");?>
|
||||
</select>
|
||||
<span id="disk[{{INDEX}}][rotatetext]" hidden>_(SSD)_:</span>
|
||||
<input type="checkbox" id="disk[{{INDEX}}][rotation]" class="narrow rotation" onchange="updateSSDCheck(this)"style="width: 50px;" name="disk[{{INDEX}}[rotation]" hidden title="_(Set SSD flag)_" value='0' >
|
||||
</td>
|
||||
@@ -1532,7 +1548,6 @@
|
||||
<table>
|
||||
<tr><td></td>
|
||||
<td>_(Select)_  _(Boot Order)_</td></tr></div>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>_(Other PCI Devices)_:</td>
|
||||
<td>
|
||||
@@ -1557,7 +1572,6 @@
|
||||
<label for="pci<?=$i?>">    <input type="checkbox" name="pci[]" id="pci<?=$i?>" value="<?=htmlspecialchars($arrDev['id'])?>" <?=$extra?>/>  
|
||||
<input type="number" size="5" maxlength="5" id="pciboot<?=$i?>" class="narrow pcibootorder" <?=$bootdisable?> style="width: 50px;" name="pciboot[<?=htmlspecialchars($arrDev['id'])?>]" title="_(Boot order)_" value="<?=$pciboot?>" >
|
||||
<?=htmlspecialchars($arrDev['name'])?> | <?=htmlspecialchars($arrDev['type'])?> (<?=htmlspecialchars($arrDev['id'])?>)</label><br/>
|
||||
<td><textarea class="xml" id="xmlpci<?=$i?>" rows=5 disabled ><?=htmlspecialchars($xml2['devices']['other'][$arrDev['id']])?></textarea></td>
|
||||
<?
|
||||
}
|
||||
}
|
||||
@@ -1567,6 +1581,7 @@
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<td><textarea class="xml" id="xmlpci<?=$i?>" rows=2 disabled ><?=htmlspecialchars($xml2['devices']['other']["allotherpci"])?></textarea></td>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@@ -1881,6 +1896,8 @@
|
||||
<script src="<?autov('/plugins/dynamix.vm.manager/scripts/codemirror/addon/hint/libvirt-schema.js')?>"></script>
|
||||
<script src="<?autov('/plugins/dynamix.vm.manager/scripts/codemirror/mode/xml/xml.js')?>"></script>
|
||||
<script type="text/javascript">
|
||||
var storageType = "<?=get_storage_fstype($arrConfig['template']['storage']);?>";
|
||||
var storageLoc = "<?=$arrConfig['template']['storage']?>";
|
||||
|
||||
function ShareChange(share) {
|
||||
var value = share.value;
|
||||
@@ -1992,6 +2009,36 @@ function SetBootorderfields(usbbootvalue) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Remove characters not allowed in share name. */
|
||||
function checkName(name) {
|
||||
/* Declare variables at the function scope */
|
||||
var isValidName
|
||||
$('#zfs-name').hide();
|
||||
isValidName = /^[A-Za-z0-9][A-Za-z0-9\-_.: ]*$/.test(name);
|
||||
if (isValidName) {
|
||||
$('#btnSubmit').prop("disabled", false);
|
||||
} else {
|
||||
if (storageType == "zfs")
|
||||
{ $('#btnSubmit').prop("disabled", true); $('#zfs-name').show(); }
|
||||
else $('#btnSubmit').prop("disabled", false);
|
||||
}
|
||||
}
|
||||
|
||||
function get_storage_fstype(item) {
|
||||
storageLoc = item.value;
|
||||
$.post("/plugins/dynamix.vm.manager/include/VMajax.php", {action:"get_storage_fstype", storage:item.value}, function( data ) {
|
||||
if (data.success) {
|
||||
if (data.fstype) {
|
||||
storageType=data.fstype;
|
||||
checkName(document.getElementById("domain_name").value);
|
||||
}}
|
||||
|
||||
if (data.error) {
|
||||
}
|
||||
}, "json");
|
||||
}
|
||||
|
||||
function USBBootChange(usbboot) {
|
||||
// Remove all boot orders if changed to Yes
|
||||
var value = usbboot.value ;
|
||||
|
@@ -18,22 +18,18 @@ Nchan="device_list,disk_load,parity_list"
|
||||
<?
|
||||
$keyfile = file_exists(_var($var,'luksKeyfile'));
|
||||
$missing = file_exists('/var/tmp/missing.tmp');
|
||||
$encrypt = false;
|
||||
$spot = _var($var,'mdResyncPos',0)>0;
|
||||
$poolsOnly = (_var($var,'SYS_ARRAY_SLOTS') == 0 ) ? true : false;
|
||||
|
||||
/* only one of $present, $missing, or $wrong will be true, or all will be false */
|
||||
$forced = $present = $wrong = false;
|
||||
foreach ($disks as $disk) {
|
||||
if (!isset($disk['fsType'])) continue;
|
||||
if (strpos(_var($disk,'fsType'),'luks:')!==false || (_var($disk,'fsType')=='auto' && (strpos(_var($var,'defaultFsType'),'luks:')!==false || _var($disk,'luksState',0)==2 || _var($disk,'luksState',0)==3))) {
|
||||
$encrypt = true;
|
||||
if (_var($disk,'luksState',0)==0) $forced = true;
|
||||
if (_var($disk,'luksState',0)==1) $present = true;
|
||||
if (_var($disk,'luksState',0)==2) $missing = true;
|
||||
if (_var($disk,'luksState',0)==3) $wrong = true;
|
||||
}
|
||||
if (strpos(_var($disk,'fsType'),'luks:')!==false || (_var($disk,'fsType')=='auto' && strpos(_var($var,'defaultFsType'),'luks:')!==false)) $forced = true;
|
||||
if (_var($disk,'luksState',0)==1) $present = true;
|
||||
if (_var($disk,'luksState',0)==2) $missing = true;
|
||||
if (_var($disk,'luksState',0)==3) $wrong = true;
|
||||
}
|
||||
$encrypt = $forced || $present || $missing || $wrong;
|
||||
if ($forced && ($present || $missing || $wrong)) $forced = false;
|
||||
|
||||
function check_encryption() {
|
||||
@@ -383,7 +379,7 @@ devices.on('message', function(msg,meta) {
|
||||
$.each(get,function(k,v) {if ($('#line'+k).length>0) $('#line'+k).html(v);});
|
||||
// button control
|
||||
if ($('#pauseButton').length>0 && $('#pauseButton').prop('disabled')==false) {
|
||||
if (!msg && $('#cancelButton').length>0 && $('#cancelButton').val()=="_(Cancel)_") {
|
||||
if ((get === "") && $('#cancelButton').val()=="_(Cancel)_") {
|
||||
$('#cancelButton').val("_(Done)_").prop('onclick',null).off('click').click(function(){refresh();});
|
||||
$('#pauseButton').prop('disabled',true);
|
||||
$('#cancelText').html('');
|
||||
@@ -464,7 +460,7 @@ window.onunload = function(){
|
||||
<? if (_var($var,'fsNumUnmountable',0)>0):?>
|
||||
<tr><td>**<?=_('Unmountable disk'.(_var($var,'fsNumUnmountable',0)==1?'':'s').' present')?>:**<br>
|
||||
<? $cache = [];
|
||||
foreach ($disks as $disk) if (substr(_var($disk,'fsStatus'),0,11)=='Unmountable' || in_array(prefix(_var($disk,'name')),$cache)) {
|
||||
foreach ($disks as $disk) if (substr(_var($disk,'fsStatus'),0,11)=='Unmountable' || in_array(pool_name(_var($disk,'name')),$cache)) {
|
||||
if (strlen(_var($disk,'id'))) echo "<span class='blue-text'>".my_disk(_var($disk,'name'))."</span> • ".my_id(_var($disk,'id'))." ("._var($disk,'device').")<br>";
|
||||
if (in_array(_var($disk,'name'),$pools)) $cache[] = $disk['name'];
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Menu="Dashboard"
|
||||
Nchan="wg_poller,update_1,update_2,update_3,ups_status:stop,vm_dashusage:stop"
|
||||
Nchan="wg_poller,update_1,update_2,update_3,ups_status:stop,vm_dashusage"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology * Copyright 2012-2023, Bergware International.
|
||||
@@ -79,7 +79,7 @@ $domain_cfg = parse_ini_file($domain_cfgfile);
|
||||
if (!isset($domain_cfg['USAGE'])) $vmusage = "N" ; else $vmusage = $domain_cfg['USAGE'];
|
||||
|
||||
// enable/disable graph elements by making hook script executable or not
|
||||
chmod("$docroot/webGui/system/VM_usage",$libvirtd ? 0755 : 0644);
|
||||
chmod("$docroot/webGui/system/VM",$libvirtd ? 0755 : 0644);
|
||||
chmod("$docroot/webGui/system/ZFS_cache",$zfs ? 0755 : 0644);
|
||||
|
||||
foreach ($disks as $disk) {
|
||||
@@ -1049,7 +1049,7 @@ function portSelect(name) {
|
||||
}
|
||||
function moreInfo(data,table) {
|
||||
var info = [];
|
||||
if (data[1]>0) info.push(data[1]+' '+(data[1]==1 ? "_(failed device)_" : "_(failed devices)_"));
|
||||
if (data[1]>0) info.push(data[1]+' '+(data[1]==1 ? "_(device warning)_" : "_(device warnings)_"));
|
||||
if (data[2]>0) info.push(data[2]+' '+(data[2]==1 ? "_(heat warning)_" : "_(heat warnings)_"));
|
||||
if (data[3]>0) info.push(data[3]+' '+(data[3]==1 ? "_(SMART error)_" : "_(SMART errors)_"));
|
||||
if (data[4]>0) info.push(data[4]+' '+(data[4]==1 ? "_(utilization warning)_" : "_(utilization warnings)_"));
|
||||
@@ -1188,6 +1188,7 @@ function SleepNow() {
|
||||
function sortTables() {
|
||||
$('table.dashboard').each(function(){
|
||||
var table = $(this);
|
||||
sanitizeMultiCookie(table.prop('id'), ';', true);
|
||||
var index = $.cookie(table.prop('id'));
|
||||
// sorting list exists
|
||||
if (index != null) {
|
||||
|
@@ -17,10 +17,10 @@ Tag="hdd-o"
|
||||
<?
|
||||
require_once "$docroot/webGui/include/Preselect.php";
|
||||
|
||||
$subpool_name = isSubpool($name) ? isSubpool($name) : '';
|
||||
$unassigned = array_key_exists($name,$devs);
|
||||
$disk = $disks[$name] ?? $devs[$name] ?? [];
|
||||
$dev = _var($disk,'device');
|
||||
$disk['id'] = _var($disk,'id');
|
||||
$events = explode('|',$disk['smEvents'] ?? $var['smEvents'] ?? $numbers);
|
||||
$mode = ['Disabled','Hourly','Daily','Weekly','Monthly'];
|
||||
$days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
|
||||
@@ -43,7 +43,9 @@ $tag = _var($disk,'name');
|
||||
$end = count($sheets)-1;
|
||||
$prev = $i>0 ? $sheets[$i-1] : $sheets[$end];
|
||||
$next = $i<$end ? $sheets[$i+1] : $sheets[0];
|
||||
$text = isPool($name) ? _('This will ERASE content of ALL devices in the pool') : _('This will ERASE ALL device content');
|
||||
$textErase = isPool($name) ? _('This will ERASE content of ALL devices in the pool') : _('This will ERASE ALL device content');
|
||||
$textDelete = _('This will unassign all devices from the pool but will NOT modify any device contents');
|
||||
$fsTypeImmutable = !(_var($var,'fsState')=='Stopped' && (empty(_var($disk,'uuid')) || _var($disk,'slots',1)==1));
|
||||
|
||||
function disabled_if($condition) {
|
||||
if ($condition !== false) echo ' disabled';
|
||||
@@ -114,12 +116,12 @@ function isPool($name) {
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/styles/style-$theme.css")?>">
|
||||
|
||||
<script>
|
||||
<?if (empty($disk)):?>
|
||||
done();
|
||||
<?endif;?>
|
||||
|
||||
String.prototype.celsius = function(){return Math.round((parseInt(this)-32)*5/9).toString();}
|
||||
|
||||
if ($.cookie('deletepool')) {
|
||||
$.removeCookie('deletepool');
|
||||
done();
|
||||
}
|
||||
function setFloor() {
|
||||
if ($('#shareFloor').length==0) return;
|
||||
const fsSize = {<?=fsSize()?>};
|
||||
@@ -198,103 +200,169 @@ function prepareZFS(form) {
|
||||
}
|
||||
<?endif;?>
|
||||
|
||||
function selectDiskFsWidth() {
|
||||
var num_slots = <?=_var($disk,'slots',0)?>;
|
||||
var selected_width = <?=_var($disk,'fsWidth',0)?>;
|
||||
$('#diskFsWidthZFS').empty();
|
||||
if (num_slots == 0) {
|
||||
$('#diskFsWidthZFS').prop('disabled',true).append($('<option>', {
|
||||
value: 0,
|
||||
text: "<?=_('no devices')?>"
|
||||
}));
|
||||
} else if ($('#diskFsProfileZFS').val() == '') {
|
||||
var label = (num_slots == 1) ? "device" : "devices";
|
||||
$('#diskFsWidthZFS').append($('<option>', {
|
||||
function selectDiskFsWidth(slots) {
|
||||
$('#diskFsWidth').empty();
|
||||
$('#diskFsWidth').append($('<option>', {value: slots, text:''}));
|
||||
$('#diskFsWidth').val(slots);
|
||||
$('#diskFsProfile').off('change');
|
||||
}
|
||||
function selectDiskFsProfileAuto() {
|
||||
$('#diskFsProfile').empty();
|
||||
$('#diskFsProfile').append($('<option>', {value: '', text:''}));
|
||||
$('#diskFsProfile').val('');
|
||||
selectDiskFsWidth('');
|
||||
}
|
||||
function selectDiskFsProfileBTRFS(slots,set_default) {
|
||||
$('#diskFsProfile').empty();
|
||||
$('#diskFsProfile').append($('<option>', {value: 'single', text:_('single')}));
|
||||
if (slots >= 2) $('#diskFsProfile').append($('<option>', {value: 'raid0', text:_('raid0')}));
|
||||
if (slots >= 2) $('#diskFsProfile').append($('<option>', {value: 'raid1', text:_('raid1')}));
|
||||
if (slots >= 3) $('#diskFsProfile').append($('<option>', {value: 'raid1c3', text:_('raid1c3')}));
|
||||
if (slots >= 4) $('#diskFsProfile').append($('<option>', {value: 'raid1c4', text:_('raid1c4')}));
|
||||
if (slots >= 4) $('#diskFsProfile').append($('<option>', {value: 'raid10', text:_('raid10')}));
|
||||
if (slots >= 3) $('#diskFsProfile').append($('<option>', {value: 'raid5', text:_('raid5')}));
|
||||
if (slots >= 4) $('#diskFsProfile').append($('<option>', {value: 'raid6', text:_('raid6')}));
|
||||
if (set_default) {
|
||||
if (slots == 1) $('#diskFsProfile').val('');
|
||||
if (slots > 1) $('#diskFsProfile').val('raid1');
|
||||
} else {
|
||||
$('#diskFsProfile').val("<?=_var($disk,'fsProfile')?>");
|
||||
}
|
||||
selectDiskFsWidth(slots);
|
||||
}
|
||||
function selectDiskFsWidthZFS(slots) {
|
||||
$('#diskFsWidth').empty();
|
||||
if ($('#diskFsProfile').val() == '') {
|
||||
var label = (slots == 1) ? "device" : "devices";
|
||||
$('#diskFsWidth').append($('<option>', {
|
||||
value: 1,
|
||||
text: _(sprintf('%s '+label,num_slots))
|
||||
text: _(sprintf('%s '+label,slots))
|
||||
}));
|
||||
} else if ($('#diskFsProfileZFS').val() == 'mirror') {
|
||||
} else if ($('#diskFsProfile').val() == 'mirror') {
|
||||
var width;
|
||||
for (width=2; width<=Math.min(num_slots,4); width++) {
|
||||
if ((num_slots % width) == 0) {
|
||||
var groups = num_slots / width;
|
||||
for (width=2; width<=Math.min(slots,4); width++) {
|
||||
if ((slots % width) == 0) {
|
||||
var groups = slots / width;
|
||||
var label = (groups == 1) ? "group" : "groups";
|
||||
$('#diskFsWidthZFS').append($('<option>', {
|
||||
$('#diskFsWidth').append($('<option>', {
|
||||
value: width,
|
||||
text: _(sprintf('%s '+label+' of %s devices',groups,width)),
|
||||
selected: (width == selected_width)
|
||||
}));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var width, min_width;
|
||||
if ($('#diskFsProfileZFS').val() == 'raidz1') min_width = 3;
|
||||
else if ($('#diskFsProfileZFS').val() == 'raidz2') min_width = 3;
|
||||
else if ($('#diskFsProfileZFS').val() == 'raidz3') min_width = 4;
|
||||
for (width=min_width; width<=num_slots; width++) {
|
||||
if ((num_slots % width) == 0) {
|
||||
var groups = num_slots / width;
|
||||
if ($('#diskFsProfile').val() == 'raidz1') min_width = 3;
|
||||
else if ($('#diskFsProfile').val() == 'raidz2') min_width = 3;
|
||||
else if ($('#diskFsProfile').val() == 'raidz3') min_width = 4;
|
||||
for (width=min_width; width<=slots; width++) {
|
||||
if ((slots % width) == 0) {
|
||||
var groups = slots / width;
|
||||
var label = (groups == 1) ? "group" : "groups";
|
||||
$('#diskFsWidthZFS').append($('<option>', {
|
||||
$('#diskFsWidth').append($('<option>', {
|
||||
value: width,
|
||||
text: _(sprintf('%s '+label+' of %s devices',groups,width)),
|
||||
selected: (width == selected_width)
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function selectDiskFsProfile(init) {
|
||||
var num_slots = <?=_var($disk,'slots',0)?>;
|
||||
var t = init ? null : 'slow';
|
||||
if (($('#diskFsType').val()||'').indexOf('auto') != -1) {
|
||||
$('#diskFsProfileBTRFS').prop('disabled',true).hide();
|
||||
$('#diskFsProfileZFS').prop('disabled',true).hide();
|
||||
$('#diskFsWidthZFS').prop('disabled',true).hide();;
|
||||
$('#compression').hide(t);
|
||||
$('#autotrim').hide(t);
|
||||
} else if (($('#diskFsType').val()||'').indexOf('btrfs') != -1) {
|
||||
if (!init) $('#diskFsProfileBTRFS').prop('disabled',false);
|
||||
$('#diskFsProfileBTRFS').show();
|
||||
$('#diskFsProfileZFS').prop('disabled',true).hide();
|
||||
$('#diskFsWidthZFS').prop('disabled',true).hide();
|
||||
$('#compression').show(t);
|
||||
<?if (diskType('Cache')):?>
|
||||
$('#autotrim').show(t);
|
||||
if (!init) {
|
||||
if (num_slots == 1) $('#diskFsProfileBTRFS').val('');
|
||||
if (num_slots > 1) $('#diskFsProfileBTRFS').val('raid1');
|
||||
function selectDiskFsProfileZFS(slots,set_default,subpool) {
|
||||
$('#diskFsProfile').empty();
|
||||
if (slots == 1) $('#diskFsProfile').append($('<option>', {value: '', text: _('single')}));
|
||||
if (slots >= 2) $('#diskFsProfile').append($('<option>', {value: '', text: _('stripe')}));
|
||||
if (subpool != 'cache' && subpool != 'spare') {
|
||||
if (slots%2 == 0 || slots%3 == 0 || slots%4 == 0) $('#diskFsProfile').append($('<option>', {value: 'mirror', text: _('mirror')}));
|
||||
if (subpool == '') {
|
||||
if (slots >= 3 && subpool == '') $('#diskFsProfile').append($('<option>', {value: 'raidz1', text: _('raidz1')}));
|
||||
if (slots >= 3 && subpool == '') $('#diskFsProfile').append($('<option>', {value: 'raidz2', text: _('raidz2')}));
|
||||
if (slots >= 4 && subpool == '') $('#diskFsProfile').append($('<option>', {value: 'raidz3', text: _('raidz3')}));
|
||||
}
|
||||
<?endif;?>
|
||||
} else if (($('#diskFsType').val()||'').indexOf('zfs') != -1) {
|
||||
$('#diskFsProfileBTRFS').prop('disabled',true).hide();
|
||||
if (!init) {
|
||||
$('#diskFsProfileZFS').prop('disabled',false);
|
||||
$('#diskFsWidthZFS').prop('disabled',false);
|
||||
}
|
||||
$('#diskFsProfileZFS').show();
|
||||
$('#diskFsWidthZFS').show();
|
||||
if (!init) {
|
||||
if (num_slots == 1) $('#diskFsProfileZFS').val('');
|
||||
if (num_slots == 2) $('#diskFsProfileZFS').val('mirror');
|
||||
if (num_slots > 2) $('#diskFsProfileZFS').val('raidz1');
|
||||
}
|
||||
selectDiskFsWidth();
|
||||
$('#compression').show(t);
|
||||
<?if (diskType('Cache')):?>
|
||||
$('#autotrim').show(t);
|
||||
} else if (($('#diskFsType').val()||'').indexOf('xfs') != -1) {
|
||||
$('#autotrim').show(t);
|
||||
<?endif;?>
|
||||
}
|
||||
if (set_default) {
|
||||
if (slots == 1) $('#diskFsProfile').val('');
|
||||
if (slots == 2) $('#diskFsProfile').val('mirror');
|
||||
if (slots > 2) $('#diskFsProfile').val('raidz1');
|
||||
selectDiskFsWidthZFS(slots);
|
||||
$('#diskFsWidth').val(slots);
|
||||
} else {
|
||||
$('#diskFsProfile').val("<?=_var($disk,'fsProfile')?>");
|
||||
selectDiskFsWidthZFS(slots);
|
||||
$('#diskFsWidth').val(<?=_var($disk,'fsWidth')?>);
|
||||
}
|
||||
$('#diskFsProfile').on('change', function() {
|
||||
selectDiskFsWidthZFS(slots);
|
||||
});
|
||||
}
|
||||
function changeFsType() {
|
||||
var fstype = ($('#diskFsType').val()||'').replace('luks:','');
|
||||
if (['btrfs', 'zfs'].includes(fstype)) $('#compression').show('slow'); else $('#compression').hide('slow');
|
||||
<?if (diskType('Cache')):?>
|
||||
if (['xfs', 'btrfs', 'zfs'].includes(fstype)) $('#autotrim').show('slow'); else $('#autotrim').hide('slow');
|
||||
function selectDiskFsProfileXFS() {
|
||||
$('#diskFsProfile').empty();
|
||||
$('#diskFsProfile').append($('<option>', {value: '', text:''}));
|
||||
$('#diskFsProfile').val('');
|
||||
selectDiskFsWidth(1);
|
||||
}
|
||||
function selectDiskFsProfile(init) {
|
||||
var t = init ? null : 'slow';
|
||||
|
||||
/* for array disks, 'slots', 'fsWidth', and 'fsGroups' is not defined so assume value 1 for all three */
|
||||
<?if ($fsTypeImmutable):?>
|
||||
var slots = <?=_var($disk,'fsWidth',1) * _var($disk,'fsGroups',1)?>;
|
||||
<?else:?>
|
||||
var slots = <?=_var($disk,'slots',1)?>;
|
||||
<?endif;?>
|
||||
|
||||
var subpool = "<?=$subpool_name?>";
|
||||
var fsType;
|
||||
var set_default;
|
||||
|
||||
if (subpool == '') {
|
||||
fsType = init ? "<?=_var($disk,'fsType','')?>" : $('#diskFsType').val();
|
||||
set_default = fsType != "<?=_var($disk,'fsType','')?>";
|
||||
} else {
|
||||
fsType = 'zfs';
|
||||
set_default = false;
|
||||
}
|
||||
|
||||
if (slots == 1 || fsType == 'auto') {
|
||||
$('#profile').hide(t);
|
||||
} else {
|
||||
$('#profile').show(t);
|
||||
if (fsType.indexOf('zfs') != -1) {
|
||||
$('#diskFsWidth').show();
|
||||
} else {
|
||||
$('#diskFsWidth').hide()
|
||||
}
|
||||
}
|
||||
|
||||
if (fsType == 'auto') {
|
||||
selectDiskFsProfileAuto();
|
||||
} else if (fsType.indexOf('btrfs') != -1) {
|
||||
selectDiskFsProfileBTRFS(slots,set_default);
|
||||
} else if (fsType.indexOf('zfs') != -1) {
|
||||
selectDiskFsProfileZFS(slots,set_default,subpool);
|
||||
} else if (fsType.indexOf('xfs') != -1) {
|
||||
selectDiskFsProfileXFS();
|
||||
}
|
||||
|
||||
if (subpool != '' || fsType == 'auto' || fsType.indexOf('xfs') != -1) {
|
||||
$('#compression').hide(t);
|
||||
$('#diskCompression').prop('disabled',true);
|
||||
} else {
|
||||
$('#compression').show(t);
|
||||
$('#diskCompression').prop('disabled',false);
|
||||
}
|
||||
|
||||
<?if (diskType('Data') || isSubpool($name)):?>
|
||||
$('#autotrim').hide(t);
|
||||
$('#diskAutotrim').prop('disabled',true);
|
||||
<?else:?>
|
||||
if (fsType == 'auto') {
|
||||
$('#autotrim').hide(t);
|
||||
$('#diskAutotrim').prop('disabled',true);
|
||||
} else {
|
||||
$('#autotrim').show(t);
|
||||
$('#diskAutotrim').prop('disabled',false);
|
||||
}
|
||||
<?endif;?>
|
||||
if (fstype=='reiserfs') $('#reiserfs').show(); else $('#reiserfs').hide();
|
||||
}
|
||||
function prepareDeviceInfo(form) {
|
||||
var events = [];
|
||||
@@ -529,14 +597,10 @@ function renamePoolPopup() {
|
||||
});
|
||||
dialogStyle();
|
||||
}
|
||||
function deletePool() {
|
||||
$.cookie('deletepool','deletepool');
|
||||
document.deletepool.submit();
|
||||
}
|
||||
function eraseDisk(name) {
|
||||
swal({
|
||||
title:"_(Erase Device Content)_?",
|
||||
text:"<?=$text?><p style='font-weight:bold;color:red;margin:8px 0'>_(Existing content is permanently lost)_</p>",
|
||||
text:"<?=$textErase?><p style='font-weight:bold;color:red;margin:8px 0'>_(Existing content is permanently lost)_</p>",
|
||||
html:true,
|
||||
type:'input',
|
||||
inputPlaceholder:"<?=sprintf(_('To confirm your action type: %s'),$name)?>",
|
||||
@@ -550,8 +614,37 @@ function eraseDisk(name) {
|
||||
swal.close();
|
||||
$('#doneButton').prop('disabled',true);
|
||||
$('#eraseButton').prop('disabled',true);
|
||||
$('#deleteButton').prop('disabled',true);
|
||||
$('div.spinner.fixed').show();
|
||||
$.get("/update.htm",{cmdWipefs:name,csrf_token:"<?=_var($var,'csrf_token')?>"},function(){
|
||||
$.post("/update.htm",{cmdWipefs:name},function(){
|
||||
$('div.spinner.fixed').hide();
|
||||
refresh();
|
||||
});
|
||||
} else {
|
||||
if (confirm.length) swal({title:"_(Incorrect confirmation)_",text:"_(Please try again)_!",type:'error',html:true,confirmButtonText:"_(Ok)_"});
|
||||
}
|
||||
});
|
||||
}
|
||||
function deletePool(name) {
|
||||
swal({
|
||||
title:"_(Delete pool)_?",
|
||||
text:"<?=$textDelete?>",
|
||||
html:true,
|
||||
type:'input',
|
||||
inputPlaceholder:"<?=sprintf(_('To confirm your action type: %s'),$name)?>",
|
||||
showCancelButton:true,
|
||||
closeOnConfirm:false,
|
||||
confirmButtonText:"_(Proceed)_",
|
||||
cancelButtonText:"_(Cancel)_"
|
||||
},
|
||||
function(confirm){
|
||||
if (confirm == "<?=$name?>") {
|
||||
swal.close();
|
||||
$('#doneButton').prop('disabled',true);
|
||||
$('#eraseButton').prop('disabled',true);
|
||||
$('#deleteButton').prop('disabled',true);
|
||||
$('div.spinner.fixed').show();
|
||||
$.post("/update.htm",{changeSlots:"apply",poolName:name,poolSlots:0},function(){
|
||||
$('div.spinner.fixed').hide();
|
||||
refresh();
|
||||
});
|
||||
@@ -601,7 +694,7 @@ _(Spinup group(s))_:
|
||||
: <input type="text" name="diskSpinupGroup.<?=_var($disk,'idx',0)?>" maxlength="256" value="<?=_var($disk,'spinupGroup')?>">
|
||||
|
||||
<?endif;?>
|
||||
<?if (diskType('Data') || isPool($tag)):?>
|
||||
<?if (diskType('Data','Parity') || isPool($tag)):?>
|
||||
_(Spin down delay)_:
|
||||
: <select name="diskSpindownDelay.<?=_var($disk,'idx',0)?>">
|
||||
<?=mk_option(_var($disk,'spindownDelay'), "-1", _('Use default'))?>
|
||||
@@ -620,87 +713,36 @@ _(Spin down delay)_:
|
||||
<?=mk_option(_var($disk,'spindownDelay'), "9", "9 "._('hours'))?>
|
||||
</select><span id="smart_selftest" class='orange-text'></span>
|
||||
|
||||
<?endif;?>
|
||||
<?if (diskType('Data') || (isPool($tag) && !isSubpool($tag))):?>
|
||||
_(File system status)_:
|
||||
: <?=_(_var($disk,'fsStatus'))?>
|
||||
|
||||
<?$fsTypeImmutable = (_var($var,'fsState')=="Stopped" && !empty(_var($disk,'uuid'))) || (_var($var,'fsState')=="Started" && _var($disk,'fsType')!='auto')?>
|
||||
<?if (diskType('Data') || (!isSubpool($name) && _var($disk,'slots',0)==1)):?>
|
||||
_(File system type)_:
|
||||
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx',0)?>" onchange="changeFsType()" <?=disabled_if($fsTypeImmutable)?>>
|
||||
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx')?>" onchange="selectDiskFsProfile(false)" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?=mk_option(_var($disk,'fsType'), "auto", _('auto'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "xfs", _('xfs'))?>
|
||||
<?if (_var($disk,'slots',1) == 1) echo mk_option(_var($disk,'fsType'), "xfs", _('xfs'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "zfs", _('zfs'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "btrfs", _('btrfs'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "reiserfs", _('reiserfs'), "disabled")?>
|
||||
<?=mk_option(_var($disk,'fsType'), "luks:xfs", _('xfs')." - "._('encrypted'))?>
|
||||
<?if (_var($disk,'slots',1) == 1) echo mk_option(_var($disk,'fsType'), "reiserfs", _('reiserfs'), "disabled")?>
|
||||
<?if (_var($disk,'slots',1) == 1) echo mk_option(_var($disk,'fsType'), "luks:xfs", _('xfs')." - "._('encrypted'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "luks:zfs", _('zfs')." - "._('encrypted'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "luks:btrfs", _('btrfs')." - "._('encrypted'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "luks:reiserfs", _('reiserfs')." - "._('encrypted'), "disabled")?>
|
||||
<?if (_var($disk,'slots',1) == 1) echo mk_option(_var($disk,'fsType'), "luks:reiserfs", _('reiserfs')." - "._('encrypted'), "disabled")?>
|
||||
</select><span id="reiserfs" class="warning"<?if (!fsType('reiserfs')):?> style="display:none"<?endif;?>><i class="fa fa-warning"></i> _(ReiserFS is deprecated, please use another file system)_!</span>
|
||||
|
||||
:info_file_system_help:
|
||||
|
||||
<?elseif (!isSubpool($name) && _var($disk,'slots',0)>1):?>
|
||||
_(File system type)_:
|
||||
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx',0)?>" onchange="selectDiskFsProfile(false)" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?=mk_option(_var($disk,'fsType'), "auto", _('auto'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "zfs", _('zfs'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "btrfs", _('btrfs'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "luks:zfs", _('zfs')." - "._('encrypted'))?>
|
||||
<?=mk_option(_var($disk,'fsType'), "luks:btrfs", _('btrfs')." - "._('encrypted'))?>
|
||||
</select>
|
||||
<select id="diskFsProfileBTRFS" name="diskFsProfile.<?=_var($disk,'idx',0)?>" style="display:none" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?=mk_option(_var($disk,'fsProfile'),"single", _('single'))?>
|
||||
<?if (_var($disk,'slots',0)>=2) echo mk_option(_var($disk,'fsProfile'),"raid0", _('raid0'))?>
|
||||
<?if (_var($disk,'slots',0)>=2) echo mk_option(_var($disk,'fsProfile'),"raid1", _('raid1'))?>
|
||||
<?if (_var($disk,'slots',0)>=3) echo mk_option(_var($disk,'fsProfile'),"raid1c3", _('raid1c3'))?>
|
||||
<?if (_var($disk,'slots',0)>=4) echo mk_option(_var($disk,'fsProfile'),"raid1c4", _('raid1c4'))?>
|
||||
<?if (_var($disk,'slots',0)>=4) echo mk_option(_var($disk,'fsProfile'),"raid10", _('raid10'))?>
|
||||
<?if (_var($disk,'slots',0)>=3) echo mk_option(_var($disk,'fsProfile'),"raid5", _('raid5'))?>
|
||||
<?if (_var($disk,'slots',0)>=4) echo mk_option(_var($disk,'fsProfile'),"raid6", _('raid6'))?>
|
||||
</select>
|
||||
<select id="diskFsProfileZFS" name="diskFsProfile.<?=_var($disk,'idx',0)?>" style="display:none" onchange="selectDiskFsWidth()" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?if (_var($disk,'slots',0)==1) echo mk_option(_var($disk,'fsProfile'),"", _('single'))?>
|
||||
<?if (_var($disk,'slots',0)>=2) echo mk_option(_var($disk,'fsProfile'),"", _('stripe'))?>
|
||||
<?if ((_var($disk,'slots',0)%2)==0 || (_var($disk,'slots',0)%3)==0 || (_var($disk,'slots',0)%4)==0) echo mk_option(_var($disk,'fsProfile'),"mirror", _('mirror'))?>
|
||||
<?if (_var($disk,'slots',0)>=3) echo mk_option(_var($disk,'fsProfile'),"raidz1", _('raidz'))?>
|
||||
<?if (_var($disk,'slots',0)>=3) echo mk_option(_var($disk,'fsProfile'),"raidz2", _('raidz2'))?>
|
||||
<?if (_var($disk,'slots',0)>=4) echo mk_option(_var($disk,'fsProfile'),"raidz3", _('raidz3'))?>
|
||||
</select>
|
||||
<select id="diskFsWidthZFS" name="diskFsWidth.<?=_var($disk,'idx',0)?>" style="display:none" <?=disabled_if($fsTypeImmutable)?>>
|
||||
</select>
|
||||
<?elseif (isSubpool($name)=="special" || isSubpool($name)=="logs" || isSubpool($name)=="dedup"):?>
|
||||
_(File system type)_:
|
||||
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx',0)?>" onchange="selectDiskFsProfile(false)" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?=mk_option(_var($disk,'fsType'), "zfs", _('zfs'))?>
|
||||
</select>
|
||||
<select id="diskFsProfileZFS" name="diskFsProfile.<?=_var($disk,'idx',0)?>" onchange="selectDiskFsWidth()" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?if (_var($disk,'slots',0)==1) echo mk_option(_var($disk,'fsProfile'),"", _('single'))?>
|
||||
<?if (_var($disk,'slots',0)>=2) echo mk_option(_var($disk,'fsProfile'),"", _('stripe'))?>
|
||||
<?if ((_var($disk,'slots',0)%2)==0 || (_var($disk,'slots',0)%3)==0 || (_var($disk,'slots',0)%4)==0) echo mk_option(_var($disk,'fsProfile'),"mirror", _('mirror'))?>
|
||||
</select>
|
||||
<select id="diskFsWidthZFS" name="diskFsWidth.<?=_var($disk,'idx',0)?>" style="display:none" <?=disabled_if($fsTypeImmutable)?>>
|
||||
</select>
|
||||
<?elseif (isSubpool($name)=="cache"):?>
|
||||
_(File system type)_:
|
||||
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx',0)?>" onchange="selectDiskFsProfile(false)" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?=mk_option(_var($disk,'fsType'), "zfs", _('zfs'))?>
|
||||
</select>
|
||||
<select id="diskFsProfileZFS" name="diskFsProfile.<?=_var($disk,'idx',0)?>" onchange="selectDiskFsWidth()" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?if (_var($disk,'slots',0)==1) echo mk_option(_var($disk,'fsProfile'),"", _('single'))?>
|
||||
<?if (_var($disk,'slots',0)>=2) echo mk_option(_var($disk,'fsProfile'),"", _('stripe'))?>
|
||||
</select>
|
||||
<select id="diskFsWidthZFS" name="diskFsWidth.<?=_var($disk,'idx',0)?>" style="display:none" <?=disabled_if($fsTypeImmutable)?>>
|
||||
</select>
|
||||
<?elseif (isSubpool($name)=="spares"):?>
|
||||
_(File system type)_:
|
||||
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx',0)?>" onchange="selectDiskFsProfile(false)" <?=disabled_if($fsTypeImmutable)?>>
|
||||
<?=mk_option(_var($disk,'fsType'), "zfs", _('zfs'))?>
|
||||
</select>
|
||||
<?endif;?>
|
||||
|
||||
<?if (isSubpool($name)===false):?>
|
||||
<div markdown="1" id="compression" style="display:none">
|
||||
<?if (diskType('Data') || isPool($tag)):?>
|
||||
<div markdown="1" id="profile">
|
||||
_(Allocation profile)_:
|
||||
: <select id="diskFsProfile" name="diskFsProfile.<?=_var($disk,'idx')?>" <?=disabled_if($fsTypeImmutable)?>>
|
||||
</select>
|
||||
<select id="diskFsWidth" name="diskFsWidth.<?=_var($disk,'idx')?>" <?=disabled_if($fsTypeImmutable)?>>
|
||||
</select>
|
||||
|
||||
:info_profile_help:
|
||||
</div>
|
||||
<div markdown="1" id="compression">
|
||||
_(Compression)_:
|
||||
: <select id="diskCompression" name="diskCompression.<?=_var($disk,'idx',0)?>" <?=disabled_if(_var($disk,'fsStatus')=='Mounted')?>>
|
||||
<?=mk_option(_var($disk,'compression'), "off", _('Off'))?>
|
||||
@@ -709,8 +751,7 @@ _(Compression)_:
|
||||
|
||||
:info_compression_help:
|
||||
</div>
|
||||
|
||||
<div markdown="1" id="autotrim" style="display:none">
|
||||
<div markdown="1" id="autotrim">
|
||||
_(Autotrim)_:
|
||||
: <select id="diskAutotrim" name="diskAutotrim.<?=_var($disk,'idx',0)?>" <?=disabled_if(_var($disk,'fsStatus')=='Mounted')?>>
|
||||
<?=mk_option(_var($disk,'autotrim'), "on", _('On'))?>
|
||||
@@ -719,7 +760,8 @@ _(Autotrim)_:
|
||||
|
||||
:info_autotrim_help:
|
||||
</div>
|
||||
<?if (isPool($name)):?>
|
||||
<?endif;?>
|
||||
<?if (isPool($tag) && !isSubpool($tag)):?>
|
||||
_(Enable user share assignment)_:
|
||||
: <select id="shareEnabled" name="diskShareEnabled.<?=_var($disk,'idx',0)?>" onchange="freeSpace(this.value)" <?=disabled_if(_var($var,'fsState')!="Stopped")?>>
|
||||
<?=mk_option(_var($disk,'shareEnabled'), "yes", _('Yes'))?>
|
||||
@@ -735,6 +777,7 @@ _(Minimum free space)_:
|
||||
:info_free_space_help:
|
||||
|
||||
<?endif;?>
|
||||
<?if (diskType('Data') || (isPool($tag) && !isSubpool($tag))):?>
|
||||
_(Warning disk utilization threshold)_ (%):
|
||||
: <input type="number" min="0" max="100" name="diskWarning.<?=_var($disk,'idx',0)?>" autocomplete="off" spellcheck="false" class="narrow" value="<?=_var($disk,'warning')?>" placeholder="<?=_var($display,'warning')?>">
|
||||
|
||||
@@ -745,7 +788,6 @@ _(Critical disk utilization threshold)_ (%):
|
||||
|
||||
:info_critical_utilization_help:
|
||||
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
|
||||
: <input type="submit" name="changeDisk" value="_(Apply)_" disabled><input type="button" id="doneButton" value="_(Done)_" onclick="done()">
|
||||
@@ -757,11 +799,11 @@ _(Critical disk utilization threshold)_ (%):
|
||||
<?endif;?>
|
||||
<?if (isPool($name) && strpos($name,$_tilde_)===false):?>
|
||||
<?if (_var($var,'fsState')=="Stopped" || (_var($var,'fsState')=="Started" && _var($var,'startMode')!="Normal")): $erasable=true; endif;?>
|
||||
<input type="button" id="eraseButton" value="_(Erase)_" onclick="eraseDisk('<?=$name?>')"<?=$erasable?'':' disabled'?>>
|
||||
<input type="button" id="eraseButton" value="_(Erase Pool)_" onclick="eraseDisk('<?=$name?>')"<?=$erasable?'':' disabled'?>>
|
||||
<?endif;?>
|
||||
<?if (_var($var,'fsState')=="Stopped" && isPool($name)):?>
|
||||
<?$empty = _var($disk,'devices',0)==0?>
|
||||
<input type="button" value="_(Delete Pool)_" onclick="deletePool()"<?=$empty?'':' disabled'?>><?if (!$empty):?>_(Unassign **ALL** devices to delete this pool)_<?endif;?>
|
||||
<?if (isPool($name)):?>
|
||||
<?$deleteable=_var($var,'fsState')=="Stopped" && !isSubpool($name)?>
|
||||
<input type="button" id="deleteButton" value="_(Delete Pool)_" onclick="deletePool('<?=$name?>')"<?=$deleteable?'':' disabled'?>>
|
||||
<?endif;?>
|
||||
</form>
|
||||
|
||||
@@ -1361,17 +1403,11 @@ _(SMART attribute notifications)_:
|
||||
<input type="hidden" name="changeSlots" value="apply">
|
||||
_(Name)_:
|
||||
: <input type="text" name="poolName" maxlength="40" value="<?=$name?>">
|
||||
<p>_(Caution)_: _(Renaming the pool will change the share storage allocations)_. _(After renaming the pool, check that your shares are assigned to the proper primary and secondary storage locations)_.</p>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<form name="deletepool" method="POST" action="/update.htm" target="progressFrame" style="display:none">
|
||||
<input type="hidden" name="changeSlots" value="apply">
|
||||
<input type="hidden" name="poolName" value="<?=$name?>">
|
||||
<input type="hidden" name="poolSlots" value="0">
|
||||
<input type='hidden' name='csrf_token' value='<?=_var($var,'csrf_token')?>'>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
<?if (count($sheets)>1):?>
|
||||
|
@@ -88,7 +88,7 @@ _(Select Proxy)_:
|
||||
>
|
||||
> Outgoing connections from the webgui and some system processes will use the specified http proxy. Docker container installs and updates will use the proxy, but the container itself will not, neither will any VMs.
|
||||
>
|
||||
> For a more comprehensive solution you might consider setting up <u><a href='https://docs.unraid.net/unraid-os/manual/security/vpn/#configuring-vpn-tunneled-access-for-system/' target='_blank'>_(VPN tunnel access for System)_</a></u>.
|
||||
> For a more comprehensive solution you might consider setting up <u><a href='https://docs.unraid.net/go/configuring-vpn-tunneled-access-for-system/' target='_blank'>_(VPN tunnel access for System)_</a></u>.
|
||||
:end
|
||||
|
||||
<p><strong>_(Outgoing Proxy)_ 1</strong></p>
|
||||
|
@@ -28,7 +28,7 @@ if (!file_exists($log)) touch($log);
|
||||
:php_settings_plug:
|
||||
This utility is used for development purposes only and allows Plugin Authors to verify their PHP code by enabling different levels of PHP error reporting.
|
||||
|
||||
By default error logging is minimum and any errors are shown on screen. Changing the **Error reporting level** will capture the selected level of errors
|
||||
By default error logging is minimum and errors are not shown on screen. Changing the **Error reporting level** will capture the selected level of errors
|
||||
into a LOG file, which can be opened in a separate window to monitor in real-time the events when visiting various GUI pages or executing background
|
||||
processes on the server.
|
||||
|
||||
@@ -46,12 +46,12 @@ under normal running conditions.
|
||||
<input type="hidden" name="log_errors" value="1">
|
||||
_(Error reporting level)_:
|
||||
: <select name="error_reporting" onchange="toggleScreen(this.selectedIndex)">
|
||||
<?=mk_option(_var($conf,'error_reporting'), "", "_(Default)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), "32767", "_(All Categories)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), "1", "_(Errors Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), "2", "_(Warnings Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), "8", "_(Notices Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), "8192", "_(Deprecated Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), strval(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED), "_(Default)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), strval(E_ALL), "_(All Categories)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), strval(E_ERROR), "_(Errors Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), strval(E_WARNING), "_(Warnings Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), strval(E_NOTICE), "_(Notices Only)_");?>
|
||||
<?=mk_option(_var($conf,'error_reporting'), strval(E_DEPRECATED), "_(Deprecated Only)_");?>
|
||||
</select>
|
||||
|
||||
|
||||
@@ -147,11 +147,12 @@ function PHPinfo() {
|
||||
}
|
||||
function preset(form) {
|
||||
// reset to default settings
|
||||
// derived from default .ini file installed at boot
|
||||
if (form.error_reporting.selectedIndex==0) {
|
||||
form.error_log.value = "";
|
||||
form.display_startup_errors.value = "";
|
||||
form.display_errors.value = "";
|
||||
form.log_errors.value = "";
|
||||
form.error_log.value = "<?=$log?>";
|
||||
form.display_startup_errors.value = "0";
|
||||
form.display_errors.value = "0";
|
||||
form.log_errors.value = "1";
|
||||
}
|
||||
$.cookie('reload_php',1);
|
||||
}
|
||||
|
@@ -86,11 +86,11 @@ _(Enter route + gateway + metric)_:
|
||||
<input type="text" name="gateway" class="fixed" value="" list="device" placeholder="_(gateway name or address)_" required>
|
||||
<datalist id="device"><?foreach ($list as $port):?><?echo "<option value='$port'>"?><?endforeach;?></datalist>
|
||||
<input type="text" name="metric" min="1" max="9999" value="" class="trim" placeholder="1"><i class="fa fa-sort-numeric-asc"></i> *_(optional metric (lowest is preferred))_*
|
||||
|
||||
<input type="hidden" name="task" value="Add Route">
|
||||
:eth_routing_table_help:
|
||||
|
||||
|
||||
: <input type="submit" name="task" value="_(Add Route)_"><input type="button" value="_(Done)_" class="lock" onclick="done()">
|
||||
: <input type="submit" value="_(Add Route)_"><input type="button" value="_(Done)_" class="lock" onclick="done()">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -19,6 +19,8 @@ Cond="(($var['shareSMBEnabled']!='no') && (isset($name)?array_key_exists($name,$
|
||||
require_once "$docroot/webGui/include/InputSecurity.php";
|
||||
|
||||
$width = [123,300];
|
||||
/* Sort user array by keys in natural order */
|
||||
uksort($users, 'strnatcmp');
|
||||
?>
|
||||
:smb_security_help:
|
||||
|
||||
|
@@ -50,6 +50,12 @@ $pools = array_filter($pools, function($pool) {
|
||||
return !isSubpool($pool);
|
||||
});
|
||||
|
||||
/* Check for cachePool2 only which is a situation where the primary device is the cachePool2 device. */
|
||||
if ((! $share['cachePool']) && ($share['cachePool2'])) {
|
||||
$share['cachePool'] = $share['cachePool2'];
|
||||
$share['cachePool2'] = "";
|
||||
}
|
||||
|
||||
/* Check for non existent pool device. */
|
||||
if ($share['cachePool'] && !in_array($share['cachePool'], $pools)) {
|
||||
$poolDefined = false;
|
||||
@@ -61,16 +67,16 @@ if ($share['cachePool'] && !in_array($share['cachePool'], $pools)) {
|
||||
|
||||
/* Check for pool 2 (or array) being defined. */
|
||||
if ((($share['useCache'] == "yes") || ($share['useCache'] == "prefer")) && ($poolsOnly) && (!$share['cachePool2'])) {
|
||||
$poolDefined2 = false;
|
||||
$share['useCache'] = "yes";
|
||||
$poolDefined2 = true;
|
||||
$share['useCache'] = "only";
|
||||
} else if ($share['cachePool2']) {
|
||||
$poolDefined2 = in_array($share['cachePool2'], $pools);
|
||||
} else {
|
||||
$poolDefined2 = true;
|
||||
}
|
||||
|
||||
$cachePoolCapitalized = ucfirst($share['cachePool']);
|
||||
$cachePoolCapitalized2 = $share['cachePool2'] ? ucfirst($share['cachePool2']) : _("Array");
|
||||
$cachePoolCapitalized = compress(my_disk($share['cachePool'],$display['raw']));
|
||||
$cachePoolCapitalized2 = $share['cachePool2'] ? compress(my_disk($share['cachePool2'],$display['raw'])) : _("Array");
|
||||
|
||||
function globalInclude($name) {
|
||||
global $var;
|
||||
|
@@ -18,7 +18,7 @@ Cond="_var($var,'fsState')!='Stopped' && _var($var,'shareUser')=='e'"
|
||||
<?
|
||||
/* Function to filter out unwanted disks, check if any valid disks exist, and ignore disks with a blank device. */
|
||||
function checkDisks($disks) {
|
||||
global $pools, $poolsOnly;
|
||||
global $pools;
|
||||
|
||||
$rc = false;
|
||||
|
||||
@@ -35,9 +35,6 @@ function checkDisks($disks) {
|
||||
return $rc;
|
||||
}
|
||||
|
||||
/* If the configuration is pools only, then no array disks are available. */
|
||||
$poolsOnly = ((int)$var['SYS_ARRAY_SLOTS'] <= 2) ? true : false;
|
||||
|
||||
/* Are there any array disks? */
|
||||
$disks = parse_ini_file('state/disks.ini',true) ?? [];
|
||||
$nodisks = checkDisks($disks) ? "" : "disabled";
|
||||
|
@@ -103,7 +103,7 @@ function checkKey(form) {
|
||||
// check syntax of ssh keys
|
||||
var rows = form.text.value.split('\n');
|
||||
for (var i=0,row; row=rows[i]; i++) {
|
||||
if (row.search(/^(ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t|ssh-rsa AAAAB3NzaC1yc2)[0-9A-Za-z+/]+[=]{0,3}(\s.*)?$/)==-1) {
|
||||
if (row.search(/^(ssh-dss AAAAB3NzaC1kc3|ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT|sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb2|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t|ssh-rsa AAAAB3NzaC1yc2)[0-9A-Za-z+/]+[=]{0,3}(\s.*)?$/)==-1) {
|
||||
swal({title:"_(Invalid Key)_",text:"["+(i+1)+"] "+row.split(' ')[0]+": _(Syntax of the key is incorrect)_!",type:"error",html:true,confirmButtonText:"_(Ok)_"});
|
||||
return false;
|
||||
}
|
||||
|
@@ -1032,14 +1032,13 @@ tstate['wg0'] = "<?=$check_wg0 ? 'active' : 'passive'?>";
|
||||
|
||||
var statistics = new NchanSubscriber('/sub/wireguard',{subscriber:'websocket'});
|
||||
statistics.on('message', function(data) {
|
||||
var list = [];
|
||||
var rows = data.split('\0');
|
||||
var list = [], n = [];
|
||||
var x = 0; var vtun = '';
|
||||
// get all existing tunnels
|
||||
$('div[id^="block-wg"]').each(function(){list.push($(this).prop('id').split('-')[1]);});
|
||||
// update active tunnels
|
||||
for (var i=0,row; row=rows[i]; i++) {
|
||||
var info = row.split(';');
|
||||
var rows = JSON.parse(data);
|
||||
for (var i=0,info; info=rows[i]; i++) {
|
||||
if (info[0] != vtun) {
|
||||
vtun = info[0];
|
||||
// remove tunnel from inactive list
|
||||
@@ -1281,7 +1280,7 @@ _(Local tunnel address IPv6)_:
|
||||
</div>
|
||||
</div>
|
||||
_(Local endpoint)_:
|
||||
: <span class="input"><input type="text" id="endpoint-wg0" name="Endpoint:0" class="subnet" value="<?=$vpn_wg0?'':_var($wg0,'Endpoint:0')?>" onchange="toLC(this);quickValidate(this);" pattern="<?=$validText?>" title="_(IP address or FQDN)_" placeholder="<?=$vpn_wg0?'(_(not used)_)':preg_replace('/^(.+?\.)[0-9a-zA-Z]+(\.(my)?unraid.net)$/','$1<hash>$2',$public)?>">:
|
||||
: <span class="input"><input type="text" class="width:10%;" id="endpoint-wg0" name="Endpoint:0" class="subnet" value="<?=$vpn_wg0?'':_var($wg0,'Endpoint:0')?>" onchange="toLC(this);quickValidate(this);" pattern="<?=$validText?>" title="_(IP address or FQDN)_" placeholder="<?=$vpn_wg0?'(_(not used)_)':preg_replace('/^(.+?\.)[0-9a-zA-Z]+(\.(my)?unraid.net)$/','$1<hash>$2',$public)?>">:
|
||||
<input type="number" name="gui:ListenPort:0" class="port" min="1" max="65535" value="<?=$vpn_wg0?'':_var($wg0,'ListenPort:0')?>" onchange="if(quickValidate(this)) {portRemark($(document.wg0),'wg0',this.value)}" placeholder="<?=$vpn_wg0?'':_var($netport,'wg0')?>"></span>
|
||||
<span class="remark block" style="display:none">_(Remark)_: _(configure your router with port forwarding of port)_ **<span id="my-port-wg0"><?=_var($wg0,'ListenPort:0')?:_var($netport,'wg0')?></span>/_(UDP)_** _(to)_ **<?=$server?>:<?=_var($wg0,'ListenPort:0')?:_var($netport,'wg0')?>**</span><span class="upnp wg0 block"></span>
|
||||
<input type="hidden" name="ListenPort:0" value=""><dl id="endpoint4-wg0" style="display:none"></dl><dl id="endpoint6-wg0" style="display:none"></dl>
|
||||
@@ -1318,7 +1317,7 @@ _(Local tunnel firewall)_:
|
||||
:wg_local_tunnel_firewall_help:
|
||||
|
||||
_(MTU size)_:
|
||||
: <span class="input"><input type="number" name="MTU:0" class="trim" min="68" max="9198" value="<?=_var($wg0,'MTU:0')?>" onchange="quickValidate(this);" placeholder="(_(automatic)_)">_(bytes)_</span>
|
||||
: <span class="input"><input type="number" name="MTU:0" class="trim" min="68" max="9198" value="<?=_var($wg0,'MTU:0')?>" onchange="quickValidate(this);" placeholder="(_(auto)_)">_(bytes)_</span>
|
||||
|
||||
:wg_mtu_size_help:
|
||||
|
||||
@@ -1362,7 +1361,7 @@ _(Peer type of access)_:
|
||||
<?=mk_option(_var($wg0,"TYPE:$i"), "7", _("VPN tunneled access for system"),count($peer_wg0)==1?'':'disabled')?>
|
||||
<?=mk_option(_var($wg0,"TYPE:$i"), "8", _("VPN tunneled access for docker"),count($peer_wg0)==1?'':'disabled')?>
|
||||
</select></span>
|
||||
<span id="access-type-<?=$i?>" class="access-type"></span>
|
||||
<span id="access-type-<?=$i?>"</span>
|
||||
|
||||
<?if ($i==1):?>
|
||||
> ?>)
|
||||
@@ -1426,8 +1425,8 @@ _(Persistent keepalive)_:
|
||||
:wg_persistent_keepalive_help:
|
||||
|
||||
</div>
|
||||
<span class="pin">_(Data received)_: <span class="rx-wg0-<?=$i?>">0 B</span>_(Data sent)_: <span class="tx-wg0-<?=$i?>">0 B</span><br>_(Last handshake)_: <span class="hs-wg0-<?=$i?>">_(unknown)_</span></span>
|
||||
</div>
|
||||
<span class="pin">_(Data received)_: <span class="rx-wg0-<?=$i?>">0 B</span>_(Data sent)_: <span class="tx-wg0-<?=$i?>">0 B</span><br>_(Last handshake)_: <span class="hs-wg0-<?=$i?>">_(unknown)_</span></span>
|
||||
<?endforeach;?>
|
||||
|
||||
|
||||
|
@@ -224,7 +224,7 @@ _(Local tunnel address IPv6)_:
|
||||
</div>
|
||||
</div>
|
||||
_(Local endpoint)_:
|
||||
: <span class="input"><input type="text" id="endpoint-wgX" name="Endpoint:0" class="subnet" value="<?=$vpn_wgX?'':_var($wgX,'Endpoint:0')?>" onchange="toLC(this);quickValidate(this);" pattern="<?=$validText?>" title="_(IP address or FQDN)_" placeholder="<?=$vpn_wgX?'(_(not used)_)':preg_replace('/^(www\.).+(\.unraid.net)$/','$1<hash>$2',$public)?>">:
|
||||
: <span class="input"><input type="text" class="width:10%;" id="endpoint-wgX" name="Endpoint:0" class="subnet" value="<?=$vpn_wgX?'':_var($wgX,'Endpoint:0')?>" onchange="toLC(this);quickValidate(this);" pattern="<?=$validText?>" title="_(IP address or FQDN)_" placeholder="<?=$vpn_wgX?'(_(not used)_)':preg_replace('/^(www\.).+(\.unraid.net)$/','$1<hash>$2',$public)?>">:
|
||||
<input type="number" name="gui:ListenPort:0" class="port" min="1" max="65535" value="<?=$vpn_wgX?'':_var($wgX,'ListenPort:0')?>" onchange="if(quickValidate(this)) {portRemark($(document.wgX),'wgX',this.value)}" placeholder="<?=$vpn_wgX?'':_var($netport,'wgX')?>"></span>
|
||||
<span class="remark block" style="display:none">_(Remark)_: _(configure your router with port forwarding of port)_ **<span id="my-port-wgX"><?=_var($wgX,'ListenPort:0')?:_var($netport,'wgX')?></span>/_(UDP)_** _(to)_ **<?=$server?>:<?=_var($wgX,'ListenPort:0')?:_var($netport,'wgX')?>**</span><span class="upnp wgX block"></span>
|
||||
<input type="hidden" name="ListenPort:0" value=""><dl id="endpoint4-wgX" style="display:none"></dl><dl id="endpoint6-wgX" style="display:none"></dl>
|
||||
@@ -261,7 +261,7 @@ _(Local tunnel firewall)_:
|
||||
:wg_local_tunnel_firewall_help:
|
||||
|
||||
_(MTU size)_:
|
||||
: <span class="input"><input type="number" name="MTU:0" class="trim" min="68" max="9198" value="<?=_var($wgX,'MTU:0')?>" onchange="quickValidate(this);" placeholder="(_(automatic)_)">_(bytes)_</span>
|
||||
: <span class="input"><input type="number" name="MTU:0" class="trim" min="68" max="9198" value="<?=_var($wgX,'MTU:0')?>" onchange="quickValidate(this);" placeholder="(_(auto)_)">_(bytes)_</span>
|
||||
|
||||
:wg_mtu_size_help:
|
||||
|
||||
@@ -305,7 +305,7 @@ _(Peer type of access)_:
|
||||
<?=mk_option(_var($wgX,"TYPE:$i"), "7", _("VPN tunneled access for system"),count($peer_wgX)==1?'':'disabled')?>
|
||||
<?=mk_option(_var($wgX,"TYPE:$i"), "8", _("VPN tunneled access for docker"))?>
|
||||
</select></span>
|
||||
<span id="access-type-<?=$i?>" class="access-type"></span>
|
||||
<span id="access-type-<?=$i?>"></span>
|
||||
|
||||
<?if ($i==1):?>
|
||||
> ?>)
|
||||
@@ -369,8 +369,8 @@ _(Persistent keepalive)_:
|
||||
:wg_persistent_keepalive_help:
|
||||
|
||||
</div>
|
||||
<span class="pin">_(Data received)_: <span class="rx-wgX-<?=$i?>">0 B</span>_(Data sent)_: <span class="tx-wgX-<?=$i?>">0 B</span><br>_(Last handshake)_: <span class="hs-wgX-<?=$i?>">_(unknown)_</span></span>
|
||||
</div>
|
||||
<span class="pin">_(Data received)_: <span class="rx-wgX-<?=$i?>">0 B</span>_(Data sent)_: <span class="tx-wgX-<?=$i?>">0 B</span><br>_(Last handshake)_: <span class="hs-wgX-<?=$i?>">_(unknown)_</span></span>
|
||||
<?endforeach;?>
|
||||
|
||||
|
||||
|
@@ -37,26 +37,3 @@
|
||||
]]>
|
||||
</Script>
|
||||
</Agent>
|
||||
<Agent>
|
||||
<Name>Pushbullet</Name>
|
||||
<Variables>
|
||||
<Variable Help="The Access Token can be found [a href='https://www.pushbullet.com/account' target='_blank'] [u]here[/u].[/a]" Desc="Access Token" Default="">TOKEN</Variable>
|
||||
<Variable Help="Specify the fields which are included in the title of the notification." Desc="Notification Title" Default="$SUBJECT">TITLE</Variable>
|
||||
<Variable Help="Specify the fields which are included in the message body of the notification." Desc="Notification Message" Default="$DESCRIPTION">MESSAGE</Variable>
|
||||
</Variables>
|
||||
<Script>
|
||||
<![CDATA[
|
||||
#!/bin/bash
|
||||
##########
|
||||
{0}
|
||||
##########
|
||||
MESSAGE=$(echo "$MESSAGE" | sed -e 's:<br[ /]*>:\\n:gI' -e 's/<[^>]*>//g')
|
||||
|
||||
curl -s -k \
|
||||
-X POST --header "Authorization: Bearer $TOKEN" \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d "{\"type\": \"note\", \"title\": \"$TITLE\", \"body\": \"$MESSAGE\"}" \
|
||||
https://api.pushbullet.com/v2/pushes 2>&1
|
||||
]]>
|
||||
</Script>
|
||||
</Agent>
|
||||
|
24
emhttp/plugins/dynamix/agents/Pushbullet.xml
Normal file
24
emhttp/plugins/dynamix/agents/Pushbullet.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Agent>
|
||||
<Name>Pushbullet</Name>
|
||||
<Variables>
|
||||
<Variable Help="The Access Token can be found [a href='https://www.pushbullet.com/account' target='_blank'] [u]here[/u].[/a]" Desc="Access Token" Default="">TOKEN</Variable>
|
||||
<Variable Help="Specify the fields which are included in the title of the notification." Desc="Notification Title" Default="$SUBJECT">TITLE</Variable>
|
||||
<Variable Help="Specify the fields which are included in the message body of the notification." Desc="Notification Message" Default="$DESCRIPTION">MESSAGE</Variable>
|
||||
</Variables>
|
||||
<Script>
|
||||
<![CDATA[
|
||||
#!/bin/bash
|
||||
##########
|
||||
{0}
|
||||
##########
|
||||
MESSAGE=$(echo "$MESSAGE" | sed -e 's:<br[ /]*>:\\n:gI' -e 's/<[^>]*>//g')
|
||||
|
||||
curl -s -k \
|
||||
-X POST --header "Authorization: Bearer $TOKEN" \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d "{\"type\": \"note\", \"title\": \"$TITLE\", \"body\": \"$MESSAGE\"}" \
|
||||
https://api.pushbullet.com/v2/pushes 2>&1
|
||||
]]>
|
||||
</Script>
|
||||
</Agent>
|
@@ -529,7 +529,12 @@ $theme_dark = in_array($display['theme'], ['black', 'gray']);
|
||||
cookieEnabled = document.cookie.indexOf("cookietest=")!=-1;
|
||||
document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
|
||||
if (!cookieEnabled) {
|
||||
document.write("<p class='error'><?=_('Browser cookie support required for Unraid OS webgui')?></p>");
|
||||
const errorElement = document.createElement('p');
|
||||
errorElement.classList.add('error');
|
||||
errorElement.textContent = "<?=_('Please enable cookies to use the Unraid webGUI')?>";
|
||||
|
||||
document.body.textContent = '';
|
||||
document.body.appendChild(errorElement);
|
||||
}
|
||||
</script>
|
||||
</form>
|
||||
@@ -543,7 +548,7 @@ $theme_dark = in_array($display['theme'], ['black', 'gray']);
|
||||
</div>
|
||||
|
||||
<? if (($twoFactorRequired && !empty($token)) || !$twoFactorRequired) { ?>
|
||||
<p class="js-removeTimeout"><a href="https://docs.unraid.net/unraid-os/manual/troubleshooting#lost-root-password" target="_blank"><?=_('Password recovery')?></a></p>
|
||||
<p class="js-removeTimeout"><a href="https://docs.unraid.net/go/lost-root-password/" target="_blank"><?=_('Password recovery')?></a></p>
|
||||
<? } ?>
|
||||
|
||||
</div>
|
||||
|
@@ -347,7 +347,14 @@ $THEME_DARK = in_array($display['theme'],['black','gray']);
|
||||
document.cookie = "cookietest=1";
|
||||
cookieEnabled = document.cookie.indexOf("cookietest=")!=-1;
|
||||
document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
|
||||
if (!cookieEnabled) document.write("<p class='error'><?=_('Browser cookie support required for Unraid OS webgui')?></p>");
|
||||
if (!cookieEnabled) {
|
||||
const errorElement = document.createElement('p');
|
||||
errorElement.classList.add('error');
|
||||
errorElement.textContent = "<?=_('Please enable cookies to use the Unraid webGUI')?>";
|
||||
|
||||
document.body.textContent = '';
|
||||
document.body.appendChild(errorElement);
|
||||
}
|
||||
// Password toggling
|
||||
const $passToggle = document.querySelector('.js-pass-toggle');
|
||||
const $passToggleHideSvg = $passToggle.querySelector('.js-pass-toggle-hide');
|
||||
|
@@ -98,24 +98,62 @@ case 'ct':
|
||||
echo "\0".implode(';',array_map('urlencode',$cts));
|
||||
break;
|
||||
case 'is':
|
||||
$syslinux = file('/boot/syslinux/syslinux.cfg',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($syslinux);
|
||||
$menu = $i = 0;
|
||||
$isol = "";
|
||||
$isolcpus = [];
|
||||
// find the default section
|
||||
while ($i < $size) {
|
||||
if (scan($syslinux[$i],'label ')) {
|
||||
$n = $i + 1;
|
||||
// find the current isolcpus setting
|
||||
while (!scan($syslinux[$n],'label ') && $n < $size) {
|
||||
if (scan($syslinux[$n],'menu default')) $menu = 1;
|
||||
if (scan($syslinux[$n],'append')) foreach (explode(' ',$syslinux[$n]) as $cmd) if (scan($cmd,'isolcpus')) {$isol = explode('=',$cmd)[1]; break;}
|
||||
$n++;
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$menu = $i = 0;
|
||||
$isol = "";
|
||||
$isolcpus = [];
|
||||
$bootcfg = file('/boot/syslinux/syslinux.cfg', FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($bootcfg);
|
||||
// find the default section
|
||||
while ($i < $size) {
|
||||
if (scan($bootcfg[$i],'label ')) {
|
||||
$n = $i + 1;
|
||||
// find the current isolcpus setting
|
||||
while (!scan($bootcfg[$n],'label ') && $n < $size) {
|
||||
if (scan($bootcfg[$n],'menu default')) $menu = 1;
|
||||
if (scan($bootcfg[$n],'append')) foreach (explode(' ',$bootcfg[$n]) as $cmd) if (scan($cmd,'isolcpus')) {$isol = explode('=',$cmd)[1]; break;}
|
||||
$n++;
|
||||
}
|
||||
if ($menu) break; else $i = $n - 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
$isol = "";
|
||||
$isolcpus = [];
|
||||
$bootcfg = file('/boot/grub/grub.cfg', FILE_IGNORE_NEW_LINES);
|
||||
// find the default section
|
||||
$menu_entries = [];
|
||||
foreach ($bootcfg as $line) {
|
||||
if (preg_match('/set default=(\d+)/', $line, $match)) {
|
||||
$bootentry = (int)$match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// split boot entries
|
||||
foreach ($bootcfg as $line) {
|
||||
if (strpos($line, 'menuentry ') === 0) {
|
||||
$in_menuentry = true;
|
||||
$current_entry = $line . "\n";
|
||||
} elseif ($in_menuentry) {
|
||||
$current_entry .= $line . "\n";
|
||||
if (trim($line) === "}") {
|
||||
$menu_entries[] = $current_entry;
|
||||
$in_menuentry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// search in selected menuentry
|
||||
$menuentry = explode("\n", $menu_entries[$bootentry]);
|
||||
// find the current isolcpus setting
|
||||
if (scan($menu_entries[$bootentry],'linux ')) {
|
||||
foreach ($menuentry as $cmd) {
|
||||
if (scan($cmd,'isolcpus')) {
|
||||
$isol = explode('=',$cmd)[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($menu) break; else $i = $n - 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
if ($isol != '') {
|
||||
// convert to individual numbers
|
||||
@@ -133,24 +171,28 @@ case 'is':
|
||||
break;
|
||||
case 'cmd':
|
||||
$isolcpus_now = $isolcpus_new = '';
|
||||
$syslinux = file('/boot/syslinux/syslinux.cfg',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$cmdline = explode(' ',file_get_contents('/proc/cmdline'));
|
||||
foreach ($cmdline as $cmd) if (scan($cmd,'isolcpus')) {$isolcpus_now = $cmd; break;}
|
||||
$size = count($syslinux);
|
||||
$menu = $i = 0;
|
||||
// find the default section
|
||||
while ($i < $size) {
|
||||
if (scan($syslinux[$i],'label ')) {
|
||||
$n = $i + 1;
|
||||
// find the current isolcpus setting
|
||||
while (!scan($syslinux[$n],'label ') && $n < $size) {
|
||||
if (scan($syslinux[$n],'menu default')) $menu = 1;
|
||||
if (scan($syslinux[$n],'append')) foreach (explode(' ',$syslinux[$n]) as $cmd) if (scan($cmd,'isolcpus')) {$isolcpus_new = $cmd; break;}
|
||||
$n++;
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$bootcfg = file('/boot/syslinux/syslinux.cfg',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($cmdline as $cmd) if (scan($cmd,'isolcpus')) {$isolcpus_now = $cmd; break;}
|
||||
$size = count($bootcfg);
|
||||
$menu = $i = 0;
|
||||
// find the default section
|
||||
while ($i < $size) {
|
||||
if (scan($bootcfg[$i],'label ')) {
|
||||
$n = $i + 1;
|
||||
// find the current isolcpus setting
|
||||
while (!scan($bootcfg[$n],'label ') && $n < $size) {
|
||||
if (scan($bootcfg[$n],'menu default')) $menu = 1;
|
||||
if (scan($bootcfg[$n],'append')) foreach (explode(' ',$bootcfg[$n]) as $cmd) if (scan($cmd,'isolcpus')) {$isolcpus_new = $cmd; break;}
|
||||
$n++;
|
||||
}
|
||||
if ($menu) break; else $i = $n - 1;
|
||||
}
|
||||
if ($menu) break; else $i = $n - 1;
|
||||
$i++;
|
||||
}
|
||||
$i++;
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
$bootcfg = file('/boot/grub/grub.cfg', FILE_IGNORE_NEW_LINES);
|
||||
}
|
||||
echo $isolcpus_now==$isolcpus_new ? 0 : 1;
|
||||
break;
|
||||
|
@@ -582,7 +582,7 @@ function viewHistory() {
|
||||
}
|
||||
function flashReport() {
|
||||
$.post('/webGui/include/Report.php',{cmd:'config'},function(check){
|
||||
if (check>0) addBannerWarning("<?=_('Your flash drive is corrupted or offline').'. '._('Post your diagnostics in the forum for help').'.'?> <a target='_blank' href='https://docs.unraid.net/unraid-os/manual/changing-the-flash-device'><?=_('See also here')?></a>");
|
||||
if (check>0) addBannerWarning("<?=_('Your flash drive is corrupted or offline').'. '._('Post your diagnostics in the forum for help').'.'?> <a target='_blank' href='https://docs.unraid.net/go/changing-the-flash-device/'><?=_('See also here')?></a>");
|
||||
});
|
||||
}
|
||||
$(function() {
|
||||
@@ -803,7 +803,7 @@ default:
|
||||
}
|
||||
echo "</span></span><span id='countdown'></span><span id='user-notice' class='red-text'></span>";
|
||||
echo "<span id='copyright'>Unraid® webGui ©2024, Lime Technology, Inc.";
|
||||
echo " <a href='https://docs.unraid.net/category/manual' target='_blank' title=\""._('Online manual')."\"><i class='fa fa-book'></i> "._('manual')."</a>";
|
||||
echo " <a href='https://docs.unraid.net/go/manual/' target='_blank' title=\""._('Online manual')."\"><i class='fa fa-book'></i> "._('manual')."</a>";
|
||||
echo "</span></div>";
|
||||
?>
|
||||
<script>
|
||||
|
@@ -50,7 +50,7 @@ $match = $_POST['match'];
|
||||
$checkbox = $_POST['multiSelect']=='true' ? "<input type='checkbox'>" : "";
|
||||
|
||||
/* Excluded folders to not show in the dropdown in the '/mnt/' directory only. */
|
||||
$excludedFolders = ["RecycleBin", "addons", "disks", "remotes", "rootshare", "user0"];
|
||||
$excludedFolders = ["RecycleBin", "addons", "remotes", "rootshare", "user0"];
|
||||
|
||||
echo "<ul class='jqueryFileTree'>";
|
||||
if ($_POST['show_parent']=='true' && is_top($rootdir)) echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"".htmlspecialchars(dirname($rootdir))."\">..</a></li>";
|
||||
|
@@ -344,6 +344,29 @@ function my_mkdir($dirname,$permissions = 0777,$recursive = false,$own = "nobody
|
||||
chgrp($dirname, $grp);
|
||||
return($rtncode);
|
||||
}
|
||||
function my_rmdir($dirname) {
|
||||
if (!is_dir($dirname)) return(false);
|
||||
if (strpos($dirname,'/mnt/user/')===0) {
|
||||
$realdisk = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($dirname)." 2>/dev/null"));
|
||||
if (!empty($realdisk)) {
|
||||
$dirname = str_replace('/mnt/user/', "/mnt/$realdisk/", $dirname);
|
||||
}
|
||||
}
|
||||
$fstype = trim(shell_exec(" stat -f -c '%T' $dirname"));
|
||||
$rtncode = false;
|
||||
switch ($fstype) {
|
||||
case "zfs":
|
||||
$zfsoutput = array();
|
||||
$zfsdataset = trim(shell_exec("zfs list -H -o name \"$dirname\"")) ;
|
||||
exec("zfs destroy \"$zfsdataset\"",$zfsoutput,$rtncode);
|
||||
break;
|
||||
case "btrfs":
|
||||
default:
|
||||
$rtncode = rmdir($dirname);
|
||||
break;
|
||||
}
|
||||
return($rtncode);
|
||||
}
|
||||
function get_realvolume($path) {
|
||||
if (strpos($path,"/mnt/user/",0) === 0)
|
||||
$reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null"));
|
||||
|
@@ -51,7 +51,7 @@ case 'ttyd':
|
||||
// no child processes, restart ttyd to pick up possible font size change
|
||||
if ($retval != 0) exec("kill ".$ttyd_pid[0]);
|
||||
}
|
||||
if ($retval != 0) exec("ttyd-exec -i '$sock' bash --login");
|
||||
if ($retval != 0) exec("ttyd-exec -i '$sock' '" . posix_getpwuid(0)['shell'] . "' --login");
|
||||
break;
|
||||
case 'syslog':
|
||||
// read syslog file
|
||||
|
@@ -130,7 +130,7 @@ uksort($shares,'strnatcasecmp');
|
||||
|
||||
/* Function to filter out unwanted disks, check if any valid disks exist, and ignore disks with a blank device. */
|
||||
function checkDisks($disks) {
|
||||
global $pools, $poolsOnly;
|
||||
global $pools;
|
||||
|
||||
$rc = false;
|
||||
|
||||
@@ -149,112 +149,177 @@ function checkDisks($disks) {
|
||||
|
||||
// Display export settings
|
||||
function user_share_settings($protocol,$share) {
|
||||
if (empty($share)) return;
|
||||
if ($protocol!='yes' || $share['export']=='-') return "-";
|
||||
return ($share['export']=='e') ? _(ucfirst($share['security'])) : '<em>'._(ucfirst($share['security'])).'</em>';
|
||||
if (empty($share)) return;
|
||||
if ($protocol!='yes' || $share['export']=='-') return "-";
|
||||
return ($share['export']=='e') ? _(ucfirst($share['security'])) : '<em>'._(ucfirst($share['security'])).'</em>';
|
||||
}
|
||||
function globalInclude($name) {
|
||||
global $var;
|
||||
return substr($name,0,4)!='disk' || !$var['shareUserInclude'] || strpos("{$var['shareUserInclude']},","$name,")!==false;
|
||||
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;
|
||||
global $include;
|
||||
return !$include || substr($name,0,4)!='disk' || strpos("$include,", "$name,")!==false;
|
||||
}
|
||||
// Compute user shares & check encryption
|
||||
$crypto = false;
|
||||
foreach ($shares as $name => $share) {
|
||||
if ($all!=0 && (!$compute || $compute==$name)) exec("$docroot/webGui/scripts/share_size ".escapeshellarg($name)." ssz1 ".escapeshellarg($pools));
|
||||
$crypto |= _var($share,'luksStatus',0)>0;
|
||||
if ($all!=0 && (!$compute || $compute==$name)) exec("$docroot/webGui/scripts/share_size ".escapeshellarg($name)." ssz1 ".escapeshellarg($pools));
|
||||
$crypto |= _var($share,'luksStatus',0)>0;
|
||||
}
|
||||
// global shares include/exclude
|
||||
$myDisks = array_filter(array_diff(array_keys($disks), explode(',',$var['shareUserExclude'])), 'globalInclude');
|
||||
|
||||
// Share size per disk
|
||||
$ssz1 = [];
|
||||
if ($all==0)
|
||||
exec("rm -f /var/local/emhttp/*.ssz1");
|
||||
else
|
||||
foreach (glob("state/*.ssz1",GLOB_NOSORT) as $entry) $ssz1[basename($entry,'.ssz1')] = parse_ini_file($entry);
|
||||
if ($all==0) {
|
||||
exec("rm -f /var/local/emhttp/*.ssz1");
|
||||
} else {
|
||||
foreach (glob("state/*.ssz1",GLOB_NOSORT) as $entry) $ssz1[basename($entry,'.ssz1')] = parse_ini_file($entry);
|
||||
}
|
||||
|
||||
/* Define constants for magic strings */
|
||||
define('STATUS_GREEN_ON', 'green-on');
|
||||
define('STATUS_YELLOW_ON', 'yellow-on');
|
||||
define('LUKS_STATUS_UNKNOWN', 0);
|
||||
define('LUKS_STATUS_ENCRYPTED', 1);
|
||||
define('LUKS_STATUS_UNENCRYPTED', 2);
|
||||
|
||||
// Build table
|
||||
$row = 0;
|
||||
foreach ($shares as $name => $share) {
|
||||
/* Check if poolsOnly is true */
|
||||
$array = $share['cachePool2'] ? ucfirst($share['cachePool2']) : "<i class='fa fa-database fa-fw'></i>"._('Array');
|
||||
/* Is cachePool2 defined? If it is we need to show the cache pool 2 device name instead of 'Array'. */
|
||||
if ($share['cachePool2']) {
|
||||
$array = compress(my_disk($share['cachePool2'],$display['raw']));
|
||||
$indicator = "<i class='fa fa-bullseye fa-fw'></i>";
|
||||
} else {
|
||||
$array = _('Array');
|
||||
$indicator = "<i class='fa fa-database fa-fw'></i>";
|
||||
}
|
||||
|
||||
/* Check that the share storage assignments are valid. */
|
||||
if (($share['cachePool']) && (! in_array($share['cachePool'], $pools_check))) {
|
||||
$array = compress(my_disk($share['cachePool'],$display['raw']));
|
||||
$indicator = "<i class='fa fa-bullseye fa-fw'></i>";
|
||||
$share_valid = false;
|
||||
} else if (($share['cachePool2']) && (! in_array($share['cachePool2'], $pools_check))) {
|
||||
$array = compress(my_disk($share['cachePool2'],$display['raw']));
|
||||
$indicator = "<i class='fa fa-bullseye fa-fw'></i>";
|
||||
$share_valid = false;
|
||||
} else if (($poolsOnly) && (! $share['cachePool']) && (! $share['cachePool2'])) {
|
||||
$share_valid = false;
|
||||
} else {
|
||||
/* Is the share exclusive? */
|
||||
$exclusive = _var($share, 'exclusive') == 'yes' ? "<i class='fa fa-caret-right '></i> " : "";
|
||||
$share_valid = true;
|
||||
}
|
||||
|
||||
/* Check if poolsOnly is true. */
|
||||
if ($poolsOnly) {
|
||||
/* If useCache is set to 'yes', change it to 'no'. */
|
||||
if (($share['useCache'] == 'yes') && (!$share['cachePool2'])) {
|
||||
$share['useCache'] = 'no';
|
||||
}
|
||||
/* If useCache is set to 'prefer', change it to 'only'. */
|
||||
if (($share['useCache'] == 'prefer') && (!$share['cachePool2'])) {
|
||||
/* If useCache is set to 'yes' or 'prefer', change it to 'only'. */
|
||||
if ((($share['useCache'] == 'yes') || ($share['useCache'] == 'prefer')) && (!$share['cachePool2'])) {
|
||||
$share['useCache'] = 'only';
|
||||
}
|
||||
}
|
||||
|
||||
$row++;
|
||||
$color = $share['color'];
|
||||
switch ($color) {
|
||||
case 'green-on' : $orb = 'circle'; $color = 'green'; $help = _('All files protected'); break;
|
||||
case 'yellow-on': $orb = 'warning'; $color = 'yellow'; $help = _('Some or all files unprotected'); break;
|
||||
}
|
||||
if ($crypto) switch ($share['luksStatus']) {
|
||||
case 0: $luks = "<i class='nolock fa fa-lock'></i>"; break;
|
||||
case 1: $luks = "<a class='info' onclick='return false'><i class='padlock fa fa-unlock-alt green-text'></i><span>"._('All files encrypted')."</span></a>"; break;
|
||||
case 2: $luks = "<a class='info' onclick='return false'><i class='padlock fa fa-unlock-alt orange-text'></i><span>"._('Some or all files unencrypted')."</span></a>"; break;
|
||||
default: $luks = "<a class='info' onclick='return false'><i class='padlock fa fa-lock red-text'></i><span>"._('Unknown encryption state')."</span></a>"; break;
|
||||
} else $luks = "";
|
||||
echo "<tr><td><a class='view' href=\"/$path/Browse?dir=/mnt/user/",rawurlencode($name),"\"><i class=\"icon-u-tab\" title=\"",_('Browse')," /mnt/user/".rawurlencode($name),"\"></i></a>";
|
||||
echo "<a class='info nohand' onclick='return false'><i class='fa fa-$orb orb $color-orb'></i><span style='left:18px'>$help</span></a>$luks<a href=\"/$path/Share?name=";
|
||||
echo rawurlencode($name),"\" onclick=\"$.cookie('one','tab1')\">$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>";
|
||||
$row++;
|
||||
$color = $share['color'] ?? '';
|
||||
$orb = '';
|
||||
$help = '';
|
||||
|
||||
// Check for non existent pool device
|
||||
if (isset($share['cachePool']) && !in_array($share['cachePool'], $pools_check)) $share['useCache'] = "no";
|
||||
switch ($color) {
|
||||
case STATUS_GREEN_ON:
|
||||
$orb = 'circle';
|
||||
$color = 'green';
|
||||
$help = _('All files protected');
|
||||
break;
|
||||
case STATUS_YELLOW_ON:
|
||||
$orb = 'warning';
|
||||
$color = 'yellow';
|
||||
$help = _('Some or all files unprotected');
|
||||
break;
|
||||
default:
|
||||
/* Handle unexpected color values */
|
||||
$orb = 'question';
|
||||
$color = 'grey';
|
||||
$help = _('Unknown protection status');
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($share['useCache']) {
|
||||
case 'no':
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-database fa-fw'></i>"._('Array')."<span>".sprintf(_('Primary storage %s'),_('Array'))."</span></a>";
|
||||
break;
|
||||
case 'yes':
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-bullseye fa-fw'></i>".compress(my_disk($share['cachePool'],$display['raw']))." <i class='fa fa-long-arrow-right fa-fw'></i>".$array."<span>"._('Primary storage to Secondary storage')."</span></a>";
|
||||
break;
|
||||
case 'prefer':
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-bullseye fa-fw'></i>".compress(my_disk($share['cachePool'],$display['raw']))." <i class='fa fa-long-arrow-left fa-fw'></i>".$array."<span>"._('Secondary storage to Primary storage')."</span></a>";
|
||||
break;
|
||||
case 'only':
|
||||
$exclusive = isset($share['exclusive']) && $share['exclusive']=='yes' ? "<i class='fa fa-caret-right '></i> " : "";
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-bullseye fa-fw'></i>$exclusive".my_disk($share['cachePool'],$display['raw'])."<span>".sprintf(_('Primary storage %s'),$share['cachePool']).($exclusive ? ", "._('Exclusive access') : "")."</span></a>";
|
||||
break;
|
||||
}
|
||||
if (array_key_exists($name,$ssz1)) {
|
||||
echo "<td>$cache</td>";
|
||||
echo "<td>",my_scale($ssz1[$name]['disk.total'], $unit)," $unit</td>";
|
||||
echo "<td>",my_scale($share['free']*1024, $unit)," $unit</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='",($inside ? "'>" : "warning'>");
|
||||
echo "<td><a class='view'></a><a href='#' title='",_('Recompute'),"...' onclick=\"computeShare('",rawurlencode($name),"',$(this).parent())\"><i class='fa fa-refresh icon'></i></a> ",_(my_disk($diskname,$display['raw']),3),"</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>",my_scale($disksize, $unit)," $unit</td>";
|
||||
echo "<td>",my_scale($disks[$diskname]['fsFree']*1024, $unit)," $unit</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
} else {
|
||||
echo "<td>$cache</td>";
|
||||
echo "<td><a href='#' onclick=\"computeShare('",rawurlencode($name),"',$(this))\">",_('Compute'),"...</a></td>";
|
||||
echo "<td>",my_scale($share['free']*1024, $unit)," $unit</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
$luks = '';
|
||||
if ($crypto) {
|
||||
switch ($share['luksStatus'] ?? LUKS_STATUS_UNKNOWN) {
|
||||
case LUKS_STATUS_UNKNOWN:
|
||||
$luks = "<i class='nolock fa fa-lock'></i>";
|
||||
break;
|
||||
case LUKS_STATUS_ENCRYPTED:
|
||||
$luks = "<a class='info' onclick='return false'><i class='padlock fa fa-unlock-alt green-text'></i><span>"._('All files encrypted')."</span></a>";
|
||||
break;
|
||||
case LUKS_STATUS_UNENCRYPTED:
|
||||
$luks = "<a class='info' onclick='return false'><i class='padlock fa fa-unlock-alt orange-text'></i><span>"._('Some or all files unencrypted')."</span></a>";
|
||||
break;
|
||||
default:
|
||||
$luks = "<a class='info' onclick='return false'><i class='padlock fa fa-lock red-text'></i><span>"._('Unknown encryption state')."</span></a>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
echo "<tr><td><a class='view' href=\"/$path/Browse?dir=/mnt/user/", rawurlencode($name), "\"><i class=\"icon-u-tab\" title=\"", _('Browse'), " /mnt/user/" . rawurlencode($name), "\"></i></a>";
|
||||
echo "<a class='info nohand' onclick='return false'><i class='fa fa-$orb orb $color-orb'></i><span style='left:18px'>$help</span></a>$luks<a href=\"/$path/Share?name=";
|
||||
echo rawurlencode($name), "\" onclick=\"$.cookie('one','tab1')\">$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>";
|
||||
|
||||
/* If the share pool or array is not valid, indicate that to the user. */
|
||||
if (!$share_valid) {
|
||||
$cache = "<a class='hand info none' onclick='return false'>".$indicator." <i class='fa fa-warning red-orb'></i> ".$array."<span>"._('This share is invalid').'; '. _('It references storage that does not exist')."</span></a>";
|
||||
} else {
|
||||
switch ($share['useCache']) {
|
||||
case 'no':
|
||||
$cache = "<a class='hand info none' onclick='return false'>".$indicator.$exclusive.$array."<span>".sprintf(_('Primary storage %s'), $array)."</span></a>";
|
||||
break;
|
||||
case 'yes':
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-bullseye fa-fw'></i>".compress(my_disk($share['cachePool'], $display['raw']))." <i class='fa fa-long-arrow-right fa-fw'></i>".$indicator.$array."<span>"._('Primary storage to Secondary storage')."</span></a>";
|
||||
break;
|
||||
case 'prefer':
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-bullseye fa-fw'></i>".compress(my_disk($share['cachePool'], $display['raw']))." <i class='fa fa-long-arrow-left fa-fw'></i>".$indicator.$array."<span>"._('Secondary storage to Primary storage')."</span></a>";
|
||||
break;
|
||||
case 'only':
|
||||
$cache = "<a class='hand info none' onclick='return false'><i class='fa fa-bullseye fa-fw'></i>$exclusive".my_disk($share['cachePool'], $display['raw'])."<span>".sprintf(_('Primary storage %s'), $share['cachePool']).($exclusive ? ", "._('Exclusive access') : "")."</span></a>";
|
||||
break;
|
||||
default:
|
||||
/* Handle unexpected useCache values */
|
||||
$cache = "<a class='hand info none' onclick='return false'>". $indicator . $array . "<span>"._('Unknown cache usage')."</span></a>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $ssz1)) {
|
||||
echo "<td>$cache</td>";
|
||||
echo "<td>", my_scale($ssz1[$name]['disk.total'], $unit), " $unit</td>";
|
||||
echo "<td>", my_scale($share['free'] * 1024, $unit), " $unit</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='", ($inside ? "'>" : "warning'>");
|
||||
echo "<td><a class='view'></a><a href='#' title='", _('Recompute'), "...' onclick=\"computeShare('", rawurlencode($name), "',$(this).parent())\"><i class='fa fa-refresh icon'></i></a> ", _(my_disk($diskname, $display['raw']), 3), "</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>", my_scale($disksize, $unit), " $unit</td>";
|
||||
echo "<td>", my_scale($disks[$diskname]['fsFree'] * 1024, $unit), " $unit</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
} else {
|
||||
echo "<td>$cache</td>";
|
||||
echo "<td><a href='#' onclick=\"computeShare('", rawurlencode($name), "',$(this))\">", _('Compute'), "...</a></td>";
|
||||
echo "<td>", my_scale($share['free'] * 1024, $unit), " $unit</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
if ($row==0) echo $noshares;
|
||||
?>
|
||||
|
@@ -169,27 +169,16 @@ case 'ct':
|
||||
break;
|
||||
|
||||
case 'is':
|
||||
/* Path to syslinux configuration file */
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
|
||||
/* Read the syslinux configuration file into an array, ignoring empty lines */
|
||||
$syslinux = file($cfg, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($syslinux);
|
||||
$make = false;
|
||||
|
||||
/* Path to the temporary file containing new isolcpus settings */
|
||||
/* Path to the temporary file containing new isolcpus settings */
|
||||
$file = "/var/tmp/$name.tmp";
|
||||
$isolcpus = file_get_contents($file);
|
||||
|
||||
$isolcpus = file_get_contents($file);
|
||||
if ($isolcpus != '') {
|
||||
/* Convert isolcpus string to an array of numbers and sort them */
|
||||
$numbers = explode(',', $isolcpus);
|
||||
sort($numbers, SORT_NUMERIC);
|
||||
|
||||
/* Initialize variables for range conversion */
|
||||
$isolcpus = $previous = array_shift($numbers);
|
||||
$range = false;
|
||||
|
||||
/* Convert sequential numbers to a range */
|
||||
foreach ($numbers as $number) {
|
||||
if ($number == $previous + 1) {
|
||||
@@ -206,55 +195,94 @@ case 'is':
|
||||
if ($range) {
|
||||
$isolcpus .= '-' . $previous;
|
||||
}
|
||||
|
||||
/* Format isolcpus string for syslinux configuration */
|
||||
/* Format isolcpus string for configuration */
|
||||
$isolcpus = "isolcpus=$isolcpus";
|
||||
}
|
||||
|
||||
/* Remove the temporary file */
|
||||
unlink($file);
|
||||
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
/* Find sections in syslinux config and exclude safemode */
|
||||
if (scan($syslinux[$i], 'label ') && !scan($syslinux[$i], 'safe mode') && !scan($syslinux[$i], 'safemode')) {
|
||||
$n = $i + 1;
|
||||
|
||||
/* Find the current requested setting */
|
||||
while ($n < $size && !scan($syslinux[$n], 'label ')) {
|
||||
if (scan($syslinux[$n], 'append ')) {
|
||||
$cmd = preg_split('/\s+/', trim($syslinux[$n]));
|
||||
|
||||
/* Replace an existing isolcpus setting */
|
||||
for ($c = 1; $c < count($cmd); $c++) {
|
||||
if (scan($cmd[$c], 'isolcpus')) {
|
||||
$make |= ($cmd[$c] != $isolcpus);
|
||||
$cmd[$c] = $isolcpus;
|
||||
break;
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
/* Path to syslinux configuration file */
|
||||
$cfg = '/boot/syslinux/syslinux.cfg';
|
||||
/* Read the syslinux configuration file into an array, ignoring empty lines */
|
||||
$bootcfg = file($cfg, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($bootcfg);
|
||||
$make = false;
|
||||
/* Remove the temporary file */
|
||||
unlink($file);
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
/* Find sections in syslinux config and exclude safemode */
|
||||
if (scan($bootcfg[$i], 'label ') && !scan($bootcfg[$i], 'safe mode') && !scan($bootcfg[$i], 'safemode')) {
|
||||
$n = $i + 1;
|
||||
/* Find the current requested setting */
|
||||
while ($n < $size && !scan($bootcfg[$n], 'label ')) {
|
||||
if (scan($bootcfg[$n], 'append ')) {
|
||||
$cmd = preg_split('/\s+/', trim($bootcfg[$n]));
|
||||
/* Replace an existing isolcpus setting */
|
||||
for ($c = 1; $c < count($cmd); $c++) {
|
||||
if (scan($cmd[$c], 'isolcpus')) {
|
||||
$make |= ($cmd[$c] != $isolcpus);
|
||||
$cmd[$c] = $isolcpus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Or insert a new isolcpus setting if not found */
|
||||
if ($c == count($cmd) && $isolcpus) {
|
||||
array_splice($cmd, -1, 0, $isolcpus);
|
||||
$make = true;
|
||||
}
|
||||
/* Update the syslinux configuration line */
|
||||
$bootcfg[$n] = ' ' . str_replace(' ', ' ', implode(' ', $cmd));
|
||||
}
|
||||
|
||||
/* Or insert a new isolcpus setting if not found */
|
||||
if ($c == count($cmd) && $isolcpus) {
|
||||
array_splice($cmd, -1, 0, $isolcpus);
|
||||
$make = true;
|
||||
}
|
||||
|
||||
/* Update the syslinux configuration line */
|
||||
$syslinux[$n] = ' ' . str_replace(' ', ' ', implode(' ', $cmd));
|
||||
$n++;
|
||||
}
|
||||
$n++;
|
||||
$i = $n - 1;
|
||||
}
|
||||
$i = $n - 1;
|
||||
$i++;
|
||||
}
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
/* Path to grub configuration file */
|
||||
$cfg = '/boot/grub/grub.cfg';
|
||||
/* Read the grub configuration file into an array, ignoring empty lines */
|
||||
$bootcfg = file($cfg, FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);
|
||||
$size = count($bootcfg);
|
||||
$make = false;
|
||||
/* Remove the temporary file */
|
||||
unlink($file);
|
||||
$i = 0;
|
||||
while ($i < $size) {
|
||||
// find sections and exclude safemode/memtest
|
||||
if (scan($bootcfg[$i],'menuentry ') && !scan($bootcfg[$i],'safe mode') && !scan($bootcfg[$i],'safemode') && !scan($bootcfg[$i],'memtest')) {
|
||||
$n = $i + 1;
|
||||
// find the current requested setting
|
||||
while (!scan($bootcfg[$n],'menuentry ') && $n < $size) {
|
||||
if (scan($bootcfg[$n],'linux ')) {
|
||||
$cmd = preg_split('/\s+/',trim($bootcfg[$n]));
|
||||
/* Replace an existing isolcpus setting */
|
||||
for ($c = 1; $c < count($cmd); $c++) {
|
||||
if (scan($cmd[$c], 'isolcpus')) {
|
||||
$make |= ($cmd[$c] != $isolcpus);
|
||||
$cmd[$c] = $isolcpus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Or insert a new isolcpus setting if not found */
|
||||
if ($c == count($cmd) && $isolcpus) {
|
||||
$cmd[] = $isolcpus;
|
||||
$make = true;
|
||||
}
|
||||
/* Update the grub configuration line */
|
||||
$bootcfg[$n] = ' ' . str_replace(' ', ' ', implode(' ', $cmd));
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
$i = $n - 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
/* Write the updated syslinux configuration back to the file if changes were made */
|
||||
/* Write the updated configuration back to the file if changes were made */
|
||||
if ($make) {
|
||||
file_put_contents_atomic($cfg, implode("\n", $syslinux) . "\n");
|
||||
file_put_contents_atomic($cfg, implode("\n", $bootcfg) . "\n");
|
||||
}
|
||||
|
||||
$reply = ['success' => $name];
|
||||
break;
|
||||
}
|
||||
|
@@ -128,6 +128,9 @@ function no_tilde($name) {
|
||||
function prefix($key) {
|
||||
return preg_replace('/\d+$/','',$key);
|
||||
}
|
||||
function pool_name($key) {
|
||||
return preg_replace('/(\d+$|~.*$)/', '', $key);
|
||||
}
|
||||
function native($name, $full=0) {
|
||||
global $_tilde_, $_arrow_;
|
||||
switch ($full) {
|
||||
|
@@ -119,12 +119,12 @@ function assignment(&$disk) {
|
||||
function vfs_luks($fs) {
|
||||
return str_starts_with($fs,'luks:');
|
||||
}
|
||||
function vfs_type(&$disk, $parent=false) {
|
||||
function vfs_type(&$disk) {
|
||||
global $disks, $pools, $crypto;
|
||||
$fsType = _var($disk,'fsType');
|
||||
$name = _var($disk,'name');
|
||||
$type = ($parent || _var($disk,'type')=='Cache' && !in_array($name,$pools)) ? _var($disks[prefix($name)],'fsType') : $fsType;
|
||||
$fsType = _var($disk,'fsType','');
|
||||
$luks = '';
|
||||
if (empty($fsType))
|
||||
return $fsType;
|
||||
if ($crypto) switch (_var($disk,'luksState',0)) {
|
||||
case 0:
|
||||
if (vfs_luks($fsType))
|
||||
@@ -146,14 +146,14 @@ function vfs_type(&$disk, $parent=false) {
|
||||
$luks = "<a class='info'><i class='padlock fa fa-lock red-text'></i><span>"._('Device locked: unknown error')."</span></a>";
|
||||
break;
|
||||
}
|
||||
return $luks.str_replace('luks:','',$type);
|
||||
return $luks.str_replace('luks:','',$fsType);
|
||||
}
|
||||
function fs_info(&$disk) {
|
||||
global $display, $pools;
|
||||
global $display;
|
||||
$echo = [];
|
||||
if (empty($disk['fsStatus']) || $disk['fsStatus']=='-') {
|
||||
return (_var($disk,'type')=='Cache') ? "<td>".vfs_type($disk,true)."</td><td colspan='3'>"._('Device is part of a pool')."</td>" : "<td colspan='4'></td>";
|
||||
} elseif (_var($disk,'fsStatus')=='Mounted') {
|
||||
if (empty(_var($disk,'fsStatus','')))
|
||||
return "<td colspan='4'></td>";
|
||||
if (_var($disk,'fsStatus')=='Mounted') {
|
||||
$echo[] = "<td>".vfs_type($disk)."</td>";
|
||||
$echo[] = "<td>".my_scale(_var($disk,'fsSize',0)*1024,$unit,-1)." $unit</td>";
|
||||
if ($display['text']%10==0) {
|
||||
@@ -183,9 +183,9 @@ function array_offline(&$disk, $pool='') {
|
||||
$status = ['DISK_DSBL','DISK_INVALID','DISK_DSBL_NEW','DISK_WRONG'];
|
||||
$text = "<span class='red-text'><em>"._('All existing data on this device will be OVERWRITTEN when array is Started')."</em></span>";
|
||||
if (_var($disk,'type')=='Cache') {
|
||||
if (!str_contains(_var($diks[$pool],'state'),'ERROR:')) {
|
||||
if (_var($disks[$pool],'state')!='NEW_ARRAY') {
|
||||
if (in_array(_var($disk,'status'),$status)) $warning = $text;
|
||||
if (!str_contains(_var($disks[$pool],'state'),'ERROR:')) {
|
||||
if (!empty(_var($disks[$pool],'uuid'))) {
|
||||
if (in_array(_var($disk,'status'),$status) || _var($disk['status'])=='DISK_NEW') $warning = $text;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -194,7 +194,7 @@ function array_offline(&$disk, $pool='') {
|
||||
if (_var($disk,'type')=='Parity') $warning = $text;
|
||||
} elseif (_var($var,'mdState')=='RECON_DISK') {
|
||||
if (in_array(_var($disk,'status'),$status)) $warning = $text;
|
||||
} elseif (_var($disk['status'])=='DISK_NEW') {
|
||||
} elseif (_var($disk['status'])=='DISK_NEW' && _var($var,'mdResyncAction')=='clear') {
|
||||
$warning = $text;
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,9 @@ function array_offline(&$disk, $pool='') {
|
||||
case 'DISK_NP_MISSING':
|
||||
$echo[] = "<td>".device_info($disk,false)."<br><span class='diskinfo'><em>"._('Missing')."</em></span></td>";
|
||||
$echo[] = "<td>".assignment($disk)."<em>{$disk['idSb']} - ".my_scale(_var($disk,'sizeSb',0)*1024,$unit)." $unit</em></td>";
|
||||
$echo[] = "<td colspan='8'></td>";
|
||||
$echo[] = "<td colspan='4'></td>";
|
||||
$echo[] = "<td>".vfs_type($disk)."</td>";
|
||||
$echo[] = "<td colspan='3'></td>";
|
||||
break;
|
||||
case 'DISK_NP_DSBL':
|
||||
$echo[] = "<td>".device_info($disk,false)."</td>";
|
||||
@@ -359,8 +361,9 @@ function array_slots() {
|
||||
return implode($echo);
|
||||
}
|
||||
function cache_slots($off,$pool,$min,$slots) {
|
||||
global $var;
|
||||
$off = $off && $min ? ' disabled' : '';
|
||||
global $var, $disks;
|
||||
// $off = $off && $min ? ' disabled' : '';
|
||||
$off = _var($disks[$pool],'fsType','auto')!='auto' && empty(_var($disks[$pool],'uuid')) ? ' disabled' : '';
|
||||
$max = _var($var,'MAX_CACHESZ');
|
||||
$echo = [];
|
||||
$echo[] = "<form method='POST' action='/update.htm' target='progressFrame'>";
|
||||
@@ -368,10 +371,13 @@ function cache_slots($off,$pool,$min,$slots) {
|
||||
$echo[] = "<input type='hidden' name='changeSlots' value='apply'>";
|
||||
$echo[] = "<input type='hidden' name='poolName' value='$pool'>";
|
||||
$echo[] = "<select class='narrow' name='poolSlots' onChange='devices.start();this.form.submit()'{$off}>";
|
||||
// if (_var($disks[$pool],'state')=='NEW_ARRAY' || str_contains(_var($diks[$pool],'state'),'NO_DEVICES')) {
|
||||
// $option = _('none');
|
||||
// $echo[] = "<option value='0'>$option</option>";
|
||||
// }
|
||||
for ($n=$min; $n<=$max; $n++) {
|
||||
$option = $n ?: _('none');
|
||||
$selected = ($n==$slots) ? ' selected' : '';
|
||||
$echo[] = "<option value='$n'{$selected}>$option</option>";
|
||||
$echo[] = "<option value='$n'{$selected}>$n</option>";
|
||||
}
|
||||
$echo[] = "</select></form>";
|
||||
return implode($echo);
|
||||
@@ -501,13 +507,16 @@ while (true) {
|
||||
if ($zfsPool) {
|
||||
$current_subpools = array_filter($pools, function($element) use ($pool,$_tilde_) {return str_contains($element,"{$pool}{$_tilde_}");});
|
||||
$current_subpools_list = str_replace("{$pool}{$_tilde_}","", implode(',', $current_subpools));
|
||||
$echo[$a][] = "<input type='button' value='"._('Add Subpool')."' class='subpool' onclick='addSubpoolPopup(\"$pool\",\"$current_subpools_list\")'".(count($current_subpools)<count($subpools)?'':' disabled').">";
|
||||
if (!empty(_var($Cache[$pool],'uuid'))) {
|
||||
$echo[$a][] = "<input type='button' value='"._('Add Subpool')."' class='subpool' onclick='addSubpoolPopup(\"$pool\",\"$current_subpools_list\")'".(count($current_subpools)<count($subpools)?'':' disabled').">";
|
||||
} else {
|
||||
$echo[$a][] = "<input type='button' value='"._('Add Subpool')."' class='subpool' disabled>";
|
||||
}
|
||||
}
|
||||
$echo[$a][] = "</span></td><td></td></tr>";
|
||||
} else {
|
||||
foreach ($Cache as $disk) if (prefix($disk['name'])==$pool) {
|
||||
$fstype = str_replace('luks:','',_var($disk,'fsType'));
|
||||
if (substr(_var($Cache[$pool],'fsStatus'),0,11)=='Unmountable' && empty($disk['fsStatus'])) $disk['fsStatus'] = _var($Cache[$pool],'fsStatus');
|
||||
$echo[$a][] = array_online($disk,$fstype);
|
||||
}
|
||||
if (strcmp($root,$pool)!=0) $Cache[$root]['devices'] += $Cache[$pool]['devices'];
|
||||
|
@@ -107,6 +107,9 @@ while (true) {
|
||||
file_put_contents($log, "$timestamp|$duration|$speed|$status|$error|$action|$size\n", FILE_APPEND);
|
||||
|
||||
delete_file($stamps, $resync);
|
||||
|
||||
/* Parity check is completed. */
|
||||
$echo = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +133,7 @@ while (true) {
|
||||
$process = 2;
|
||||
} elseif (exec('ps -C btrfs -o cmd=|grep -cv show') > 0) {
|
||||
$process = 3;
|
||||
} elseif (exec("zpool status|grep -c 'scrub in progress'") > 0) {
|
||||
} elseif (exec("zpool status 2>/dev/null | grep -c 'scrub in progress'") > 0) {
|
||||
$process = 4;
|
||||
} else {
|
||||
$process = 0;
|
||||
|
@@ -59,7 +59,7 @@ while (true) {
|
||||
exec("sensors -uA 2>/dev/null|grep -Po 'fan\d_input: \K\d+'",$fans);
|
||||
[$total,$free] = $memory;
|
||||
$used = $total-$free;
|
||||
$names = [_('Services'),_('Free')];
|
||||
$names = [_('System'),_('Free')];
|
||||
$bytes = $echo = [];
|
||||
$hooks = array_filter(glob("/usr/local/emhttp/plugins/*/system/*",GLOB_NOSORT),function($file){return is_executable($file);});
|
||||
foreach ($hooks as $hook) {
|
||||
|
@@ -142,6 +142,17 @@ function device_name(&$disk) {
|
||||
$fancy = _(my_disk(native($name,1)),3);
|
||||
return "<i class='icon-$type f14'></i> <a href=\"".htmlspecialchars("/Dashboard/Main/Settings/Device?name=$name")."\" title=\"$fancy settings\">$fancy</a>";
|
||||
}
|
||||
function yellow_text($disk) {
|
||||
global $var;
|
||||
if (_var($disk, 'type')=='Parity') {
|
||||
$text = _var($var,'mdResync')==0 ? 'invalid' : 'syncing';
|
||||
} elseif (_var($disk, 'type')=='Data') {
|
||||
$text = _var($var,'mdResync')==0 ? 'emulated' : 'rebuilding';
|
||||
} else {
|
||||
$text = 'invalid';
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
function device_status(&$disk, &$error, &$warning) {
|
||||
global $var;
|
||||
if (_var($disk,'type')!='Extra' && _var($var,'fsState')=='Stopped') {
|
||||
@@ -151,8 +162,8 @@ function device_status(&$disk, &$error, &$warning) {
|
||||
case 'green-blink' : $color = 'grey'; $text = 'standby'; break;
|
||||
case 'blue-on' : $color = 'blue'; $text = 'unassigned'; break;
|
||||
case 'blue-blink' : $color = 'grey'; $text = 'unassigned'; break;
|
||||
case 'yellow-on' : $color = 'yellow'; $text = _var($disk,'type')=='Parity' ? 'invalid' : 'emulated'; $warning++; break;
|
||||
case 'yellow-blink': $color = 'grey'; $text = _var($disk,'type')=='Parity' ? 'invalid' : 'emulated'; $warning++; break;
|
||||
case 'yellow-on' : $color = 'yellow'; $text = yellow_text($disk); $warning++; break;
|
||||
case 'yellow-blink': $color = 'grey'; $text = yellow_text($disk); $warning++; break;
|
||||
case 'red-on' : $color = 'red'; $text = 'disabled'; $error++; break;
|
||||
case 'red-blink' : $color = 'grey'; $text = 'disabled'; $error++; break;
|
||||
case 'red-off' : $color = 'red'; $text = 'faulty'; $error++; break;
|
||||
|
@@ -73,11 +73,13 @@ while (true) {
|
||||
if ($vmdata['state'] == 1) {
|
||||
$vmencode = str_replace(" "," ",$vm);
|
||||
$vmencode = $lv->domain_get_uuid($vm);
|
||||
$echo[$vmencode ]['gcpu'] = "<span class='advanced'>"._("Guest CPU").": <span class='cpug-".$vm."'>".$vmdata['cpuguest']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuguest']."%;'></span><span></span></div></span>";
|
||||
$echo[$vmencode ]['hcpu'] = "<span class='advanced'>"._("Host CPU").": <span class='cpug-".$vm."'>".$vmdata['cpuhost']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuhost']."%;'></span><span></span></div></span>";
|
||||
$echo[$vmencode ]['mem'] = "<span>Mem: ".my_scale($vmdata['mem']*1024,$unit)."$unit / ".my_scale($vmdata['maxmem']*1024,$unit)."$unit</span>";
|
||||
$echo[$vmencode ]['disk'] = "<span>Disk: "._("Rd").": ".my_scale($vmdata['rdrate'],$unit)."$unit/s "._("Wr").": ".my_scale($vmdata['wrrate'],$unit)."$unit/s</span>";
|
||||
$echo[$vmencode ]['net'] = "<span>Net: "._("RX").": ".my_scale($vmdata['rxrate'],$unit)."$unit/s "._("TX").": ".my_scale($vmdata['txrate'],$unit)."$unit/s</span>";
|
||||
$echo[$vmencode ]['gcpu'] = "<span class='advanced'>"._("Guest CPU").": <span class='cpug-".$vm."'>".$vmdata['cpuguest']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuguest']."%;'>  </span><span></span></div></span>";
|
||||
$echo[$vmencode ]['hcpu'] = "<span class='advanced'>"._("Host CPU").": <span class='cpug-".$vm."'>".$vmdata['cpuhost']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuhost']."%;'>  </span><span></span></div></span>";
|
||||
$echo[$vmencode ]['mem'] = "<span>Mem: ".my_scale($vmdata['mem']*1024,$unit)."$unit / ".my_scale($vmdata['curmem']*1024,$unit)."$unit";
|
||||
if ($vmdata['maxmem'] == $vmdata['curmem']) $echo[$vmencode ]['mem'] .="  </span>";
|
||||
else $echo[$vmencode ]['mem'] .= " / ".my_scale($vmdata['maxmem']*1024,$unit)."$unit  </span>";
|
||||
$echo[$vmencode ]['disk'] = "<span>Disk: "._("Rd").": ".my_scale($vmdata['rdrate'],$unit)."$unit/s "._("Wr").": ".my_scale($vmdata['wrrate'],$unit)."$unit/s  </span>";
|
||||
$echo[$vmencode ]['net'] = "<span>Net: "._("RX").": ".my_scale($vmdata['rxrate'],$unit)."$unit/s "._("TX").": ".my_scale($vmdata['txrate'],$unit)."$unit/s  </span>";
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,13 @@ $docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
|
||||
$folders = ['/boot','/boot/config','/boot/config/plugins','/boot/syslinux','/var/log','/var/log/plugins','/boot/extra','/var/log/packages','/var/lib/pkgtools/packages','/tmp'];
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$bootenv = '/boot/syslinux';
|
||||
} elseif (is_file('/boot/grub/grub.cfg')) {
|
||||
$bootenv = '/boot/grub';
|
||||
}
|
||||
|
||||
$folders = ['/boot','/boot/config','/boot/config/plugins',$bootenv,'/var/log','/var/log/plugins','/boot/extra','/var/log/packages','/var/lib/pkgtools/packages','/tmp'];
|
||||
|
||||
// global variables
|
||||
$path = "/var/local/emhttp";
|
||||
@@ -113,8 +119,18 @@ function maskIP($file) {
|
||||
// anonymize public IPv4 addresses
|
||||
$rfc1918 = "(127|10|172\.1[6-9]|172\.2[0-9]|172\.3[0-1]|192\.168)((\.[0-9]{1,3}){2,3}([/\" .]|$))";
|
||||
run("sed -ri 's/([\"\[ ]){$rfc1918}/\\1@@@\\2\\3/g; s/([\"\[ ][0-9]{1,3}\.)([0-9]{1,3}\.){2}([0-9]{1,3})([/\" .]|$)/\\1XXX.XXX.\\3\\4/g; s/@@@//g' ".escapeshellarg($file)." 2>/dev/null");
|
||||
|
||||
// anonymize full IPv6 addresses
|
||||
run("sed -ri 's/([\"\[ ]([0-9a-f]{1,4}:){4})(([0-9a-f]{1,4}:){3}|:)([0-9a-f]{1,4})([/\" .]|$)/\\1XXXX:XXXX:XXXX:\\5\\6/g' ".escapeshellarg($file)." 2>/dev/null");
|
||||
$file_escaped = escapeshellarg($file);
|
||||
|
||||
// Anonymize IPv6 addresses without brackets
|
||||
run("sed -ri 's/(([0-9a-f]{1,4}:){4})(([0-9a-f]{1,4}:){3}|:)([0-9a-f]{1,4})([ .:/]|$)/\\1XXXX:XXXX:XXXX:\\5\\6/g' $file_escaped 2>/dev/null");
|
||||
|
||||
// Anonymize IPv6 addresses with brackets
|
||||
run("sed -ri 's/(\[([0-9a-f]{1,4}:){4})(([0-9a-f]{1,4}:){3}|:)([0-9a-f]{1,4})(\])([ .:/]|$)/\\1XXXX:XXXX:XXXX:\\5\\6/g' $file_escaped 2>/dev/null");
|
||||
|
||||
// Handle any remaining edge cases, e.g., addresses with subnet masks
|
||||
run("sed -ri 's/(([0-9a-f]{1,4}:){4})(([0-9a-f]{1,4}:){3}|:)([0-9a-f]{1,4})(\/[0-9]{1,3})([ .:/]|$)/\\1XXXX:XXXX:XXXX:\\5\\7/g' $file_escaped 2>/dev/null");
|
||||
}
|
||||
function download_url($url, $path="", $bg=false, $timeout=15) {
|
||||
$ch = curl_init();
|
||||
@@ -469,7 +485,7 @@ if (!$all) {
|
||||
maskIP("/$diag/config/network.cfg");
|
||||
}
|
||||
// include listening interfaces
|
||||
run("$docroot/webGui/scripts/show_interfaces ip|tr -d ' '|tr '#' ' '|tr ',' '\n' >".escapeshellarg("/$diag/config/listen.txt"));
|
||||
run("$docroot/webGui/scripts/show_interfaces ip|tr ',' '\n' >".escapeshellarg("/$diag/config/listen.txt"));
|
||||
run("$docroot/webGui/scripts/error_interfaces|sed 's/<i.*i>//' >>".escapeshellarg("/$diag/config/listen.txt"));
|
||||
if (!$all) maskIP("/$diag/config/listen.txt");
|
||||
|
||||
|
@@ -19,7 +19,7 @@ $proxy_ini = '/usr/local/emhttp/state/proxy.ini';
|
||||
$rnd = rand();
|
||||
|
||||
/* Comments to beginning of the proxy_sh file. */
|
||||
$comments = "#!/bin/bash\n"."# Do not edit. This file is autogenerated by /usr/local/sbin/set_proxy.\n";
|
||||
$comments = "#!/bin/bash\n"."# Do not edit. This file is autogenerated by /usr/local/emhttp/plugins/dynamix/scripts/set_proxy.\n";
|
||||
|
||||
/* Set verbose if command line switch is set. */
|
||||
$verbose = false;
|
||||
|
@@ -7,12 +7,12 @@ CALLER="show"
|
||||
|
||||
# include IP addresses?
|
||||
if check && [[ $1 == ip ]]; then
|
||||
ip=()
|
||||
for net in $bind; do
|
||||
ip+=("$net#[$(show -4 dev $net)#$(show -6 dev $net)]")
|
||||
IP=()
|
||||
for NET in $BIND; do
|
||||
IP+=("$NET [$(echo $(show dev $NET)|xargs)]")
|
||||
done
|
||||
bind=${ip[@]}
|
||||
BIND=${IP[@]}
|
||||
fi
|
||||
|
||||
# return list
|
||||
echo ${bind// /, }
|
||||
echo $BIND
|
||||
|
@@ -19,7 +19,7 @@ extract(parse_plugin_cfg('dynamix',true));
|
||||
// cron operation
|
||||
if ($argc==2 && $argv[1]=='cron') {
|
||||
// trim btrfs, xfs
|
||||
echo shell_exec("fstrim -va 2>/dev/null");
|
||||
xfs_btrfs_trim(false);
|
||||
// trim zfs
|
||||
zfs_trim(false);
|
||||
exit(0);
|
||||
@@ -72,16 +72,24 @@ function zfs_trim($write) {
|
||||
}
|
||||
}
|
||||
|
||||
function xfs_btrfs_trim($write) {
|
||||
exec("findmnt -lnt btrfs,xfs -o target,source|awk '\$2!~\"\\\\[\"{print \$1,\$2}'",$mounts);
|
||||
foreach ($mounts as $mount) {
|
||||
[$target,$source] = explode(' ',$mount);
|
||||
if (is_hdd($source)) continue;
|
||||
if ($write) write("$target: ... <i class='fa fa-spin fa-circle-o-notch'></i>\r");
|
||||
$trim = exec("fstrim -v $target 2>/dev/null");
|
||||
if ($write) {
|
||||
if ($trim) write("$trim on $source\r","\n"); else write("\r");
|
||||
} else {
|
||||
if ($trim) echo("$trim on $source\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write(_("TRIM operation started")."\n","\n","\n");
|
||||
// trim btrfs, xfs
|
||||
exec("findmnt -lnt btrfs,xfs -o target,source|awk '\$2!~\"\\\\[\"{print \$1,\$2}'",$mounts);
|
||||
foreach ($mounts as $mount) {
|
||||
[$target,$source] = explode(' ',$mount);
|
||||
if (is_hdd($source)) continue;
|
||||
write("$target: ... <i class='fa fa-spin fa-circle-o-notch'></i>\r");
|
||||
$trim = exec("fstrim -v $target 2>/dev/null");
|
||||
if ($trim) write("$trim on $source\r","\n"); else write("\r");
|
||||
}
|
||||
xfs_btrfs_trim(true);
|
||||
// trim zfs
|
||||
zfs_trim(true);
|
||||
write(_("Finished")."\n",'_DONE_','');
|
||||
|
@@ -64,7 +64,7 @@ if (!empty($strLoadedModules)) {
|
||||
// Yah! CPU and motherboard supported and enabled in BIOS
|
||||
$hvm = _('Enabled');
|
||||
} else {
|
||||
$hvm = '<a href="https://docs.unraid.net/unraid-os/manual/vm-management#system-preparation" target="_blank">';
|
||||
$hvm = '<a href="https://docs.unraid.net/go/vm-system-preparation/" target="_blank">';
|
||||
if (strpos($strCPUInfo,'vmx')===false && strpos($strCPUInfo, 'svm')===false) {
|
||||
// CPU doesn't support virtualization
|
||||
$hvm .= _('Not Available');
|
||||
@@ -82,7 +82,7 @@ if (!empty($iommu_groups)) {
|
||||
// Yah! CPU and motherboard supported and enabled in BIOS
|
||||
$iommu = _('Enabled');
|
||||
} else {
|
||||
$iommu = '<a href="https://docs.unraid.net/unraid-os/manual/vm-management#determining-hvmiommu-hardware-support" target="_blank">';
|
||||
$iommu = '<a href="https://docs.unraid.net/go/determining-hvmiommu-hardware-support/" target="_blank">';
|
||||
if (strpos($strCPUInfo,'vmx')===false && strpos($strCPUInfo, 'svm')===false) {
|
||||
// CPU doesn't support virtualization so iommu would be impossible
|
||||
$iommu .= _('Not Available');
|
||||
|
0
emhttp/plugins/dynamix/system/ZFS_cache
Normal file → Executable file
0
emhttp/plugins/dynamix/system/ZFS_cache
Normal file → Executable file
@@ -16,18 +16,32 @@
|
||||
* The $_POST variable contains a list of key/value parameters to be updated in the file.
|
||||
* There are a number of special parameters prefixed with a hash '#' character:
|
||||
*
|
||||
* #file : the pathname of the file to be updated. It does not need to previously exist.
|
||||
* If pathname is relative (no leading '/'), the configuration file will placed
|
||||
* placed under '/boot/config/plugins'.
|
||||
* This parameter may be omitted to perform a command execution only (see #command).
|
||||
* #section : if present, then the ini file consists of a set of named sections, and all of the
|
||||
* configuration parameters apply to this one particular section.
|
||||
* if omitted, then it's just a flat ini file without sections.
|
||||
* #default : if present, then the default values will be restored instead.
|
||||
* #include : specifies name of an include file to read and execute in before saving the file contents
|
||||
* #cleanup : if present then parameters with empty strings are omitted from being written to the file
|
||||
* #command : a shell command to execute after updating the configuration file
|
||||
* #arg : an array of arguments for the shell command
|
||||
* #file : The pathname of the file to be updated. It does not need to previously exist.
|
||||
* If pathname is relative (no leading '/'), the configuration file will placed
|
||||
* placed under '/boot/config/plugins/'.
|
||||
* This parameter may be omitted to perform a command execution only (see #command).
|
||||
*
|
||||
* #section : If present, then the ini file consists of a set of named sections, and all of the
|
||||
* configuration parameters apply to this one particular section.
|
||||
* If omitted, then it's just a flat ini file without sections.
|
||||
*
|
||||
* #default : If present, then the default values will be restored instead (from 'default.cfg').
|
||||
*
|
||||
* #defaultfile : If present in combination with #default, a custom defaults file will be restored
|
||||
* instead of the regular 'default.cfg' file. If pathname is relative (no leading '/'),
|
||||
* the given configuration file will be searched for under '/usr/local/emhttp/plugins/'.
|
||||
*
|
||||
* #defaults : If present in combination with #default, no defaults file but an associative array
|
||||
* passed through POST in format of '#defaults[key]=value' will be restored instead.
|
||||
* e.g. <input type="hidden" name="#defaults[SERVICE]" value="enable">
|
||||
* e.g. <input type="hidden" name="#defaults[INTERVAL]" value="25">
|
||||
* Beware: Browsers generally do not send empty values, if your default values include
|
||||
* any empty strings, you should preferably use a default configuration file instead.
|
||||
*
|
||||
* #include : Specifies name of an include file to read and execute in before saving the file contents.
|
||||
* #cleanup : If present then parameters with empty strings are omitted from being written to the file.
|
||||
* #command : A shell command to execute after updating the configuration file.
|
||||
* #arg : An array of arguments for the shell command.
|
||||
*/
|
||||
function write_log($string) {
|
||||
if (empty($string)) return;
|
||||
@@ -50,7 +64,19 @@ if (isset($_POST['#file'])) {
|
||||
if ($file && $file[0]!='/') $file = "/boot/config/plugins/$file";
|
||||
$section = $_POST['#section'] ?? false;
|
||||
$cleanup = isset($_POST['#cleanup']);
|
||||
$default = ($file && isset($_POST['#default'])) ? @parse_ini_file("$docroot/plugins/".basename(dirname($file))."/default.cfg", $section) : [];
|
||||
|
||||
$default = [];
|
||||
if($file && isset($_POST['#default'])) {
|
||||
if(isset($_POST['#defaultfile'])) {
|
||||
$defaultfile = $_POST['#defaultfile'];
|
||||
if ($defaultfile && $defaultfile[0]!='/') $defaultfile = "$docroot/plugins/$defaultfile";
|
||||
$default = parse_ini_file($defaultfile, $section) ?: [];
|
||||
} elseif(isset($_POST['#defaults'])) {
|
||||
$default = is_array($_POST['#defaults']) ? ($_POST['#defaults'] ?: []) : [];
|
||||
} else {
|
||||
$default = parse_ini_file("$docroot/plugins/".basename(dirname($file))."/default.cfg", $section) ?: [];
|
||||
}
|
||||
}
|
||||
|
||||
// if the file is not a raw file, it can be parsed
|
||||
$keys = (is_file($file) && !$raw_file) ? (parse_ini_file($file, $section) ?: []) : [];
|
||||
@@ -102,6 +128,7 @@ if (isset($_POST['#command'])) {
|
||||
while (!feof($proc)) {
|
||||
write_log(fgets($proc));
|
||||
}
|
||||
@pclose($proc);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@@ -76,6 +76,7 @@ done
|
||||
# LimeTech - poll for device with $UNRAIDLABEL present, with 30-sec timeout
|
||||
# this serves to synchronize this script with USB subsystem
|
||||
abort() {
|
||||
/bin/umount -a
|
||||
read -p "$1 - press ENTER key to reboot..."
|
||||
/bin/echo
|
||||
/sbin/reboot
|
||||
@@ -103,7 +104,12 @@ elif /sbin/blkid -s TYPE $DEVICE | /bin/grep -q "xfs" ; then
|
||||
/sbin/mount -v -t xfs -o auto,rw,noatime,nodiratime,discard $DEVICE /boot || abort "cannot mount $DEVICE"
|
||||
else
|
||||
/bin/echo "Checking $DEVICE ..."
|
||||
/sbin/fsck.fat -a -w $DEVICE 2>/dev/null
|
||||
FSCK=$(/sbin/fsck.fat -a -w $DEVICE 2>/dev/null)
|
||||
if [[ "$FSCK" == *"differences between boot sector and its backup"* ]] ; then
|
||||
/sbin/fsck.fat -w $DEVICE 2>/dev/null <<< "1"
|
||||
else
|
||||
echo "$FSCK"
|
||||
fi
|
||||
/sbin/mount -v -t vfat -o auto,rw,flush,noatime,nodiratime,dmask=77,fmask=177,shortname=mixed $DEVICE /boot || abort "cannot mount $DEVICE"
|
||||
fi
|
||||
|
||||
@@ -161,6 +167,7 @@ else
|
||||
/sbin/mount -w -v -n -o remount /
|
||||
RETVAL=$?
|
||||
[[ $RETVAL -gt 0 ]] && abort "failed to remount $UNRAIDROOT r/w with return value $RETVAL"
|
||||
[[ ! -f /etc/rc.d/rc.S.cont ]] && abort "unable to continue - you must remove 'root=$UNRAIDROOT' from syslinux.cfg"
|
||||
fi
|
||||
|
||||
# set permissions for non vfat boot on /boot
|
||||
|
@@ -58,14 +58,18 @@ if [[ -f $DOCKER_CFG ]]; then
|
||||
. $DOCKER_CFG
|
||||
fi
|
||||
|
||||
# Set storage driver appropriate for backing filesystem, override user setting
|
||||
BACKINGFS=$(findmnt --output FSTYPE --noheadings $DOCKER_ROOT)
|
||||
if [[ $BACKINGFS == btrfs ]]; then
|
||||
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=btrfs"
|
||||
elif [[ $BACKINGFS == xfs ]]; then
|
||||
# set storage driver to overlay2 if config value is found, otherwise fall back to native FS driver
|
||||
if [[ $(awk -F'"' '/^DOCKER_BACKINGFS=/{print $2}' $DOCKER_CFG 2>/dev/null) == overlay2 ]]; then
|
||||
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=overlay2"
|
||||
elif [[ $BACKINGFS == zfs ]]; then
|
||||
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=zfs"
|
||||
else
|
||||
BACKINGFS=$(findmnt --output FSTYPE --noheadings $DOCKER_ROOT)
|
||||
if [[ $BACKINGFS == btrfs ]]; then
|
||||
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=btrfs"
|
||||
elif [[ $BACKINGFS == xfs ]]; then
|
||||
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=overlay2"
|
||||
elif [[ $BACKINGFS == zfs ]]; then
|
||||
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=zfs"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Less verbose logging by default
|
||||
@@ -86,6 +90,7 @@ MTU=$(ip link show $PORT | grep -Po 'mtu \K\d+')
|
||||
if [[ -n $(ip -6 route show default dev $PORT) ]]; then
|
||||
DOCKER0='fd17::/64'
|
||||
DOCKER_OPTS="--ipv6 --fixed-cidr-v6=$DOCKER0 $DOCKER_OPTS"
|
||||
IPV6_FORWARD=${IPV6_FORWARD:=accept}
|
||||
# create IPv6 NAT rule for docker0
|
||||
[[ -z $(ip6tables -t nat -S | grep -o "$DOCKER0") ]] && run ip6tables -t nat -A POSTROUTING -s $DOCKER0 ! -o docker0 -j MASQUERADE
|
||||
else
|
||||
@@ -453,6 +458,11 @@ docker_network_start(){
|
||||
fi
|
||||
fi
|
||||
done
|
||||
# create IPv6 forward accept rule
|
||||
if [[ $IPV6_FORWARD == accept ]]; then
|
||||
log "creating forward accept rule for IPv6 network"
|
||||
ip6tables -P FORWARD ACCEPT
|
||||
fi
|
||||
log "Network started."
|
||||
}
|
||||
|
||||
|
@@ -61,6 +61,11 @@ mkdir -p $CONFIG/shares
|
||||
mkdir -p $CONFIG/ssh/root
|
||||
mkdir -p $CONFIG/ssl/certs
|
||||
|
||||
# Set default Docker backingfs type to native for existing installations
|
||||
if [[ -f "/boot/config/docker.cfg" ]]; then
|
||||
grep -q "DOCKER_BACKINGFS=" /boot/config/docker.cfg || echo "DOCKER_BACKINGFS=\"native\"" >> /boot/config/docker.cfg
|
||||
fi
|
||||
|
||||
# upgrade network configuration (if needed) and (re)generates our welcome text
|
||||
if [[ -x /usr/local/sbin/create_network_ini ]]; then
|
||||
/usr/local/sbin/create_network_ini init &>/dev/null &
|
||||
@@ -76,11 +81,6 @@ fi
|
||||
/usr/local/emhttp/webGui/scripts/notify smtp-init
|
||||
/usr/local/emhttp/webGui/scripts/notify cron-init
|
||||
|
||||
# restore favorites
|
||||
if [[ -x /usr/local/emhttp/webGui/scripts/restore_favorites ]]; then
|
||||
/usr/local/emhttp/webGui/scripts/restore_favorites
|
||||
fi
|
||||
|
||||
# start nchan monitoring -> stop all running nchan processes when no subscribers are connected
|
||||
if [[ -x /usr/local/sbin/monitor_nchan ]]; then
|
||||
/usr/local/sbin/monitor_nchan
|
||||
@@ -177,6 +177,11 @@ for LANGUAGE in $CONFIG/plugins/lang-*.xml; do
|
||||
done
|
||||
shopt -u nullglob
|
||||
|
||||
# restore favorites
|
||||
if [[ -x /usr/local/emhttp/webGui/scripts/restore_favorites ]]; then
|
||||
/usr/local/emhttp/webGui/scripts/restore_favorites
|
||||
fi
|
||||
|
||||
# Enable persistent bash history
|
||||
PERSISTENT_BASH_HISTORY=$(grep "persist_bash_history" /boot/config/plugins/dynamix/dynamix.cfg 2>/dev/null | cut -d'=' -f2 | sed 's/"//g')
|
||||
if [[ $PERSISTENT_BASH_HISTORY == 1 ]]; then
|
||||
|
@@ -123,8 +123,8 @@ nfsd_restart(){
|
||||
}
|
||||
|
||||
nfsd_reload(){
|
||||
# restart without info
|
||||
nfsd_restart &>/dev/null
|
||||
# reload without info
|
||||
$EXPORTFS -r &>/dev/null
|
||||
}
|
||||
|
||||
nfsd_update(){
|
||||
|
@@ -26,7 +26,7 @@ ntpd_running(){
|
||||
}
|
||||
|
||||
ntpd_build(){
|
||||
cp $CONF- $CONF
|
||||
[[ -f $CONF.orig ]] && cp $CONF.orig $CONF || cp $CONF $CONF.orig
|
||||
echo "# Generated entries follow:" >>$CONF
|
||||
echo "interface ignore wildcard" >>$CONF
|
||||
if check && [[ -n $BIND ]]; then
|
||||
@@ -101,8 +101,11 @@ ntpd_reload(){
|
||||
|
||||
ntpd_update(){
|
||||
# 0 = update needed, 1 = no action
|
||||
if ! ntpd_running; then exit 1; fi
|
||||
if check && [[ "$(this 'interface listen')" == "$BIND" ]]; then exit 1; else exit 0; fi
|
||||
# if ! ntpd_running; then exit 1; fi
|
||||
# if check && [[ "$(this 'interface listen')" == "$BIND" ]]; then exit 1; else exit 0; fi
|
||||
# upon network change always return 'update needed' - subsequent reload stops ntp complaining
|
||||
# when multiple interfaces have same IP address (such as shim-br0).
|
||||
if ntpd_running; then exit 0; else exit 1; fi
|
||||
}
|
||||
|
||||
ntpd_status(){
|
||||
|
@@ -65,6 +65,7 @@ samba_settings(){
|
||||
else
|
||||
echo "local master = No" >>$CONF
|
||||
fi
|
||||
echo "nmbd bind explicit broadcast = no" >> $CONF
|
||||
else
|
||||
echo "disable netbios = yes" >>$CONF
|
||||
echo "server min protocol = SMB2" >>$CONF
|
||||
|
58
etc/rc.d/rc.sysstat
Normal file
58
etc/rc.d/rc.sysstat
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# chkconfig: 12345 01 99
|
||||
# description: Reset the system activity logs
|
||||
#
|
||||
# /etc/rc.d/rc.sysstat
|
||||
# (C) 2000-2024 Sebastien Godard (sysstat <at> orange.fr)
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: sysstat
|
||||
# Required-Start:
|
||||
# Required-Stop:
|
||||
# Default-Start: 1 2 3 4 5
|
||||
# Default-Stop: 0 6
|
||||
# Description: Reset the system activity logs
|
||||
# Short-Description: Reset the system activity logs
|
||||
### END INIT INFO
|
||||
#@(#) sysstat-12.7.6 startup script:
|
||||
#@(#) Insert a dummy record in current daily data file.
|
||||
#@(#) This indicates that the counters have restarted from 0.
|
||||
|
||||
# Source functions library
|
||||
[ -r /etc/rc.d/init.d/functions ] && . /etc/rc.d/init.d/functions
|
||||
|
||||
RETVAL=0
|
||||
PIDFILE=/var/run/sysstat.pid
|
||||
[ -z "$UID" ] && UID=`id -u`
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start)
|
||||
[ $UID -eq 0 ] || exit 4
|
||||
echo $$ > $PIDFILE || exit 1
|
||||
echo -n "Calling the system activity data collector (sadc)... "
|
||||
/usr/lib64/sa/sa1 --boot
|
||||
[ $? -eq 0 ] || RETVAL=1
|
||||
rm -f $PIDFILE
|
||||
echo
|
||||
;;
|
||||
|
||||
status)
|
||||
[ -f $PIDFILE ] || RETVAL=3
|
||||
;;
|
||||
|
||||
stop)
|
||||
[ $UID -eq 0 ] || exit 4
|
||||
;;
|
||||
|
||||
restart|reload|force-reload|condrestart|try-restart)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: sysstat {start|stop|status|restart|reload|force-reload|condrestart|try-restart}"
|
||||
RETVAL=2
|
||||
esac
|
||||
|
||||
exit ${RETVAL}
|
||||
|
@@ -23,7 +23,6 @@
|
||||
# operation continues on to the next file.
|
||||
|
||||
PIDFILE="/var/run/mover.pid"
|
||||
CFGFILE="/boot/config/share.cfg"
|
||||
DEBUGGING=""
|
||||
|
||||
move() {
|
||||
@@ -38,14 +37,6 @@ start() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f $CFGFILE ]; then
|
||||
# Only start if shfs includes pools
|
||||
if ! grep -qs 'shareCacheEnabled="yes"' $CFGFILE ; then
|
||||
echo "mover: cache not enabled"
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $$ >/var/run/mover.pid
|
||||
echo "mover: started"
|
||||
|
||||
|
@@ -1,26 +0,0 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2021, Scott Ellis
|
||||
All rights reserved.
|
||||
Copyright (c) 2023, Limetech,Simon Fairweather
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -1 +0,0 @@
|
||||
module github.com/SimonFair/unraidwold/v2
|
@@ -1,63 +0,0 @@
|
||||
github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8Tw=
|
||||
github.com/antchfx/xmlquery v1.3.15/go.mod h1:zMDv5tIGjOxY/JCNNinnle7V/EwthZ5IT8eeCGJKRWA=
|
||||
github.com/antchfx/xpath v1.2.3 h1:CCZWOzv5bAqjVv0offZ2LVgVYFbeldKQVuLNbViZdes=
|
||||
github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20221205150000-2939327a8519 h1:OpkN/n40cmKenDQS+IOAeW9DLhYy4DADSeZnouCEV/E=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20221205150000-2939327a8519/go.mod h1:WyJJyfmJ0gWJvjV+ZH4DOgtOYZc1KOvYyBXWCLKxsUU=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@@ -1,236 +0,0 @@
|
||||
// Copyright (c) 2021, Scott Ellis
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2023 Limetech, Simon Fairweather.
|
||||
//
|
||||
// Unraid Wake-on-LAN(V1.0.0)
|
||||
//
|
||||
// Listens for a WOL magic packet (UDP) and ether frame type 0x0842
|
||||
// If a matching VM/Docker or LXC is found, it is started (if not already running) and resumed if paused
|
||||
//
|
||||
// Filters on ether proto 0x0842 or udp port 9
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/pcap"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
)
|
||||
|
||||
var logger *log.Logger
|
||||
|
||||
func main() {
|
||||
var logOutput io.Writer
|
||||
var (
|
||||
appVersion bool
|
||||
interfaceName string
|
||||
logFile string
|
||||
promiscuous bool
|
||||
)
|
||||
|
||||
flag.BoolVar(&appVersion, "version", false, "Print the version and copyright information")
|
||||
flag.StringVar(&interfaceName, "interface", "", "Network interface name (required)")
|
||||
flag.StringVar(&logFile, "log", "", "Log file path")
|
||||
flag.BoolVar(&promiscuous, "promiscuous", false, "Enable promiscuous mode")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
versionInfo := "Unraid Wake-on-LAN (V1.0.0)\nCopyright (c) 2021, Scott Ellis\nAll rights reserved.\nCopyright (c) 2023 Limetech, Simon Fairweather.\n"
|
||||
|
||||
// Check if the version flag is set
|
||||
if appVersion {
|
||||
fmt.Println(versionInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the required --interface flag is provided
|
||||
if interfaceName == "" {
|
||||
fmt.Println("Error: The --interface flag is required")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
deviceError := deviceExists(interfaceName)
|
||||
if (! deviceError) {
|
||||
fmt.Println("Error: The --interface network address is not valid")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
// Set up logging
|
||||
|
||||
if logFile != "" {
|
||||
// If a log file is specified, create or append to the file
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
//defer file.Close()
|
||||
logOutput = io.MultiWriter(file, os.Stdout) // Log to both file and stdout
|
||||
} else {
|
||||
// If no log file is specified, log to syslog
|
||||
syslogWriter, err := syslog.New(syslog.LOG_INFO|syslog.LOG_DAEMON, "Unraidwold")
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
logOutput = syslogWriter
|
||||
}
|
||||
|
||||
// Create a logger that writes to the specified output
|
||||
logger = log.New(logOutput, "", log.LstdFlags)
|
||||
|
||||
|
||||
var filter = "ether proto 0x0842 or udp port 9"
|
||||
|
||||
// Create a PID file
|
||||
pidFile := "/var/run/unraidwold.pid" // Change the path as needed
|
||||
err := writePIDFile(pidFile)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
logger.Println("Processing WOL Requests.")
|
||||
// Check if promiscuous mode is enabled
|
||||
if promiscuous {
|
||||
logger.Println("Promiscuous mode is enabled")
|
||||
}
|
||||
|
||||
|
||||
handle, err := pcap.OpenLive(interfaceName, 1600, promiscuous, pcap.BlockForever)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if err := handle.SetBPFFilter(filter); err != nil {
|
||||
log.Fatalf("Something in the BPF went wrong!: %v", err)
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
doneChan := make(chan bool, 1)
|
||||
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go processPackets(handle, signalChan, doneChan)
|
||||
|
||||
// Wait for a signal to exit
|
||||
<-doneChan
|
||||
//fmt.Println("Exiting...")
|
||||
removePIDFile(pidFile)
|
||||
logger.Println("Stopping WOL Daemon.")
|
||||
// Close down.
|
||||
os.Exit(1)
|
||||
//return
|
||||
}
|
||||
|
||||
func writePIDFile(pidFile string) error {
|
||||
pid := os.Getpid()
|
||||
pidStr := fmt.Sprintf("%d\n", pid)
|
||||
return ioutil.WriteFile(pidFile, []byte(pidStr), 0644)
|
||||
}
|
||||
|
||||
func removePIDFile(pidFile string) {
|
||||
err := os.Remove(pidFile)
|
||||
if err != nil {
|
||||
logger.Printf("Error removing PID file: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func processPackets(handle *pcap.Handle, signalChan chan os.Signal, doneChan chan bool) error {
|
||||
var mac string
|
||||
|
||||
source := gopacket.NewPacketSource(handle, handle.LinkType())
|
||||
for {
|
||||
select {
|
||||
case packet := <-source.Packets():
|
||||
ethLayer := packet.Layer(layers.LayerTypeEthernet)
|
||||
udpLayer := packet.Layer(layers.LayerTypeUDP)
|
||||
|
||||
if ethLayer != nil {
|
||||
ethernetPacket, _ := ethLayer.(*layers.Ethernet)
|
||||
if ethernetPacket.EthernetType == 0x0842 {
|
||||
payload := ethernetPacket.Payload
|
||||
mac = fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", payload[6], payload[7], payload[8], payload[9], payload[10], payload[11])
|
||||
}
|
||||
}
|
||||
|
||||
if udpLayer != nil {
|
||||
udpPacket, _ := udpLayer.(*layers.UDP)
|
||||
if udpPacket.DstPort == layers.UDPPort(9) {
|
||||
appPacket := packet.ApplicationLayer()
|
||||
if appPacket != nil {
|
||||
payload := appPacket.Payload()
|
||||
mac = fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", payload[12], payload[13], payload[14], payload[15], payload[16], payload[17])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go runcmd(mac)
|
||||
|
||||
case sig := <-signalChan:
|
||||
fmt.Printf("Received signal: %v\n", sig)
|
||||
doneChan <- true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func runcmd(mac string) bool {
|
||||
app := "/usr/local/emhttp/plugins/dynamix/include/WOLrun.php"
|
||||
arg := mac
|
||||
cmd := exec.Command(app, arg)
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
// Print the output
|
||||
logger.Println(string(stdout))
|
||||
return true
|
||||
}
|
||||
|
||||
// Return the first MAC address seen in the UDP WOL packet
|
||||
func GrabMACAddrUDP(packet gopacket.Packet) (string, error) {
|
||||
app := packet.ApplicationLayer()
|
||||
if app != nil {
|
||||
payload := app.Payload()
|
||||
mac := fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", payload[12], payload[13], payload[14], payload[15], payload[16], payload[17])
|
||||
//fmt.Printf("found MAC: %s\n", mac)
|
||||
return mac, nil
|
||||
}
|
||||
return "", errors.New("no MAC found in packet")
|
||||
}
|
||||
|
||||
// Check if the network device exists
|
||||
func deviceExists(interfacename string) bool {
|
||||
if interfacename == "" {
|
||||
fmt.Printf("No valid interface to listen on specified\n\n")
|
||||
return false
|
||||
}
|
||||
devices, err := pcap.FindAllDevs()
|
||||
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
if device.Name == interfacename {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
cd /tmp
|
||||
rm -rf unraidwol
|
||||
git clone https://github.com/SimonFair/unraidwol
|
||||
cd unraidwol/
|
||||
DATA_DIR=$(pwd)
|
||||
git checkout main
|
||||
PATH="$PATH:/usr/local/go/bin"
|
||||
go mod tidy
|
||||
go build
|
||||
cp /tmp/unraidwol/unraidwold /usr/libexec/unraid
|
Reference in New Issue
Block a user