Merge branch 'refactor/default-page-layout-nchan-abstraction' into refactor/default-page-layout-navigation

This commit is contained in:
Zack Spear
2025-05-05 15:00:32 -07:00
committed by GitHub
47 changed files with 2247 additions and 2074 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +1,6 @@
{
"src/register.ts": {
"file": "register._OziqIBF.js",
"file": "register.D9MKs8Co.js",
"name": "register",
"src": "src/register.ts",
"isEntry": true

View File

@@ -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:

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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;}

View File

@@ -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() {

View File

@@ -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>
&nbsp;
: <input type="submit" value="_(Apply)_" disabled><input type="button" value="_(Done)_" onclick="done()"><?if ($var['fsState']=="Started"):?>*_(Array must be **Stopped** to change)_*<?endif;?>

View File

@@ -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);

View File

@@ -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>

View File

@@ -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('"','&#34;',$data);
return str_replace('"','&#34;', $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>&nbsp;---';
} 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>';
?>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>";
}
}
}

View File

@@ -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;
}
?>

View File

@@ -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>";
?>

View File

@@ -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 = "";
}

View File

@@ -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\">&nbsp;</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\">&nbsp;</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\">&nbsp;</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();

View File

@@ -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

View File

@@ -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");

View File

@@ -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:')) {

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,6 @@
textarea {
resize: none;
font-family: bitstream;
width: 60%;
margin: 0;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}

View File

@@ -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
;;
*)

View File

@@ -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 &

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
#
# This selects your default screen font from among the ones in
# /usr/share/kbd/consolefonts.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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