mirror of
https://github.com/unraid/webgui.git
synced 2026-01-01 06:59:56 -06:00
Merge branch 'refactor/default-page-layout-nchan-abstraction' into refactor/default-page-layout-navigation
This commit is contained in:
@@ -146,11 +146,9 @@ _(Enable Docker)_:
|
||||
<?=mk_option(_var($dockercfg,'DOCKER_ENABLED'), 'no', _('No'))?>
|
||||
<?=mk_option(_var($dockercfg,'DOCKER_ENABLED'), 'yes', _('Yes'))?>
|
||||
</select>
|
||||
<?if (_var($var,'fsState')!="Started"):?>
|
||||
<?if (_var($var,'fsState')!="Started"):?>
|
||||
<span id="arraystopped"><i class="fa fa-warning icon warning"></i> <?=(_var($dockercfg,'DOCKER_ENABLED')=='yes') ? '_(Docker will be available after Array is Started)_' : '_(Apply to activate Docker after Array is Started)_'?></span>
|
||||
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE'))) || !is_dir(_var($dockercfg,'DOCKER_APP_CONFIG_PATH'))):?>
|
||||
<span class="basic"><i class="fa fa-warning icon warning"></i> _(One or more paths do not exist)_ (<a href="#" onclick="$('.advancedview').switchButton('option','checked',true); return false">_(view)_</a>)</span>
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
|
||||
:docker_enable_help:
|
||||
|
||||
@@ -192,9 +190,10 @@ _(Docker vDisk size)_:
|
||||
_(Docker vDisk location)_:
|
||||
: <input type="text" id="DOCKER_IMAGE_FILE1" name="DOCKER_IMAGE_FILE1" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars(_var($dockercfg,'DOCKER_IMAGE_FILE'))?>" placeholder="_(e.g.)_ /mnt/user/system/docker.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*(docker-xfs\.img|docker\.img)$">
|
||||
<span class="deleteLabel"><label><input type="checkbox" class="deleteCheckbox"> _(Delete vDisk file)_</label></span>
|
||||
<?if ($var['fsState'] != "Started"):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?elseif (!is_dir(dirname(htmlspecialchars(_var($dockercfg,'DOCKER_IMAGE_FILE'))))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
|
||||
<?endif;?><span id="IMAGE_ERROR1" class="errortext"></span>
|
||||
<?if ($var['fsState'] != "Started"):?>
|
||||
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?endif;?>
|
||||
<span id="IMAGE_ERROR1" class="errortext"></span>
|
||||
|
||||
:docker_vdisk_location_help:
|
||||
|
||||
@@ -203,9 +202,10 @@ _(Docker vDisk location)_:
|
||||
_(Docker directory)_:
|
||||
: <input type="text" id="DOCKER_IMAGE_FILE2" name="DOCKER_IMAGE_FILE2" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars(_var($dockercfg,'DOCKER_IMAGE_FILE'))?>" placeholder="_(e.g.)_ /mnt/user/system/docker" data-pickcloseonfile="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*/$">
|
||||
<span class="deleteLabel"><label><input type="checkbox" class="deleteCheckbox"> _(Delete directory)_</label></span>
|
||||
<?if ($var['fsState'] != "Started"):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE')))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
|
||||
<?endif;?><span id="IMAGE_ERROR2" class="errortext"></span>
|
||||
<?if ($var['fsState'] != "Started"):?>
|
||||
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?endif;?>
|
||||
<span id="IMAGE_ERROR2" class="errortext"></span>
|
||||
|
||||
:docker_vdisk_directory_help:
|
||||
|
||||
@@ -217,11 +217,9 @@ _(Docker storage driver)_:
|
||||
<?=mk_option(_var($dockercfg,'DOCKER_BACKINGFS'), 'overlay2', _('overlay2'))?>
|
||||
<?=mk_option(_var($dockercfg,'DOCKER_BACKINGFS'), 'native', _('native'))?>
|
||||
</select>
|
||||
<?if ($var['fsState'] != "Started"):?>
|
||||
<?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;?>
|
||||
<?endif;?>
|
||||
|
||||
:docker_storage_driver_help:
|
||||
|
||||
@@ -229,11 +227,9 @@ _(Docker storage driver)_:
|
||||
|
||||
_(Default appdata storage location)_:
|
||||
: <input type="text" id="DOCKER_APP_CONFIG_PATH" name="DOCKER_APP_CONFIG_PATH" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars(_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"):?>
|
||||
<?if ($var['fsState'] != "Started"):?>
|
||||
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?elseif (!is_dir(_var($dockercfg,'DOCKER_APP_CONFIG_PATH'))):?>
|
||||
<span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
|
||||
:docker_appdata_location_help:
|
||||
|
||||
|
||||
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-CirJJAiJ.js",
|
||||
"file": "_nuxt/unraid-components.client-Chq1f4tv.js",
|
||||
"name": "unraid-components.client",
|
||||
"src": ".nuxt/nuxt-custom-elements/entries/unraid-components.client.mjs",
|
||||
"isEntry": true,
|
||||
@@ -8,5 +8,5 @@
|
||||
"_nuxt/unraid-components-BvEqdEb_.css"
|
||||
]
|
||||
},
|
||||
"ts": 1743636862
|
||||
"ts": 1746122627
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
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 @@
|
||||
{
|
||||
"src/register.ts": {
|
||||
"file": "register._OziqIBF.js",
|
||||
"file": "register.D9MKs8Co.js",
|
||||
"name": "register",
|
||||
"src": "src/register.ts",
|
||||
"isEntry": true
|
||||
|
||||
@@ -4,8 +4,8 @@ Icon="icon-virtualization"
|
||||
Tag="columns"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
/* Copyright 2005-2025, Lime Technology
|
||||
* Copyright 2012-2025, Bergware International.
|
||||
* Copyright 2015-2021, Derek Macias, Eric Schultz, Jon Panozzo.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -112,13 +112,11 @@ _(Enable VMs)_:
|
||||
<?= mk_option($libvirt_service, 'disable', _('No'))?>
|
||||
<?= mk_option($libvirt_service, 'enable', _('Yes'))?>
|
||||
</select>
|
||||
<?if ($hardware):?>
|
||||
<?if (!$started):?>
|
||||
<?if ($hardware):?>
|
||||
<?if (!$started):?>
|
||||
<span id="arraystopped"><i class="fa fa-warning icon warning"></i> <?=($libvirt_service=='enable')?'_(VMs will be available after Array is Started)_':'_(Apply to activate VMs after Array is Started)_'?></span>
|
||||
<?elseif (!is_dir(dirname($domain_cfg['IMAGE_FILE'])) || !is_dir($domain_cfg['DOMAINDIR']) || !is_dir($domain_cfg['MEDIADIR'])):?>
|
||||
<span class="basic" style="display:inline"><i class="fa fa-warning icon warning"></i> _(One or more paths do not exist)_ (<a href="#" onclick="$('.advancedview').switchButton('option','checked',true); return false">_(view)_</a>)</span>
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
|
||||
:vms_enable_help:
|
||||
|
||||
@@ -154,25 +152,29 @@ _(Libvirt vdisk size)_:
|
||||
|
||||
_(Libvirt storage location)_:
|
||||
: <input type="text" id="IMAGE_FILE" name="IMAGE_FILE" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars($domain_cfg['IMAGE_FILE']);?>" placeholder="e.g. /mnt/user/system/libvirt/libvirt.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" required pattern="^[^\\]*libvirt\.img$">
|
||||
<?if (file_exists($domain_cfg['IMAGE_FILE'])):?><span id="deletePanel"><label><input type="checkbox" id="deleteCheckbox" /> _(Delete Image File)_</label></span><?endif;?>
|
||||
<?if (!$started):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?elseif (!is_dir(dirname($domain_cfg['IMAGE_FILE']))):?><span><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
|
||||
<?endif;?><span id="IMAGE_ERROR" class="errortext"></span>
|
||||
<?if (file_exists($domain_cfg['IMAGE_FILE'])):?>
|
||||
<span id="deletePanel"><label><input type="checkbox" id="deleteCheckbox" /> _(Delete Image File)_</label></span><?endif;?>
|
||||
<?if (!$started):?>
|
||||
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?endif;?>
|
||||
<span id="IMAGE_ERROR" class="errortext"></span>
|
||||
|
||||
:vms_libvirt_location_help:
|
||||
|
||||
<?endif;?>
|
||||
_(Default VM storage path)_:
|
||||
: <input type="text" id="domaindir" name="DOMAINDIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" value="<?=htmlspecialchars($domain_cfg['DOMAINDIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$">
|
||||
<?if (!$started):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?elseif (!is_dir($domain_cfg['DOMAINDIR'])):?><span><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span><?endif;?>
|
||||
<?if (!$started):?>
|
||||
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?endif;?>
|
||||
|
||||
:vms_libvirt_storage_help:
|
||||
|
||||
_(Default ISO storage path)_:
|
||||
: <input type="text" id="mediadir" name="MEDIADIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="<?=is_dir('/mnt/user')?'/mnt/user':'/mnt'?>" value="<?=htmlspecialchars($domain_cfg['MEDIADIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$">
|
||||
<?if (!$started):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?elseif (!is_dir($domain_cfg['MEDIADIR'])):?><span><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span><?endif;?>
|
||||
<?if (!$started):?>
|
||||
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
|
||||
<?endif;?>
|
||||
|
||||
:vms_libvirt_iso_storage_help:
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ Markdown="false"
|
||||
?>
|
||||
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.switchbutton.css')?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/jquery.filetree.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/jquery.ui.css")?>">
|
||||
|
||||
<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
|
||||
<script src="<?autov('/plugins/dynamix.vm.manager/javascript/dynamix.vm.manager.js')?>"></script>
|
||||
|
||||
@@ -110,6 +110,7 @@ if (strpos($strSelectedTemplate,"User-") !== false) {
|
||||
<form id="vmform" method="POST">
|
||||
<input type="hidden" name="domain[type]" value="kvm" />
|
||||
<input type="hidden" name="template[name]" value="<?=htmlspecialchars($strSelectedTemplateUT)?>" />
|
||||
<input type="hidden" name="template[iconold]" value="<?=htmlspecialchars($arrLoad['icon'])?>" />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
||||
@@ -245,6 +245,7 @@ if (isset($_POST['updatevm'])) {
|
||||
$xml = $_POST['xmldesc'];
|
||||
$arrExistingConfig = custom::createArray('domain',$xml);
|
||||
$newuuid = $arrExistingConfig['uuid'];
|
||||
if ($_POST['template']['iconold'] != $_POST['template']['icon']) $xml = preg_replace('/icon="[^"]*"/','icon="' . $_POST['template']['icon'] . '"',$xml);
|
||||
$xml = str_replace($olduuid,$newuuid,$xml);
|
||||
} else {
|
||||
// form view
|
||||
|
||||
@@ -466,11 +466,11 @@ function formatWarning(val) {
|
||||
|
||||
window.onunload = function(){
|
||||
<?if (_var($var,'fsState')=='Started'):?>
|
||||
mymonitor.stop();
|
||||
devices.stop();
|
||||
try {mymonitor.stop();} catch(e) {}
|
||||
try {devices.stop();} catch(e) {}
|
||||
<?endif;?>
|
||||
<?if ($spot==0):?>
|
||||
paritymonitor.stop();
|
||||
try {paritymonitor.stop();} catch(e) {}
|
||||
<?endif;?>
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -360,10 +360,10 @@ function doAction(action, title, id) {
|
||||
if (!access.includes(path[0])) return;
|
||||
var user = /^(user0?|rootshare)$/.test(path[1]);
|
||||
var root = '/'+path[0]+(user ? '/'+path[1] : '');
|
||||
var share = user||!path[2]||(link&&path.length==3) ? '' : path[2]+'/';
|
||||
var share = user || !path[2] || (link && path.length==3) ? '' : path[2]+'/';
|
||||
var ud = ['disks','remotes'].includes(path[1]); // unassigned devices
|
||||
var match = ud||user ? '' : '^(?!\\/mnt\\/user0?\\/).*$';
|
||||
var name = path.pop()||path.pop();
|
||||
var match = ud || user ? '' : '^(?!\\/mnt\\/(user0?|rootshare)\\/).*$';
|
||||
var name = path.pop() || path.pop();
|
||||
var hdlink = "<?=$var['fuse_directio'] == 1 ? '1' : ''?>";
|
||||
dfm.window = $("#dfm_dialogWindow");
|
||||
switch (action) {
|
||||
@@ -488,7 +488,7 @@ function doAction(action, title, id) {
|
||||
case 4: // move folder
|
||||
case 9: // move file
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
// check if 'mv' can be used
|
||||
if (path.length > 2) {
|
||||
if (user) {
|
||||
@@ -520,7 +520,7 @@ function doAction(action, title, id) {
|
||||
break;
|
||||
default:
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
break;
|
||||
}
|
||||
if (!target || !valid.test(target)) {errorTarget(); return;}
|
||||
@@ -558,7 +558,7 @@ function doAction(action, title, id) {
|
||||
case 4: // move folder
|
||||
case 9: // move file
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
// check if 'mv' can be used
|
||||
if (path.length > 2) {
|
||||
if (user) {
|
||||
@@ -583,7 +583,7 @@ function doAction(action, title, id) {
|
||||
break;
|
||||
default:
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
break;
|
||||
}
|
||||
if (!target || !valid.test(target)) {errorTarget(); return;}
|
||||
@@ -625,10 +625,10 @@ function doActions(action, title) {
|
||||
if (!access.includes(path[0])) return;
|
||||
var user = /^(user0?|rootshare)$/.test(path[1]);
|
||||
var root = '/'+path[0]+(user ? '/'+path[1] : '');
|
||||
var share = user||!path[2] ? '' : path[2]+'/';
|
||||
var share = user || !path[2] ? '' : path[2]+'/';
|
||||
var ud = ['disks','remotes'].includes(path[1]); // unassigned devices
|
||||
var match = ud||user ? '' : '^(?!.*(user0?|rootshare)).*$';
|
||||
var name = path.pop()||path.pop();
|
||||
var match = ud || user ? '' : '^(?!\\/mnt\\/(user0?|rootshare)\\/).*$';
|
||||
var name = path.pop() || path.pop();
|
||||
var hdlink = "<?=$var['fuse_directio'] == 1 ? '1' : ''?>";
|
||||
var u = false;
|
||||
var d = false;
|
||||
@@ -767,7 +767,7 @@ function doActions(action, title) {
|
||||
break;
|
||||
case 4: // move object
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
// check if 'mv' can be used
|
||||
if (path.length > 2) {
|
||||
if (user) {
|
||||
@@ -801,7 +801,7 @@ function doActions(action, title) {
|
||||
break;
|
||||
default:
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
break;
|
||||
}
|
||||
if (!target || !valid.test(target)) {errorTarget(); return;}
|
||||
@@ -839,7 +839,7 @@ function doActions(action, title) {
|
||||
break;
|
||||
case 4: // move object
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
// check if 'mv' can be used
|
||||
if (path.length > 2) {
|
||||
if (user) {
|
||||
@@ -866,7 +866,7 @@ function doActions(action, title) {
|
||||
break;
|
||||
default:
|
||||
// disallow mixing of disk and user shares
|
||||
var valid = ud ? /\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|rootshare)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)).+$|^\/boot\/.+/);
|
||||
var valid = ud ? /^\/mnt\/.+/ : (user ? /^\/mnt\/(user0?|disks|remotes)\/.+/ : /^\/mnt\/(?!.*(user0?|rootshare)\/).+$|^\/boot\/.+/);
|
||||
break;
|
||||
}
|
||||
if (!target || !valid.test(target)) {errorTarget(); return;}
|
||||
|
||||
@@ -120,17 +120,25 @@ $names = [];
|
||||
$SMBpublic = $NFSpublic= 0;
|
||||
foreach ($sec as $share => $prop) {
|
||||
if ( $prop['export'] == "-") continue;
|
||||
if ( $prop['security'] == "public") $SMBpublic++;
|
||||
if ( ($var['shareDisk']??"") == "no" && $share !=="flash" ) continue;
|
||||
if ( $prop['security'] == "public") {
|
||||
if ( $share == "flash" || (! isset($disks[$share]) || (isset($disks[$share]) && ($var['shareDisk']??"") == "yes" && $share !=="flash") ) )
|
||||
$SMBpublic++;
|
||||
}
|
||||
if ( ($var['shareDisk']??"") !== "yes" && $share !=="flash" ) continue;
|
||||
if ( ! $started && isset($disks[$share]) && $share !=="flash" ) continue;
|
||||
if ( ! isset($shares[$share]) && isset($disks[$share]) ) {$shares[$share]=$disks[$share]; $shares[$share]['diskexport'] = true;}
|
||||
if ( (! isset($shares[$share]) && isset($disks[$share]) && ($var['shareDisk']??"") == "yes" ) || $share == "flash" ) {$shares[$share]=$disks[$share]; $shares[$share]['diskexport'] = true;}
|
||||
}
|
||||
foreach ($sec_nfs as $share => $prop) {
|
||||
if ( $prop['export'] == "-") continue;
|
||||
if ( $prop['security'] == "public") $NFSpublic++;
|
||||
if ( ($var['shareDisk']??"") == "no" && $share !=="flash" ) continue;
|
||||
if ( ! $started && isset($disks[$share]) && $share !=="flash" ) continue;
|
||||
if ( ! isset($shares[$share]) && isset($disks[$share]) ) {$shares[$share]=$disks[$share]; $shares[$share]['diskexport'] = true;}
|
||||
if ($var['shareNFSEnabled']=='yes') {
|
||||
foreach ($sec_nfs as $share => $prop) {
|
||||
if ( $prop['export'] == "-") continue;
|
||||
if ( $prop['security'] == "public") {
|
||||
if ( $share == "flash" || (! isset($disks[$share]) || (isset($disks[$share]) && ($var['shareDisk']??"") == "yes" && $share !== "flash" ) ) )
|
||||
$NFSpublic++;
|
||||
}
|
||||
if ( ($var['shareDisk']??"") == "no" && $share !=="flash" ) continue;
|
||||
if ( ! $started && isset($disks[$share]) && $share !=="flash" ) continue;
|
||||
if ( ( ! isset($shares[$share]) && isset($disks[$share]) && ($var['shareDisk']??"") == "yes") || $share == "flash" ) {$shares[$share]=$disks[$share]; $shares[$share]['diskexport'] = true;}
|
||||
}
|
||||
}
|
||||
|
||||
$passwd = $nopass = 0;
|
||||
@@ -786,8 +794,10 @@ jQuery.prototype.mixedView = function(s) {
|
||||
}
|
||||
}
|
||||
|
||||
<?if (file_exists($cookie)):?>
|
||||
var cookie = JSON.parse('<?=file_get_contents($cookie)?>');
|
||||
<?
|
||||
$cookie_content = @file_get_contents($cookie);
|
||||
if ( @json_decode($cookie_content,true) ):?>
|
||||
var cookie = JSON.parse('<?=trim($cookie_content);?>');
|
||||
<?else:?>
|
||||
var cookie = {};
|
||||
<?endif;?>
|
||||
@@ -872,31 +882,30 @@ function sanitizeMultiCookie(cookieName, delimiter, removeDuplicates=false) {
|
||||
}
|
||||
|
||||
function initCharts(clear) {
|
||||
$.post('/webGui/include/InitCharts.php',{cmd:'get'},function(data) {
|
||||
data = JSON.parse(data);
|
||||
// initialize graphs entries
|
||||
var now = new Date().getTime();
|
||||
if (!clear) {
|
||||
var c = data.cpu.split(';');
|
||||
var r = data.rxd.split(';');
|
||||
var t = data.txd.split(';');
|
||||
for (var i=0; i < cpuline; i++) {
|
||||
var x = now + i;
|
||||
var y = c[i]||0; cpu.push({x,y});
|
||||
}
|
||||
cputime = x + 1;
|
||||
} else {
|
||||
// clear network graph
|
||||
var r = ''; var t = '';
|
||||
rxd = []; txd = [];
|
||||
}
|
||||
for (var i=0; i < netline; i++) {
|
||||
// initialize graphs entries
|
||||
var data = [];
|
||||
data.cpu = data.rxd = data.txd ="";
|
||||
var now = new Date().getTime();
|
||||
if (!clear) {
|
||||
var c = data.cpu.split(';');
|
||||
var r = data.rxd.split(';');
|
||||
var t = data.txd.split(';');
|
||||
for (var i=0; i < cpuline; i++) {
|
||||
var x = now + i;
|
||||
var y = r[i]||0; rxd.push({x,y});
|
||||
var y = t[i]||0; txd.push({x,y});
|
||||
var y = c[i]||0; cpu.push({x,y});
|
||||
}
|
||||
nettime = x + 1;
|
||||
});
|
||||
cputime = x + 1;
|
||||
} else {
|
||||
// clear network graph
|
||||
var r = ''; var t = '';
|
||||
rxd = []; txd = [];
|
||||
}
|
||||
for (var i=0; i < netline; i++) {
|
||||
var x = now + i;
|
||||
var y = r[i]||0; rxd.push({x,y});
|
||||
var y = t[i]||0; txd.push({x,y});
|
||||
}
|
||||
nettime = x + 1;
|
||||
}
|
||||
|
||||
function resetCharts() {
|
||||
|
||||
@@ -33,7 +33,7 @@ $(function(){
|
||||
<input type="hidden" name="#include" value="/webGui/include/update.file.php">
|
||||
<input type="hidden" name="#file" value="<?=$file;?>">
|
||||
_(Samba extra configuration)_:
|
||||
: <textarea spellcheck="false" cols="80" rows="<?=substr_count($text,"\n")+1?>" maxlength="2048" name="text" style="resize:none;font-family:bitstream;width:65.5%" <?if ($var['fsState']=="Started"):?>disabled<?endif;?>><?=htmlspecialchars($text)?></textarea>
|
||||
: <textarea spellcheck="false" cols="80" rows="<?=substr_count($text,"\n")+1?>" maxlength="2048" name="text" <?if ($var['fsState']=="Started"):?>disabled<?endif;?>><?=htmlspecialchars($text)?></textarea>
|
||||
|
||||
|
||||
: <input type="submit" value="_(Apply)_" disabled><input type="button" value="_(Done)_" onclick="done()"><?if ($var['fsState']=="Started"):?>*_(Array must be **Stopped** to change)_*<?endif;?>
|
||||
|
||||
@@ -83,7 +83,7 @@ function update_wifi(load) {
|
||||
var wifi = JSON.parse(text);
|
||||
$('#connected').html(wifi.active);
|
||||
$('#my_networks').html(wifi.saved);
|
||||
if (wifi.other.length) $('#other_networks').html(wifi.other);
|
||||
if (typeof wifi.other == 'string') $('#other_networks').html(wifi.other);
|
||||
}
|
||||
});
|
||||
timers.wifi = setTimeout(update_wifi,6000);
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// Included in login.php
|
||||
|
||||
// Only start a session to check if they have a cookie that looks like our session
|
||||
$server_name = strtok($_SERVER['HTTP_HOST'],":");
|
||||
$server_name = strtok($_SERVER['HTTP_HOST'], ":");
|
||||
if (!empty($_COOKIE['unraid_'.md5($server_name)])) {
|
||||
// Start the session so we can check if $_SESSION has data
|
||||
if (session_status()==PHP_SESSION_NONE) session_start();
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Check if the user is already logged in
|
||||
if ($_SESSION && !empty($_SESSION['unraid_user'])) {
|
||||
@@ -15,10 +17,11 @@ if (!empty($_COOKIE['unraid_'.md5($server_name)])) {
|
||||
}
|
||||
}
|
||||
|
||||
function readFromFile($file): string {
|
||||
function readFromFile($file): string
|
||||
{
|
||||
$text = "";
|
||||
if (file_exists($file) && filesize($file) > 0) {
|
||||
$fp = fopen($file,"r");
|
||||
$fp = fopen($file, "r");
|
||||
if (flock($fp, LOCK_EX)) {
|
||||
$text = fread($fp, filesize($file));
|
||||
flock($fp, LOCK_UN);
|
||||
@@ -28,8 +31,9 @@ function readFromFile($file): string {
|
||||
return $text;
|
||||
}
|
||||
|
||||
function appendToFile($file, $text): void {
|
||||
$fp = fopen($file,"a");
|
||||
function appendToFile($file, $text): void
|
||||
{
|
||||
$fp = fopen($file, "a");
|
||||
if (flock($fp, LOCK_EX)) {
|
||||
fwrite($fp, $text);
|
||||
fflush($fp);
|
||||
@@ -38,8 +42,9 @@ function appendToFile($file, $text): void {
|
||||
}
|
||||
}
|
||||
|
||||
function writeToFile($file, $text): void {
|
||||
$fp = fopen($file,"w");
|
||||
function writeToFile($file, $text): void
|
||||
{
|
||||
$fp = fopen($file, "w");
|
||||
if (flock($fp, LOCK_EX)) {
|
||||
fwrite($fp, $text);
|
||||
fflush($fp);
|
||||
@@ -56,7 +61,8 @@ function isValidTimeStamp($timestamp)
|
||||
&& ($timestamp >= ~PHP_INT_MAX);
|
||||
}
|
||||
|
||||
function cleanupFails(string $failFile, int $time): int {
|
||||
function cleanupFails(string $failFile, int $time): int
|
||||
{
|
||||
global $cooldown;
|
||||
|
||||
// Read existing fails
|
||||
@@ -67,8 +73,8 @@ function cleanupFails(string $failFile, int $time): int {
|
||||
// Remove entries older than $cooldown minutes, and entries that are not timestamps
|
||||
$updateFails = false;
|
||||
foreach ((array) $fails as $key => $value) {
|
||||
if ( !isValidTimeStamp($value) || ($time - $value > $cooldown) || ($value > $time) ) {
|
||||
unset ($fails[$key]);
|
||||
if (!isValidTimeStamp($value) || ($time - $value > $cooldown) || ($value > $time)) {
|
||||
unset($fails[$key]);
|
||||
$updateFails = true;
|
||||
}
|
||||
}
|
||||
@@ -81,94 +87,19 @@ function cleanupFails(string $failFile, int $time): int {
|
||||
return count($fails);
|
||||
}
|
||||
|
||||
function verifyUsernamePassword(string $username, string $password): bool {
|
||||
if ($username != "root") return false;
|
||||
function verifyUsernamePassword(string $username, string $password): bool
|
||||
{
|
||||
if ($username != "root") {
|
||||
return false;
|
||||
}
|
||||
|
||||
$output = exec("/usr/bin/getent shadow $username");
|
||||
if ($output === false) return false;
|
||||
if ($output === false) {
|
||||
return false;
|
||||
}
|
||||
$credentials = explode(":", $output);
|
||||
return password_verify($password, $credentials[1]);
|
||||
}
|
||||
|
||||
function verifyTwoFactorToken(string $username, string $token): bool {
|
||||
try {
|
||||
// Create curl client
|
||||
$curlClient = curl_init();
|
||||
curl_setopt($curlClient, CURLOPT_HEADER, true);
|
||||
curl_setopt($curlClient, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curlClient, CURLOPT_UNIX_SOCKET_PATH, '/var/run/unraid-api.sock');
|
||||
curl_setopt($curlClient, CURLOPT_URL, 'http://unixsocket/verify');
|
||||
curl_setopt($curlClient, CURLOPT_BUFFERSIZE, 256);
|
||||
curl_setopt($curlClient, CURLOPT_TIMEOUT, 5);
|
||||
curl_setopt($curlClient, CURLOPT_HTTPHEADER, array('Content-Type:application/json', 'Origin: /var/run/unraid-notifications.sock'));
|
||||
curl_setopt($curlClient, CURLOPT_POSTFIELDS, json_encode([
|
||||
'username' => $username,
|
||||
'token' => $token
|
||||
]));
|
||||
|
||||
// Send the request
|
||||
curl_exec($curlClient);
|
||||
|
||||
// Get the http status code
|
||||
$httpCode = curl_getinfo($curlClient, CURLINFO_HTTP_CODE);
|
||||
|
||||
// Close the connection
|
||||
curl_close($curlClient);
|
||||
|
||||
// Error
|
||||
// This should accept 200 or 204 status codes
|
||||
if ($httpCode !== 200 && $httpCode !== 204) {
|
||||
// Log error to syslog
|
||||
my_logger("2FA code for {$username} is invalid, blocking access!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log success to syslog
|
||||
my_logger("2FA code for {$username} is valid, allowing login!");
|
||||
|
||||
// Success
|
||||
return true;
|
||||
} catch (Exception $exception) {
|
||||
// Error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a haystack ends in a needle
|
||||
function endsWith($haystack, $needle): bool {
|
||||
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
||||
}
|
||||
|
||||
// Check if we're accessing this via a wildcard cert
|
||||
function isWildcardCert(): bool {
|
||||
global $server_name;
|
||||
return endsWith($server_name, '.myunraid.net');
|
||||
}
|
||||
|
||||
// Check if we're accessing this locally via the expected myunraid.net url
|
||||
function isLocalAccess(): bool {
|
||||
global $nginx, $server_name;
|
||||
return isWildcardCert() && $nginx['NGINX_LANFQDN'] === $server_name;
|
||||
}
|
||||
|
||||
// Check if we're accessing this remotely via the expected myunraid.net url
|
||||
function isRemoteAccess(): bool {
|
||||
global $nginx, $server_name;
|
||||
return isWildcardCert() && $nginx['NGINX_WANFQDN'] === $server_name;
|
||||
}
|
||||
|
||||
// Check if 2fa is enabled for local (requires USE_SSL to be "auto" so no alternate urls can access the server)
|
||||
function isLocalTwoFactorEnabled(): bool {
|
||||
global $nginx, $my_servers;
|
||||
return $nginx['NGINX_USESSL'] === "auto" && ($my_servers['local']['2Fa']??'') === 'yes';
|
||||
}
|
||||
|
||||
// Check if 2fa is enabled for remote
|
||||
function isRemoteTwoFactorEnabled(): bool {
|
||||
global $my_servers;
|
||||
return ($my_servers['remote']['2Fa']??'') === 'yes';
|
||||
}
|
||||
|
||||
// Load configs into memory
|
||||
$my_servers = @parse_ini_file('/boot/config/plugins/dynamix.my.servers/myservers.cfg', true);
|
||||
$nginx = @parse_ini_file('/var/local/emhttp/nginx.ini');
|
||||
@@ -180,38 +111,34 @@ $remote_addr = $_SERVER['REMOTE_ADDR'] ?? "unknown";
|
||||
$failFile = "/var/log/pwfail/{$remote_addr}";
|
||||
|
||||
// Get the credentials
|
||||
$username = $_POST['username']??'';
|
||||
$password = $_POST['password']??'';
|
||||
$token = $_REQUEST['token']??'';
|
||||
|
||||
// Check if we need 2fa
|
||||
$twoFactorRequired = (isLocalAccess() && isLocalTwoFactorEnabled()) || (isRemoteAccess() && isRemoteTwoFactorEnabled());
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
// If we have a username + password combo attempt to login
|
||||
if (!empty($username) && !empty($password)) {
|
||||
try {
|
||||
// Bail if we're missing the 2FA token and we expect one
|
||||
if (isWildcardCert() && $twoFactorRequired && empty($token)) throw new Exception(_('No 2FA token detected'));
|
||||
|
||||
// Read existing fails, cleanup expired ones
|
||||
$time = time();
|
||||
$failCount = cleanupFails($failFile, $time);
|
||||
|
||||
// Check if we're limited
|
||||
if ($failCount >= $maxFails) {
|
||||
if ($failCount == $maxFails) my_logger("Ignoring login attempts for {$username} from {$remote_addr}");
|
||||
if ($failCount == $maxFails) {
|
||||
my_logger("Ignoring login attempts for {$username} from {$remote_addr}");
|
||||
}
|
||||
throw new Exception(_('Too many invalid login attempts'));
|
||||
}
|
||||
|
||||
// Bail if username + password combo doesn't work
|
||||
if (!verifyUsernamePassword($username, $password)) throw new Exception(_('Invalid username or password'));
|
||||
|
||||
// Bail if we need a token but it's invalid
|
||||
if (isWildcardCert() && $twoFactorRequired && !verifyTwoFactorToken($username, $token)) throw new Exception(_('Invalid 2FA token'));
|
||||
if (!verifyUsernamePassword($username, $password)) {
|
||||
throw new Exception(_('Invalid username or password'));
|
||||
}
|
||||
|
||||
// Successful login, start session
|
||||
@unlink($failFile);
|
||||
if (session_status()==PHP_SESSION_NONE) session_start();
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
$_SESSION['unraid_login'] = time();
|
||||
$_SESSION['unraid_user'] = $username;
|
||||
session_regenerate_id(true);
|
||||
@@ -395,7 +322,7 @@ $isDarkTheme = $themeHelper->isDarkTheme();
|
||||
}
|
||||
#login .error {
|
||||
color: red;
|
||||
margin-top: -20px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#login .content {
|
||||
padding: 2rem;
|
||||
@@ -486,7 +413,7 @@ $isDarkTheme = $themeHelper->isDarkTheme();
|
||||
<?=htmlspecialchars($var['NAME'])?>
|
||||
</h1>
|
||||
<h2>
|
||||
<?=htmlspecialchars($var['COMMENT'])?>
|
||||
<?=htmlspecialchars($var['COMMENT'])?>
|
||||
</h2>
|
||||
|
||||
<div class="case">
|
||||
@@ -500,82 +427,38 @@ $isDarkTheme = $themeHelper->isDarkTheme();
|
||||
</div>
|
||||
|
||||
<div class="form">
|
||||
<form class="js-removeTimeout" action="/login" method="POST">
|
||||
<? if (($twoFactorRequired && !empty($token)) || !$twoFactorRequired) { ?>
|
||||
<p>
|
||||
<input name="username" type="text" placeholder="<?=_('Username')?>" autocapitalize="none" autocomplete="off" spellcheck="false" autofocus required>
|
||||
<input name="password" type="password" placeholder="<?=_('Password')?>" required>
|
||||
<? if ($twoFactorRequired && !empty($token)) { ?>
|
||||
<input name="token" type="hidden" value="<?= $token ?>">
|
||||
<? } ?>
|
||||
</p>
|
||||
<? if ($error) echo "<p class='error'>$error</p>"; ?>
|
||||
<p>
|
||||
<button type="submit" class="button button--small"><?=_('Login')?></button>
|
||||
</p>
|
||||
<? } else { ?>
|
||||
<? if ($error) { ?>
|
||||
<div>
|
||||
<p class="error" style="padding-top:10px;"><?= $error ?></p>
|
||||
</div>
|
||||
<? } else { ?>
|
||||
<div>
|
||||
<p class="error" style="padding-top:10px;" title="<?= _('Please access this server via the My Servers Dashboard') ?>"><?= _('No 2FA token detected') ?></p>
|
||||
</div>
|
||||
<? } ?>
|
||||
<div>
|
||||
<a href="https://forums.unraid.net/my-servers/" class="button button--small" title="<?=_('Go to My Servers Dashboard')?>"><?=_('Go to My Servers Dashboard')?></a>
|
||||
</div>
|
||||
<? } ?>
|
||||
<script type="text/javascript">
|
||||
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) {
|
||||
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 action="/login" method="POST">
|
||||
<p>
|
||||
<input name="username" type="text" placeholder="<?=_('Username')?>" autocapitalize="none" autocomplete="off" spellcheck="false" autofocus required>
|
||||
<input name="password" type="password" placeholder="<?=_('Password')?>" required>
|
||||
</p>
|
||||
<p>
|
||||
<button type="submit" class="button button--small"><?=_('Login')?></button>
|
||||
</p>
|
||||
<?php if ($error) { ?>
|
||||
<p class="error"><?= $error ?></p>
|
||||
<?php } ?>
|
||||
</form>
|
||||
|
||||
<? if (($twoFactorRequired && !empty($token)) || !$twoFactorRequired) { ?>
|
||||
<div class="js-addTimeout hidden">
|
||||
<p class="error" style="padding-top:10px;"><?=_('Transparent 2FA Token timed out')?></p>
|
||||
<a href="https://forums.unraid.net/my-servers/" class="button button--small" title="<?=_('Go to My Servers Dashboard')?>"><?=_('Go to My Servers Dashboard')?></a>
|
||||
</div>
|
||||
<? } ?>
|
||||
</div>
|
||||
|
||||
<? if (($twoFactorRequired && !empty($token)) || !$twoFactorRequired) { ?>
|
||||
<p class="js-removeTimeout"><a href="https://docs.unraid.net/go/lost-root-password/" target="_blank"><?=_('Password recovery')?></a></p>
|
||||
<? } ?>
|
||||
<a href="https://docs.unraid.net/go/lost-root-password/" target="_blank"><?=_('Password recovery')?></a>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<? if ($twoFactorRequired && !empty($token)) { ?>
|
||||
<script type="text/javascript">
|
||||
const $elsToRemove = document.querySelectorAll('.js-removeTimeout');
|
||||
const $elsToShow = document.querySelectorAll('.js-addTimeout');
|
||||
/**
|
||||
* A user can manually refresh the page or submit with the wrong username/password
|
||||
* the t2fa token will be re-used on these page refreshes. We need to keep track of the timeout across potential page
|
||||
* loads rather than setting the timer with a fresh timeout each page load
|
||||
*/
|
||||
const tokenName = '<?=$token?>'.slice(-20);
|
||||
const ts = Date.now();
|
||||
const timeoutStarted = sessionStorage.getItem(tokenName) ? Number(sessionStorage.getItem(tokenName)) : ts;
|
||||
const timeoutDiff = ts - timeoutStarted; // current timestamp minus timestamp when token first set
|
||||
const timeoutMS = 297000 - timeoutDiff; // 5 minutes minus 3seconds or (5*60)*1000ms - 3000ms = 297000
|
||||
sessionStorage.setItem(tokenName, timeoutStarted);
|
||||
const tokenTimeout = setTimeout(() => {
|
||||
$elsToRemove.forEach(z => z.remove()); // remove elements
|
||||
$elsToShow.forEach(z => z.classList.remove('hidden')); // add elements
|
||||
}, timeoutMS); // if timeoutMS is negative value the timeout will trigger immediately
|
||||
</script>
|
||||
<? } ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
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) {
|
||||
const formParentElement = document.querySelector('.form');
|
||||
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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -25,18 +25,18 @@ function write(&$rows) {
|
||||
|
||||
function validdir($dir) {
|
||||
$path = realpath($dir);
|
||||
return in_array(explode('/',$path)[1]??'',['mnt','boot']) ? $path : '';
|
||||
return in_array(explode('/', $path)[1] ?? '', ['mnt','boot']) ? $path : '';
|
||||
}
|
||||
|
||||
function escapeQuote($data) {
|
||||
return str_replace('"','"',$data);
|
||||
return str_replace('"','"', $data);
|
||||
}
|
||||
|
||||
function add($number, $name, $single='', $plural='s') {
|
||||
return $number.' '._($name.($number==1 ? $single : $plural));
|
||||
}
|
||||
|
||||
function age($number,$time) {
|
||||
function age($number, $time) {
|
||||
return sprintf(_('%s '.($number==1 ? $time : $time.'s').' ago'),$number);
|
||||
}
|
||||
|
||||
@@ -44,25 +44,25 @@ function my_age($time) {
|
||||
if (!is_numeric($time)) $time = time();
|
||||
$age = new DateTime('@'.$time);
|
||||
$age = date_create('now')->diff($age);
|
||||
if ($age->y > 0) return age($age->y,'year');
|
||||
if ($age->m > 0) return age($age->m,'month');
|
||||
if ($age->d > 0) return age($age->d,'day');
|
||||
if ($age->h > 0) return age($age->h,'hour');
|
||||
if ($age->i > 0) return age($age->i,'minute');
|
||||
return age($age->s,'second');
|
||||
if ($age->y > 0) return age($age->y, 'year');
|
||||
if ($age->m > 0) return age($age->m, 'month');
|
||||
if ($age->d > 0) return age($age->d, 'day');
|
||||
if ($age->h > 0) return age($age->h, 'hour');
|
||||
if ($age->i > 0) return age($age->i, 'minute');
|
||||
return age($age->s, 'second');
|
||||
}
|
||||
|
||||
function parent_link() {
|
||||
global $dir,$path;
|
||||
global $dir, $path;
|
||||
$parent = dirname($dir);
|
||||
return $parent=='/' ? false : '<a href="/'.$path.'?dir='.rawurlencode(htmlspecialchars($parent)).'">'._('Parent Directory').'</a>';
|
||||
return $parent == '/' ? false : '<a href="/'.$path.'?dir='.rawurlencode(htmlspecialchars($parent)).'">'._('Parent Directory').'</a>';
|
||||
}
|
||||
|
||||
function my_devs(&$devs,$name,$menu) {
|
||||
global $disks,$lock;
|
||||
global $disks, $lock;
|
||||
$text = []; $i = 0;
|
||||
foreach ($devs as $dev) {
|
||||
if ($lock=='---') {
|
||||
if ($lock == '---') {
|
||||
$text[$i] = '<a class="info" onclick="return false"><i class="lock fa fa-fw fa-hdd-o grey-text"></i></a> ---';
|
||||
} else {
|
||||
switch ($disks[$dev]['luksState']??0) {
|
||||
@@ -72,7 +72,7 @@ function my_devs(&$devs,$name,$menu) {
|
||||
case 3: $text[$i] = '<span class="dfm_device"><a class="info" onclick="return false"><i class="lock fa fa-fw fa-lock red-text"></i><span>'._('Locked: wrong encryption key').'</span></a>'; break;
|
||||
default: $text[$i] = '<span class="dfm_device"><a class="info" onclick="return false"><i class="lock fa fa-fw fa-lock red-text"></i><span>'._('Locked: unknown error').'</span></a>'; break;
|
||||
}
|
||||
$root = $dev=='flash' ? "/boot/$name" : "/mnt/$dev/$name";
|
||||
$root = ($dev == 'flash' ? "/boot/$name" : "/mnt/$dev/$name");
|
||||
$text[$i] .= '<span id="device_'.$i.'" class="hand" onclick="'.$menu.'(\''.$root.'\','.$i.')" oncontextmenu="'.$menu.'(\''.$root.'\','.$i.');return false">'.compress($dev,11,0).'</span></span>';
|
||||
}
|
||||
$i++;
|
||||
@@ -141,8 +141,8 @@ $path = unscript($_GET['path']);
|
||||
$fmt = "%F {$display['time']}";
|
||||
$dirs = $files = [];
|
||||
$total = $objs = 0;
|
||||
[$null,$root,$main,$next,$rest] = my_explode('/',$dir,5);
|
||||
$user = $root=='mnt' && in_array($main,['user','user0']);
|
||||
[$null,$root,$main,$next,$rest] = my_explode('/', $dir, 5);
|
||||
$user = $root=='mnt' && in_array($main, ['user','user0']);
|
||||
$lock = $root=='mnt' ? ($main ?: '---') : ($root=='boot' ? _('flash') : '---');
|
||||
$ishare = $root=='mnt' && (!$main || !$next || ($main=='rootshare' && !$rest));
|
||||
$folder = $lock=='---' ? _('DEVICE') : ($ishare ? _('SHARE') : _('FOLDER'));
|
||||
@@ -154,13 +154,14 @@ if ($user ) {
|
||||
}
|
||||
|
||||
$stat = popen("shopt -s dotglob;stat -L -c'%F|%U|%A|%s|%Y|%n' ".escapeshellarg($dir)."/* 2>/dev/null",'r');
|
||||
while (($row = fgets($stat))!==false) {
|
||||
|
||||
while (($row = fgets($stat)) !== false) {
|
||||
[$type,$owner,$perm,$size,$time,$name] = explode('|',rtrim($row,"\n"),6);
|
||||
$dev = explode('/',$name,5);
|
||||
$devs = explode(',',$user ? $set[basename($name)]??$shares[$dev[3]]['cachePool']??'' : $lock);
|
||||
$dev = explode('/', $name, 5);
|
||||
$devs = explode(',', $user ? $set[basename($name)] ?? $shares[$dev[3]]['cachePool'] ?? '' : $lock);
|
||||
$objs++;
|
||||
$text = [];
|
||||
if ($type[0]=='d') {
|
||||
if ($type[0] == 'd') {
|
||||
$text[] = '<tr><td><i id="check_'.$objs.'" class="fa fa-fw fa-square-o" onclick="selectOne(this.id)"></i></td>';
|
||||
$text[] = '<td data=""><i class="fa fa-folder-o"></i></td>';
|
||||
$text[] = '<td><a id="name_'.$objs.'" oncontextmenu="folderContextMenu(this.id,\'right\');return false" href="/'.$path.'?dir='.rawurlencode(htmlspecialchars($name)).'">'.htmlspecialchars(basename($name)).'</a></td>';
|
||||
@@ -172,8 +173,8 @@ while (($row = fgets($stat))!==false) {
|
||||
$text[] = '<td><i id="row_'.$objs.'" data="'.escapeQuote($name).'" type="d" class="fa fa-plus-square-o" onclick="folderContextMenu(this.id,\'both\')" oncontextmenu="folderContextMenu(this.id,\'both\');return false">...</i></td></tr>';
|
||||
$dirs[] = gzdeflate(implode($text));
|
||||
} else {
|
||||
$ext = strtolower(pathinfo($name,PATHINFO_EXTENSION));
|
||||
$tag = count($devs)>1 ? 'warning' : '';
|
||||
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
||||
$tag = count($devs) > 1 ? 'warning' : '';
|
||||
$text[] = '<tr><td><i id="check_'.$objs.'" class="fa fa-fw fa-square-o" onclick="selectOne(this.id)"></i></td>';
|
||||
$text[] = '<td class="ext" data="'.$ext.'"><i class="'.icon_class($ext).'"></i></td>';
|
||||
$text[] = '<td id="name_'.$objs.'" class="'.$tag.'" onclick="fileEdit(this.id)" oncontextmenu="fileContextMenu(this.id,\'right\');return false">'.htmlspecialchars(basename($name)).'</td>';
|
||||
@@ -189,6 +190,7 @@ while (($row = fgets($stat))!==false) {
|
||||
}
|
||||
|
||||
pclose($stat);
|
||||
|
||||
if ($link = parent_link()) echo '<tbody class="tablesorter-infoOnly"><tr><td></td><td><i class="fa fa-folder-open-o"></i></td><td>',$link,'</td><td colspan="6"></td></tr></tbody>';
|
||||
echo write($dirs),write($files),'<tfoot><tr><td></td><td></td><td colspan="7">',add($objs,'object'),': ',add($dirs,'director','y','ies'),', ',add($files,'file'),' (',my_scale($total,$unit),' ',$unit,' ',_('total'),')</td></tr></tfoot>';
|
||||
?>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?PHP
|
||||
<?php
|
||||
/* Copyright 2005-2024, Lime Technology
|
||||
* Copyright 2012-2024, Bergware International.
|
||||
*
|
||||
@@ -10,7 +10,7 @@
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
<?php
|
||||
require_once "$docroot/plugins/dynamix/include/ThemeHelper.php";
|
||||
$themeHelper = new ThemeHelper($display['theme'], $display['width']);
|
||||
$theme = $themeHelper->getThemeName(); // keep $theme, $themes1, $themes2 vars for plugin backwards compatibility for the time being
|
||||
@@ -18,7 +18,7 @@ $themes1 = $themeHelper->isTopNavTheme();
|
||||
$themes2 = $themeHelper->isSidebarTheme();
|
||||
$themeHelper->updateDockerLogColor($docroot);
|
||||
|
||||
$display['font'] = filter_var($_COOKIE['fontSize'] ?? $display['font'] ?? '',FILTER_SANITIZE_NUMBER_FLOAT,FILTER_FLAG_ALLOW_FRACTION);
|
||||
$display['font'] = filter_var($_COOKIE['fontSize'] ?? $display['font'] ?? '', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
|
||||
|
||||
$header = $display['header']; // keep $header, $backgnd vars for plugin backwards compatibility for the time being
|
||||
$backgnd = $display['background'];
|
||||
@@ -32,7 +32,9 @@ $safemode = _var($var,'safeMode')=='yes';
|
||||
$banner = "$config/plugins/dynamix/banner.png";
|
||||
|
||||
$notes = '/var/tmp/unRAIDServer.txt';
|
||||
if (!file_exists($notes)) file_put_contents($notes,shell_exec("$docroot/plugins/dynamix.plugin.manager/scripts/plugin changes $docroot/plugins/unRAIDServer/unRAIDServer.plg"));
|
||||
if (!file_exists($notes)) {
|
||||
file_put_contents($notes, shell_exec("$docroot/plugins/dynamix.plugin.manager/scripts/plugin changes $docroot/plugins/unRAIDServer/unRAIDServer.plg"));
|
||||
}
|
||||
|
||||
$taskPages = find_pages('Tasks');
|
||||
$buttonPages = find_pages('Buttons');
|
||||
@@ -70,18 +72,24 @@ if (count($pages)) {
|
||||
if (count($running)) file_put_contents($nchan_pid,implode("\n",$running)."\n"); else @unlink($nchan_pid);
|
||||
}
|
||||
|
||||
function annotate($text) {echo "\n<!--\n",str_repeat("#",strlen($text)),"\n$text\n",str_repeat("#",strlen($text)),"\n-->\n";}
|
||||
function annotate($text)
|
||||
{
|
||||
echo "\n<!--\n",str_repeat("#", strlen($text)),"\n$text\n",str_repeat("#", strlen($text)),"\n-->\n";
|
||||
}
|
||||
|
||||
function generateReloadScript($loadMinutes) {
|
||||
if ($loadMinutes <= 0) return '';
|
||||
function generateReloadScript($loadMinutes)
|
||||
{
|
||||
if ($loadMinutes <= 0) {
|
||||
return '';
|
||||
}
|
||||
$interval = $loadMinutes * 60000;
|
||||
return "\n<script>timers.reload = setInterval(function(){if (nchanPaused === false)location.reload();},{$interval});</script>\n";
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html <?=$display['rtl']?>lang="<?=strtok($locale,'_')?:'en'?>" class="<?= $themeHelper->getThemeHtmlClass() ?>">
|
||||
<html <?=$display['rtl']?>lang="<?=strtok($locale, '_') ?: 'en'?>" class="<?= $themeHelper->getThemeHtmlClass() ?>">
|
||||
<head>
|
||||
<title><?=_var($var,'NAME')?>/<?=_var($myPage,'name')?></title>
|
||||
<title><?=_var($var, 'NAME')?>/<?=_var($myPage, 'name')?></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
|
||||
@@ -89,7 +97,7 @@ function generateReloadScript($loadMinutes) {
|
||||
<meta name="viewport" content="width=1300">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="referrer" content="same-origin">
|
||||
<link type="image/png" rel="shortcut icon" href="/webGui/images/<?=_var($var,'mdColor','red-on')?>.png">
|
||||
<link type="image/png" rel="shortcut icon" href="/webGui/images/<?=_var($var, 'mdColor', 'red-on')?>.png">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-fonts.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-cases.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/font-awesome.css")?>">
|
||||
@@ -117,10 +125,10 @@ function generateReloadScript($loadMinutes) {
|
||||
<?endif;?>
|
||||
}
|
||||
|
||||
<?
|
||||
<?php
|
||||
// Generate sidebar icon CSS if using sidebar theme
|
||||
if ($themeHelper->isSidebarTheme()) {
|
||||
echo generate_sidebar_icon_css($taskPages, $buttonPages);
|
||||
echo generate_sidebar_icon_css($taskPages, $buttonPages);
|
||||
}
|
||||
?>
|
||||
</style>
|
||||
@@ -132,24 +140,23 @@ if ($themeHelper->isSidebarTheme()) {
|
||||
<script src="<?autov('/webGui/javascript/dynamix.js')?>"></script>
|
||||
<script src="<?autov('/webGui/javascript/translate.'.($locale?:'en_US').'.js')?>"></script>
|
||||
|
||||
<? require_once "$docroot/plugins/dynamix/include/DefaultPageLayout/HeadInlineJS.php"; ?>
|
||||
<?php require_once "$docroot/plugins/dynamix/include/DefaultPageLayout/HeadInlineJS.php"; ?>
|
||||
|
||||
<?
|
||||
<?php
|
||||
foreach ($buttonPages as $button) {
|
||||
annotate($button['file']);
|
||||
// include page specific stylesheets (if existing)
|
||||
$css = "/{$button['root']}/sheets/{$button['name']}";
|
||||
$css_stock = "$css.css";
|
||||
$css_theme = "$css-$theme.css"; // @todo add syslog for deprecation notice
|
||||
if (is_file($docroot.$css_stock)) echo '<link type="text/css" rel="stylesheet" href="',autov($css_stock),'">',"\n";
|
||||
if (is_file($docroot.$css_theme)) echo '<link type="text/css" rel="stylesheet" href="',autov($css_theme),'">',"\n";
|
||||
// create page content
|
||||
eval('?>'.parse_text($button['text']));
|
||||
}
|
||||
|
||||
// Reload page every X minutes during extended viewing?
|
||||
if (isset($myPage['Load'])) {
|
||||
echo generateReloadScript($myPage['Load']);
|
||||
annotate($button['file']);
|
||||
// include page specific stylesheets (if existing)
|
||||
$css = "/{$button['root']}/sheets/{$button['name']}";
|
||||
$css_stock = "$css.css";
|
||||
$css_theme = "$css-$theme.css"; // @todo add syslog for deprecation notice
|
||||
if (is_file($docroot.$css_stock)) {
|
||||
echo '<link type="text/css" rel="stylesheet" href="',autov($css_stock),'">',"\n";
|
||||
}
|
||||
if (is_file($docroot.$css_theme)) {
|
||||
echo '<link type="text/css" rel="stylesheet" href="',autov($css_theme),'">',"\n";
|
||||
}
|
||||
// create page content
|
||||
eval('?>'.parse_text($button['text']));
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -174,55 +181,59 @@ if (isset($myPage['Tabs'])) $display['tabs'] = strtolower($myPage['Tabs'])=='tru
|
||||
$tabbed = $display['tabs']==0 && count($pages)>1;
|
||||
|
||||
foreach ($pages as $page) {
|
||||
$close = false;
|
||||
if (isset($page['Title'])) {
|
||||
eval("\$title=\"".htmlspecialchars($page['Title'])."\";");
|
||||
if ($tabbed) {
|
||||
echo "<div class='tab'><input type='radio' id='tab{$tab}' name='tabs' onclick='settab(this.id)'><label for='tab{$tab}'>";
|
||||
echo tab_title($title,$page['root'],_var($page,'Tag',false));
|
||||
echo "</label><div class='content'>";
|
||||
$close = true;
|
||||
} else {
|
||||
if ($tab==1) echo "<div class='tab'><input type='radio' id='tab{$tab}' name='tabs'><div class='content shift'>";
|
||||
echo "<div class='title'><span class='left'>";
|
||||
echo tab_title($title,$page['root'],_var($page,'Tag',false));
|
||||
echo "</span></div>";
|
||||
}
|
||||
$tab++;
|
||||
}
|
||||
if (isset($page['Type']) && $page['Type']=='menu') {
|
||||
$pgs = find_pages($page['name']);
|
||||
foreach ($pgs as $pg) {
|
||||
@eval("\$title=\"".htmlspecialchars($pg['Title'])."\";");
|
||||
$icon = _var($pg,'Icon',"<i class='icon-app PanelIcon'></i>");
|
||||
if (substr($icon,-4)=='.png') {
|
||||
$root = $pg['root'];
|
||||
if (file_exists("$docroot/$root/images/$icon")) {
|
||||
$icon = "<img src='/$root/images/$icon' class='PanelImg'>";
|
||||
} elseif (file_exists("$docroot/$root/$icon")) {
|
||||
$icon = "<img src='/$root/$icon' class='PanelImg'>";
|
||||
$close = false;
|
||||
if (isset($page['Title'])) {
|
||||
eval("\$title=\"".htmlspecialchars($page['Title'])."\";");
|
||||
if ($tabbed) {
|
||||
echo "<div class='tab'><input type='radio' id='tab{$tab}' name='tabs' onclick='settab(this.id)'><label for='tab{$tab}'>";
|
||||
echo tab_title($title, $page['root'], _var($page, 'Tag', false));
|
||||
echo "</label><div class='content'>";
|
||||
$close = true;
|
||||
} else {
|
||||
$icon = "<i class='icon-app PanelIcon'></i>";
|
||||
if ($tab == 1) {
|
||||
echo "<div class='tab'><input type='radio' id='tab{$tab}' name='tabs'><div class='content shift'>";
|
||||
}
|
||||
echo "<div class='title'><span class='left'>";
|
||||
echo tab_title($title, $page['root'], _var($page, 'Tag', false));
|
||||
echo "</span></div>";
|
||||
}
|
||||
} elseif (substr($icon,0,5)=='icon-') {
|
||||
$icon = "<i class='$icon PanelIcon'></i>";
|
||||
} elseif ($icon[0]!='<') {
|
||||
if (substr($icon,0,3)!='fa-') $icon = "fa-$icon";
|
||||
$icon = "<i class='fa $icon PanelIcon'></i>";
|
||||
}
|
||||
echo "<div class=\"Panel\"><a href=\"/$path/{$pg['name']}\" onclick=\"$.cookie('one','tab1')\"><span>$icon</span><div class=\"PanelText\">"._($title)."</div></a></div>";
|
||||
$tab++;
|
||||
}
|
||||
}
|
||||
annotate($page['file']);
|
||||
// include page specific stylesheets (if existing)
|
||||
$css = "/{$page['root']}/sheets/{$page['name']}";
|
||||
$css_stock = "$css.css";
|
||||
$css_theme = "$css-$theme.css";
|
||||
if (is_file($docroot.$css_stock)) echo '<link type="text/css" rel="stylesheet" href="',autov($css_stock),'">',"\n";
|
||||
if (is_file($docroot.$css_theme)) echo '<link type="text/css" rel="stylesheet" href="',autov($css_theme),'">',"\n";
|
||||
// create page content
|
||||
empty($page['Markdown']) || $page['Markdown']=='true' ? eval('?>'.Markdown(parse_text($page['text']))) : eval('?>'.parse_text($page['text']));
|
||||
if ($close) echo "</div></div>";
|
||||
if (isset($page['Type']) && $page['Type'] == 'menu') {
|
||||
$pgs = find_pages($page['name']);
|
||||
foreach ($pgs as $pg) {
|
||||
@eval("\$title=\"".htmlspecialchars($pg['Title'])."\";");
|
||||
$icon = _var($pg, 'Icon', "<i class='icon-app PanelIcon'></i>");
|
||||
if (substr($icon, -4) == '.png') {
|
||||
$root = $pg['root'];
|
||||
if (file_exists("$docroot/$root/images/$icon")) {
|
||||
$icon = "<img src='/$root/images/$icon' class='PanelImg'>";
|
||||
} elseif (file_exists("$docroot/$root/$icon")) {
|
||||
$icon = "<img src='/$root/$icon' class='PanelImg'>";
|
||||
} else {
|
||||
$icon = "<i class='icon-app PanelIcon'></i>";
|
||||
}
|
||||
} elseif (substr($icon, 0, 5) == 'icon-') {
|
||||
$icon = "<i class='$icon PanelIcon'></i>";
|
||||
} elseif ($icon[0] != '<') {
|
||||
if (substr($icon, 0, 3) != 'fa-') {
|
||||
$icon = "fa-$icon";
|
||||
}
|
||||
$icon = "<i class='fa $icon PanelIcon'></i>";
|
||||
}
|
||||
echo "<div class=\"Panel\"><a href=\"/$path/{$pg['name']}\" onclick=\"$.cookie('one','tab1')\"><span>$icon</span><div class=\"PanelText\">"._($title)."</div></a></div>";
|
||||
}
|
||||
}
|
||||
annotate($page['file']);
|
||||
// include page specific stylesheets (if existing)
|
||||
$css = "/{$page['root']}/sheets/{$page['name']}";
|
||||
$css_stock = "$css.css";
|
||||
$css_theme = "$css-$theme.css";
|
||||
if (is_file($docroot.$css_stock)) echo '<link type="text/css" rel="stylesheet" href="',autov($css_stock),'">',"\n";
|
||||
if (is_file($docroot.$css_theme)) echo '<link type="text/css" rel="stylesheet" href="',autov($css_theme),'">',"\n";
|
||||
// create page content
|
||||
empty($page['Markdown']) || $page['Markdown']=='true' ? eval('?>'.Markdown(parse_text($page['text']))) : eval('?>'.parse_text($page['text']));
|
||||
if ($close) echo "</div></div>";
|
||||
}
|
||||
unset($pages,$page,$pgs,$pg,$icon,$nchan,$running,$start,$stop,$row,$script,$opt,$nchan_run);
|
||||
?>
|
||||
@@ -231,7 +242,7 @@ unset($pages,$page,$pgs,$pg,$icon,$nchan,$running,$start,$stop,$row,$script,$opt
|
||||
<form name="rebootNow" method="POST" action="/webGui/include/Boot.php"><input type="hidden" name="cmd" value="reboot"></form>
|
||||
<iframe id="progressFrame" name="progressFrame" frameborder="0"></iframe>
|
||||
|
||||
<? require_once "$docroot/webGui/include/DefaultPageLayout/Footer.php"; ?>
|
||||
<? require_once "$docroot/plugins/dynamix/include/DefaultPageLayout/BodyInlineJS.php"; ?>
|
||||
<?php require_once "$docroot/webGui/include/DefaultPageLayout/Footer.php"; ?>
|
||||
<?php require_once "$docroot/plugins/dynamix/include/DefaultPageLayout/BodyInlineJS.php"; ?>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -407,35 +407,40 @@ $('body').on('click','a,.ca_href', function(e) {
|
||||
}
|
||||
});
|
||||
|
||||
// Start & stop live updates when window loses focus
|
||||
// Only include window focus/blur event handlers when live updates are disabled
|
||||
// to prevent unnecessary page reloads when live updates are already handling data refreshes
|
||||
// nchanPaused / blurTimer used elsewhere so need to always be defined
|
||||
var nchanPaused = false;
|
||||
var blurTimer = false;
|
||||
|
||||
<? if ( $display['liveUpdate'] == "no" ):?>
|
||||
$(window).focus(function() {
|
||||
nchanFocusStart();
|
||||
});
|
||||
|
||||
// Stop nchan on loss of focus
|
||||
<? if ( $display['liveUpdate'] == "no" ):?>
|
||||
$(window).blur(function() {
|
||||
blurTimer = setTimeout(function(){
|
||||
nchanFocusStop();
|
||||
},30000);
|
||||
});
|
||||
<?endif;?>
|
||||
|
||||
document.addEventListener("visibilitychange", (event) => {
|
||||
<? if ( $display['liveUpdate'] == "no" ):?>
|
||||
if (document.hidden) {
|
||||
nchanFocusStop();
|
||||
}
|
||||
<?else:?>
|
||||
if (document.hidden) {
|
||||
nchanFocusStop();
|
||||
} else {
|
||||
nchanFocusStart();
|
||||
<? if (isset($myPage['Load']) && $myPage['Load'] > 0):?>
|
||||
if ( dialogOpen() ) {
|
||||
clearInterval(timers.reload);
|
||||
setTimerReload();
|
||||
nchanFocusStart();
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
<?else:?>
|
||||
nchanFocusStart();
|
||||
<?endif;?>
|
||||
}
|
||||
<?endif;?>
|
||||
});
|
||||
|
||||
function nchanFocusStart() {
|
||||
@@ -476,4 +481,5 @@ function nchanFocusStop(banner=true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
<?endif;?>
|
||||
</script>
|
||||
|
||||
@@ -585,4 +585,23 @@ $.ajaxPrefilter(function(s, orig, xhr){
|
||||
s.data += "csrf_token="+csrf_token;
|
||||
}
|
||||
});
|
||||
|
||||
<?if (isset($myPage['Load']) && $myPage['Load'] > 0):?>
|
||||
// Reload page every X minutes during extended viewing?
|
||||
function setTimerReload() {
|
||||
timers.reload = setInterval(function(){
|
||||
if (nchanPaused === false && ! dialogOpen() ) {
|
||||
location.reload();
|
||||
}
|
||||
},<?=$myPage['Load'] * 60000?>);
|
||||
}
|
||||
$(document).click(function(e) {
|
||||
clearInterval(timers.reload);
|
||||
setTimerReload();
|
||||
});
|
||||
function dialogOpen() {
|
||||
return ($('.sweet-alert').is(':visible') || $('.swal-overlay--show-modal').is(':visible') );
|
||||
}
|
||||
setTimerReload();
|
||||
<?endif;?>
|
||||
</script>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*
|
||||
* History:
|
||||
*
|
||||
* 1.2.2 - allow user shares to UD shares
|
||||
* 1.2.1 - exclude folders from the /mnt/ root folder
|
||||
* 1.2.0 - adapted by Bergware for use in Unraid - support UTF-8 encoding & hardening
|
||||
* 1.1.1 - SECURITY: forcing root to prevent users from determining system's file structure (per DaveBrad)
|
||||
@@ -27,15 +28,21 @@
|
||||
*/
|
||||
|
||||
function path($dir) {
|
||||
return mb_substr($dir,-1)=='/' ? $dir : $dir.'/';
|
||||
return mb_substr($dir,-1) == '/' ? $dir : $dir.'/';
|
||||
}
|
||||
|
||||
function is_top($dir) {
|
||||
global $root;
|
||||
return mb_strlen($dir)>mb_strlen($root);
|
||||
return mb_strlen($dir) > mb_strlen($root);
|
||||
}
|
||||
function is_low($dir) {
|
||||
global $root;
|
||||
return mb_substr($dir,0,mb_strlen($root))==$root;
|
||||
|
||||
function no_dots($name) {
|
||||
return !in_array($name, ['.','..']);
|
||||
}
|
||||
|
||||
function my_dir($name) {
|
||||
global $rootdir, $userdir, $topdir, $UDincluded;
|
||||
return ($rootdir === $userdir && in_array($name, $UDincluded)) ? $topdir : $rootdir;
|
||||
}
|
||||
|
||||
$root = path(realpath($_POST['root']));
|
||||
@@ -44,49 +51,60 @@ if (!$root) exit("ERROR: Root filesystem directory not set in jqueryFileTree.php
|
||||
$docroot = '/usr/local/emhttp';
|
||||
require_once "$docroot/webGui/include/Secure.php";
|
||||
|
||||
$mntdir = '/mnt/';
|
||||
$userdir = '/mnt/user/';
|
||||
$rootdir = path(realpath($_POST['dir']));
|
||||
$topdir = str_replace($userdir, $mntdir, $rootdir);
|
||||
$filters = (array)$_POST['filter'];
|
||||
$match = $_POST['match'];
|
||||
$checkbox = $_POST['multiSelect']=='true' ? "<input type='checkbox'>" : "";
|
||||
$checkbox = $_POST['multiSelect'] == 'true' ? "<input type='checkbox'>" : "";
|
||||
|
||||
/* Excluded folders to not show in the dropdown in the '/mnt/' directory only. */
|
||||
$excludedFolders = ["RecycleBin", "addons", "rootshare", "user0"];
|
||||
// Excluded UD shares to hide under '/mnt'
|
||||
$UDexcluded = ['RecycleBin', 'addons', 'rootshare'];
|
||||
// Included UD shares to show under '/mnt/user'
|
||||
$UDincluded = ['disks','remotes'];
|
||||
|
||||
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>";
|
||||
if ($_POST['show_parent'] == 'true' && is_top($rootdir)) {
|
||||
echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"".htmlspecialchars(dirname($rootdir))."\">..</a></li>";
|
||||
}
|
||||
|
||||
if (is_low($rootdir) && is_dir($rootdir)) {
|
||||
$dirs = $files = [];
|
||||
$names = array_filter(scandir($rootdir, SCANDIR_SORT_NONE), function($n) { return $n != '.' && $n != '..'; });
|
||||
if (is_dir($rootdir)) {
|
||||
$dirs = $files = [];
|
||||
$names = array_filter(scandir($rootdir, SCANDIR_SORT_NONE), 'no_dots');
|
||||
// add UD shares under /mnt/user
|
||||
foreach ($UDincluded as $name) {
|
||||
if (!is_dir($topdir.$name)) continue;
|
||||
if ($rootdir === $userdir) {
|
||||
if (!in_array($name, $names)) $names[] = $name;
|
||||
} else {
|
||||
if (explode('/', $topdir)[2] === $name) $names = array_merge($names, array_filter(scandir($topdir, SCANDIR_SORT_NONE), 'no_dots'));
|
||||
}
|
||||
}
|
||||
natcasesort($names);
|
||||
foreach ($names as $name) {
|
||||
if (is_dir($rootdir . $name)) {
|
||||
if (is_dir(my_dir($name).$name)) {
|
||||
$dirs[] = $name;
|
||||
} else {
|
||||
$files[] = $name;
|
||||
}
|
||||
}
|
||||
foreach ($dirs as $name) {
|
||||
$htmlRel = htmlspecialchars($rootdir . $name);
|
||||
$htmlName = htmlspecialchars(mb_strlen($name) <= 33 ? $name : mb_substr($name, 0, 30) . '...');
|
||||
|
||||
/* Exclude '.Recycle.Bin' from all directories */
|
||||
if ($name === ".Recycle.Bin") continue;
|
||||
|
||||
/* Exclude folders only when directory is '/mnt/' */
|
||||
if (in_array($name, $excludedFolders) && $rootdir === "/mnt/") continue;
|
||||
|
||||
echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"$htmlRel/\">$htmlName</a></li>";
|
||||
// Exclude '.Recycle.Bin' from all shares and UD folders from '/mnt'
|
||||
if ($name === '.Recycle.Bin' || ($rootdir === $mntdir && in_array($name, $UDexcluded))) continue;
|
||||
$htmlRel = htmlspecialchars(my_dir($name).$name);
|
||||
$htmlName = htmlspecialchars(mb_strlen($name) <= 33 ? $name : mb_substr($name, 0, 30).'...');
|
||||
if (empty($match) || preg_match("/$match/", $rootdir.$name.'/')) {
|
||||
echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"$htmlRel/\">$htmlName</a></li>";
|
||||
}
|
||||
}
|
||||
foreach ($files as $name) {
|
||||
$htmlRel = htmlspecialchars($rootdir . $name);
|
||||
$htmlRel = htmlspecialchars(my_dir($name).$name);
|
||||
$htmlName = htmlspecialchars($name);
|
||||
$ext = mb_strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
||||
foreach ($filters as $filter) {
|
||||
if (empty($filter) || $ext == $filter) {
|
||||
if (empty($match) || preg_match("/$match/", $name)) {
|
||||
echo "<li class='file ext_$ext'>$checkbox<a href='#' rel=\"$htmlRel\">$htmlName</a></li>";
|
||||
}
|
||||
foreach ($filters as $filter) if (empty($filter) || $ext === $filter) {
|
||||
if (empty($match) || preg_match("/$match/", $name)) {
|
||||
echo "<li class='file ext_$ext'>$checkbox<a href='#' rel=\"$htmlRel\">$htmlName</a></li>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,13 @@
|
||||
<?
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
|
||||
require_once "$docroot/plugins/dynamix/include/Wrappers.php";
|
||||
|
||||
$charts = '/var/tmp/charts_data.tmp';
|
||||
$cookie = '/boot/config/dashboard_settings.json';
|
||||
|
||||
// get and set commands no longer utilized in dashstats.page, but leave in place for future reference
|
||||
|
||||
switch ($_POST['cmd']) {
|
||||
case 'get':
|
||||
echo @file_get_contents($charts) ?: '{"cpu":"","rxd":"","txd":""}';
|
||||
@@ -24,7 +28,7 @@ case 'set':
|
||||
file_put_contents($charts,$_POST['data']);
|
||||
break;
|
||||
case 'cookie':
|
||||
if ($_POST['data'] == '{}') @unlink($cookie); else file_put_contents($cookie,$_POST['data']);
|
||||
if ($_POST['data'] == '{}') @unlink($cookie); else file_put_contents_atomic($cookie,$_POST['data']);
|
||||
break;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -20,7 +20,13 @@ if (isset($_POST['listen'])) {
|
||||
die(exec("$docroot/webGui/scripts/show_interfaces")?:_('Any'));
|
||||
}
|
||||
|
||||
function port($eth) {
|
||||
// Helper function to normalize bitrate values
|
||||
function normalizeBitrate($rate) {
|
||||
$parts = explode(' ', $rate);
|
||||
return intval($parts[0] ?? 0).' '.($parts[1] ?? 'Bit/s');
|
||||
}
|
||||
|
||||
function isPort($eth) {
|
||||
$sys = "/sys/class/net";
|
||||
if (substr($eth,0,4) == 'wlan') return $eth;
|
||||
$x = preg_replace('/[^0-9]/', '', $eth) ?: '0';
|
||||
@@ -31,7 +37,7 @@ exec("grep -Po 'nameserver \K\S+' /etc/resolv.conf 2>/dev/null",$ns);
|
||||
$eth = $_POST['port'] ?? '';
|
||||
$vlan = $_POST['vlan'] ?? '';
|
||||
$wlan0 = $eth == 'wlan0';
|
||||
$port = port($eth).($vlan ? ".$vlan" : "");
|
||||
$port = isPort($eth).($vlan ? ".$vlan" : "");
|
||||
$v6on = trim(file_get_contents("/proc/sys/net/ipv6/conf/$port/disable_ipv6"))==='0';
|
||||
$none = _('None');
|
||||
$error = "<span class='red-text'>"._('Missing')."</span>";
|
||||
@@ -39,7 +45,6 @@ $note = in_array($eth,['eth0','wlan0']) && !$vlan ? $error : $none;
|
||||
$ipv4 = array_filter(explode(' ',exec("ip -4 -br addr show ".escapeshellarg($port)." scope global 2>/dev/null | awk '{\$1=\$2=\"\";print;exit}' | sed -r 's/ metric [0-9]+//g; s/\/[0-9]+//g'")));
|
||||
$gw4 = exec("ip -4 route show default dev ".escapeshellarg($port)." 2>/dev/null | awk '{print \$3;exit}'") ?: $note;
|
||||
$dns4 = array_filter($ns,function($ns){return strpos($ns,':') === false;});
|
||||
$domain = exec("grep -Pom1 'domain \K.*' /etc/resolv.conf 2>/dev/null") ?: '---';
|
||||
|
||||
if ($v6on) {
|
||||
$ipv6 = array_filter(explode(' ',exec("ip -6 -br addr show ".escapeshellarg($port)." scope global -temporary 2>/dev/null | awk '{\$1=\$2=\"\";print;exit}' | sed -r 's/ metric [0-9]+//g; s/\/[0-9]+//g'")));
|
||||
@@ -57,6 +62,8 @@ if ($wlan0) {
|
||||
$signal = explode(': ', $speed[2])[1];
|
||||
$rxrate = explode(': ', $speed[3])[1];
|
||||
$txrate = explode(': ', $speed[4])[1];
|
||||
$rxrate = normalizeBitrate($rxrate);
|
||||
$txrate = normalizeBitrate($txrate);
|
||||
$tmp = '/var/tmp/attr';
|
||||
$band = [];
|
||||
$attr = is_readable($tmp) ? (array)parse_ini_file($tmp,true) : [];
|
||||
@@ -110,6 +117,5 @@ if ($v6on) {
|
||||
echo "<tr><td>"._('IPv6 DNS server').":</td><td>$error</td></tr>";
|
||||
}
|
||||
}
|
||||
echo "<tr><td>"._('Domain name').":</td><td>$domain</td></tr>";
|
||||
echo "</table>";
|
||||
?>
|
||||
|
||||
@@ -187,7 +187,7 @@ function decrypt_data($data) {
|
||||
|
||||
/* Ensure the decrypted data is UTF-8 encoded. */
|
||||
if (!mb_check_encoding($decrypted, 'UTF-8')) {
|
||||
unassigned_log("Warning: Data is not UTF-8 encoded");
|
||||
outgoingproxy_log("Warning: Data is not UTF-8 encoded");
|
||||
$decrypted = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ $_SERVER['REQUEST_URI'] = 'settings';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
|
||||
function escapeSSID($text) {
|
||||
return str_replace('"', '\"', $text);
|
||||
}
|
||||
|
||||
function scanWifi($port) {
|
||||
$wlan = [];
|
||||
exec("iw ".escapeshellarg($port)." scan | grep -P '^BSS|freq:|signal:|SSID:|Authentication suites:' | sed -r ':a;N;\$!ba;s/\\n\\s+/ /g'", $scan);
|
||||
@@ -135,7 +139,7 @@ case 'list':
|
||||
case 'join':
|
||||
if (is_readable($ssl)) extract(parse_ini_file($ssl));
|
||||
$token = parse_ini_file($var)['csrf_token'];
|
||||
$ssid = rawurldecode($_POST['ssid']);
|
||||
$ssid = escapeSSID(rawurldecode($_POST['ssid']));
|
||||
$drop = $_POST['task'] == 1;
|
||||
$manual = $_POST['task'] == 3;
|
||||
$user = _var($wifi[$ssid],'USERNAME') && isset($cipher, $key, $iv) ? openssl_decrypt($wifi[$ssid]['USERNAME'], $cipher, $key, 0, $iv) : _var($wifi[$ssid],'USERNAME');
|
||||
@@ -161,13 +165,13 @@ case 'join':
|
||||
$ieee1 = strpos($attr3,'IEEE') !== false;
|
||||
$ieee2 = strpos($safe,'IEEE') !== false;
|
||||
$hide0 = ($manual || !$ieee2) && !$ieee1 && $safe != 'auto' ? 'hide' : '';
|
||||
$hide1 = $safe == 'open' || $attr3 == 'open' || !$attr3 ? 'hide' : '';
|
||||
$hide1 = !$manual && ($safe == 'open' || $attr3 == 'open' || !$attr3) ? 'hide' : '';
|
||||
$hide2 = $dhcp4 == 'no' ? '' : 'hide';
|
||||
$hide3 = $dns4 == 'no' ? 'hide' : '';
|
||||
$hide4 = $dhcp6 == 'no' ? '' : 'hide';
|
||||
$hide5 = $dhcp6 == '' ? 'hide' : '';
|
||||
$hide6 = $dns6 == 'no' ? 'hide' : '';
|
||||
echo "<form name=\"wifi\" method=\"POST\" action=\"/update.php\" target=\"progressFrame\">";
|
||||
echo "<form name=\"wifi\" method=\"POST\" action=\"/update.php\" target=\"progressFrame\" autocomplete=\"off\" spellcheck=\"false\">";
|
||||
echo "<input type=\"hidden\" name=\"#file\" value=\"$cfg\">";
|
||||
echo "<input type=\"hidden\" name=\"#include\" value=\"/webGui/include/update.wireless.php\">";
|
||||
echo "<input type=\"hidden\" name=\"#command\" value=\"/webGui/scripts/wireless\">";
|
||||
@@ -195,41 +199,41 @@ case 'join':
|
||||
echo mk_option($safe, 'IEEE 802.1X/SHA-256', _('WPA3 Enterprise'));
|
||||
echo "</select></td></tr>";
|
||||
}
|
||||
if ($ieee1 || $manual || $safe) echo "<tr id=\"username\" class=\"$hide0\"><td>"._('Username').":</td><td><input type=\"text\" name=\"USERNAME\" class=\"narrow\" maxlength=\"63\" autocomplete=\"off\" spellcheck=\"false\" value=\"$user\"></td></tr>";
|
||||
if ($attr3 || $manual || $safe) echo "<tr id=\"password\" class=\"$hide1\"><td>"._('Password').":</td><td><input type=\"password\" name=\"PASSWORD\" class=\"narrow\" maxlength=\"63\" autocomplete=\"off\" spellcheck=\"false\" value=\"$passwd\"><i id=\"showPass\" class=\"fa fa-eye\" onclick=\"showPassword()\"></i></td></tr>";
|
||||
if ($ieee1 || $manual || $safe) echo "<tr id=\"username\" class=\"$hide0\"><td>"._('Username').":</td><td><input type=\"text\" name=\"USERNAME\" class=\"narrow\" maxlength=\"63\" value=\"$user\"></td></tr>";
|
||||
if ($attr3 || $manual || $safe) echo "<tr id=\"password\" class=\"$hide1\"><td>"._('Password').":</td><td><input type=\"password\" name=\"PASSWORD\" class=\"narrow\" maxlength=\"63\" value=\"$passwd\"><i id=\"showPass\" class=\"fa fa-eye\" onclick=\"showPassword()\"></i></td></tr>";
|
||||
echo "<tr><td colspan=\"2\"> </td></tr>";
|
||||
echo "<tr><td>"._('IPv4 address assignment').":</td><td><select name=\"DHCP4\" onclick=\"showDHCP(this.value,4)\">";
|
||||
echo mk_option($dhcp4, 'yes', _('Automatic'));
|
||||
echo mk_option($dhcp4, 'no', _('Static'));
|
||||
echo "</select></td></tr>";
|
||||
echo "<tr class=\"static4 $hide2\"><td>"._('IPv4 address').":</td><td><input type=\"text\" name=\"IP4\" class=\"narrow\" maxlength=\"15\" autocomplete=\"off\" spellcheck=\"false\" value=\"$ip4\">/<select name=\"MASK4\" class=\"slim\">";
|
||||
echo "<tr class=\"static4 $hide2\"><td>"._('IPv4 address').":</td><td><input type=\"text\" name=\"IP4\" class=\"narrow\" maxlength=\"15\" value=\"$ip4\">/<select name=\"MASK4\" class=\"slim\">";
|
||||
foreach ($masks as $mask => $prefix) echo mk_option($mask4, $mask, $prefix);
|
||||
echo "</select></td></tr>";
|
||||
echo "<tr class=\"static4 $hide2\"><td>"._('IPv4 default gateway').":</td><td><input type=\"text\" name=\"GATEWAY4\" class=\"narrow\" maxlength=\"15\" autocomplete=\"off\" spellcheck=\"false\" value=\"$gw4\"></td></tr>";
|
||||
echo "<tr class=\"static4 $hide2\"><td>"._('IPv4 default gateway').":</td><td><input type=\"text\" name=\"GATEWAY4\" class=\"narrow\" maxlength=\"15\" value=\"$gw4\"></td></tr>";
|
||||
echo "<tr class=\"dns4\"><td>"._('IPv4 DNS assignment').":</td><td><select name=\"DNS4\" onclick=\"showDNS(this.value,4)\">";
|
||||
echo mk_option($dns4, "no", _("Automatic"));
|
||||
echo mk_option($dns4, "yes", _("Static"));
|
||||
echo "</select></td></tr>";
|
||||
echo "<tr class=\"server4 $hide3\"><td>"._('DNSv4 server').":</td><td><input type=\"text\" name=\"SERVER4\" class=\"narrow\" autocomplete=\"off\" spellcheck=\"false\" value=\"$server4\"></td></tr>";
|
||||
echo "<tr class=\"server4 $hide3\"><td>"._('DNSv4 server').":</td><td><input type=\"text\" name=\"SERVER4\" class=\"narrow\" value=\"$server4\"></td></tr>";
|
||||
echo "<tr><td colspan=\"2\"> </td></tr>";
|
||||
echo "<tr><td>"._('IPv6 address assignment').":</td><td><select name=\"DHCP6\" onclick=\"showDHCP(this.value,6)\">";
|
||||
echo mk_option($dhcp6, '', _('None'));
|
||||
echo mk_option($dhcp6, 'yes', _('Automatic'));
|
||||
echo mk_option($dhcp6, 'no', _('Static'));
|
||||
echo "</select></td></tr>";
|
||||
echo "<tr class=\"static6 $hide4\"><td>"._('IPv6 address').":</td><td><input type=\"text\" name=\"IP6\" class=\"narrow\" maxlength=\"39\" autocomplete=\"off\" spellcheck=\"false\" value=\"$ip6\">/<input type=\"number\" min=\"1\" max=\"128\" maxlength=\"3\" name=\"MASK6\" class=\"slim\" value=\"$mask6\"></td></tr>";
|
||||
echo "<tr class=\"static6 $hide4\"><td>"._('IPv6 default gateway').":</td><td><input type=\"text\" name=\"GATEWAY6\" class=\"narrow\" maxlength=\"39\" autocomplete=\"off\" spellcheck=\"false\" value=\"$gw6\"></td></tr>";
|
||||
echo "<tr class=\"static6 $hide4\"><td>"._('IPv6 address').":</td><td><input type=\"text\" name=\"IP6\" class=\"narrow\" maxlength=\"39\" value=\"$ip6\">/<input type=\"number\" min=\"1\" max=\"128\" maxlength=\"3\" name=\"MASK6\" class=\"slim\" value=\"$mask6\"></td></tr>";
|
||||
echo "<tr class=\"static6 $hide4\"><td>"._('IPv6 default gateway').":</td><td><input type=\"text\" name=\"GATEWAY6\" class=\"narrow\" maxlength=\"39\" value=\"$gw6\"></td></tr>";
|
||||
echo "<tr class=\"dns6 $hide5\"><td>"._('IPv6 DNS assignment').":</td><td><select name=\"DNS6\" onclick=\"showDNS(this.value,6)\">";
|
||||
echo mk_option($dns6, "no", _("Automatic"));
|
||||
echo mk_option($dns6, "yes", _("Static"));
|
||||
echo "</select></td></tr>";
|
||||
echo "<tr class=\"server6 $hide6\"><td>"._('DNSv6 server').":</td><td><input type=\"text\" name=\"SERVER6\" class=\"narrow\" autocomplete=\"off\" spellcheck=\"false\" value=\"$server6\"></td></tr>";
|
||||
echo "<tr class=\"server6 $hide6\"><td>"._('DNSv6 server').":</td><td><input type=\"text\" name=\"SERVER6\" class=\"narrow\" value=\"$server6\"></td></tr>";
|
||||
echo "<tr><td colspan=\"2\"> </td></tr>";
|
||||
echo "</table>";
|
||||
echo "</form>";
|
||||
break;
|
||||
case 'forget':
|
||||
$ssid = rawurldecode($_POST['ssid']);
|
||||
$ssid = escapeSSID(rawurldecode($_POST['ssid']));
|
||||
if ($wifi[$ssid]['GROUP'] == 'active') exec("/etc/rc.d/rc.wireless stop &>/dev/null &");
|
||||
unset($wifi[$ssid]);
|
||||
saveWifi();
|
||||
|
||||
@@ -23,24 +23,36 @@ function curl_socket($socket, $url, $message='') {
|
||||
if ($reply===false) my_logger("curl to $url failed", 'curl_socket');
|
||||
return $reply;
|
||||
}
|
||||
|
||||
function publish($endpoint, $message, $len=1) {
|
||||
static $com = [];
|
||||
static $lens = [];
|
||||
|
||||
if ( is_file("/tmp/publishPaused") )
|
||||
return false;
|
||||
|
||||
$com = curl_init("http://localhost/pub/$endpoint?buffer_length=$len");
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_UNIX_SOCKET_PATH => "/var/run/nginx.socket",
|
||||
CURLOPT_HTTPHEADER => ['Accept:text/json'],
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $message,
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
CURLOPT_FAILONERROR => true
|
||||
]);
|
||||
$reply = curl_exec($com);
|
||||
$err = curl_error($com);
|
||||
curl_close($com);
|
||||
// Check for the unlikely case of a buffer length change
|
||||
if ( (($lens[$endpoint] ?? 1) !== $len) && isset($com[$endpoint]) ) {
|
||||
curl_close($com[$endpoint]);
|
||||
unset($com[$endpoint]);
|
||||
}
|
||||
if ( !isset($com[$endpoint]) ) {
|
||||
$lens[$endpoint] = $len;
|
||||
$com[$endpoint] = curl_init("http://localhost/pub/$endpoint?buffer_length=$len");
|
||||
curl_setopt_array($com[$endpoint],[
|
||||
CURLOPT_UNIX_SOCKET_PATH => "/var/run/nginx.socket",
|
||||
CURLOPT_HTTPHEADER => ['Accept:text/json'],
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
CURLOPT_FAILONERROR => true
|
||||
]);
|
||||
}
|
||||
curl_setopt($com[$endpoint], CURLOPT_POSTFIELDS, $message);
|
||||
$reply = curl_exec($com[$endpoint]);
|
||||
$err = curl_error($com[$endpoint]);
|
||||
if ($err) {
|
||||
curl_close($com[$endpoint]);
|
||||
unset($com[$endpoint]);
|
||||
|
||||
preg_match_all("/[0-9]+/",$err,$matches);
|
||||
// 500: out of shared memory when creating a channel
|
||||
// 507: out of shared memory publishing a message
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
/* Copyright 2005-2025, Lime Technology
|
||||
* Copyright 2012-2025, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
@@ -32,57 +32,82 @@ $dockernet = "172.31";
|
||||
$t1 = '10'; // 10 sec timeout
|
||||
$t2 = '15'; // 15 sec timeout
|
||||
|
||||
function mask2cidr($mask) {
|
||||
$long = ip2long($mask);
|
||||
$base = ip2long('255.255.255.255');
|
||||
return 32-log(($long ^ $base)+1,2);
|
||||
function isPort($dev) {
|
||||
return file_exists("/sys/class/net/$dev");
|
||||
}
|
||||
function thisNet($ethX='eth0') {
|
||||
extract(parse_ini_file('state/network.ini',true));
|
||||
$net = long2ip(ip2long(_var($$ethX,'IPADDR:0')) & ip2long(_var($$ethX,'NETMASK:0'))).'/'.mask2cidr(_var($$ethX,'NETMASK:0'));
|
||||
$dev = _var($$ethX,'BRIDGING')=='yes' ? _var($$ethX,'BRNAME') : (_var($$ethX,'BONDING')=='yes' ? _var($$ethX,'BONDNAME') : $ethX);
|
||||
return [$dev,$net,$$ethX['GATEWAY:0']];
|
||||
|
||||
function carrier($dev, $loop=3) {
|
||||
if (!isPort($dev)) return false;
|
||||
try {
|
||||
for ($n=0; $n<$loop; $n++) {
|
||||
if (@file_get_contents("/sys/class/net/$dev/carrier") == 1) return true;
|
||||
if ($loop > 1) sleep(1);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function thisNet() {
|
||||
$dev = isPort('br0') ? 'br0' : (isPort('bond0') ? 'bond0' : 'eth0');
|
||||
if (!carrier($dev) && carrier('wlan0', 1)) $dev = 'wlan0';
|
||||
$ip4 = exec("ip -4 -br addr show dev $dev | awk '{print \$3;exit}'");
|
||||
$net = exec("ip -4 route show $ip4 dev $dev | awk '{print \$1;exit}'");
|
||||
$gw = exec("ip -4 route show default dev $dev | awk '{print \$3;exit}'");
|
||||
return [$dev, $net, $gw];
|
||||
}
|
||||
|
||||
function ipv4($ip) {
|
||||
return strpos($ip,'.')!==false;
|
||||
return strpos($ip, '.') !== false;
|
||||
}
|
||||
|
||||
function ipv6($ip) {
|
||||
return strpos($ip,':')!==false;
|
||||
return strpos($ip, ':') !== false;
|
||||
}
|
||||
|
||||
function ipset($ip) {
|
||||
return ipv4($ip) ? $ip : "[$ip]";
|
||||
}
|
||||
|
||||
function ipsplit($ip) {
|
||||
return ipv4($ip) ? ':' : ']:';
|
||||
}
|
||||
|
||||
function ipv4Addr($value) {
|
||||
return array_filter(array_map('trim',explode(',',$value)),'ipv4');
|
||||
return array_filter(array_map('trim', explode(',', $value)), 'ipv4');
|
||||
}
|
||||
|
||||
function ipv6Addr($value) {
|
||||
return array_filter(array_map('trim',explode(',',$value)),'ipv6');
|
||||
return array_filter(array_map('trim', explode(',', $value)), 'ipv6');
|
||||
}
|
||||
|
||||
function ipfilter(&$list) {
|
||||
// we only import IPv4 addresses, strip any IPv6 addresses
|
||||
$list = implode(', ',ipv4Addr($list));
|
||||
$list = implode(', ', ipv4Addr($list));
|
||||
}
|
||||
|
||||
function host($ip) {
|
||||
return strpos($ip,'/')!==false ? $ip : (ipv4($ip) ? "$ip/32" : "$ip/128");
|
||||
return strpos($ip, '/') !== false ? $ip : (ipv4($ip) ? "$ip/32" : "$ip/128");
|
||||
}
|
||||
|
||||
function isNet($network) {
|
||||
return !empty(exec("ip rule|grep -Pom1 'from $network'"));
|
||||
return !empty(exec("ip rule | grep -Pom1 'from $network'"));
|
||||
}
|
||||
|
||||
function newNet($vtun) {
|
||||
global $dockernet;
|
||||
$i = substr($vtun,2)+200;
|
||||
return [$i,"$dockernet.$i.0/24"];
|
||||
$i = substr($vtun ,2) + 200;
|
||||
return [$i, "$dockernet.$i.0/24"];
|
||||
}
|
||||
function wgState($vtun,$state,$type=0) {
|
||||
global $t1,$etc;
|
||||
|
||||
function wgState($vtun, $state, $type=0) {
|
||||
global $t1, $etc;
|
||||
$tmp = '/tmp/wg-quick.tmp';
|
||||
$log = '/var/log/wg-quick.log';
|
||||
exec("timeout $t1 wg-quick $state $vtun 2>$tmp");
|
||||
file_put_contents($log, "wg-quick $state $vtun\n".file_get_contents($tmp)."\n", FILE_APPEND);
|
||||
if ($type==8) {
|
||||
if ($type == 8) {
|
||||
// make VPN tunneled access for Docker containers only
|
||||
$table = exec("grep -Pom1 'fwmark \K[\d]+' $tmp");
|
||||
$route = implode(ipv4Addr(exec("grep -Pom1 '^Address=\K.+$' $etc/$vtun.conf")));
|
||||
@@ -92,23 +117,28 @@ function wgState($vtun,$state,$type=0) {
|
||||
}
|
||||
delete_file($tmp);
|
||||
}
|
||||
|
||||
function status($vtun) {
|
||||
return in_array($vtun,explode(" ",exec("wg show interfaces")));
|
||||
return in_array($vtun, explode(" ", exec("wg show interfaces")));
|
||||
}
|
||||
|
||||
function vtun() {
|
||||
global $etc;
|
||||
$x = 0; while (file_exists("$etc/wg{$x}.conf")) $x++;
|
||||
return "wg{$x}";
|
||||
}
|
||||
|
||||
function normalize(&$id) {
|
||||
// ensure correct capitalization of keywords, some VPN providers use the wrong case
|
||||
global $normalize;
|
||||
// allow fallback for non-included keywords
|
||||
$id = $normalize[strtolower($id)] ?? $id;
|
||||
}
|
||||
|
||||
function dockerNet($vtun) {
|
||||
return empty(exec("docker network ls --filter name='$vtun' --format='{{.Name}}'"));
|
||||
}
|
||||
|
||||
function addDocker($vtun) {
|
||||
global $dockerd;
|
||||
$error = false;
|
||||
@@ -118,13 +148,16 @@ function addDocker($vtun) {
|
||||
$error = dockerNet($vtun);
|
||||
}
|
||||
if (!$error && !isNet($network)) {
|
||||
[$device,$thisnet,$gateway] = thisNet();
|
||||
exec("ip -4 rule add from $network table $index");
|
||||
exec("ip -4 route add unreachable default table $index");
|
||||
exec("ip -4 route add $thisnet via $gateway dev $device table $index");
|
||||
[$device, $thisnet, $gateway] = thisNet();
|
||||
if (!empty($device) && !empty($thisnet) && !empty($gateway)) {
|
||||
exec("ip -4 rule add from $network table $index");
|
||||
exec("ip -4 route add unreachable default table $index");
|
||||
exec("ip -4 route add $thisnet via $gateway dev $device table $index");
|
||||
}
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
|
||||
function delDocker($vtun) {
|
||||
global $dockerd;
|
||||
$error = false;
|
||||
@@ -139,13 +172,15 @@ function delDocker($vtun) {
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
function delPeer($vtun,$id='') {
|
||||
global $etc,$name;
|
||||
|
||||
function delPeer($vtun, $id='') {
|
||||
global $etc, $name;
|
||||
$dir = "$etc/peers";
|
||||
foreach (glob("$dir/peer-$name-$vtun-$id*",GLOB_NOSORT) as $peer) delete_file($peer);
|
||||
foreach (glob("$dir/peer-$name-$vtun-$id*", GLOB_NOSORT) as $peer) delete_file($peer);
|
||||
}
|
||||
|
||||
function addPeer(&$x) {
|
||||
global $peers,$var;
|
||||
global $peers, $var;
|
||||
$peers[$x] = ['[Interface]']; // [Interface]
|
||||
if (isset($var['client'])) $peers[$x][] = $var['client']; // #name
|
||||
if (isset($var['privateKey'])) $peers[$x][] = $var['privateKey']; // PrivateKey
|
||||
@@ -164,38 +199,40 @@ function addPeer(&$x) {
|
||||
$peers[$x][] = _var($var,'allowedIPs'); // AllowedIPs
|
||||
$x++;
|
||||
}
|
||||
function autostart($vtun,$cmd) {
|
||||
|
||||
function autostart($vtun, $cmd) {
|
||||
global $etc;
|
||||
$autostart = "$etc/autostart";
|
||||
$list = file_exists($autostart) ? array_filter(explode(' ',file_get_contents($autostart))) : [];
|
||||
$key = array_search($vtun,$list);
|
||||
$list = file_exists($autostart) ? array_filter(explode(' ', file_get_contents($autostart))) : [];
|
||||
$key = array_search($vtun, $list);
|
||||
switch ($cmd) {
|
||||
case 'off': if ($key!==false) unset($list[$key]); break;
|
||||
case 'on' : if ($key===false) $list[] = $vtun; break;
|
||||
case 'off': if ($key !== false) unset($list[$key]); break;
|
||||
case 'on' : if ($key === false) $list[] = $vtun; break;
|
||||
}
|
||||
if (count($list)) file_put_contents($autostart,implode(' ',$list)); else delete_file($autostart);
|
||||
if (count($list)) file_put_contents($autostart, implode(' ', $list)); else delete_file($autostart);
|
||||
}
|
||||
|
||||
function createPeerFiles($vtun) {
|
||||
global $etc,$peers,$name,$gone,$vpn;
|
||||
global $etc, $peers, $name, $gone, $vpn;
|
||||
$dir = "$etc/peers";
|
||||
$tmp = "/tmp/list.tmp";
|
||||
if (is_dir($dir)) {
|
||||
if (count($gone)) {
|
||||
foreach ($gone as $peer) {
|
||||
// one or more peers are removed, delete the associated files
|
||||
[$n,$i] = my_explode('-',$peer);
|
||||
delPeer($n,$i);
|
||||
[$n, $i] = my_explode('-', $peer);
|
||||
delPeer($n, $i);
|
||||
}
|
||||
$new = 1;
|
||||
$peer = "$dir/peer-$name-$vtun";
|
||||
$files = glob("$peer-*.conf",GLOB_NOSORT);
|
||||
$files = glob("$peer-*.conf", GLOB_NOSORT);
|
||||
natsort($files);
|
||||
foreach ($files as $file) {
|
||||
$id = explode('-',basename($file,'.conf'))[3];
|
||||
$id = explode('-', basename($file,'.conf'))[3];
|
||||
if ($id > $new) {
|
||||
// rename files to match revised peers list
|
||||
rename($file,"$peer-$new.conf");
|
||||
rename(str_replace('.conf','.png',$file),"$peer-$new.png");
|
||||
rename($file, "$peer-$new.conf");
|
||||
rename(str_replace('.conf','.png', $file), "$peer-$new.png");
|
||||
}
|
||||
$new++;
|
||||
}
|
||||
@@ -208,43 +245,48 @@ function createPeerFiles($vtun) {
|
||||
if (empty($peer[1])) break; // tunnel without any peers
|
||||
$cfg = "$dir/peer-$name-$vtun-$id.conf";
|
||||
$cfgold = @file_get_contents($cfg) ?: '';
|
||||
$cfgnew = implode("\n",$peer)."\n";
|
||||
if ($cfgnew !== $cfgold && $vpn==0) {
|
||||
$list[] = "$vtun: peer $id (".($peer[1][0]=='#' ? substr($peer[1],1) : _('no name')).')';
|
||||
file_put_contents($cfg,$cfgnew);
|
||||
$png = str_replace('.conf','.png',$cfg);
|
||||
$cfgnew = implode("\n", $peer)."\n";
|
||||
if ($cfgnew !== $cfgold && $vpn == 0) {
|
||||
$list[] = "$vtun: peer $id (".($peer[1][0] == '#' ? substr($peer[1],1) : _('no name')).')';
|
||||
file_put_contents($cfg, $cfgnew);
|
||||
$png = str_replace('.conf', '.png', $cfg);
|
||||
exec("qrencode -t PNG -r $cfg -o $png");
|
||||
}
|
||||
}
|
||||
// store the peer names which are updated
|
||||
if (count($list)) file_put_contents($tmp,implode("<br>",$list)); else delete_file($tmp);
|
||||
if (count($list)) file_put_contents($tmp, implode("<br>", $list)); else delete_file($tmp);
|
||||
}
|
||||
|
||||
function createList($list) {
|
||||
return implode(', ',array_unique(array_filter(array_map('trim',explode(',',$list)))));
|
||||
return implode(', ', array_unique(array_filter(array_map('trim', explode(',', $list)))));
|
||||
}
|
||||
|
||||
function createIPs($list) {
|
||||
return implode(', ',array_map('host',array_filter(array_map('trim',explode(',',$list)))));
|
||||
return implode(', ', array_map('host', array_filter(array_map('trim', explode(',', $list)))));
|
||||
}
|
||||
function parseInput($vtun,&$input,&$x) {
|
||||
|
||||
function parseInput($vtun, &$input, &$x) {
|
||||
// assign values to parameters, be aware that certain parameters are assigned by parseInput itself
|
||||
// this is based on the sequence of processing
|
||||
global $conf,$user,$var,$default4,$default6,$vpn,$tunip;
|
||||
global $conf, $user, $var, $default4, $default6, $vpn, $tunip;
|
||||
$tunnel = $protocol = null; // satisfy code checkers
|
||||
$section = 0; $addPeer = false;
|
||||
foreach ($input as $key => $value) {
|
||||
if ($key[0]=='#') continue;
|
||||
[$id,$i] = array_pad(explode(':',$key),2,0);
|
||||
if ($key[0] == '#') continue;
|
||||
[$id, $i] = array_pad(explode(':', $key), 2, 0);
|
||||
if ($i != $section) {
|
||||
if ($section==0) {
|
||||
if ($section == 0) {
|
||||
// add WG routing for docker containers. Only IPv4 supported
|
||||
[$index,$network] = newNet($vtun);
|
||||
[$device,$thisnet,$gateway] = thisNet();
|
||||
$conf[] = "PostUp=ip -4 route flush table $index";
|
||||
$conf[] = "PostUp=ip -4 route add default via $tunip dev $vtun table $index";
|
||||
$conf[] = "PostUp=ip -4 route add $thisnet via $gateway dev $device table $index";
|
||||
$conf[] = "PostDown=ip -4 route flush table $index";
|
||||
$conf[] = "PostDown=ip -4 route add unreachable default table $index";
|
||||
$conf[] = "PostDown=ip -4 route add $thisnet via $gateway dev $device table $index";
|
||||
[$index, $network] = newNet($vtun);
|
||||
[$device, $thisnet, $gateway] = thisNet();
|
||||
if (!empty($device) && !empty($thisnet) && !empty($gateway)) {
|
||||
$conf[] = "PostUp=ip -4 route flush table $index";
|
||||
$conf[] = "PostUp=ip -4 route add default via $tunip dev $vtun table $index";
|
||||
$conf[] = "PostUp=ip -4 route add $thisnet via $gateway dev $device table $index";
|
||||
$conf[] = "PostDown=ip -4 route flush table $index";
|
||||
$conf[] = "PostDown=ip -4 route add unreachable default table $index";
|
||||
$conf[] = "PostDown=ip -4 route add $thisnet via $gateway dev $device table $index";
|
||||
}
|
||||
}
|
||||
$conf[] = "\n[Peer]";
|
||||
// add peers, this is only used for peer sections
|
||||
@@ -254,14 +296,14 @@ function parseInput($vtun,&$input,&$x) {
|
||||
switch ($id) {
|
||||
case 'Name':
|
||||
if ($value) $conf[] = "#$value";
|
||||
if ($i==0) {
|
||||
if ($i == 0) {
|
||||
$var['server'] = $value ? "#$value" : false;
|
||||
} else {
|
||||
$var['client'] = $value ? "#$value" : false;
|
||||
}
|
||||
break;
|
||||
case 'PrivateKey':
|
||||
if ($i==0) {
|
||||
if ($i == 0) {
|
||||
$conf[] = "$id=$value";
|
||||
} else {
|
||||
if ($value) $user[] = "$id:$x=\"$value\"";
|
||||
@@ -269,7 +311,7 @@ function parseInput($vtun,&$input,&$x) {
|
||||
}
|
||||
break;
|
||||
case 'PublicKey':
|
||||
if ($i==0) {
|
||||
if ($i == 0) {
|
||||
$user[] = "$id:0=\"$value\"";
|
||||
$var['publicKey'] = "$id=$value";
|
||||
} else {
|
||||
@@ -286,17 +328,17 @@ function parseInput($vtun,&$input,&$x) {
|
||||
$protocol = $value;
|
||||
$user[] = "$id:0=\"$value\"";
|
||||
switch ($protocol) {
|
||||
case '46': $var['default'] = "AllowedIPs=$default4, $default6"; break;
|
||||
case '6' : $var['default'] = "AllowedIPs=$default6"; break;
|
||||
default : $var['default'] = "AllowedIPs=$default4"; break;
|
||||
case '46': $var['default'] = "AllowedIPs=$default4, $default6"; break;
|
||||
case '6' : $var['default'] = "AllowedIPs=$default6"; break;
|
||||
default : $var['default'] = "AllowedIPs=$default4"; break;
|
||||
}
|
||||
break;
|
||||
case 'TYPE':
|
||||
$list = $value<4 ? ($value%2==1 ? _var($var,'subnets1') : _var($var,'subnets2')) : ($value<6 ? ($value%2==1 ? _var($var,'shared1') : _var($var,'shared2')) : _var($var,'default'));
|
||||
$list = $value < 4 ? ($value%2 == 1 ? _var($var,'subnets1') : _var($var,'subnets2')) : ($value < 6 ? ($value%2 == 1 ? _var($var,'shared1') : _var($var,'shared2')) : _var($var,'default'));
|
||||
$var['allowedIPs'] = createIPs($list);
|
||||
$var['tunnel'] = ($value==2||$value==3) ? $tunnel : false;
|
||||
$var['tunnel'] = ($value == 2 || $value == 3) ? $tunnel : false;
|
||||
$user[] = "$id:$x=\"$value\"";
|
||||
if ($value>=7) $vpn = $value;
|
||||
if ($value >= 7) $vpn = $value;
|
||||
break;
|
||||
case 'Network6': if (!$protocol) break;
|
||||
case 'Network':
|
||||
@@ -308,7 +350,7 @@ function parseInput($vtun,&$input,&$x) {
|
||||
break;
|
||||
case 'Address':
|
||||
$hosts = createIPs($value);
|
||||
if ($i==0) {
|
||||
if ($i == 0) {
|
||||
$conf[] = "$id=$value";
|
||||
$tunnel = "$id=$hosts";
|
||||
$tunip = implode(ipv4Addr($value));
|
||||
@@ -322,13 +364,13 @@ function parseInput($vtun,&$input,&$x) {
|
||||
$var['mtu'] = $value ? "$id=$value" : false;
|
||||
break;
|
||||
case 'Endpoint':
|
||||
if ($i==0) {
|
||||
if ($i == 0) {
|
||||
$user[] = "$id:0=\"$value\"";
|
||||
$var['endpoint'] = $value ? "Endpoint=".ipset($value) : false;
|
||||
} else {
|
||||
if ($value) $conf[] = "$id=$value";
|
||||
$var['listenport'] = $value ? "ListenPort=".(explode(ipsplit($value),$value)[1]??'') : false;
|
||||
if ($var['endpoint'] && strpos(_var($var,'endpoint'),ipsplit(_var($var,'endpoint')))===false) $var['endpoint'] .= ":".(explode(ipsplit(_var($var,'internet')),_var($var,'internet'))[1]??'');
|
||||
if ($var['endpoint'] && strpos(_var($var,'endpoint'),ipsplit(_var($var,'endpoint'))) === false) $var['endpoint'] .= ":".(explode(ipsplit(_var($var,'internet')),_var($var,'internet'))[1] ?? '');
|
||||
}
|
||||
break;
|
||||
case 'PersistentKeepalive':
|
||||
@@ -348,6 +390,7 @@ function parseInput($vtun,&$input,&$x) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$default4 = '0.0.0.0/0';
|
||||
$default6 = '::/0';
|
||||
|
||||
@@ -381,20 +424,20 @@ case 'update':
|
||||
$var['shared2'] = "AllowedIPs=".createList(_var($_POST,'#shared2'));
|
||||
$var['internet'] = "Endpoint=".createList(_var($_POST,'#internet'));
|
||||
$x = 1; $vpn = 0;
|
||||
parseInput($vtun,$_POST,$x);
|
||||
parseInput($vtun, $_POST, $x);
|
||||
addPeer($x);
|
||||
addDocker($vtun);
|
||||
$upstate = status($vtun);
|
||||
wgState($vtun,'down');
|
||||
file_put_contents($file,implode("\n",$conf)."\n");
|
||||
file_put_contents($cfg,implode("\n",$user)."\n");
|
||||
file_put_contents($file, implode("\n", $conf)."\n");
|
||||
file_put_contents($cfg, implode("\n", $user)."\n");
|
||||
createPeerFiles($vtun);
|
||||
if ($upstate) wgState($vtun,'up',_var($_POST,'#type'));
|
||||
if ($upstate) wgState($vtun, 'up', _var($_POST,'#type'));
|
||||
// if $tunip (with dots to slashes) not found in nginx config, then reload nginx to add it
|
||||
$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
|
||||
if (stripos($nginx['NGINX_CERTNAME'],'.myunraid.net')!==false) {
|
||||
if (stripos($nginx['NGINX_CERTNAME'],'.myunraid.net') !== false) {
|
||||
$key = 'NGINX_'.strtoupper($vtun).'FQDN';
|
||||
if (!isset($nginx[$key]) || stripos($nginx[$key],str_replace('.','-',$tunip))===false) {
|
||||
if (!isset($nginx[$key]) || stripos($nginx[$key], str_replace('.', '-', $tunip)) === false) {
|
||||
exec("/etc/rc.d/rc.nginx reload");
|
||||
}
|
||||
}
|
||||
@@ -404,28 +447,28 @@ case 'toggle':
|
||||
$vtun = _var($_POST,'#vtun');
|
||||
switch (_var($_POST,'#wg')) {
|
||||
case 'stop':
|
||||
wgState($vtun,'down');
|
||||
wgState($vtun, 'down');
|
||||
echo status($vtun) ? 1 : 0;
|
||||
break;
|
||||
case 'start':
|
||||
[$index,$network] = newNet($vtun);
|
||||
[$index, $network] = newNet($vtun);
|
||||
if (!isNet($network)) {
|
||||
exec("ip -4 rule add from $network table $index");
|
||||
exec("ip -4 route add unreachable default table $index");
|
||||
}
|
||||
wgState($vtun,'up',_var($_POST,'#type'));
|
||||
wgState($vtun, 'up', _var($_POST,'#type'));
|
||||
echo status($vtun) ? 0 : 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'ping':
|
||||
$addr = $_POST['#addr'];
|
||||
echo exec("ping -qc1 -W4 $addr|grep -Pom1 '1 received'");
|
||||
echo exec("ping -qc1 -W4 $addr | grep -Pom1 '1 received'");
|
||||
break;
|
||||
case 'public':
|
||||
$ip = _var($_POST,'#ip');
|
||||
$v4 = _var($_POST,'#prot')!='6';
|
||||
$v6 = _var($_POST,'#prot')!='';
|
||||
$v4 = _var($_POST,'#prot') != '6';
|
||||
$v6 = _var($_POST,'#prot') != '';
|
||||
$int_ipv4 = $v4 ? (preg_match("/^$validIP4$/",$ip) ? $ip : (@dns_get_record($ip,DNS_A)[0]['ip'] ?: '')) : '';
|
||||
$ext_ipv4 = $v4 ? (http_get_contents('https://wanip4.unraid.net') ?: '') : '';
|
||||
$int_ipv6 = $v6 ? (preg_match("/^$validIP6$/",$ip) ? $ip : (@dns_get_record($ip,DNS_AAAA)[0]['ipv6'] ?: '')) : '';
|
||||
@@ -436,20 +479,20 @@ case 'addtunnel':
|
||||
$vtun = vtun();
|
||||
$name = _var($_POST,'#name');
|
||||
touch("$etc/$vtun.conf");
|
||||
wgState($vtun,'down');
|
||||
wgState($vtun, 'down');
|
||||
delete_file("$etc/$vtun.cfg");
|
||||
delPeer($vtun);
|
||||
autostart($vtun,'off');
|
||||
autostart($vtun, 'off');
|
||||
break;
|
||||
case 'deltunnel':
|
||||
$vtun = _var($_POST,'#vtun');
|
||||
$name = _var($_POST,'#name');
|
||||
$error = delDocker($vtun);
|
||||
if (!$error) {
|
||||
wgState($vtun,'down');
|
||||
delete_file("$etc/$vtun.conf","$etc/$vtun.cfg");
|
||||
wgState($vtun, 'down');
|
||||
delete_file("$etc/$vtun.conf", "$etc/$vtun.cfg");
|
||||
delPeer($vtun);
|
||||
autostart($vtun,'off');
|
||||
autostart($vtun, 'off');
|
||||
// remove tunnel url from nginx config
|
||||
$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
|
||||
$key = 'NGINX_'.strtoupper($vtun).'FQDN';
|
||||
@@ -462,16 +505,16 @@ case 'deltunnel':
|
||||
case 'import':
|
||||
$name = _var($_POST,'#name');
|
||||
$user = $peers = $var = $import = $sort = [];
|
||||
$entries = array_filter(array_map('trim',preg_split('/\[(Interface|Peer)\]/',_var($_POST,'#data'))));
|
||||
$entries = array_filter(array_map('trim',preg_split('/\[(Interface|Peer)\]/', _var($_POST,'#data'))));
|
||||
foreach($entries as $key => $entry) {
|
||||
$i = $key-1;
|
||||
foreach (explode("\n",$entry) as $row) {
|
||||
if (ltrim($row)[0]!='#') {
|
||||
[$id,$data] = array_map('trim',my_explode('=',$row));
|
||||
if (ltrim($row)[0] != '#') {
|
||||
[$id, $data] = array_map('trim', my_explode('=',$row));
|
||||
normalize($id);
|
||||
$import["$id:$i"] = $data;
|
||||
} elseif ($i >= 0) {
|
||||
$import["Name:$i"] = substr(trim($row),1);
|
||||
$import["Name:$i"] = substr(trim($row), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,7 +523,7 @@ case 'import':
|
||||
unset($import['ListenPort:0']);
|
||||
$import['UPNP:0'] = 'no';
|
||||
$import['NAT:0'] = 'no';
|
||||
[$subnet,$mask] = my_explode('/',_var($import,'Address:0'));
|
||||
[$subnet, $mask] = my_explode('/', _var($import,'Address:0'));
|
||||
if (ipv4($subnet)) {
|
||||
$mask = ($mask > 0 && $mask < 32) ? $mask : 24;
|
||||
$import['Network:0'] = long2ip(ip2long($subnet) & (0x100000000-2**(32-$mask))).'/'.$mask;
|
||||
@@ -488,37 +531,37 @@ case 'import':
|
||||
$import['PROT:0'] = '';
|
||||
} else {
|
||||
$mask = ($mask > 0 && $mask < 128) ? $mask : 64;
|
||||
$import['Network6:0'] = strstr($subnet,'::',true).'::/'.$mask;
|
||||
$import['Network6:0'] = strstr($subnet, '::', true).'::/'.$mask;
|
||||
$import['Address:0'] = $subnet;
|
||||
$import['PROT:0'] = '6';
|
||||
}
|
||||
$import['Endpoint:0'] = '';
|
||||
for ($n = 1; $n <= $i; $n++) {
|
||||
$vpn = array_map('trim',explode(',',_var($import,"AllowedIPs:$n")));
|
||||
$vpn = array_map('trim',explode(',', _var($import,"AllowedIPs:$n")));
|
||||
$vpn = (in_array($default4,$vpn) || in_array($default6,$vpn)) ? 8 : 0;
|
||||
if ($vpn==8) $import["Address:$n"] = '';
|
||||
$import["TYPE:$n"] = $vpn;
|
||||
ipfilter(_var($import,"AllowedIPs:$n"));
|
||||
if (_var($import,"TYPE:$n")==0) $var['subnets1'] = "AllowedIPs="._var($import,"AllowedIPs:$n");
|
||||
if (_var($import,"TYPE:$n") == 0) $var['subnets1'] = "AllowedIPs="._var($import,"AllowedIPs:$n");
|
||||
}
|
||||
foreach ($import as $key => $val) $sort[] = explode(':',$key)[1];
|
||||
array_multisort($sort,$import);
|
||||
array_multisort($sort, $import);
|
||||
$x = 1;
|
||||
$conf = ['[Interface]'];
|
||||
$var['default'] = _var($import,'PROT:0')=='' ? "AllowedIPs=$default4" : "AllowedIPs=$default6";
|
||||
$var['default'] = _var($import,'PROT:0') == '' ? "AllowedIPs=$default4" : "AllowedIPs=$default6";
|
||||
$var['internet'] = "Endpoint=unknown";
|
||||
$vtun = vtun();
|
||||
parseInput($vtun,$import,$x);
|
||||
parseInput($vtun, $import, $x);
|
||||
addPeer($x);
|
||||
file_put_contents("$etc/$vtun.conf",implode("\n",$conf)."\n");
|
||||
file_put_contents("$etc/$vtun.cfg",implode("\n",$user)."\n");
|
||||
file_put_contents("$etc/$vtun.conf", implode("\n",$conf)."\n");
|
||||
file_put_contents("$etc/$vtun.cfg", implode("\n",$user)."\n");
|
||||
delPeer($vtun);
|
||||
addDocker($vtun);
|
||||
autostart($vtun,'off');
|
||||
autostart($vtun, 'off');
|
||||
echo $vtun;
|
||||
break;
|
||||
case 'autostart':
|
||||
autostart(_var($_POST,'#vtun'),_var($_POST,'#start'));
|
||||
autostart(_var($_POST,'#vtun'), _var($_POST,'#start'));
|
||||
break;
|
||||
case 'upnp':
|
||||
$upnp = '/var/tmp/upnp';
|
||||
@@ -528,14 +571,14 @@ case 'upnp':
|
||||
$xml = @file_get_contents($upnp) ?: '';
|
||||
if ($xml) {
|
||||
exec("timeout $t1 stdbuf -o0 upnpc -u $xml -m $link -l 2>&1|grep -qm1 'refused'",$output,$code);
|
||||
if ($code!=1) $xml = '';
|
||||
if ($code != 1) $xml = '';
|
||||
}
|
||||
if (!$xml) {
|
||||
exec("timeout $t2 stdbuf -o0 upnpc -m $link -l 2>/dev/null|grep -Po 'desc: \K.+'",$desc);
|
||||
foreach ($desc as $url) if ($url && strpos($url,$gw)!==false) {$xml = $url; break;}
|
||||
foreach ($desc as $url) if ($url && strpos($url,$gw) !== false) {$xml = $url; break;}
|
||||
}
|
||||
} else $xml = "";
|
||||
file_put_contents($upnp,$xml);
|
||||
file_put_contents($upnp, $xml);
|
||||
echo $xml;
|
||||
break;
|
||||
case 'upnpc':
|
||||
@@ -544,10 +587,10 @@ case 'upnpc':
|
||||
$vtun = _var($_POST,'#vtun');
|
||||
$link = _var($_POST,'#link');
|
||||
$ip = _var($_POST,'#ip');
|
||||
if (_var($_POST,'#wg')=='active') {
|
||||
exec("timeout $t1 stdbuf -o0 upnpc -u $xml -m $link -l 2>/dev/null|grep -Po \"^(ExternalIPAddress = \K.+|.+\KUDP.+>$ip:[0-9]+ 'WireGuard-$vtun')\"",$upnp);
|
||||
[$addr,$upnp] = array_pad($upnp,2,'');
|
||||
[$type,$rule] = my_explode(' ',$upnp);
|
||||
if (_var($_POST,'#wg') == 'active') {
|
||||
exec("timeout $t1 stdbuf -o0 upnpc -u $xml -m $link -l 2>/dev/null|grep -Po \"^(ExternalIPAddress = \K.+|.+\KUDP.+>$ip:[0-9]+ 'WireGuard-$vtun')\"", $upnp);
|
||||
[$addr, $upnp] = array_pad($upnp, 2, '');
|
||||
[$type, $rule] = my_explode(' ', $upnp);
|
||||
echo $rule ? "UPnP: $addr:$rule/$type" : _("UPnP: forwarding not set");
|
||||
} else {
|
||||
echo _("UPnP: tunnel is inactive");
|
||||
|
||||
@@ -183,7 +183,7 @@ function array_offline(&$disk, $pool='') {
|
||||
global $var, $disks, $display;
|
||||
$disk['power'] ??= (_var($display,'power') && _var($disk,'transport')=='nvme' ? get_nvme_info(_var($disk,'device'),'power') : 0);
|
||||
$echo = []; $warning = '';
|
||||
$status = ['DISK_DSBL','DISK_INVALID','DISK_DSBL_NEW','DISK_WRONG'];
|
||||
$status = ['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($disks[$pool],'state'),'ERROR:')) {
|
||||
|
||||
@@ -27,6 +27,7 @@ $cli = empty($zip);
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
require_once "$docroot/webGui/include/publish.php";
|
||||
|
||||
if (is_file('/boot/syslinux/syslinux.cfg')) {
|
||||
$bootenv = '/boot/syslinux';
|
||||
@@ -34,7 +35,7 @@ if (is_file('/boot/syslinux/syslinux.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'];
|
||||
$folders = ['/boot','/boot/config','/boot/config/plugins','/boot/config/firmware',$bootenv,'/var/log','/var/log/plugins','/boot/extra','/var/log/packages','/var/lib/pkgtools/packages','/tmp'];
|
||||
|
||||
// global variables
|
||||
$path = "/var/local/emhttp";
|
||||
@@ -45,26 +46,33 @@ $pools = pools_filter($disks);
|
||||
require_once "$docroot/webGui/include/CustomMerge.php";
|
||||
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/diagnostics?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
publish('diagnostics', $message);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
|
||||
// Add error logging function
|
||||
function log_error($message, $command = '') {
|
||||
global $diag;
|
||||
$error_log = "/$diag/logs/diagnostics.error.log";
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$log_message = "[$timestamp] $message";
|
||||
if ($command) {
|
||||
$log_message .= " (Command: $command)";
|
||||
}
|
||||
file_put_contents($error_log, $log_message . "\r\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
// Modify run function to include error logging
|
||||
function run($cmd, &$save=null, $timeout=30) {
|
||||
global $cli,$diag;
|
||||
// output command for display
|
||||
write($cmd);
|
||||
// execute command with timeout of 30s
|
||||
exec("timeout -s9 $timeout $cmd", $save);
|
||||
exec("LC_ALL=en_US.UTF-8 timeout -s9 $timeout $cmd 2>&1", $save, $return_code);
|
||||
if ($return_code !== 0) {
|
||||
log_error("Command failed with return code $return_code", $cmd);
|
||||
}
|
||||
return implode("\n",$save);
|
||||
}
|
||||
|
||||
@@ -121,19 +129,16 @@ 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
|
||||
$file_escaped = escapeshellarg($file);
|
||||
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));
|
||||
|
||||
// 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");
|
||||
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));
|
||||
|
||||
// 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");
|
||||
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));
|
||||
|
||||
// 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");
|
||||
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' ".escapeshellarg($file));
|
||||
}
|
||||
|
||||
function download_url($url, $path="", $bg=false, $timeout=15) {
|
||||
@@ -333,16 +338,16 @@ function anonymize_syslog($file) {
|
||||
foreach ($titles as $mover) {
|
||||
if (!$mover) continue;
|
||||
$title = "/{$mover[0]}..".substr($mover,-1)."/...";
|
||||
run("sed -i 's/".str_replace("/","\/",$mover)."/".str_replace("/","\/",$title)."/g' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
//run("sed -ri 's|(file: [.>cr].*)[ /]$mover/.*$|\\1 file: $title|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
run("sed -i 's/".str_replace("/","\/",$mover)."/".str_replace("/","\/",$title)."/g' ".escapeshellarg("$log.txt"));
|
||||
//run("sed -ri 's|(file: [.>cr].*)[ /]$mover/.*$|\\1 file: $title|' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
run("grep -n ' cache_dirs: -' ".escapeshellarg("$log.txt")." 2>/dev/null|cut -d: -f1", $rows);
|
||||
for ($i = 0; $i < count($rows); $i += 2) for ($row = $rows[$i]+1; $row < $rows[$i+1]; $row++) run("sed -ri '$row s|(cache_dirs: \S).*(\S)|\\1..\\2|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
for ($i = 0; $i < count($rows); $i += 2) for ($row = $rows[$i]+1; $row < $rows[$i+1]; $row++) run("sed -ri '$row s|(cache_dirs: \S).*(\S)|\\1..\\2|' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
// replace consecutive repeated lines in syslog
|
||||
run("awk -i inplace '{if(s!=substr(\$0,17)){if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\";print;x=0}else{x++}s=substr(\$0,17)}END{if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\"}' ".escapeshellarg("$log.txt"));
|
||||
run("awk -b -i inplace '{if(s!=substr($0,17)){if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\r\";print;x=0}else{x++}s=substr($0,17)}END{if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\r\"}' ".escapeshellarg("$log.txt"));
|
||||
// remove SHA256 hashes
|
||||
run("sed -ri 's/(SHA256:).+[^\s\b]/SHA256:***REMOVED***/gm' $log.txt");
|
||||
run("sed -ri 's/(SHA256:).+[^\s\b]/SHA256:***REMOVED***/gm' ".escapeshellarg("$log.txt"));
|
||||
// truncate syslog if too big
|
||||
if (basename($file)=='syslog' && filesize($file)>=$max) run("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
run("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
@@ -447,7 +452,7 @@ file_put_contents("/$diag/system/loads.txt",$cpuload.implode("\r\n",$loadTxt));
|
||||
run("lscpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/lscpu.txt"));
|
||||
run("lsscsi -vgl 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsscsi.txt"));
|
||||
run("lspci -knn 2>/dev/null|todos >".escapeshellarg("/$diag/system/lspci.txt"));
|
||||
run("lspci -vv 2>/dev/null| awk '/ASPM/{print $0}' RS=|grep -P '(^[a-z0-9:.]+|ASPM |Disabled;|Enabled;)'|todos >".escapeshellarg("/$diag/system/aspm-status.txt"));
|
||||
run("lspci -vv 2>/dev/null| awk -b '/ASPM/{print $0}' RS=|grep -P '(^[a-z0-9:.]+|ASPM |Disabled;|Enabled;)'|todos >".escapeshellarg("/$diag/system/aspm-status.txt"));
|
||||
run("lsusb 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsusb.txt"));
|
||||
run("free -mth 2>/dev/null|todos >".escapeshellarg("/$diag/system/memory.txt"));
|
||||
run("lsof -Pni 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsof.txt"));
|
||||
@@ -470,7 +475,7 @@ foreach ($ports as $port) {
|
||||
file_put_contents("/$diag/system/ethtool.txt", "--------------------------------\r\n", FILE_APPEND);
|
||||
}
|
||||
run("ip -br a|todos >".escapeshellarg("/$diag/system/ifconfig.txt"));
|
||||
if (!$all) maskIP("/$diag/system/ifconfig.txt");
|
||||
maskIP("/$diag/system/ifconfig.txt");
|
||||
|
||||
// create system information (suppress errors)
|
||||
run("find /sys/kernel/iommu_groups/ -type l 2>/dev/null|sort -V|todos >".escapeshellarg("/$diag/system/iommu_groups.txt"));
|
||||
@@ -482,9 +487,17 @@ foreach ($folders as $folder) {
|
||||
if (is_dir($folder)) run("echo -ne ".escapeshellarg("\r\n$folder\r\n")." >>".escapeshellarg($dest).";ls -l ".escapeshellarg($folder)."|todos >>".escapeshellarg("$dest")); else run("echo -ne ".escapeshellarg("\r\n$folder\r\nfolder does not exist\r\n")." >>".escapeshellarg("$dest"));
|
||||
}
|
||||
|
||||
// copy configuration files (suppress errors)
|
||||
run("cp /boot/config/*.{cfg,conf,dat} ".escapeshellarg("/$diag/config")." 2>/dev/null");
|
||||
run("cp /boot/config/go ".escapeshellarg("/$diag/config/go.txt")." 2>/dev/null");
|
||||
// copy configuration files
|
||||
if (glob("/boot/config/*.cfg")) {
|
||||
run("cp -- /boot/config/*.cfg ".escapeshellarg("/$diag/config"));
|
||||
}
|
||||
if (glob("/boot/config/*.conf")) {
|
||||
run("cp -- /boot/config/*.conf ".escapeshellarg("/$diag/config"));
|
||||
}
|
||||
if (glob("/boot/config/*.dat")) {
|
||||
run("cp -- /boot/config/*.dat ".escapeshellarg("/$diag/config"));
|
||||
}
|
||||
run("cp /boot/config/go ".escapeshellarg("/$diag/config/go.txt"));
|
||||
|
||||
// anonymize go file
|
||||
if (!$all) {
|
||||
@@ -492,18 +505,18 @@ if (!$all) {
|
||||
}
|
||||
// anonymize configuration files
|
||||
if (!$all) {
|
||||
run("sed -ri 's/^((disk|flash)(Read|Write)List.*=\")[^\"]+/\\1.../' ".escapeshellarg("/$diag/config/*.cfg")." 2>/dev/null");
|
||||
run("find ".escapeshellarg("/$diag/config")." -name '*.cfg' -exec sed -ri 's/^((disk|flash)(Read|Write)List.*=\")[^\"]+/\\1.../' {} \\;");
|
||||
// anonymize IP addresses
|
||||
maskIP("/$diag/config/network.cfg");
|
||||
// anonymize wireless credentials
|
||||
if (file_exists("/$diag/config/wireless.cfg")) {
|
||||
run("sed -ri 's/^((USERNAME|PASSWORD)=\")[^\"]+/\\1.../' ".escapeshellarg("/$diag/config/wireless.cfg")." 2>/dev/null");
|
||||
run("sed -ri 's/^((USERNAME|PASSWORD)=\")[^\"]+/\\1.../' ".escapeshellarg("/$diag/config/wireless.cfg"));
|
||||
}
|
||||
}
|
||||
// include listening interfaces
|
||||
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");
|
||||
maskIP("/$diag/config/listen.txt");
|
||||
|
||||
// copy share information (anonymize if applicable)
|
||||
$files = glob("/boot/config/shares/*.cfg");
|
||||
@@ -513,7 +526,7 @@ foreach ($files as $file) {
|
||||
$share = basename($file,'.cfg');
|
||||
if (!in_array($share,$showshares)) $dest = anonymize($dest,2);
|
||||
@copy($file, $dest);
|
||||
if (!$all) run("sed -ri 's/^(share(Comment|ReadList|WriteList)=\")[^\"]+/\\1.../' ".escapeshellarg($dest)." 2>/dev/null");
|
||||
if (!$all) run("sed -ri 's/^(share(Comment|ReadList|WriteList)=\")[^\"]+/\\1.../' ".escapeshellarg($dest));
|
||||
$home = shareDisks($share);
|
||||
$home = $home ? "# Share exists on $home\r\n" : "# Share does not exist\r\n";
|
||||
$shareDisk[] = str_pad(basename($dest,'.cfg'),34).str_pad(exec("grep -m1 'shareUseCache' ".escapeshellarg($file)),24).$home;
|
||||
@@ -669,13 +682,15 @@ if ($qemu) {
|
||||
}
|
||||
|
||||
// copy VM XML config files
|
||||
run("cp /etc/libvirt/qemu/*.xml ".escapeshellarg("/$diag/xml")." 2>/dev/null");
|
||||
if (glob("/etc/libvirt/qemu/*.xml")) {
|
||||
run("cp -- /etc/libvirt/qemu/*.xml ".escapeshellarg("/$diag/xml"));
|
||||
}
|
||||
|
||||
// anonymize MAC OSK info
|
||||
$all_xml = glob("/$diag/xml/*.xml");
|
||||
foreach ($all_xml as $xml) {
|
||||
run("sed -ri 's/(,osk=).+/\\1.../' ".escapeshellarg("$xml")." 2>/dev/null");
|
||||
run("sed -ri 's/(passwd=).+/\\1.../' ".escapeshellarg("$xml")." 2>/dev/null");
|
||||
run("sed -ri 's/(,osk=).+/\\1.../' ".escapeshellarg("$xml"));
|
||||
run("sed -ri 's/(passwd=).+/\\1.../' ".escapeshellarg("$xml"));
|
||||
}
|
||||
|
||||
// copy syslog information (anonymize if applicable)
|
||||
@@ -691,7 +706,7 @@ $dhcplog = "/var/log/dhcplog";
|
||||
if (file_exists($dhcplog)) {
|
||||
$log = "/$diag/logs/dhcplog.txt";
|
||||
run("todos <$dhcplog >".escapeshellarg($log));
|
||||
if (!$all) maskIP($log);
|
||||
maskIP($log);
|
||||
}
|
||||
|
||||
// copy phplog
|
||||
@@ -722,12 +737,6 @@ if (file_exists($wgquick)) {
|
||||
run("todos <$wgquick >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// generate unraid-api.txt
|
||||
if (file_exists("/usr/local/sbin/unraid-api")) {
|
||||
$log = "/$diag/system/unraid-api.txt";
|
||||
run("unraid-api report | todos >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// generate testparm.txt
|
||||
$testparm = run("testparm -s 2>/dev/null");
|
||||
if (!$all)
|
||||
@@ -748,8 +757,8 @@ newline("/$diag/system/sshd.txt");
|
||||
// copy servers.conf
|
||||
copy("/etc/nginx/conf.d/servers.conf", "/$diag/system/servers.conf.txt");
|
||||
maskIP("/$diag/system/servers.conf.txt");
|
||||
run("sed -Ei 's/[01234567890abcdef]+\.((my)?unraid\.net)/hash.\\1/gm;t' ".escapeshellarg("/$diag/system/servers.conf.txt")." 2>/dev/null");
|
||||
run("sed -Ei 's/\.[^\.]*\.ts\.net/\.magicdns\.ts\.net/gm' ".escapeshellarg("/$diag/system/servers.conf.txt")." 2>/dev/null");
|
||||
run("sed -Ei 's/[01234567890abcdef]+\.((my)?unraid\.net)/hash.\\1/gm;t' ".escapeshellarg("/$diag/system/servers.conf.txt"));
|
||||
run("sed -Ei 's/\.[^\.]*\.ts\.net/\.magicdns\.ts\.net/gm' ".escapeshellarg("/$diag/system/servers.conf.txt"));
|
||||
newline("/$diag/system/servers.conf.txt");
|
||||
|
||||
// show installed patches
|
||||
|
||||
@@ -19,7 +19,7 @@ switch ($argv[1]) {
|
||||
case 'reload':
|
||||
if (file_exists($ssl_input)) break;
|
||||
case 'load':
|
||||
$key = exec("dmidecode -qt1 | grep -Pom1 'Manufacturer: \K.+' | sed 's/ /_/g'")."ABCDEFGH";
|
||||
$key = exec("dmidecode -qt1 | grep -Pom1 'Manufacturer: \K.+' | sed -r 's/[^0-9a-zA-Z._-]/_/g'")."ABCDEFGH";
|
||||
$iv = "12".exec("cat /sys/class/net/wlan0/address | sed 's/://g'")."34";
|
||||
file_put_contents($ssl_input, "cipher=aes-256-cbc\nkey=".substr($key,0,63)."\niv=$iv\n");
|
||||
break;
|
||||
|
||||
@@ -15,15 +15,8 @@ SERV=/etc/services
|
||||
[[ -z $PORTSSH ]] && PORTSSH=22
|
||||
[[ -z $USE_UPNP ]] && USE_UPNP=no
|
||||
|
||||
# update ssh daemon listening port
|
||||
# determine ssh daemon listening port
|
||||
CURRENT=$(grep -Pom1 '^#?Port \K\d+$' $CONF)
|
||||
if [[ $PORTSSH != $CURRENT ]]; then
|
||||
if [[ $PORTSSH == 22 ]]; then
|
||||
sed -ri 's/^#?Port [0-9]+$/#Port 22/' $CONF
|
||||
else
|
||||
sed -ri "s/^#?Port [0-9]+\$/Port $PORTSSH/" $CONF
|
||||
fi
|
||||
fi
|
||||
|
||||
# enable/disable SSH service
|
||||
if [[ $USE_SSH == yes ]]; then
|
||||
@@ -31,7 +24,7 @@ if [[ $USE_SSH == yes ]]; then
|
||||
# start non-running ssh daemon
|
||||
/etc/rc.d/rc.sshd start
|
||||
else
|
||||
# restart when port has changed
|
||||
# restart when port has changed, rc.sshd will update value in $CONF
|
||||
[[ $PORTSSH != $CURRENT ]] && /etc/rc.d/rc.sshd restart
|
||||
fi
|
||||
elif [[ $(pgrep --ns $$ -cf $SSHD) -gt 0 ]]; then
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
.Theme--azure:root,
|
||||
.Theme--white:root {
|
||||
--browse-table-tbody-tr-hover-td-bg-color: var(--black-opacity-10);
|
||||
--browse-icon-color: var(--browse-text-color);
|
||||
}
|
||||
|
||||
div#ace_settingsmenu,
|
||||
@@ -126,4 +127,4 @@ table.tablesorter.indexer tbody tr:hover td {
|
||||
div.autoheight {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
emhttp/plugins/dynamix/sheets/SMBExtras.css
Normal file
6
emhttp/plugins/dynamix/sheets/SMBExtras.css
Normal file
@@ -0,0 +1,6 @@
|
||||
textarea {
|
||||
resize: none;
|
||||
font-family: bitstream;
|
||||
width: 60%;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -1580,4 +1580,282 @@ div.icon-zip {
|
||||
border: none;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
- Styles migrated from original file dynamix.docker.manager/styles/DockerManager.css
|
||||
- Majority of these styles appear to be modifying /webGui/styles/jquery.ui.css
|
||||
*/
|
||||
.Theme--azure:root,
|
||||
.Theme--white:root {
|
||||
--dynamix-jquery-ui-button-text-color: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-background-start: var(--red-800);
|
||||
--dynamix-jquery-ui-button-background-end: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-hover-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-default-background: transparent;
|
||||
--dynamix-jquery-ui-border-color: var(--black);
|
||||
--dynamix-jquery-ui-header-background: var(--gray-150); /* Condensed from: #e3e3e3 */
|
||||
--dynamix-jquery-ui-header-color: var(--black);
|
||||
--dynamix-jquery-ui-content-background: var(--gray-100);
|
||||
--dynamix-jquery-ui-content-color: var(--black);
|
||||
--dynamix-jquery-ui-active-background: var(--gray-150);
|
||||
--dynamix-jquery-ui-dropcontainer-background: var(--gray-100);
|
||||
--dynamix-jquery-ui-disabled-color: var(--black);
|
||||
--dynamix-jquery-ui-disabled-border-color: var(--gray-400); /* Condensed from: #a2a2a2 */
|
||||
--dynamix-jquery-ui-disabled-background: var(--gray-150);
|
||||
--dynamix-jquery-ui-text-color: var(--black);
|
||||
}
|
||||
|
||||
.Theme--black:root,
|
||||
.Theme--gray:root {
|
||||
--dynamix-jquery-ui-button-text-color: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-background-start: var(--red-800);
|
||||
--dynamix-jquery-ui-button-background-end: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-hover-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-default-background: transparent;
|
||||
--dynamix-jquery-ui-border-color: var(--gray-200); /* Condensed from: #e5e5e5 */
|
||||
--dynamix-jquery-ui-header-background: var(--gray-800); /* Condensed from: #2b2b2b */
|
||||
--dynamix-jquery-ui-header-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-content-background: var(--black);
|
||||
--dynamix-jquery-ui-content-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-active-background: var(--gray-700); /* Condensed from: #262626 */
|
||||
--dynamix-jquery-ui-dropcontainer-background: var(--black);
|
||||
--dynamix-jquery-ui-disabled-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-disabled-border-color: var(--gray-500); /* Condensed from: #6c6c6c */
|
||||
--dynamix-jquery-ui-disabled-background: var(--gray-700); /* Condensed from: #262626 */
|
||||
--dynamix-jquery-ui-text-color: var(--gray-100);
|
||||
}
|
||||
|
||||
.ui-dialog {
|
||||
.ui-dialog-buttonpane {
|
||||
.ui-dialog-buttonset {
|
||||
button {
|
||||
font-family: clear-sans;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
margin: 10px 12px 10px 0;
|
||||
padding: 9px 18px;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
color: var(--dynamix-jquery-ui-button-text-color);
|
||||
background:
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
)
|
||||
0 0 no-repeat,
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
) 0 100% no-repeat,
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-start))
|
||||
) 0 100% no-repeat,
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
from(var(--dynamix-jquery-ui-button-background-end)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
) 100% 100% no-repeat;
|
||||
background:
|
||||
linear-gradient(90deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-end)) 0 0 no-repeat,
|
||||
linear-gradient(90deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-end)) 0 100% no-repeat,
|
||||
linear-gradient(0deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-start)) 0 100% no-repeat,
|
||||
linear-gradient(0deg, var(--dynamix-jquery-ui-button-background-end) 0, var(--dynamix-jquery-ui-button-background-end)) 100% 100% no-repeat;
|
||||
background-size:
|
||||
100% 2px,
|
||||
100% 2px,
|
||||
2px 100%,
|
||||
2px 100%;
|
||||
|
||||
&:hover {
|
||||
color: var(--dynamix-jquery-ui-button-hover-color);
|
||||
background: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
);
|
||||
background: linear-gradient(90deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-end));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Consolidated .ui-dropdownchecklist styles */
|
||||
.ui-dropdownchecklist {
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(66.6deg, transparent 60%, var(--input-border-color) 40%),
|
||||
linear-gradient(113.4deg, var(--input-border-color) 40%, transparent 60%);
|
||||
background-position: calc(100% - 4px), 100%;
|
||||
background-size:
|
||||
4px 6px,
|
||||
4px 6px;
|
||||
background-repeat: no-repeat;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--input-border-color);
|
||||
outline: none;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
.ui-state-default {
|
||||
background: var(--dynamix-jquery-ui-default-background);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
height: 2.2rem;
|
||||
line-height: 2.2rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-widget-content {
|
||||
.ui-state-default {
|
||||
background: var(--dynamix-jquery-ui-default-background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-dropcontainer-wrapper {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-group {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
padding: 1px 9px 1px 8px;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-selector {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--dynamix-ui-dropdownchecklist-color);
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-selector-wrapper {
|
||||
vertical-align: middle;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-dropcontainer {
|
||||
color: var(--dynamix-ui-dropdownchecklist-color);
|
||||
background-color: var(--dynamix-ui-dropdownchecklist-color-alt1);
|
||||
border: 1px solid var(--dynamix-jquery-ui-border-color);
|
||||
}
|
||||
|
||||
.ui-state-disabled {
|
||||
color: var(--dynamix-ui-dropdownchecklist-color);
|
||||
border-bottom-color: var(--dynamix-ui-dropdownchecklist-color-alt2);
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-indent {
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-text {
|
||||
font-family: clear-sans;
|
||||
font-size: 1.3rem;
|
||||
color: var(--dynamix-ui-dropdownchecklist-color);
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-item {
|
||||
height: 2.2rem;
|
||||
line-height: 2.2rem;
|
||||
|
||||
input {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
/* Theme-specific styles for sidebar */
|
||||
.Theme--sidebar {
|
||||
.ui-dropdownchecklist {
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
border: 1px solid var(--border-color);
|
||||
background-position: calc(100% - 8px), calc(100% - 4px);
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-selector {
|
||||
padding: 4px 6px 1px 6px;
|
||||
/* 1px 14px 1px 6px makes the dropdown wider for the share read / write dropdown but
|
||||
IMO not needed because it makes the dropdown too wide when accompany other select inputs */
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-group {
|
||||
padding: 1px 6px;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-dropcontainer {
|
||||
border: 1px solid var(--dynamix-ui-dropdownchecklist-dropcontainer-border-color);
|
||||
}
|
||||
|
||||
.ui-state-disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-indent {
|
||||
padding-left: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-widget-header {
|
||||
border: none;
|
||||
background: var(--dynamix-jquery-ui-header-background);
|
||||
color: var(--dynamix-jquery-ui-header-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ui-widget-content {
|
||||
border: 1px solid var(--dynamix-jquery-ui-border-color);
|
||||
background: var(--dynamix-jquery-ui-content-background);
|
||||
color: var(--dynamix-jquery-ui-content-color);
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
background: var(--dynamix-jquery-ui-active-background);
|
||||
}
|
||||
|
||||
.ui-tailscale {
|
||||
&-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-width: 300px;
|
||||
width: 100%;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
&-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-value {
|
||||
flex: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
- Styles migrated from original file dynamix.docker.manager/styles/DockerManager.css
|
||||
- Majority of these styles appear to be modifying /webGui/styles/jquery.ui.css
|
||||
*/
|
||||
.Theme--azure:root,
|
||||
.Theme--white:root {
|
||||
--dynamix-jquery-ui-button-text-color: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-background-start: var(--red-800);
|
||||
--dynamix-jquery-ui-button-background-end: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-hover-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-default-background: transparent;
|
||||
--dynamix-jquery-ui-border-color: var(--black);
|
||||
--dynamix-jquery-ui-header-background: var(--gray-150); /* Condensed from: #e3e3e3 */
|
||||
--dynamix-jquery-ui-header-color: var(--black);
|
||||
--dynamix-jquery-ui-content-background: var(--gray-100);
|
||||
--dynamix-jquery-ui-content-color: var(--black);
|
||||
--dynamix-jquery-ui-active-background: var(--gray-150);
|
||||
--dynamix-jquery-ui-dropcontainer-background: var(--gray-100);
|
||||
--dynamix-jquery-ui-disabled-color: var(--black);
|
||||
--dynamix-jquery-ui-disabled-border-color: var(--gray-400); /* Condensed from: #a2a2a2 */
|
||||
--dynamix-jquery-ui-disabled-background: var(--gray-150);
|
||||
--dynamix-jquery-ui-text-color: var(--black);
|
||||
}
|
||||
|
||||
.Theme--black:root,
|
||||
.Theme--gray:root {
|
||||
--dynamix-jquery-ui-button-text-color: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-background-start: var(--red-800);
|
||||
--dynamix-jquery-ui-button-background-end: var(--orange-500);
|
||||
--dynamix-jquery-ui-button-hover-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-default-background: transparent;
|
||||
--dynamix-jquery-ui-border-color: var(--gray-200); /* Condensed from: #e5e5e5 */
|
||||
--dynamix-jquery-ui-header-background: var(--gray-800); /* Condensed from: #2b2b2b */
|
||||
--dynamix-jquery-ui-header-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-content-background: var(--black);
|
||||
--dynamix-jquery-ui-content-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-active-background: var(--gray-700); /* Condensed from: #262626 */
|
||||
--dynamix-jquery-ui-dropcontainer-background: var(--black);
|
||||
--dynamix-jquery-ui-disabled-color: var(--gray-100);
|
||||
--dynamix-jquery-ui-disabled-border-color: var(--gray-500); /* Condensed from: #6c6c6c */
|
||||
--dynamix-jquery-ui-disabled-background: var(--gray-700); /* Condensed from: #262626 */
|
||||
--dynamix-jquery-ui-text-color: var(--gray-100);
|
||||
}
|
||||
|
||||
.ui-dialog {
|
||||
.ui-dialog-buttonpane {
|
||||
.ui-dialog-buttonset {
|
||||
button {
|
||||
font-family: clear-sans;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
margin: 10px 12px 10px 0;
|
||||
padding: 9px 18px;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
color: var(--dynamix-jquery-ui-button-text-color);
|
||||
background:
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
)
|
||||
0 0 no-repeat,
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
) 0 100% no-repeat,
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-start))
|
||||
) 0 100% no-repeat,
|
||||
-webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
from(var(--dynamix-jquery-ui-button-background-end)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
) 100% 100% no-repeat;
|
||||
background:
|
||||
linear-gradient(90deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-end)) 0 0 no-repeat,
|
||||
linear-gradient(90deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-end)) 0 100% no-repeat,
|
||||
linear-gradient(0deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-start)) 0 100% no-repeat,
|
||||
linear-gradient(0deg, var(--dynamix-jquery-ui-button-background-end) 0, var(--dynamix-jquery-ui-button-background-end)) 100% 100% no-repeat;
|
||||
background-size:
|
||||
100% 2px,
|
||||
100% 2px,
|
||||
2px 100%,
|
||||
2px 100%;
|
||||
|
||||
&:hover {
|
||||
color: var(--dynamix-jquery-ui-button-hover-color);
|
||||
background: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
from(var(--dynamix-jquery-ui-button-background-start)),
|
||||
to(var(--dynamix-jquery-ui-button-background-end))
|
||||
);
|
||||
background: linear-gradient(90deg, var(--dynamix-jquery-ui-button-background-start) 0, var(--dynamix-jquery-ui-button-background-end));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Consolidated .ui-dropdownchecklist styles */
|
||||
.ui-dropdownchecklist {
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(66.6deg, transparent 60%, var(--input-border-color) 40%),
|
||||
linear-gradient(113.4deg, var(--input-border-color) 40%, transparent 60%);
|
||||
background-position: calc(100% - 4px), 100%;
|
||||
background-size:
|
||||
4px 6px,
|
||||
4px 6px;
|
||||
background-repeat: no-repeat;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--input-border-color);
|
||||
outline: none;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
.ui-state-default {
|
||||
background: var(--dynamix-jquery-ui-default-background);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
height: 2.2rem;
|
||||
line-height: 2.2rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-widget-content {
|
||||
.ui-state-default {
|
||||
background: var(--dynamix-jquery-ui-default-background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-dropcontainer-wrapper {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-group {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
padding: 1px 9px 1px 8px;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-selector {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--dynamix-ui-dropdownchecklist-color);
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-selector-wrapper {
|
||||
vertical-align: middle;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-dropcontainer {
|
||||
color: var(--dynamix-ui-dropdownchecklist-color);
|
||||
background-color: var(--dynamix-ui-dropdownchecklist-color-alt1);
|
||||
border: 1px solid var(--dynamix-jquery-ui-border-color);
|
||||
}
|
||||
|
||||
.ui-state-disabled {
|
||||
color: var(--dynamix-ui-dropdownchecklist-color);
|
||||
border-bottom-color: var(--dynamix-ui-dropdownchecklist-color-alt2);
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-indent {
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-text {
|
||||
font-family: clear-sans;
|
||||
font-size: 1.3rem;
|
||||
color: var(--dynamix-ui-dropdownchecklist-color);
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-item {
|
||||
height: 2.2rem;
|
||||
line-height: 2.2rem;
|
||||
|
||||
input {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
/* Theme-specific styles for sidebar */
|
||||
.Theme--sidebar {
|
||||
.ui-dropdownchecklist {
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
border: 1px solid var(--border-color);
|
||||
background-position: calc(100% - 8px), calc(100% - 4px);
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-selector {
|
||||
padding: 4px 6px 1px 6px;
|
||||
/* 1px 14px 1px 6px makes the dropdown wider for the share read / write dropdown but
|
||||
IMO not needed because it makes the dropdown too wide when accompany other select inputs */
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-group {
|
||||
padding: 1px 6px;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-dropcontainer {
|
||||
border: 1px solid var(--dynamix-ui-dropdownchecklist-dropcontainer-border-color);
|
||||
}
|
||||
|
||||
.ui-state-disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.ui-dropdownchecklist-indent {
|
||||
padding-left: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-widget-header {
|
||||
border: none;
|
||||
background: var(--dynamix-jquery-ui-header-background);
|
||||
color: var(--dynamix-jquery-ui-header-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ui-widget-content {
|
||||
border: 1px solid var(--dynamix-jquery-ui-border-color);
|
||||
background: var(--dynamix-jquery-ui-content-background);
|
||||
color: var(--dynamix-jquery-ui-content-color);
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
background: var(--dynamix-jquery-ui-active-background);
|
||||
}
|
||||
|
||||
.ui-tailscale {
|
||||
&-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-width: 300px;
|
||||
width: 100%;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
&-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-value {
|
||||
flex: 3;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
### END INIT INFO
|
||||
|
||||
# get correct location of binaries from configure
|
||||
sbindir=${exec_prefix}/sbin
|
||||
sbindir=/usr/sbin
|
||||
CGCONFIGPARSER_BIN=$sbindir/cgconfigparser
|
||||
CONFIG_FILE=/etc/cgconfig.conf
|
||||
CONFIG_DIR=/etc/cgconfig.d
|
||||
@@ -32,6 +32,25 @@ servicename=cgconfig
|
||||
|
||||
|
||||
lockfile=/run/lock/subsys/$servicename
|
||||
#
|
||||
# Source LSB routines
|
||||
#
|
||||
SYSLIBFILE=/lib/lsb/init-functions
|
||||
OLDSYSLIBFILE=/etc/init.d/functions
|
||||
if [[ -x $SYSLIBFILE ]] ; then
|
||||
# shellcheck disable=SC1090
|
||||
source $SYSLIBFILE
|
||||
elif [[ -x $OLDSYSLIBFILE ]] ; then
|
||||
# shellcheck disable=SC1090
|
||||
source $OLDSYSLIBFILE
|
||||
log_warning_msg() ( warning "$@" ; printf "\n" 1>&2 ; )
|
||||
log_failure_msg() ( failure "$@" ; printf "\n" 1>&2 ; )
|
||||
log_success_msg() ( success "$@" ; printf "\n" 1>&2 ; )
|
||||
else
|
||||
log_warning_msg() ( printf "warning:%s\n" "$@" 1>&2 ;)
|
||||
log_failure_msg() ( printf "failure:%s\n" "$@" 1>&2 ;)
|
||||
log_success_msg() ( printf "success:%s\n" "$@" 1>&2 ;)
|
||||
fi
|
||||
|
||||
# read the config
|
||||
CREATE_DEFAULT=yes
|
||||
@@ -50,8 +69,8 @@ create_default_groups() {
|
||||
read -r user ctrl defaultcgroup <<< \
|
||||
"$(grep -m1 '^\*[[:space:]]\+' /etc/cgrules.conf)"
|
||||
if [[ ( -n "$defaultcgroup" ) && ( "$defaultcgroup" = "*" ) ]]; then
|
||||
echo "/etc/cgrules.conf incorrect"
|
||||
echo "Overriding it"
|
||||
log_warning_msg "/etc/cgrules.conf incorrect"
|
||||
log_warning_msg "Overriding it"
|
||||
defaultcgroup=
|
||||
fi
|
||||
fi
|
||||
@@ -94,19 +113,19 @@ create_default_groups() {
|
||||
start() {
|
||||
printf "Starting %s service: " "$servicename"
|
||||
if [[ -f "$lockfile" ]]; then
|
||||
echo "lock file already exists"
|
||||
log_warning_msg "lock file already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ! -s "$CONFIG_FILE" ]]; then
|
||||
echo $CONFIG_FILE "is not configured"
|
||||
log_failure_msg $CONFIG_FILE "is not configured"
|
||||
return 6
|
||||
fi
|
||||
|
||||
|
||||
if ! "$CGCONFIGPARSER_BIN" -l "$CONFIG_FILE" -L "$CONFIG_DIR"
|
||||
then
|
||||
echo "Failed to parse " "$CONFIG_FILE" "or" "$CONFIG_DIR"'/*'
|
||||
log_failure_msg "Failed to parse " "$CONFIG_FILE" "or" "$CONFIG_DIR"'/*'
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -115,22 +134,22 @@ start() {
|
||||
fi
|
||||
|
||||
if ! mkdir -p "$lockfiledir" ; then
|
||||
echo "Failed to mkdir $lockfiledir directory"
|
||||
log_failure_msg "Failed to mkdir $lockfiledir directory"
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
if ! touch "$lockfile" ; then
|
||||
echo "Failed to touch $lockfile"
|
||||
log_failure_msg "Failed to touch $lockfile"
|
||||
return 1
|
||||
fi
|
||||
echo "Started $servicename"
|
||||
log_success_msg "Started $servicename"
|
||||
return 0
|
||||
}
|
||||
|
||||
stop() {
|
||||
printf "Stopping %s service is not supported!: " "$servicename"
|
||||
echo "Failed to stop $servicename"
|
||||
log_failure_msg "Failed to stop $servicename"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
# cgroups to classify processes
|
||||
### END INIT INFO
|
||||
|
||||
sbindir=${exec_prefix}/sbin
|
||||
sbindir=/usr/sbin
|
||||
CGRED_BIN=$sbindir/cgrulesengd
|
||||
|
||||
# Sanity checks
|
||||
@@ -35,7 +35,7 @@ CGRED_BIN=$sbindir/cgrulesengd
|
||||
#
|
||||
# Source LSB routines
|
||||
#
|
||||
SYSLIBFILE=/etc/rc.d/init.d/functions
|
||||
SYSLIBFILE=/lib/lsb/init-functions
|
||||
OLDSYSLIBFILE=/etc/init.d/functions
|
||||
if [[ -x $SYSLIBFILE ]] ; then
|
||||
# shellcheck disable=SC1090
|
||||
@@ -53,9 +53,9 @@ else
|
||||
fi
|
||||
|
||||
# Read in configuration options.
|
||||
if [[ -f "/etc/cgred.conf" ]] ; then
|
||||
if [[ -f "/etc/sysconfig/cgred.conf" ]] ; then
|
||||
# shellcheck disable=SC1091
|
||||
source /etc/cgred.conf
|
||||
source /etc/sysconfig/cgred.conf
|
||||
OPTIONS="$NODAEMON $LOG"
|
||||
if [[ -n "$LOG_FILE" ]]; then
|
||||
OPTIONS="$OPTIONS --logfile=$LOG_FILE"
|
||||
@@ -80,13 +80,13 @@ start()
|
||||
{
|
||||
echo -n $"Starting CGroup Rules Engine Daemon: "
|
||||
if [[ -f "$lockfile" ]]; then
|
||||
echo "$servicename is already running with PID $(cat ${pidfile})"
|
||||
log_failure_msg "$servicename is already running with PID $(cat ${pidfile})"
|
||||
return 0
|
||||
fi
|
||||
num=$(grep "cgroup" /proc/mounts | awk '$3=="cgroup"' | wc -l)
|
||||
if [[ "$num" -eq 0 ]]; then
|
||||
echo
|
||||
echo $"Cannot find cgroups, is cgconfig service running?"
|
||||
log_failure_msg $"Cannot find cgroups, is cgconfig service running?"
|
||||
return 1
|
||||
fi
|
||||
daemon --check $servicename --pidfile $pidfile $CGRED_BIN $OPTIONS
|
||||
@@ -106,7 +106,7 @@ stop()
|
||||
{
|
||||
echo -n $"Stopping CGroup Rules Engine Daemon..."
|
||||
if [[ ! -f $pidfile ]]; then
|
||||
#log_success_msg
|
||||
log_success_msg
|
||||
return 0
|
||||
fi
|
||||
killproc -p $pidfile -TERM "$processname"
|
||||
@@ -152,13 +152,13 @@ case "$1" in
|
||||
echo $"Reloading rules configuration..."
|
||||
kill -s 12 "$(cat ${pidfile})"
|
||||
RETVAL=$?
|
||||
#if [[ $RETVAL -eq 0 ]] ; then
|
||||
# log_success_msg ""
|
||||
#else
|
||||
# log_failure_msg ""
|
||||
#fi
|
||||
if [[ $RETVAL -eq 0 ]] ; then
|
||||
log_success_msg ""
|
||||
else
|
||||
log_failure_msg ""
|
||||
fi
|
||||
else
|
||||
echo "$servicename is not running."
|
||||
log_failure_msg "$servicename is not running."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
|
||||
@@ -35,73 +35,87 @@ TMP=/var/tmp/network.tmp
|
||||
# run & log functions
|
||||
. /etc/rc.d/rc.runlog
|
||||
|
||||
# determine active port name
|
||||
[[ -e $SYSTEM/bond0 ]] && PORT=bond0 || PORT=eth0
|
||||
[[ -e $SYSTEM/br0 ]] && PORT=br0
|
||||
[[ $(cat $SYSTEM/$PORT/carrier) == 0 && -e $SYSTEM/wlan0 && $(cat $SYSTEM/wlan0/carrier) == 1 ]] && PORT=wlan0
|
||||
|
||||
# Set defaults used by the docker daemon
|
||||
if [[ -f $DOCKER_CFG ]]; then
|
||||
for NIC in $NICS; do
|
||||
if [[ ${NIC:0:3} == eth ]]; then
|
||||
if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
|
||||
NIC=${NIC/eth/br}
|
||||
elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
|
||||
NIC=${NIC/eth/bond}
|
||||
fi
|
||||
fi
|
||||
CFG=($(grep -Pom2 "_SUBNET_|_${NIC^^}(_[0-9]+)?=" $DOCKER_CFG))
|
||||
if [[ ${CFG[0]} == _SUBNET_ && -z ${CFG[1]} ]]; then
|
||||
# interface has changed, update configuration
|
||||
X=${NIC//[^0-9]/}
|
||||
sed -ri "s/_(BR|BOND|ETH|WLAN)$X(_[0-9]+)?=/_${NIC^^}\2=/; s/(br|bond|eth|wlan)$X(\.[0-9]+)? /$NIC\2 /g" $DOCKER_CFG
|
||||
fi
|
||||
# wait for interface to go up
|
||||
carrier(){
|
||||
local n e
|
||||
[[ -e $SYSTEM/$1 ]] && e=${2:-10} || return 1
|
||||
for ((n=0; n<$e; n++)); do
|
||||
[[ $(cat $SYSTEM/$1/carrier 2>/dev/null) == 1 ]] && return 0
|
||||
[[ $e -gt 1 ]] && sleep 1
|
||||
done
|
||||
# Read (updated) Unraid docker configuration file
|
||||
. $DOCKER_CFG
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# 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"
|
||||
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"
|
||||
# initialize docker settings
|
||||
docker_read_options(){
|
||||
# determine active port name
|
||||
[[ -e $SYSTEM/bond0 ]] && PORT=bond0 || PORT=eth0
|
||||
[[ -e $SYSTEM/br0 ]] && PORT=br0
|
||||
[[ ! $(carrier $PORT) && $(carrier wlan0 1) ]] && PORT=wlan0
|
||||
|
||||
# Set defaults used by the docker daemon
|
||||
if [[ -f $DOCKER_CFG ]]; then
|
||||
for NIC in $NICS; do
|
||||
if [[ ${NIC:0:3} == eth ]]; then
|
||||
if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
|
||||
NIC=${NIC/eth/br}
|
||||
elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
|
||||
NIC=${NIC/eth/bond}
|
||||
fi
|
||||
fi
|
||||
CFG=($(grep -Pom2 "_SUBNET_|_${NIC^^}(_[0-9]+)?=" $DOCKER_CFG))
|
||||
if [[ ${CFG[0]} == _SUBNET_ && -z ${CFG[1]} ]]; then
|
||||
# interface has changed, update configuration
|
||||
X=${NIC//[^0-9]/}
|
||||
sed -ri "s/_(BR|BOND|ETH|WLAN)$X(_[0-9]+)?=/_${NIC^^}\2=/; s/(br|bond|eth|wlan)$X(\.[0-9]+)? /$NIC\2 /g" $DOCKER_CFG
|
||||
fi
|
||||
done
|
||||
# Read (updated) Unraid docker configuration file
|
||||
. $DOCKER_CFG
|
||||
fi
|
||||
fi
|
||||
|
||||
# Less verbose logging by default
|
||||
DOCKER_OPTS="--log-level=fatal $DOCKER_OPTS"
|
||||
# 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"
|
||||
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
|
||||
|
||||
# Enable global docker LOG rotation
|
||||
if [[ $DOCKER_LOG_ROTATION == yes ]]; then
|
||||
[[ -z $DOCKER_LOG_SIZE ]] && DOCKER_LOG_SIZE=10m
|
||||
[[ -z $DOCKER_LOG_FILES ]] && DOCKER_LOG_FILES=1
|
||||
DOCKER_OPTS="--log-opt max-size=$DOCKER_LOG_SIZE --log-opt max-file=$DOCKER_LOG_FILES $DOCKER_OPTS"
|
||||
fi
|
||||
# Less verbose logging by default
|
||||
DOCKER_OPTS="--log-level=fatal $DOCKER_OPTS"
|
||||
|
||||
# Adjust MTU size if non-default
|
||||
MTU=$(ip link show $PORT | grep -Po 'mtu \K\d+')
|
||||
[[ -n $MTU && $MTU -ne 1500 ]] && DOCKER_OPTS="--mtu=$MTU $DOCKER_OPTS"
|
||||
# Enable global docker LOG rotation
|
||||
if [[ $DOCKER_LOG_ROTATION == yes ]]; then
|
||||
[[ -z $DOCKER_LOG_SIZE ]] && DOCKER_LOG_SIZE=10m
|
||||
[[ -z $DOCKER_LOG_FILES ]] && DOCKER_LOG_FILES=1
|
||||
DOCKER_OPTS="--log-opt max-size=$DOCKER_LOG_SIZE --log-opt max-file=$DOCKER_LOG_FILES $DOCKER_OPTS"
|
||||
fi
|
||||
|
||||
# Enable IPv6 for docker bridge network
|
||||
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
|
||||
# ipv6 disabled
|
||||
[[ -d $CONF6/docker0 ]] && echo 1 > $CONF6/docker0/disable_ipv6
|
||||
fi
|
||||
# Adjust MTU size if non-default
|
||||
MTU=$(ip link show $PORT | grep -Po 'mtu \K\d+')
|
||||
[[ -n $MTU && $MTU -ne 1500 ]] && DOCKER_OPTS="--mtu=$MTU $DOCKER_OPTS"
|
||||
|
||||
export DOCKER_RAMDISK=true
|
||||
# Enable IPv6 for docker bridge network
|
||||
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
|
||||
# ipv6 disabled
|
||||
[[ -d $CONF6/docker0 ]] && echo 1 > $CONF6/docker0/disable_ipv6
|
||||
fi
|
||||
|
||||
export DOCKER_RAMDISK=true
|
||||
}
|
||||
|
||||
# Get docker daemon PID (if existing)
|
||||
docker_pid(){
|
||||
@@ -250,7 +264,7 @@ docker_network_start(){
|
||||
fi
|
||||
X=${NIC//[^0-9]/}
|
||||
REF=$(grep -Pom1 "<Network>\K(br|bond|eth|wlan)$X" $XMLFILE)
|
||||
[[ $X == 0 && $(cat $SYSTEM/$NIC/carrier) == 0 ]] && continue
|
||||
[[ $X == 0 && ! $(carrier $NIC 1) ]] && continue
|
||||
[[ $X == 0 && $NIC != wlan0 ]] && MAIN=$NIC
|
||||
[[ $NIC == wlan0 && -n $MAIN ]] && continue
|
||||
if [[ -n $REF && $REF != $NIC ]]; then
|
||||
@@ -632,6 +646,7 @@ docker_status(){
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
docker_read_options
|
||||
docker_service_start
|
||||
docker_network_start
|
||||
docker_container_start &>/dev/null &
|
||||
@@ -651,6 +666,7 @@ case "$1" in
|
||||
docker_network_stop
|
||||
docker_service_stop
|
||||
sleep 1
|
||||
docker_read_options
|
||||
docker_service_start
|
||||
docker_network_start
|
||||
docker_container_start &>/dev/null &
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
#
|
||||
# This selects your default screen font from among the ones in
|
||||
# /usr/share/kbd/consolefonts.
|
||||
|
||||
@@ -91,21 +91,37 @@ rm -f $CONFIG/plugins/unRAIDServer.plg
|
||||
|
||||
# These plugins are now integrated in the OS or obsolete and may interfere
|
||||
OBSOLETE="vfio.pci dynamix.wireguard dynamix.ssd.trim dynamix.file.manager gui.search unlimited-width proxy.editor unraid.patch AAA-UnraidPatch-BootLoader-DO_NOT_DELETE"
|
||||
QUIET="unraid.patch AAA-UnraidPatch-BootLoader-DO_NOT_DELETE"
|
||||
|
||||
for PLUGIN in $OBSOLETE; do
|
||||
if [[ -e $CONFIG/plugins/$PLUGIN.plg ]]; then
|
||||
log "moving obsolete plugin $PLUGIN.plg to $CONFIG/plugins-error"
|
||||
if [[ -e "$CONFIG/plugins/$PLUGIN.plg" ]]; then
|
||||
# preserve ssd-trim config
|
||||
if [[ $PLUGIN == dynamix.ssd.trim ]]; then
|
||||
if [[ -e $CONFIG/plugins/$PLUGIN/$PLUGIN.cfg ]]; then
|
||||
echo "[ssd]" >> $CONFIG/plugins/dynamix/dynamix.cfg
|
||||
cat $CONFIG/plugins/$PLUGIN/$PLUGIN.cfg >> $CONFIG/plugins/dynamix/dynamix.cfg
|
||||
if [[ -e "$CONFIG/plugins/$PLUGIN/$PLUGIN.cfg" ]]; then
|
||||
echo "[ssd]" >> "$CONFIG/plugins/dynamix/dynamix.cfg"
|
||||
cat "$CONFIG/plugins/$PLUGIN/$PLUGIN.cfg" >> "$CONFIG/plugins/dynamix/dynamix.cfg"
|
||||
fi
|
||||
if [[ -e $CONFIG/plugins/$PLUGIN/ssd-trim.cron ]]; then
|
||||
mv $CONFIG/plugins/$PLUGIN/ssd-trim.cron $CONFIG/plugins/dynamix/ssd-trim.cron
|
||||
if [[ -e "$CONFIG/plugins/$PLUGIN/ssd-trim.cron" ]]; then
|
||||
mv "$CONFIG/plugins/$PLUGIN/ssd-trim.cron" "$CONFIG/plugins/dynamix/ssd-trim.cron"
|
||||
fi
|
||||
fi
|
||||
mv $CONFIG/plugins/$PLUGIN.plg $CONFIG/plugins-error/
|
||||
rm -rf $CONFIG/plugins/$PLUGIN
|
||||
|
||||
if [[ " $QUIET " == *" $PLUGIN "* ]]; then
|
||||
rm "$CONFIG/plugins/$PLUGIN.plg"
|
||||
else
|
||||
log "moving obsolete plugin $PLUGIN.plg to $CONFIG/plugins-error"
|
||||
mv "$CONFIG/plugins/$PLUGIN.plg" "$CONFIG/plugins-error/"
|
||||
fi
|
||||
|
||||
# also remove $PLUGIN configuration directory
|
||||
rm -rf "$CONFIG/plugins/$PLUGIN"
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean up any quiet plugin plg files from previous runs
|
||||
for PLUGIN in $QUIET; do
|
||||
if [[ -e "$CONFIG/plugins-error/$PLUGIN.plg" ]]; then
|
||||
rm "$CONFIG/plugins-error/$PLUGIN.plg"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
@@ -36,7 +36,41 @@ dbus_start(){
|
||||
mkdir -p $(dirname $PIDFILE)
|
||||
rm -f $(dirname $PIDFILE)/*
|
||||
if [[ -x /usr/bin/dbus-uuidgen && -x /usr/bin/dbus-daemon ]]; then
|
||||
run /usr/bin/dbus-uuidgen --ensure
|
||||
if [ ! -e /etc/no-machine-id ]; then
|
||||
# Ah, the machine-id. DBus won't work right without it, and browsers and
|
||||
# other software will make use of this identifier. If you hate that idea,
|
||||
# you may create /etc/no-machine-id and then delete /etc/machine-id and
|
||||
# /var/lib/dbus/machine-id and we won't try to create these again.
|
||||
# You might pay for your "privacy" with bugs, though.
|
||||
# It is not recommended to do this, but it's your machine.
|
||||
#
|
||||
# If /etc/machine-id is a symlink, get rid of it:
|
||||
if [ -L /etc/machine-id ]; then
|
||||
rm -f /etc/machine-id
|
||||
fi
|
||||
# If /var/lib/dbus/machine-id is a symlink, get rid of it:
|
||||
if [ -L /var/lib/dbus/machine-id ]; then
|
||||
rm -f /var/lib/dbus/machine-id
|
||||
fi
|
||||
# If you have both /etc/machine-id and /var/lib/dbus/machine-id then we will
|
||||
# keep /etc/machine-id and back up /var/lib/dbus/machine-id:
|
||||
if [ -r /etc/machine-id -a -r /var/lib/dbus/machine-id ]; then
|
||||
mv /var/lib/dbus/machine-id /var/lib/dbus/machine-id.backup
|
||||
fi
|
||||
# If there's a /var/lib/dbus/machine-id file, and no /etc/machine-id, move it:
|
||||
if [ -r /var/lib/dbus/machine-id -a ! -e /etc/machine-id ]; then
|
||||
mv /var/lib/dbus/machine-id /etc/machine-id
|
||||
chmod 444 /etc/machine-id
|
||||
fi
|
||||
# If there's no /etc/machine-id, fix that:
|
||||
if [ ! -r /etc/machine-id ]; then
|
||||
/usr/bin/dbus-uuidgen --ensure=/etc/machine-id
|
||||
chmod 444 /etc/machine-id
|
||||
fi
|
||||
# Create the var/lib/dbus/machine-id symlink:
|
||||
rm -f /var/lib/dbus/machine-id
|
||||
ln -sf /etc/machine-id /var/lib/dbus/machine-id
|
||||
fi
|
||||
run /usr/bin/dbus-daemon --system
|
||||
fi
|
||||
if dbus_running; then REPLY="Started"; else REPLY="Failed"; fi
|
||||
|
||||
@@ -30,3 +30,7 @@ MODPROBE="/boot/config/modprobe.d"
|
||||
if [[ -d $MODPROBE ]]; then
|
||||
find $MODPROBE -type f -exec install -p -m 0644 "{}" /etc/modprobe.d \;
|
||||
fi
|
||||
|
||||
# LimeTech - grab any user downloaded module firmware files
|
||||
FIRMWARE="/boot/config/firmware"
|
||||
[[ -d $FIRMWARE ]] && cp -rpn $FIRMWARE/* /lib/firmware
|
||||
|
||||
@@ -11,6 +11,7 @@ DAEMON="SSH server daemon"
|
||||
CALLER="ssh"
|
||||
SSHD="/usr/sbin/sshd"
|
||||
CONF="/etc/ssh/sshd_config"
|
||||
INET=/etc/inetd.conf
|
||||
PID="/var/run/sshd.pid"
|
||||
SSH_BOOT="/boot/config/ssh"
|
||||
SSH_ETC="/etc/ssh"
|
||||
@@ -21,6 +22,13 @@ SSH_ETC="/etc/ssh"
|
||||
# library functions
|
||||
. /etc/rc.d/rc.library.source
|
||||
|
||||
# read settings
|
||||
[[ -e /boot/config/ident.cfg ]] && . <(fromdos </boot/config/ident.cfg)
|
||||
|
||||
# preset default values
|
||||
[[ -z $USE_SSH ]] && USE_SSH=no
|
||||
[[ -z $PORTSSH ]] && PORTSSH=22
|
||||
|
||||
sshd_running(){
|
||||
sleep 0.1
|
||||
# get all pids from sshd
|
||||
@@ -28,6 +36,7 @@ sshd_running(){
|
||||
}
|
||||
|
||||
sshd_build(){
|
||||
# update interfaces
|
||||
if check && [[ -n $BIND ]]; then
|
||||
# remove existing entries
|
||||
sed -ri '/^#?(ListenAddress|AddressFamily) /d' $CONF
|
||||
@@ -37,6 +46,15 @@ sshd_build(){
|
||||
done
|
||||
sed -ri "/^#?Port /a AddressFamily $FAMILY" $CONF
|
||||
fi
|
||||
# update ssh daemon listening port
|
||||
CURRENT=$(grep -Pom1 '^#?Port \K\d+$' $CONF)
|
||||
if [[ $PORTSSH != $CURRENT ]]; then
|
||||
if [[ $PORTSSH == 22 ]]; then
|
||||
sed -ri 's/^#?Port [0-9]+$/#Port 22/' $CONF
|
||||
else
|
||||
sed -ri "s/^#?Port [0-9]+\$/Port $PORTSSH/" $CONF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
sshd_start(){
|
||||
@@ -44,11 +62,13 @@ sshd_start(){
|
||||
local REPLY
|
||||
if sshd_running; then
|
||||
REPLY="Already started"
|
||||
elif [[ $USE_SSH != yes ]]; then
|
||||
REPLY="Disabled"
|
||||
else
|
||||
# make sure ssh dir exists on flash
|
||||
mkdir -p $SSH_BOOT
|
||||
# restore saved keys, config file, etc. (but not subdirs)
|
||||
cp -n $SSH_BOOT/* $SSH_ETC 2>/dev/null
|
||||
cp -f $SSH_BOOT/* $SSH_ETC 2>/dev/null
|
||||
chmod 600 $SSH_ETC/* 2>/dev/null
|
||||
# create host keys if needed and copy any newly generated key(s) back to flash
|
||||
ssh-keygen -A
|
||||
|
||||
@@ -15,8 +15,8 @@ STARTWIFI="/usr/local/emhttp/webGui/scripts/wireless"
|
||||
WPA="/etc/wpa_supplicant.conf"
|
||||
|
||||
# system network references
|
||||
SYSTEM=/sys/class/net
|
||||
CONF6=/proc/sys/net/ipv6/conf
|
||||
SYSTEM="/sys/class/net"
|
||||
CONF6="/proc/sys/net/ipv6/conf"
|
||||
|
||||
# run & log functions
|
||||
. /etc/rc.d/rc.runlog
|
||||
@@ -28,34 +28,66 @@ CONF6=/proc/sys/net/ipv6/conf
|
||||
[[ -r $INI ]] && . $INI
|
||||
PORT=${PORT:-wlan0}
|
||||
|
||||
# function to remove leading zeros in IPv4 address
|
||||
# translate security to informational text
|
||||
trans(){
|
||||
case "$1" in
|
||||
"IEEE-802.1X/SHA-256") echo "WPA3 Enterprise" ;;
|
||||
"IEEE-802.1X") echo "WPA2 Enterprise" ;;
|
||||
"SAE") echo "WPA3 Personal" ;;
|
||||
"PSK") echo "WPA2 Personal" ;;
|
||||
"WEP") echo "WEP (decprecated)" ;;
|
||||
"open") echo "Open network" ;;
|
||||
"FT/IEEE-802.1X" | "FT/SAE" | "FT/PSK") echo "Roaming Profile" ;;
|
||||
*) echo "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# set security priority
|
||||
priority(){
|
||||
case "$1" in
|
||||
"IEEE-802.1X/SHA-256") echo 25 ;;
|
||||
"FT/IEEE-802.1X") echo 18 ;;
|
||||
"IEEE-802.1X") echo 15 ;;
|
||||
"FT/SAE") echo 12 ;;
|
||||
"SAE") echo 10 ;;
|
||||
"FT/PSK") echo 8 ;;
|
||||
"PSK") echo 6 ;;
|
||||
"WEP") echo 4 ;;
|
||||
"open") echo 2 ;;
|
||||
*) echo 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# remove leading zeros in IPv4 address
|
||||
unzero(){
|
||||
local M Q
|
||||
echo -n $(for Q in ${1//./ }; do printf "$M%x" "0x$Q"; M=.; done)
|
||||
}
|
||||
|
||||
# function to remove leading zeros in IPv6 address
|
||||
# remove leading zeros in IPv6 address
|
||||
unzero6(){
|
||||
local A M Q
|
||||
A=${1/::/:-:}
|
||||
echo -n $(for Q in ${A//:/ }; do [[ $Q != - ]] && printf "$M%x" "0x$Q" || printf ":"; M=:; done)
|
||||
}
|
||||
|
||||
# function to convert text to hex
|
||||
# convert text to hex
|
||||
hex(){
|
||||
echo -n $1 | od -An -tx1 | tr -d ' \n'
|
||||
}
|
||||
|
||||
# function to wait for carrier of interface
|
||||
carrier_up(){
|
||||
local n
|
||||
for n in {1..10}; do
|
||||
[[ $(cat $SYSTEM/$1/carrier 2>/dev/null) == 1 ]] && return 0 || sleep 1
|
||||
# wait for interface to go up
|
||||
carrier(){
|
||||
local n e
|
||||
[[ -e $SYSTEM/$1 ]] && e=${2:-10} || return 1
|
||||
for ((n=0; n<$e; n++)); do
|
||||
[[ $(cat $SYSTEM/$1/carrier 2>/dev/null) == 1 ]] && return 0
|
||||
[[ $e -gt 1 ]] && sleep 1
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# function to enable/disable ipv6 assignment per interface
|
||||
# enable/disable ipv6 assignment per interface
|
||||
ipv6_addr(){
|
||||
if [[ -d $CONF6/$1 ]]; then
|
||||
echo $2 >$CONF6/$1/accept_ra
|
||||
@@ -64,7 +96,7 @@ ipv6_addr(){
|
||||
fi
|
||||
}
|
||||
|
||||
# function to assign IP address
|
||||
# assign IP address
|
||||
ipaddr_up(){
|
||||
# disable IPv6 per interface when IPv4 only
|
||||
[[ $IP == ipv4 ]] && DISABLE6=1 || DISABLE6=0
|
||||
@@ -77,7 +109,7 @@ ipaddr_up(){
|
||||
[[ $DNS == yes ]] && OPTIONS="$OPTIONS -C resolv.conf"
|
||||
[[ $IP == ipv4 ]] && OPTIONS="$OPTIONS -4"
|
||||
[[ $IP == ipv6 ]] && OPTIONS="$OPTIONS -6"
|
||||
if carrier_up $PORT; then
|
||||
if carrier $PORT; then
|
||||
# interface is UP
|
||||
log "interface $PORT is UP, polling up to 60 sec for DHCP $IP server"
|
||||
if ! run timeout 60 dhcpcd -w $OPTIONS $PORT; then
|
||||
@@ -91,7 +123,7 @@ ipaddr_up(){
|
||||
fi
|
||||
elif [[ $DHCP == no ]]; then
|
||||
# bring up interface using static IP address
|
||||
if carrier_up $PORT; then STATE="UP"; else STATE="DOWN"; fi
|
||||
if carrier $PORT; then STATE="UP"; else STATE="DOWN"; fi
|
||||
log "interface $PORT is $STATE, setting static $IP address"
|
||||
ipv6_addr $PORT 0 1
|
||||
if [[ $IP == ipv4 ]]; then
|
||||
@@ -112,7 +144,7 @@ ipaddr_up(){
|
||||
fi
|
||||
}
|
||||
|
||||
# function to release IP address
|
||||
# release IP address
|
||||
ipaddr_down(){
|
||||
if [[ $DHCP == yes ]]; then
|
||||
# release DHCP assigned address and default route
|
||||
@@ -138,35 +170,45 @@ ipaddr_down(){
|
||||
# WPA3 OK
|
||||
# WPA2 Enterprise OK
|
||||
# WPA2/WPA3 Enterprise OK
|
||||
# WPA3 Enterprise dynamic IP NOK, static IP OK
|
||||
# WPA3 Enterprise OK
|
||||
|
||||
wpa_configuration(){
|
||||
PSK=$(wpa_passphrase "$SSID" "$PASSWORD" 2>/dev/null | grep -Pom1 '^\s+psk=\K.+')
|
||||
[[ -z $PSK ]] && PSK="\"$PASSWORD\""
|
||||
[[ -z $2 ]] && echo "bgscan=\"\"" >$WPA || echo >$WPA
|
||||
[[ -z $2 && -n $CC ]] && echo "country=${CC,,}" >>$WPA
|
||||
log "wpa_configuration: $(trans "$1")"
|
||||
if [[ ! -e $WPA ]]; then
|
||||
echo "bgscan=\"\"" >$WPA
|
||||
echo "ctrl_interface=/run/wpa_supplicant" >>$WPA
|
||||
[[ -n $CC ]] && echo "country=${CC,,}" >>$WPA
|
||||
fi
|
||||
if [[ $1 =~ "PSK" ]]; then
|
||||
PSK=$(wpa_passphrase "$SSID" "$PASSWORD" 2>/dev/null | grep -Pom1 '^\s+psk=\K.+')
|
||||
[[ -z $PSK ]] && PSK="\"$PASSWORD\""
|
||||
fi
|
||||
echo "network={" >>$WPA
|
||||
echo "ssid=\"$SSID\"" >>$WPA
|
||||
echo "scan_ssid=1" >>$WPA
|
||||
[[ $1 == "open" ]] && echo "key_mgmt=NONE" >>$WPA
|
||||
[[ $1 == "PSK" ]] && echo "key_mgmt=WPA-PSK" >>$WPA
|
||||
[[ $1 == "FT/PSK" ]] && echo "key_mgmt=FT-PSK" >>$WPA
|
||||
[[ $1 == "SAE" ]] && echo "key_mgmt=SAE" >>$WPA
|
||||
[[ $1 =~ "IEEE" && $1 != "IEEE 802.1X/SHA-256" ]] && echo "key_mgmt=WPA-EAP" >>$WPA
|
||||
[[ $1 == "IEEE 802.1X/SHA-256" ]] && echo "key_mgmt=WPA-EAP-SHA256" >>$WPA
|
||||
[[ $1 =~ "IEEE 802.1X" ]] && echo "eap=PEAP" >>$WPA
|
||||
[[ $1 != "SAE" && ! $1 =~ "IEEE" ]] && echo "psk=$PSK" >>$WPA
|
||||
[[ $1 == "FT/SAE" ]] && echo "key_mgmt=FT-SAE" >>$WPA
|
||||
[[ $1 == "IEEE-802.1X" ]] && echo "key_mgmt=WPA-EAP" >>$WPA
|
||||
[[ $1 == "FT/IEEE-802.1X" ]] && echo "key_mgmt=FT-EAP" >>$WPA
|
||||
[[ $1 == "IEEE-802.1X/SHA-256" ]] && echo "key_mgmt=WPA-EAP-SHA256" >>$WPA
|
||||
[[ $1 =~ "PSK" ]] && echo "psk=$PSK" >>$WPA
|
||||
[[ $1 =~ "SAE" ]] && echo "sae_password=\"$PASSWORD\"" >>$WPA
|
||||
[[ $1 =~ "IEEE" ]] && echo "eap=PEAP" >>$WPA
|
||||
[[ $1 =~ "IEEE" ]] && echo "identity=\"$USERNAME\"" >>$WPA
|
||||
[[ $1 =~ "IEEE" && $1 != "IEEE 802.1X/SHA-256" ]] && echo "password=\"$PASSWORD\"" >>$WPA
|
||||
[[ $1 == "SAE" || $1 == "IEEE 802.1X/SHA-256" ]] && echo "sae_password=\"$PASSWORD\"" >>$WPA
|
||||
[[ $1 == "SAE" || $1 == "IEEE 802.1X/SHA-256" ]] && echo "ieee80211w=2" >>$WPA
|
||||
[[ $1 =~ "IEEE" ]] && echo "password=\"$PASSWORD\"" >>$WPA
|
||||
[[ $1 == "IEEE-802.1X" || $1 == "FT/IEEE-802.1X" ]] && echo "ieee80211w=1" >>$WPA
|
||||
[[ $1 =~ "SAE" || $1 == "IEEE-802.1X/SHA-256" ]] && echo "ieee80211w=2" >>$WPA
|
||||
[[ $1 =~ "IEEE" ]] && echo "phase2=\"auth=MSCHAPV2\"" >>$WPA
|
||||
[[ -n $2 ]] && echo "priority=$2" >>$WPA
|
||||
echo "priority=$(priority "$1")" >>$WPA
|
||||
echo "}" >>$WPA
|
||||
[[ -n $2 ]] && cat $WPA >>$WPA.tmp
|
||||
}
|
||||
|
||||
wifi_running(){
|
||||
sleep 0.1
|
||||
[[ $(cat $SYSTEM/$PORT/carrier 2>/dev/null) == 1 ]]
|
||||
carrier $PORT 1
|
||||
}
|
||||
|
||||
wifi_start(){
|
||||
@@ -182,6 +224,8 @@ wifi_start(){
|
||||
fi
|
||||
LINK=shim-$PORT
|
||||
[[ -e $SYSTEM/$LINK ]] || run ip link add link $PORT name $LINK type ipvtap mode l2 bridge
|
||||
run ip link set $PORT up
|
||||
run ip link set $LINK up
|
||||
# set regulatory region (if set) upon start
|
||||
REGION=$(var REGION $CFG)
|
||||
REGION_XX=$(var REGION_XX $CFG)
|
||||
@@ -191,11 +235,11 @@ wifi_start(){
|
||||
$OPENSSL load
|
||||
# start active SSID
|
||||
$STARTWIFI
|
||||
if ! carrier_up $PORT; then
|
||||
if ! wifi_running; then
|
||||
# try the saved SSIDs
|
||||
for SSID in $(grep -P '^\[.+\]$' $CFG | sed 1d | sed -r 's/\[|\]/"/g'); do
|
||||
[[ -n $SSID ]] && $STARTWIFI "$SSID" || break
|
||||
if carrier_up $PORT; then break; fi
|
||||
if wifi_running; then break; fi
|
||||
done
|
||||
fi
|
||||
if wifi_running; then REPLY="Started"; else REPLY="Failed"; fi
|
||||
@@ -249,31 +293,18 @@ wifi_join(){
|
||||
sed -ri "s/^(PASSWORD=\").+$/\1$ENCRYPT2\"/" $CFG
|
||||
fi
|
||||
SECURITY=${SECURITY:-$ATTR3}
|
||||
# replace space in enterprise security type
|
||||
SECURITY=${SECURITY//IEEE 802/IEEE-802}
|
||||
# regulatory region
|
||||
REGION=$(grep -Pom1 '^REGION="\K[^"]+' $CFG)
|
||||
REGION_XX=$(grep -Pom1 '^REGION_XX="\K[^"]+' $CFG)
|
||||
[[ $REGION == '00' ]] && CC=$REGION_XX || CC=$REGION
|
||||
if [[ ${SECURITY^^} == AUTO ]]; then
|
||||
# auto generate config
|
||||
log "wpa_configuration AUTO"
|
||||
echo "bgscan=\"\"" >$WPA.tmp
|
||||
[[ -n $CC ]] && echo "country=${CC,,}" >>$WPA.tmp
|
||||
wpa_configuration "IEEE 802.1X/SHA-256" 25
|
||||
wpa_configuration "IEEE 802.1X" 18
|
||||
wpa_configuration "SAE" 15
|
||||
wpa_configuration "PSK" 12
|
||||
mv $WPA.tmp $WPA
|
||||
[[ -n $(pgrep wpa_supplicant) ]] && pkill wpa_supplicant
|
||||
run wpa_supplicant -B -i $PORT -c $WPA
|
||||
elif [[ -z $SECURITY || ${SECURITY^^} == "OPEN" ]]; then
|
||||
# open network
|
||||
run iw dev $PORT connect "$SSID" auth open
|
||||
else
|
||||
# WPA encryption
|
||||
run wpa_configuration "$SECURITY"
|
||||
[[ -n $(pgrep wpa_supplicant) ]] && pkill wpa_supplicant
|
||||
run wpa_supplicant -B -i $PORT -c $WPA
|
||||
fi
|
||||
[[ -n $(pgrep wpa_supplicant) ]] && pkill wpa_supplicant
|
||||
rm -f $WPA
|
||||
# list of possible security types when "auto"
|
||||
[[ $SECURITY == "auto" ]] && SECURITY="IEEE-802.1X/SHA-256 FT/IEEE-802.1X IEEE-802.1X FT/SAE SAE FT/PSK PSK open"
|
||||
for TYPE in $SECURITY; do wpa_configuration "$TYPE"; done
|
||||
run wpa_supplicant -B -q -i $PORT -c $WPA
|
||||
# IPv4 address assignment
|
||||
IP=ipv4
|
||||
DHCP=$DHCP4
|
||||
|
||||
41
sbin/mover
41
sbin/mover
@@ -23,6 +23,7 @@
|
||||
# operation continues on to the next file.
|
||||
|
||||
PIDFILE="/var/run/mover.pid"
|
||||
CFGPATH="/boot/config/shares"
|
||||
DEBUGGING=""
|
||||
|
||||
move() {
|
||||
@@ -42,7 +43,7 @@ start() {
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
for SHARECFG in /boot/config/shares/*.cfg ; do
|
||||
for SHARECFG in $CFGPATH/*.cfg ; do
|
||||
SHARE=$(basename "$SHARECFG" .cfg)
|
||||
source <(fromdos < "$SHARECFG")
|
||||
|
||||
@@ -85,6 +86,8 @@ start() {
|
||||
}
|
||||
|
||||
empty() {
|
||||
DISK="$1"
|
||||
|
||||
if [ -f $PIDFILE ]; then
|
||||
if ps h $(cat $PIDFILE) | grep mover ; then
|
||||
echo "mover: already running"
|
||||
@@ -97,17 +100,22 @@ empty() {
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
# we can only empty share directories
|
||||
for SHAREPATH in /mnt/$DISK/* ; do
|
||||
if [[ -d "$SHAREPATH" ]]; then
|
||||
SHARE=$(basename "$SHAREPATH")
|
||||
if [[ -d "$SHAREPATH" && -f "$CFGPATH/$SHARE.cfg" ]]; then
|
||||
find "$SHAREPATH" -depth 2>/dev/null | /usr/libexec/unraid/move -e $DEBUGGING
|
||||
fi
|
||||
done
|
||||
|
||||
# output list of files which could not be moved
|
||||
shopt -s dotglob ; FILES=/mnt/$DISK/* ; shopt -u dotglob
|
||||
if [ ${#files[@]} -gt 2 ]; then
|
||||
echo "mover: not moved:"
|
||||
ls -1 --almost-all --recursive /mnt/$DISK
|
||||
# use 'find' in case huge number of files left in /mnt/$DISK
|
||||
count=$(find /mnt/$DISK -mindepth 1 | wc -l)
|
||||
if [ "$count" -gt 0 ]; then
|
||||
find /mnt/$DISK -mindepth 1 -depth -printf 'move: %p Not moved\n' | head -n 100
|
||||
if [ "$count" -gt 100 ]; then
|
||||
echo "[output truncated to first 100 entries]"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f $PIDFILE
|
||||
@@ -134,20 +142,24 @@ stop() {
|
||||
echo "mover: stopped"
|
||||
}
|
||||
|
||||
# Function to display usage
|
||||
# display usage and then exit
|
||||
usage() {
|
||||
echo "Usage: $0 start [-e] <disk_name>"
|
||||
echo "Usage: $0 start [-e <disk_name>]"
|
||||
echo " $0 stop|status"
|
||||
echo " <disk_name> must match pattern 'disk[0-9]*'"
|
||||
echo " <disk_name> must match pattern 'disk[0-9]*' and /mnt/<disk_name> must be a mountpoint"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# validate disk name
|
||||
validate_disk() {
|
||||
if [[ ! "$1" =~ ^disk[0-9]+$ ]]; then
|
||||
echo "Error: <disk_name> must match pattern 'disk[0-9]*'"
|
||||
usage
|
||||
fi
|
||||
if [[ ! "$1" =~ ^disk[0-9]+$ ]]; then
|
||||
echo "Error: <disk_name> must match pattern 'disk[0-9]+$'"
|
||||
usage
|
||||
fi
|
||||
if ! mountpoint --nofollow /mnt/$1 > /dev/null 2>&1; then
|
||||
echo "Error: nothing mounted at /mnt/$1"
|
||||
usage
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
@@ -164,8 +176,7 @@ start)
|
||||
usage
|
||||
else
|
||||
validate_disk "$3"
|
||||
DISK="$3"
|
||||
empty
|
||||
empty "$3"
|
||||
fi
|
||||
else
|
||||
usage
|
||||
|
||||
Reference in New Issue
Block a user