mirror of
https://github.com/unraid/webgui.git
synced 2025-12-30 22:20:23 -06:00
Merge remote-tracking branch 'upstream/master' into SR-IOV
This commit is contained in:
1
emhttp/plugins/dynamix.gui.search/sheets/gui_search.css
Normal file → Executable file
1
emhttp/plugins/dynamix.gui.search/sheets/gui_search.css
Normal file → Executable file
@@ -58,6 +58,7 @@
|
||||
#guiSearchBoxSpan {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 18px !important;
|
||||
}
|
||||
#guiSearchBox {
|
||||
position: relative;
|
||||
|
||||
@@ -67,6 +67,8 @@ case 'language':
|
||||
// nothing defined
|
||||
break;
|
||||
}
|
||||
// unset GUI search cache
|
||||
@unlink("/tmp/gui.search/searchResults.json");
|
||||
|
||||
// unset pending status
|
||||
if ($method != 'check') @unlink("$pending/$name");
|
||||
|
||||
@@ -23,9 +23,6 @@ Usage: plugin install PLUGIN-FILE [forced]
|
||||
|
||||
forced is optional and can be used to install a lower version than currently running.
|
||||
|
||||
This command will process all FILE elements in PLUGIN-FILE which are tagged with the "install" method (or
|
||||
that have no method tag).
|
||||
|
||||
This command has two major use cases:
|
||||
|
||||
1) Invoked at system startup by /etc/rc.d/rc.local on each .plg file found int /boot/config/plugins.
|
||||
@@ -79,6 +76,17 @@ Usage: plugin update PLUGIN
|
||||
Note: to support `plugin check` and `plugin update` the plugin file must contain both "pluginURL" and
|
||||
"version" attributes.
|
||||
|
||||
Usage: plugin download PLUGIN-FILE [TARGET-VERSION] [forced]
|
||||
Downloads newer versions of installed plugin files without executing any Run commands.
|
||||
This method downloads the updated plugin definition file (.plg) to /boot/config/plugins-nextboot/
|
||||
then downloads all required files to the same directory but skips script execution.
|
||||
On the next boot, rc.local will move the files from /boot/config/plugins-nextboot/ to /boot/config/plugins/
|
||||
and install them normally.
|
||||
This method is suitable for updating plugin files before an Unraid OS upgrade.
|
||||
|
||||
TARGET-VERSION is optional and specifies the Unraid version to use for version compatibility
|
||||
checks (Min/Max attributes). If omitted, the current Unraid version is used.
|
||||
|
||||
Usage: plugin [attribute name] PLUGIN-FILE
|
||||
|
||||
Any method which is not one of the actions listed above is assumed to be the name of an attribute of
|
||||
@@ -127,6 +135,12 @@ Here is the set of directories and files used by the plugin system:
|
||||
successful `plugin install`, the plugin file is copied here (if not here already). Upon successful
|
||||
`plugin remove`, the plugin file is deleted from here.
|
||||
|
||||
/boot/config/plugins-nextboot/
|
||||
This directory contains the plugin files for plugins to be updated at next boot-time.
|
||||
Upon successful `plugin download`, plugin files will be located here.
|
||||
On the next boot, rc.local will move the files from /boot/config/plugins-nextboot/ to /boot/config/plugins/
|
||||
and install them normally.
|
||||
|
||||
/boot/config/plugins-error/
|
||||
This directory contains plugin files that failed to install.
|
||||
|
||||
@@ -202,7 +216,8 @@ function run($command) {
|
||||
// hook programs receives three parameters: type=plugin and method and plugin-name
|
||||
//
|
||||
function pre_hooks() {
|
||||
global $method, $plugin;
|
||||
global $method, $plugin, $download_only;
|
||||
if ($download_only) return;
|
||||
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/pre-hooks";
|
||||
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
|
||||
write("Executing hook script: ".basename($hook)."\n");
|
||||
@@ -215,7 +230,8 @@ function pre_hooks() {
|
||||
// hook programs receives four parameters: type=plugin and method and plugin-name and error (empty if none)
|
||||
//
|
||||
function post_hooks($error='') {
|
||||
global $method, $plugin;
|
||||
global $method, $plugin, $download_only;
|
||||
if ($download_only) return;
|
||||
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/post-hooks";
|
||||
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
|
||||
write("Executing hook script: ".basename($hook)."\n");
|
||||
@@ -285,7 +301,7 @@ function filter_url($url) {
|
||||
// is processed for any of those methods.
|
||||
//
|
||||
function plugin($method, $plugin_file, &$error) {
|
||||
global $unraid, $logger;
|
||||
global $logger, $download_only, $check_version, $boot, $nextboot;
|
||||
$methods = ['install', 'remove'];
|
||||
|
||||
// parse plugin definition XML file
|
||||
@@ -352,17 +368,31 @@ function plugin($method, $plugin_file, &$error) {
|
||||
$name = $file->attributes()->Name ?: '';
|
||||
// bergware - check Unraid version dependency (if present)
|
||||
$min = $file->attributes()->Min;
|
||||
if ($min && version_compare($unraid['version'],$min,'<')) {
|
||||
write("plugin: skipping: ".basename($name)." - Unraid version too low, requires at least version $min\n");
|
||||
if ($min && version_compare($check_version,$min,'<')) {
|
||||
if (!$download_only) {
|
||||
write("plugin: skipping: ".basename($name)." - Unraid version too low, requires at least version $min\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$max = $file->attributes()->Max;
|
||||
if ($max && version_compare($unraid['version'],$max,'>')) {
|
||||
write("plugin: skipping: ".basename($name)." - Unraid version too high, requires at most version $max\n");
|
||||
if ($max && version_compare($check_version,$max,'>')) {
|
||||
if (!$download_only) {
|
||||
write("plugin: skipping: ".basename($name)." - Unraid version too high, requires at most version $max\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Name can be missing but only makes sense if Run attribute is present
|
||||
if ($name) {
|
||||
// If download_only mode, only process files that go to $boot/ (persistent location)
|
||||
if ($download_only) {
|
||||
if (strpos($name, "$boot/") === 0) {
|
||||
// Redirect $boot/ paths to $nextboot/ to be installed at next boot
|
||||
$name = "$nextboot/" . substr($name, strlen("$boot/"));
|
||||
} else {
|
||||
// Skip other files - they won't persist after boot
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Ensure parent directory exists
|
||||
//
|
||||
if (!file_exists(dirname($name))) {
|
||||
@@ -455,6 +485,11 @@ function plugin($method, $plugin_file, &$error) {
|
||||
//
|
||||
if ($file->attributes()->Run) {
|
||||
$command = $file->attributes()->Run;
|
||||
if ($download_only) {
|
||||
$target = $name ?: ($file->LOCAL ?: 'inline script');
|
||||
my_logger("skipping run: $command $target - download-only mode", $logger);
|
||||
continue;
|
||||
}
|
||||
if ($name) {
|
||||
my_logger("running: $command $name", $logger);
|
||||
$retval = run("$command $name");
|
||||
@@ -462,7 +497,6 @@ function plugin($method, $plugin_file, &$error) {
|
||||
my_logger("running: $command $file->LOCAL", $logger);
|
||||
$retval = run("$command $file->LOCAL");
|
||||
} elseif ($file->INLINE) {
|
||||
|
||||
$name = '/tmp/inline'.$current_file.'-'.pathinfo($plugin_file, PATHINFO_FILENAME).'.sh';
|
||||
file_put_contents($name, $file->INLINE);
|
||||
$exec = $command." ".escapeshellarg($name);
|
||||
@@ -484,13 +518,44 @@ function move($src_file, $tar_dir) {
|
||||
return rename($src_file, $tar_dir."/".basename($src_file));
|
||||
}
|
||||
|
||||
$notify = '/usr/local/emhttp/webGui/scripts/notify';
|
||||
$boot = '/boot/config/plugins';
|
||||
$plugins = '/var/log/plugins';
|
||||
$tmp = '/tmp/plugins';
|
||||
$method = $argv[1];
|
||||
$builtin = ['unRAIDServer','unRAIDServer-'];
|
||||
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
|
||||
$notify = '/usr/local/emhttp/webGui/scripts/notify';
|
||||
$boot = '/boot/config/plugins';
|
||||
$nextboot = '/boot/config/plugins-nextboot';
|
||||
$plugins = '/var/log/plugins';
|
||||
$tmp = '/tmp/plugins';
|
||||
$download_only = false;
|
||||
$script = $argv[0] ?? '';
|
||||
$method = $argv[1] ?? '';
|
||||
$args = array_slice($argv, 2);
|
||||
$extra_args = array_slice($args, 1); // anything after the plugin path
|
||||
$builtin = ['unRAIDServer','unRAIDServer-'];
|
||||
|
||||
// Load Unraid version and initialize check_version
|
||||
$unraid = parse_ini_file('/etc/unraid-version');
|
||||
$check_version = $unraid['version'];
|
||||
|
||||
// Optional flags
|
||||
// nchan must be the final argument
|
||||
$nchan = ($argc > 0) && ($argv[$argc-1] === 'nchan'); // console or nchan output
|
||||
if ($nchan) array_pop($extra_args);
|
||||
|
||||
// Extract target version if present (for download/update methods)
|
||||
// Version pattern: starts with digit, contains dots, optionally has dash suffix (e.g., "7.2.0", "7.2.0-rc.1")
|
||||
if (!empty($extra_args) && ($method === 'download' || $method === 'update')) {
|
||||
$first_arg = $extra_args[0];
|
||||
if (preg_match('/^\d+\.\d+\.\d+(-.*)?$/', $first_arg)) {
|
||||
$check_version = $first_arg;
|
||||
array_shift($extra_args);
|
||||
}
|
||||
}
|
||||
|
||||
$forced = !empty($extra_args); // any extra arg (besides nchan and TARGET-VERSION) signals forced
|
||||
|
||||
// Normalize download to reuse the update flow while skipping run steps.
|
||||
if ($method === 'download') {
|
||||
$download_only = true;
|
||||
$method = 'update';
|
||||
}
|
||||
|
||||
// In following code,
|
||||
// $plugin - is a basename of a plugin, eg, "myplugin.plg"
|
||||
@@ -506,8 +571,8 @@ if ($argc < 2) {
|
||||
// check all installed plugins, except built-in
|
||||
//
|
||||
if ($method == 'checkall') {
|
||||
if (!$cmd = realpath($argv[0])) {
|
||||
write("Unknown command: {$argv[0]}\n");
|
||||
if (!$cmd = realpath($script)) {
|
||||
write("Unknown command: {$script}\n");
|
||||
done(1);
|
||||
}
|
||||
foreach (glob("$plugins/*.plg", GLOB_NOSORT) as $link) {
|
||||
@@ -529,8 +594,8 @@ if ($method == 'checkall') {
|
||||
// update all installed plugins, which have a update available
|
||||
//
|
||||
if ($method == 'updateall') {
|
||||
if (!$cmd = realpath($argv[0])) {
|
||||
write("Unknown command: {$argv[0]}\n");
|
||||
if (!$cmd = realpath($script)) {
|
||||
write("Unknown command: {$script}\n");
|
||||
done(1);
|
||||
}
|
||||
foreach (glob("$plugins/*.plg", GLOB_NOSORT) as $link) {
|
||||
@@ -557,8 +622,8 @@ if ($method == 'updateall') {
|
||||
// check built-in only
|
||||
//
|
||||
if ($method == 'checkos') {
|
||||
if (!$cmd = realpath($argv[0])) {
|
||||
write("Unknown command: {$argv[0]}\n");
|
||||
if (!$cmd = realpath($script)) {
|
||||
write("Unknown command: {$script}\n");
|
||||
done(1);
|
||||
}
|
||||
foreach ($builtin as $link) {
|
||||
@@ -580,13 +645,12 @@ if ($argc < 3) {
|
||||
done(1);
|
||||
}
|
||||
|
||||
// plugin install [plugin_file]
|
||||
// plugin install [plugin_file] / plugin download [plugin_file]
|
||||
// cases:
|
||||
// a) dirname of [plugin_file] is /boot/config/plugins (system startup)
|
||||
// b) [plugin_file] is a URL
|
||||
// c) dirname of [plugin_file] is not /boot/config/plugins
|
||||
//
|
||||
$unraid = parse_ini_file('/etc/unraid-version');
|
||||
if ($method == 'install') {
|
||||
$argv[2] = preg_replace('#[\x00-\x1F\x80-\xFF]#', '', $argv[2]);
|
||||
$plugin = basename($argv[2]);
|
||||
@@ -594,7 +658,8 @@ if ($method == 'install') {
|
||||
write("plugin: $plugin is not a plg file\n");
|
||||
done(1);
|
||||
}
|
||||
write("plugin: installing: $plugin\n");
|
||||
$action = 'installing';
|
||||
write("plugin: $action: $plugin\n");
|
||||
// check for URL
|
||||
if (preg_match('#^https?://#',$argv[2])) {
|
||||
$pluginURL = $argv[2];
|
||||
@@ -613,8 +678,9 @@ if ($method == 'install') {
|
||||
$plugin_file = realpath($argv[2]);
|
||||
}
|
||||
// bergware - check Unraid version dependency (if present)
|
||||
global $check_version;
|
||||
$min = plugin('min', $plugin_file, $error);
|
||||
if ($min && version_compare($unraid['version'], $min, '<')) {
|
||||
if ($min && version_compare($check_version, $min, '<')) {
|
||||
write("plugin: installed Unraid version is too low, require at least version $min\n");
|
||||
if (dirname($plugin_file) == "$boot") {
|
||||
move($plugin_file, "$boot-error");
|
||||
@@ -624,7 +690,7 @@ if ($method == 'install') {
|
||||
done(1);
|
||||
}
|
||||
$max = plugin('max', $plugin_file, $error) ?: plugin('Unraid', $plugin_file, $error);
|
||||
if ($max && version_compare($unraid['version'], $max, '>')) {
|
||||
if ($max && version_compare($check_version, $max, '>')) {
|
||||
write("plugin: installed Unraid version is too high, require at most version $max\n");
|
||||
if (dirname($plugin_file) == "$boot") {
|
||||
move($plugin_file, "$boot-error");
|
||||
@@ -659,7 +725,6 @@ if ($method == 'install') {
|
||||
done(1);
|
||||
}
|
||||
// check version installation?
|
||||
$forced = $nchan ? ($argc==5 ? $argv[4] : false) : ($argc==4 ? $argv[3] : false);
|
||||
if (!$forced) {
|
||||
// do not re-install if same plugin already installed or has higher version
|
||||
if (strcmp($version, $installed_version) < 0) {
|
||||
@@ -711,11 +776,13 @@ if ($method == 'install') {
|
||||
if (!plugin('noInstall', $plugin_file, $error)) {
|
||||
if ($target != $plugin_file) copy($plugin_file, $target);
|
||||
symlink($target, $symlink);
|
||||
write("plugin: $plugin installed\n");
|
||||
my_logger("$plugin installed", $logger);
|
||||
$status = 'installed';
|
||||
write("plugin: $plugin $status\n");
|
||||
my_logger("$plugin $status", $logger);
|
||||
} else {
|
||||
write("script: $plugin executed\n");
|
||||
my_logger("script: $plugin executed", $logger);
|
||||
$script_action = 'executed';
|
||||
write("script: $plugin $script_action\n");
|
||||
my_logger("script: $plugin $script_action", $logger);
|
||||
}
|
||||
// run hook scripts for post processing
|
||||
post_hooks();
|
||||
@@ -778,7 +845,11 @@ if ($method == 'check') {
|
||||
if ($method == 'update') {
|
||||
$plugin = $argv[2];
|
||||
$symlink = "$plugins/$plugin";
|
||||
write("plugin: updating: $plugin\n");
|
||||
if ($download_only) {
|
||||
write("plugin: download-only mode enabled, skipping install commands\n");
|
||||
}
|
||||
$action = $download_only ? 'downloading' : 'updating';
|
||||
write("plugin: $action: $plugin\n");
|
||||
$installed_plugin_file = @readlink($symlink);
|
||||
if ($installed_plugin_file === false) {
|
||||
write("plugin: $plugin not installed\n");
|
||||
@@ -794,14 +865,14 @@ if ($method == 'update') {
|
||||
}
|
||||
// bergware - check Unraid version dependency (if present)
|
||||
$min = plugin('min', $plugin_file, $error);
|
||||
if ($min && version_compare($unraid['version'], $min, '<')) {
|
||||
if ($min && version_compare($check_version, $min, '<')) {
|
||||
write("plugin: installed Unraid version is too low, require at least version $min\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks($error);
|
||||
done(1);
|
||||
}
|
||||
$max = plugin('max', $plugin_file, $error) ?: plugin('Unraid', $plugin_file, $error);
|
||||
if ($max && version_compare($unraid['version'], $max, '>')) {
|
||||
if ($max && version_compare($check_version, $max, '>')) {
|
||||
write("plugin: installed Unraid version is too high, require at most version $max\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks($error);
|
||||
@@ -824,12 +895,17 @@ if ($method == 'update') {
|
||||
done(1);
|
||||
}
|
||||
// install was successful, save the updated plugin so it installs again next boot
|
||||
unlink($symlink);
|
||||
$target = "$boot/$plugin";
|
||||
$target = $download_only ? "$nextboot/$plugin" : "$boot/$plugin";
|
||||
$status = $download_only ? 'downloaded' : 'updated';
|
||||
// For normal update, unlink existing symlink before copying
|
||||
if (!$download_only) unlink($symlink);
|
||||
// Ensure target directory exists and copy plugin file
|
||||
@mkdir(dirname($target), 0770, true);
|
||||
copy($plugin_file, $target);
|
||||
symlink($target, $symlink);
|
||||
write("plugin: $plugin updated\n");
|
||||
my_logger("$plugin updated", $logger);
|
||||
// For normal update, create symlink to mark as installed
|
||||
if (!$download_only) symlink($target, $symlink);
|
||||
write("plugin: $plugin $status\n");
|
||||
my_logger("$plugin $status", $logger);
|
||||
// run hook scripts for post processing
|
||||
post_hooks();
|
||||
done(0);
|
||||
|
||||
@@ -3,6 +3,7 @@ Title="Virtual Machines"
|
||||
Tag="columns"
|
||||
Cond="is_file('/var/run/libvirt/libvirtd.pid')"
|
||||
Markdown="false"
|
||||
Focus="tableHeaderResize"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
@@ -462,37 +463,43 @@ function loadlist() {
|
||||
$('.text').click(showInput);
|
||||
$('.input').blur(hideInput);
|
||||
<?if (_var($display,'resize')):?>
|
||||
fillAvailableHeight({
|
||||
targetElementSelector: '.js-fill-available-height',
|
||||
elementSelectorsForHeight: [
|
||||
'.js-actions',
|
||||
'#kvm_table thead',
|
||||
],
|
||||
elementSelectorsForSpacing: [
|
||||
'#kvm_table',
|
||||
],
|
||||
manualSpacingOffset: 50, // without this, the main content will still be scrollable by like 20px
|
||||
});
|
||||
// Handle table header fixed positioning after resize
|
||||
function tableHeaderResize() {
|
||||
$('#kvm_table thead,#kvm_table tbody').removeClass('fixed');
|
||||
$('#kvm_table thead tr th').each(function(){$(this).width($(this).width());});
|
||||
$('#kvm_table tbody tr td').each(function(){$(this).width($(this).width());});
|
||||
$('#kvm_table thead,#kvm_table tbody').not('.child').addClass('fixed');
|
||||
}
|
||||
tableHeaderResize();
|
||||
$(window).bind('resize',function(){
|
||||
tableHeaderResize();
|
||||
});
|
||||
// Handle table header resizing when tab is clicked
|
||||
<?if (count($pages) > 2):?>
|
||||
$("#tab1").on('click',function(){
|
||||
|
||||
// Handle table header resizing when window is resized with debouncing
|
||||
var resizeTimeout;
|
||||
$(window).off('resize.vmTableResize').on('resize.vmTableResize',function(){
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(function(){
|
||||
tableHeaderResize();
|
||||
});
|
||||
<?endif;?>
|
||||
<?endif;?>
|
||||
}, 150);
|
||||
});
|
||||
<?endif;?>
|
||||
});
|
||||
}
|
||||
|
||||
// Handle table header fixed positioning after resize
|
||||
// Only applicable to listing height: fixed
|
||||
// Function needs to exist in the global scope due to "Focus" attribute in the page
|
||||
function tableHeaderResize() {
|
||||
<?if (_var($display,'resize')):?>
|
||||
fillAvailableHeight({
|
||||
targetElementSelector: '.js-fill-available-height',
|
||||
elementSelectorsForHeight: [
|
||||
'.js-actions',
|
||||
'#kvm_table thead',
|
||||
],
|
||||
elementSelectorsForSpacing: [
|
||||
'#kvm_table',
|
||||
],
|
||||
manualSpacingOffset: 50, // without this, the main content will still be scrollable by like 20px
|
||||
});
|
||||
$('#kvm_table thead,#kvm_table tbody').removeClass('fixed');
|
||||
$('#kvm_table thead tr th').each(function(){$(this).width($(this).width());});
|
||||
$('#kvm_table tbody tr td').each(function(){$(this).width($(this).width());});
|
||||
$('#kvm_table thead,#kvm_table tbody').not('.child').addClass('fixed');
|
||||
<?endif;?>
|
||||
}
|
||||
|
||||
$(function() {
|
||||
<?if ($msg):?>
|
||||
<?$color = strpos($msg, "rror:")!==false ? 'red-text':'green-text'?>
|
||||
|
||||
1
emhttp/plugins/dynamix.vm.manager/novnc/defaults.json
Normal file
1
emhttp/plugins/dynamix.vm.manager/novnc/defaults.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
emhttp/plugins/dynamix.vm.manager/novnc/mandatory.json
Normal file
1
emhttp/plugins/dynamix.vm.manager/novnc/mandatory.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
29
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
Normal file → Executable file
29
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
Normal file → Executable file
@@ -104,8 +104,8 @@ $arrConfigDefaults = [
|
||||
'autoport' => 'yes',
|
||||
'model' => 'qxl',
|
||||
'keymap' => 'none',
|
||||
'port' => -1 ,
|
||||
'wsport' => -1,
|
||||
'port' => 5900,
|
||||
'wsport' => 5901,
|
||||
'copypaste' => 'no',
|
||||
'render' => 'auto',
|
||||
'DisplayOptions' => ""
|
||||
@@ -256,7 +256,7 @@ if (isset($_POST['updatevm'])) {
|
||||
$xml = str_replace($olduuid,$newuuid,$xml);
|
||||
} else {
|
||||
// form view
|
||||
if ($error = create_vdisk($_POST) === false) {
|
||||
if (($error = create_vdisk($_POST)) === false) {
|
||||
$arrExistingConfig = custom::createArray('domain',$strXML);
|
||||
$arrUpdatedConfig = custom::createArray('domain',$lv->config_to_xml($_POST));
|
||||
if ($debug) {
|
||||
@@ -1327,9 +1327,9 @@ foreach ($arrConfig['shares'] as $i => $arrShare) {
|
||||
?>
|
||||
</select></span>
|
||||
<span id="Porttext" class="label <?=$hiddenport?>">_(VM Console Port)_:</span>
|
||||
<input id="port" type="number" size="5" maxlength="5" class="trim second <?=$hiddenport?>" name="gpu[<?=$i?>][port]" value="<?=$arrGPU['port']?>">
|
||||
<input id="port" onchange="checkVNCPorts()" min="5900" max="65535" type="number" size="5" maxlength="5" class="trim second <?=$hiddenport?>" name="gpu[<?=$i?>][port]" value="<?=$arrGPU['port']?>">
|
||||
<span id="WSPorttext" class="label <?=$hiddenwsport?>">_(VM Console WS Port)_:</span>
|
||||
<input id="wsport" type="number" size="5" maxlength="5" class="trim second <?=$hiddenwsport?>" name="gpu[<?=$i?>][wsport]" value="<?=$arrGPU['wsport']?>">
|
||||
<input id="wsport" onchange="checkVNCPorts()" min="5900" max="65535" type="number" size="5" maxlength="5" class="trim second <?=$hiddenwsport?>" name="gpu[<?=$i?>][wsport]" value="<?=$arrGPU['wsport']?>">
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
@@ -2124,6 +2124,18 @@ foreach ($arrConfig['evdev'] as $i => $arrEvdev) {
|
||||
var storageType = "<?=get_storage_fstype($arrConfig['template']['storage']);?>";
|
||||
var storageLoc = "<?=$arrConfig['template']['storage']?>";
|
||||
|
||||
function checkVNCPorts() {
|
||||
const port = $("#port").val();
|
||||
const wsport = $("#wsport").val();
|
||||
if (port < 5900 || port > 65535 || wsport < 5900 || wsport > 65535 || port == wsport) {
|
||||
swal({
|
||||
title: "_(Invalid Port)_",
|
||||
text: "_(VNC/SPICE ports must be between 5900 and 65535, and cannot be equal to each other)_",
|
||||
type: "error",
|
||||
confirmButtonText: "_(Ok)_"
|
||||
});
|
||||
}
|
||||
}
|
||||
function updateMAC(index, port) {
|
||||
var wlan0 = '<?=$mac?>'; // mac address of wlan0
|
||||
var mac = $('input[name="nic['+index+'][mac]"');
|
||||
@@ -2635,6 +2647,13 @@ $(function() {
|
||||
});
|
||||
<?endif?>
|
||||
|
||||
$("#vmform #domain_machine").change(function changeMachineEvent(){
|
||||
// Cdrom Bus: select IDE for i440 and SATA for q35
|
||||
if ($(this).val().indexOf('q35') != -1) {
|
||||
$('#vmform .cdrom_bus').val('sata');
|
||||
}
|
||||
});
|
||||
|
||||
$("#vmform .domain_vcpu").change(function changeVCPUEvent(){
|
||||
var $cores = $("#vmform .domain_vcpu:checked");
|
||||
if ($cores.length < 1) {
|
||||
|
||||
36
emhttp/plugins/dynamix/ArrayOperation.page
Normal file → Executable file
36
emhttp/plugins/dynamix/ArrayOperation.page
Normal file → Executable file
@@ -348,8 +348,40 @@ function shutdown_now(form, cmd) {
|
||||
$(form).append('<input type="hidden" name="cmd" value="'+cmd+'">');
|
||||
<?if ($confirm['down']):?>
|
||||
switch (cmd) {
|
||||
case 'reboot': var text = "_(This will reboot the system)_"; break;
|
||||
case 'shutdown': var text = "_(This will shutdown the system)_"; break;
|
||||
case 'reboot':
|
||||
if ($('input[name="safemode"]').prop('checked')) {
|
||||
<?
|
||||
$nginx = @parse_ini_file("/var/local/emhttp/nginx.ini") ?: [];
|
||||
$tailscaleUI = false;
|
||||
$connectRemote = false;
|
||||
foreach ($nginx as $key => $value) {
|
||||
if (strpos($key, 'NGINX_TAILSCALE') !== false && $value == $_SERVER['HTTP_HOST']) {
|
||||
$tailscaleUI = true;
|
||||
break;
|
||||
}
|
||||
if (strpos($key, 'NGINX_WANFQDN') !== false && $value == $_SERVER['HTTP_HOST']) {
|
||||
$connectRemote = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
var text = "_(This will reboot the server in safe mode.)_\n\n" + "_(No plugins will be loaded in safe mode.)_";
|
||||
<? if (is_file("/var/log/plugins/tailscale.plg") || is_file("/var/log/plugins/tailscale-preview.plg") || is_file("/var/log/plugins/dynamix.unraid.net.plg")): ?>
|
||||
text += " _(If you use Tailscale or Unraid Connect Remote Access to access the webGUI or services of the server, this access will be lost.)_";
|
||||
<? if ($tailscaleUI): ?>
|
||||
text += "\n\n<span class='strong'>_(You are currently accessing the webGUI of the server via Tailscale.)_</span>";
|
||||
<? endif; ?>
|
||||
<? if ($connectRemote): ?>
|
||||
text += "\n\n<span class='strong'>_(You are currently accessing the webGUI of the server via Unraid Connect Remote Access.)_</span>";
|
||||
<? endif; ?>
|
||||
<? endif; ?>
|
||||
} else {
|
||||
var text = "_(This will reboot the server.)_";
|
||||
}
|
||||
break;
|
||||
case 'shutdown':
|
||||
var text = "_(This will shutdown the server.)_";
|
||||
break;
|
||||
}
|
||||
swal({
|
||||
title:"_(Proceed)_?",
|
||||
|
||||
@@ -94,7 +94,7 @@ foreach ($devs as $disk) {
|
||||
}
|
||||
|
||||
$array_percent = number_format(100*$array_used/($array_size ?: 1),1,$dot,'');
|
||||
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
|
||||
$cpus=get_cpu_packages();
|
||||
$wg_up = $wireguard ? exec("wg show interfaces") : '';
|
||||
$wg_up = $wg_up ? explode(' ',$wg_up) : [];
|
||||
$up = count($wg_up);
|
||||
@@ -351,8 +351,13 @@ switch ($themeHelper->getThemeName()) { // $themeHelper set in DefaultPageLayout
|
||||
<td>
|
||||
<span class='flex flex-row flex-wrap items-center gap-4'>
|
||||
<span class="head_info">
|
||||
<span id='cpu-temp'></span>
|
||||
<span id='cpu-total-power'><i class='fa fa-fw fa-plug'></i>_(Total)_ _(Power)_: N/A</span>
|
||||
</span>
|
||||
<?if (count($cpus)<2):?>
|
||||
<span class="head_info">
|
||||
<i class="fa fa-thermometer"></i> _(Temperature)_: <span id='cpu-temp0'>N/A</span>
|
||||
</span>
|
||||
<?endif;?>
|
||||
<span class="switch">
|
||||
_(Load)_:<span class="head_bar">
|
||||
<span class='cpu_ load'>0%</span>
|
||||
@@ -398,22 +403,29 @@ switch ($themeHelper->getThemeName()) { // $themeHelper set in DefaultPageLayout
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<?
|
||||
foreach ($cpus as $pair) {
|
||||
[$cpu1, $cpu2] = my_preg_split('/[,-]/',$pair);
|
||||
echo "<tr class='cpu_open'>";
|
||||
if ($is_intel_cpu && count($core_types) > 0)
|
||||
$core_type = "({$core_types[$cpu1]})";
|
||||
else
|
||||
$core_type = "";
|
||||
foreach ($cpus as $cpu_index=>$package) {
|
||||
if (count($cpus) > 1) {
|
||||
echo "<td><span class='cpu_open w72'><i class='fa fa-plug'></i> "._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A </span> ";
|
||||
if (count($cpus)>1) echo "<i class='fa fa-thermometer'></i> "._("Temperature").": <span id='cpu-temp$cpu_index'>N/A</span>";
|
||||
echo "</td></span></tr>";
|
||||
}
|
||||
foreach ($package as $pair) {
|
||||
[$cpu1, $cpu2] = my_preg_split('/[,-]/',$pair);
|
||||
echo "<tr class='cpu_open'>";
|
||||
if ($is_intel_cpu && count($core_types) > 0)
|
||||
$core_type = "({$core_types[$cpu1]})";
|
||||
else
|
||||
$core_type = "";
|
||||
|
||||
if ($cpu2)
|
||||
echo "<td><span class='w26'>CPU $cpu1 $core_type - HT $cpu2 </span><span class='dashboard w36'><span class='cpu$cpu1 load resize'>0%</span><div class='usage-disk sys'><span id='cpu$cpu1'></span><span></span></div></span><span class='dashboard w36'><span class='cpu$cpu2 load resize'>0%</span><div class='usage-disk sys'><span id='cpu$cpu2'></span><span></span></div></span></td>";
|
||||
else
|
||||
echo "<td><span class='w26'>CPU $cpu1 $core_type</span><span class='w72'><span class='cpu$cpu1 load resize'>0%</span><div class='usage-disk sys'><span id='cpu$cpu1'></span><span></span></div></span></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
if ($cpu2)
|
||||
echo "<td><span class='w26'>CPU $cpu1 $core_type - HT $cpu2 </span><span class='dashboard w36'><span class='cpu$cpu1 load resize'>0%</span><div class='usage-disk sys'><span id='cpu$cpu1'></span><span></span></div></span><span class='dashboard w36'><span class='cpu$cpu2 load resize'>0%</span><div class='usage-disk sys'><span id='cpu$cpu2'></span><span></span></div></span></td>";
|
||||
else
|
||||
echo "<td><span class='w26'>CPU $cpu1 $core_type</span><span class='w72'><span class='cpu$cpu1 load resize'>0%</span><div class='usage-disk sys'><span id='cpu$cpu1'></span><span></span></div></span></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<tr id='cpu_chart'>
|
||||
<td>
|
||||
@@ -1441,6 +1453,7 @@ var startup = true;
|
||||
var stopgap = '<thead class="stopgap"><tr><td class="stopgap"></td></tr></thead>';
|
||||
var recall = null;
|
||||
var recover = null;
|
||||
var tempunit="<?=_var($display,'unit','C');?>";
|
||||
|
||||
// Helper function to calculate millisPerPixel based on container width
|
||||
function getMillisPerPixel(timeInSeconds, containerId) {
|
||||
@@ -1695,6 +1708,39 @@ function addChartNet(rx, tx) {
|
||||
txTimeSeries.append(now, Math.floor(tx / 1000));
|
||||
}
|
||||
|
||||
function updateCPUPower() {
|
||||
if (!cpupower) return;
|
||||
|
||||
// Update total power
|
||||
const totalEl = document.getElementById('cpu-total-power');
|
||||
const totalPower = cpupower.totalPower ?? 0;
|
||||
if (totalEl) {
|
||||
totalEl.innerHTML = `<i class="fa fa-fw fa-plug"></i> _(Total)_ _(Power)_: ${totalPower.toFixed(2)} W`;
|
||||
}
|
||||
|
||||
// Update each core's span
|
||||
const cpuspower = cpupower.power ?? [];
|
||||
cpuspower.forEach((power, index) => {
|
||||
const coreEl = document.getElementById(`cpu-power${index}`);
|
||||
if (coreEl) {
|
||||
coreEl.innerHTML = `${power.toFixed(2)} W`;
|
||||
}
|
||||
});
|
||||
|
||||
const cpustemps = cpupower.temp ?? [];
|
||||
cpustemps.forEach((temp, index) => {
|
||||
const coreTempEl = document.getElementById(`cpu-temp${index}`);
|
||||
if (coreTempEl) {
|
||||
tempdisplay = temp.toFixed(0);
|
||||
if (tempunit === "F") {
|
||||
tempdisplay = ((temp.toFixed(0))* 9 / 5) + 32;
|
||||
}
|
||||
coreTempEl.innerHTML = Math.round(tempdisplay)+` °`+tempunit;;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Cache for last values to avoid unnecessary DOM updates
|
||||
var lastCpuValues = {
|
||||
load: -1,
|
||||
@@ -2770,6 +2816,60 @@ $(function() {
|
||||
setTimeout(function() {
|
||||
// Charts initialized
|
||||
},500);
|
||||
|
||||
|
||||
|
||||
// Start GraphQL CPU power subscription with retry logic
|
||||
let cpuInitPWRAttempts = 0, cpuPWRRetryMs = 100;
|
||||
function initPwrCpuSubscription() {
|
||||
|
||||
|
||||
if (window.gql && window.apolloClient) {
|
||||
// Define the subscription query when GraphQL is available
|
||||
// corepower has the temps currently.
|
||||
CPU_POWER_SUBSCRIPTION = window.gql(`
|
||||
subscription SystemMetricsCpuTelemetry {
|
||||
systemMetricsCpuTelemetry {
|
||||
totalPower,
|
||||
power,
|
||||
temp,
|
||||
}
|
||||
}
|
||||
`);
|
||||
cpuPowerSubscription = window.apolloClient.subscribe({
|
||||
query: CPU_POWER_SUBSCRIPTION
|
||||
}).subscribe({
|
||||
next: (result) => {
|
||||
|
||||
|
||||
if (result.data?.systemMetricsCpuTelemetry){
|
||||
cpupower = result.data.systemMetricsCpuTelemetry;
|
||||
|
||||
updateCPUPower();
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('CPU power subscription error:', err);
|
||||
// Try to resubscribe with capped backoff
|
||||
if (cpuPowerSubscription) { try { cpuPowerSubscription.unsubscribe(); } catch(e){} }
|
||||
setTimeout(initPwrCpuSubscription, Math.min(cpuPWRRetryMs *= 2, 5000));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Retry with capped backoff if GraphQL client not ready
|
||||
cpuInitPWRAttempts++;
|
||||
setTimeout(initPwrCpuSubscription, Math.min(cpuPWRRetryMs *= 2, 2000));
|
||||
}
|
||||
}
|
||||
initPwrCpuSubscription();
|
||||
// Cleanup GraphQL subscription on page unload
|
||||
$(window).on('beforeunload', function() {
|
||||
if (cpuPowerSubscription) {
|
||||
cpuPowerSubscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Cleanup GraphQL subscription on page unload
|
||||
$(window).on('beforeunload', function() {
|
||||
|
||||
@@ -344,9 +344,9 @@ function selectDiskFsWidthZFS(slots,init) {
|
||||
value: width,
|
||||
text: _(sprintf('%s '+label+' of %s devices',groups,width)),
|
||||
}));
|
||||
if (selected_width == 0) selected_width = width;
|
||||
}
|
||||
}
|
||||
selected_width = slots;
|
||||
}
|
||||
$('#diskFsWidth').val(selected_width);
|
||||
}
|
||||
@@ -990,7 +990,7 @@ _(Critical disk utilization threshold)_ (%):
|
||||
</form>
|
||||
|
||||
<?if (fsType('btrfs')):?>
|
||||
<?if (!maintenance_mode()):?>
|
||||
<?if (_var($disk,'fsStatus')=="Mounted"):?>
|
||||
<div class="title nocontrol">
|
||||
<span class="left">
|
||||
<i class="title fa fa-hdd-o"></i> _(Pool Device Status)_
|
||||
|
||||
@@ -15,6 +15,8 @@ Tag="calendar-check-o"
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$mode = ['Disabled','Hourly','Daily','Weekly','Monthly'];
|
||||
$days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
|
||||
$setup = true;
|
||||
if (!$pool_devices) {
|
||||
echo "<p class='notice'>"._('No Cache device present')."!</p>";
|
||||
@@ -23,11 +25,13 @@ if (!$pool_devices) {
|
||||
echo "<p class='notice'>"._('User shares not enabled')."!</p>";
|
||||
$setup = false;
|
||||
}
|
||||
$cron = explode(' ',$var['shareMoverSchedule']);
|
||||
$move = $cron[2]!='*' ? 3 : ($cron[4]!='*' ? 2 : (substr($cron[1],0,1)!='*' ? 1 : 0));
|
||||
$mode = ['Hourly','Daily','Weekly','Monthly'];
|
||||
$days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
|
||||
|
||||
if (empty($var['shareMoverSchedule'])) {
|
||||
$cron = explode(' ', "* * * * *");
|
||||
$move = 0;
|
||||
} else {
|
||||
$cron = explode(' ', $var['shareMoverSchedule']);
|
||||
$move = $cron[2]!='*' ? 4 : ($cron[4]!='*' ? 3 : (substr($cron[1],0,1)!='*' ? 2 : 1));
|
||||
}
|
||||
$showMoverButton = $setup && $pool_devices;
|
||||
$moverRunning = file_exists('/var/run/mover.pid');
|
||||
?>
|
||||
@@ -37,27 +41,33 @@ $(function() {
|
||||
presetMover(document.mover_schedule);
|
||||
});
|
||||
<? endif; ?>
|
||||
function presetMover(form) {
|
||||
var mode = form.shareMoverSchedule.value;
|
||||
form.min.disabled = mode==0;
|
||||
form.day.disabled = mode==0 || mode!=3;
|
||||
form.dotm.disabled = mode==0 || mode!=4;
|
||||
form.hour1.disabled = mode==0;
|
||||
form.hour2.disabled = mode==0;
|
||||
form.day.value = form.day.disabled ? '*' : (form.day.value=='*' ? 0 : form.day.value);
|
||||
form.dotm.value = form.dotm.disabled ? '*' : (form.dotm.value=='*' ? 1 : form.dotm.value);
|
||||
if (mode==1) {$('#H1').hide(); $('#H2').show();} else {$('#H2').hide(); $('#H1').show();}
|
||||
}
|
||||
// Fool Unraid by simulating the original input field
|
||||
function prepareMover(form) {
|
||||
var mode = form.shareMoverSchedule.value;
|
||||
var min = mode!=0 ? form.min.value : 0;
|
||||
var hour = mode!=0 ? form.hour1.value : form.hour2.value;
|
||||
form.shareMoverSchedule.options[mode].value = min+' '+hour+' '+form.dotm.value+' * '+form.day.value;
|
||||
if (mode == 0)
|
||||
form.shareMoverSchedule.options[mode].value = '';
|
||||
else {
|
||||
var hour = mode!=1 ? form.hour1.value : form.hour2.value;
|
||||
var min = mode!=1 ? form.min.value : 0;
|
||||
form.shareMoverSchedule.options[mode].value = min+' '+hour+' '+form.dotm.value+' * '+form.day.value;
|
||||
}
|
||||
form.min.disabled = true;
|
||||
form.hour1.disabled = true;
|
||||
form.hour2.disabled = true;
|
||||
form.dotm.disabled = true;
|
||||
form.day.disabled = true;
|
||||
}
|
||||
function presetMover(form) {
|
||||
var mode = form.shareMoverSchedule.value;
|
||||
form.min.disabled = false;
|
||||
form.day.disabled = mode!=2;
|
||||
form.dotm.disabled = mode!=3;
|
||||
form.day.value = form.day.disabled ? '*' : (form.day.value=='*' ? 0 : form.day.value);
|
||||
form.dotm.value = form.dotm.disabled ? '*' : (form.dotm.value=='*' ? 1 : form.dotm.value);
|
||||
if (mode==0) {$('#H1').hide(); $('#H2').show();} else {$('#H2').hide(); $('#H1').show();}
|
||||
}
|
||||
</script>
|
||||
<form markdown="1" name="mover_schedule" method="POST" action="/update.htm" target="progressFrame" onsubmit="prepareMover(this)">
|
||||
<?if ($setup):?>
|
||||
@@ -92,7 +102,7 @@ _(Day of the month)_:
|
||||
|
||||
_(Time of the day)_:
|
||||
: <span>
|
||||
<span id="H1"<?if ($move==0):?> style="display:none"<?endif;?>><select name="hour1" class="narrow">
|
||||
<span id="H1"<?if ($move==1):?> style="display:none"<?endif;?>><select name="hour1" class="narrow">
|
||||
<?for ($d=0; $d<=23; $d++):?>
|
||||
<?=mk_option($cron[1], strval($d), sprintf("%02d", $d))?>
|
||||
<?endfor;?>
|
||||
@@ -102,7 +112,7 @@ _(Time of the day)_:
|
||||
<?=mk_option($cron[0], strval($d), sprintf("%02d", $d))?>
|
||||
<?endfor;?>
|
||||
</select> _(HH:MM)_</span>
|
||||
<span id="H2"<?if ($move!=0):?> style="display:none"<?endif;?>><select name="hour2">
|
||||
<span id="H2"<?if ($move!=1):?> style="display:none"<?endif;?>><select name="hour2">
|
||||
<?=mk_option($cron[1], "*/1", _("Every hour"))?>
|
||||
<?=mk_option($cron[1], "*/2", _("Every 2 hours"))?>
|
||||
<?=mk_option($cron[1], "*/3", _("Every 3 hours"))?>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
Menu="Notifications:3"
|
||||
Title="Notification Agents"
|
||||
Tag="rss-square"
|
||||
Focus="initDropdown"
|
||||
---
|
||||
<?php
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
/* Copyright 2005-2025, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -21,12 +22,14 @@ var disabledAgents = new Object();
|
||||
var openPage = true;
|
||||
|
||||
<?$width = [166,300]?>
|
||||
<?if ($tabbed):?>
|
||||
$('#tab3').bind({click:function(){initDropdown();}});
|
||||
$(function(){if ($('#tab3').is(':checked')) initDropdown();});
|
||||
<?else:?>
|
||||
|
||||
<? if (!$tabbed): ?>
|
||||
$(function(){initDropdown();});
|
||||
<?endif;?>
|
||||
<? else: ?>
|
||||
$(function(){
|
||||
if ( $('#tab3').attr('checked') ) initDropdown();
|
||||
});
|
||||
<? endif; ?>
|
||||
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function() {
|
||||
|
||||
@@ -214,7 +214,7 @@ function writeNFS(data, n, i) {
|
||||
var data = [];
|
||||
|
||||
/* Get the setting from the share config. */
|
||||
var hostList = $('textarea[name="shareHostListNFS"]').val().trim();
|
||||
var hostList = $('textarea[name="shareHostListNFS"]').val()?.trim() || "";
|
||||
|
||||
/* Replace all new lines in data.hostList with spaces. */
|
||||
var formattedHostList = <?= json_encode($sec_nfs[$name]['hostList']); ?>.replace(/\n/g, ' ');
|
||||
|
||||
25
emhttp/plugins/dynamix/agents/Join.xml
Normal file
25
emhttp/plugins/dynamix/agents/Join.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<Agent>
|
||||
<Name>Join</Name>
|
||||
<Variables>
|
||||
<Variable Help="The API key can be found [a href='https://joinjoaomgcd.appspot.com' target='_blank'] [u]here[/u].[/a]" Desc="API key" Default="">API_KEY</Variable>
|
||||
<Variable Help="Specify the fields which are included in the title of the notification." Desc="Notification Title" Default="$SUBJECT">TITLE</Variable>
|
||||
<Variable Help="Specify the fields which are included in the message body of the notification." Desc="Notification Message" Default="$DESCRIPTION">MESSAGE</Variable>
|
||||
</Variables>
|
||||
<Script>
|
||||
<![CDATA[
|
||||
#!/bin/bash
|
||||
##########
|
||||
{0}
|
||||
##########
|
||||
TITLE=$(echo -e "$TITLE")
|
||||
MESSAGE=$(echo -e "$MESSAGE")
|
||||
|
||||
curl -s -k -G \
|
||||
-d "apikey=$API_KEY" \
|
||||
--data-urlencode "title=$TITLE" \
|
||||
--data-urlencode "text=$MESSAGE" \
|
||||
-d "deviceId=group.all" \
|
||||
https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush 2>&1
|
||||
]]>
|
||||
</Script>
|
||||
</Agent>
|
||||
4
emhttp/plugins/dynamix/include/DefaultPageLayout/Header.php
Normal file → Executable file
4
emhttp/plugins/dynamix/include/DefaultPageLayout/Header.php
Normal file → Executable file
@@ -1,5 +1,7 @@
|
||||
<div id="header" class="<?=$display['banner']?>">
|
||||
<unraid-header-os-version></unraid-header-os-version>
|
||||
|
||||
<? if ($display['usage'] && $themeHelper->isSidebarTheme()): ?>
|
||||
<span id='array-usage-sidenav'></span>
|
||||
<? endif; ?>
|
||||
<?include "$docroot/plugins/dynamix.my.servers/include/myservers2.php"?>
|
||||
</div>
|
||||
|
||||
6
emhttp/plugins/dynamix/include/DefaultPageLayout/Navigation/Main.php
Normal file → Executable file
6
emhttp/plugins/dynamix/include/DefaultPageLayout/Navigation/Main.php
Normal file → Executable file
@@ -37,10 +37,12 @@
|
||||
</div>
|
||||
<? endif; ?>
|
||||
|
||||
<? if ($display['usage']): ?>
|
||||
<? if ($display['usage'] && ! $themeHelper->isSidebarTheme()): ?>
|
||||
<? my_usage(); ?>
|
||||
<? endif; ?>
|
||||
|
||||
<? if ($display['usage'] && $themeHelper->isSidebarTheme()): ?>
|
||||
<script>$("#array-usage-sidenav").html("<?my_usage();?>");</script>
|
||||
<? endif; ?>
|
||||
<? foreach ($buttonPages as $button): ?>
|
||||
<? if (empty($button['Link'])): ?>
|
||||
<? $icon = $button['Icon']; ?>
|
||||
|
||||
@@ -565,10 +565,8 @@ function dmidecode($key, $n, $all=true) {
|
||||
}
|
||||
|
||||
function is_intel_cpu() {
|
||||
$cpu = dmidecode('Processor Information','4',0);
|
||||
$cpu_vendor = $cpu['Manufacturer'] ?? "";
|
||||
$is_intel_cpu = stripos($cpu_vendor, "intel") !== false ? true : false;
|
||||
return $is_intel_cpu;
|
||||
$cpu_vendor_check = exec("grep -Pom1 '^model name\s+:\s*\K.+' /proc/cpuinfo") ?? "";
|
||||
return stripos($cpu_vendor_check, "intel") !== false;
|
||||
}
|
||||
|
||||
// Load saved PCI data
|
||||
@@ -874,7 +872,6 @@ function get_cpu_packages(string $separator = ','): array {
|
||||
return $packages;
|
||||
}
|
||||
|
||||
|
||||
function getIpAddressesByPci(string $pciAddress): array
|
||||
{
|
||||
$base = "/sys/bus/pci/devices/$pciAddress/net";
|
||||
|
||||
22
emhttp/plugins/dynamix/include/cpulist.php
Normal file
22
emhttp/plugins/dynamix/include/cpulist.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
function get_cpu_packages(string $separator = ','): array {
|
||||
$packages = [];
|
||||
|
||||
foreach (glob("/sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list") as $path) {
|
||||
$pkg_id = (int)file_get_contents(dirname($path) . "/physical_package_id");
|
||||
$siblings = str_replace(",", $separator, trim(file_get_contents($path)));
|
||||
|
||||
if (!in_array($siblings, $packages[$pkg_id] ?? [])) {
|
||||
$packages[$pkg_id][] = $siblings;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort groups within each package by first CPU number
|
||||
foreach ($packages as &$list) {
|
||||
$keys = array_map(fn($s) => (int)explode($separator, $s)[0], $list);
|
||||
array_multisort($keys, SORT_ASC, SORT_NUMERIC, $list);
|
||||
}
|
||||
unset($list);
|
||||
|
||||
return $packages;
|
||||
}
|
||||
@@ -497,7 +497,7 @@ 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 -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("lsusb -vt 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"));
|
||||
run("lsmod|sort 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsmod.txt"));
|
||||
|
||||
@@ -104,6 +104,37 @@ function clean_subject($subject) {
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap string values in double quotes for INI compatibility and escape quotes/backslashes.
|
||||
* Numeric types remain unquoted so they can be parsed as-is.
|
||||
*/
|
||||
function ini_encode_value($value) {
|
||||
if (is_int($value) || is_float($value)) return $value;
|
||||
if (is_bool($value)) return $value ? 'true' : 'false';
|
||||
$value = (string)$value;
|
||||
return '"'.strtr($value, ["\\"=>"\\\\", '"' => '\\"']).'"';
|
||||
}
|
||||
|
||||
function build_ini_string(array $data) {
|
||||
$lines = [];
|
||||
foreach ($data as $key => $value) {
|
||||
$lines[] = "{$key}=".ini_encode_value($value);
|
||||
}
|
||||
return implode("\n", $lines)."\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Trims and unescapes strings (eg quotes, backslashes) if necessary.
|
||||
*/
|
||||
function ini_decode_value($value) {
|
||||
$value = trim($value);
|
||||
$length = strlen($value);
|
||||
if ($length >= 2 && $value[0] === '"' && $value[$length-1] === '"') {
|
||||
return stripslashes(substr($value, 1, -1));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
// start
|
||||
if ($argc == 1) exit(usage());
|
||||
|
||||
@@ -222,10 +253,29 @@ case 'add':
|
||||
$archive = "{$archive}/".safe_filename("{$event}-{$ticket}.notify");
|
||||
if (file_exists($archive)) break;
|
||||
$entity = $overrule===false ? $notify[$importance] : $overrule;
|
||||
if (!$mailtest) file_put_contents($archive,"timestamp=$timestamp\nevent=$event\nsubject=$subject\ndescription=$description\nimportance=$importance\n".($message ? "message=".str_replace('\n','<br>',$message)."\n" : ""));
|
||||
if (($entity & 1)==1 && !$mailtest && !$noBrowser) file_put_contents($unread,"timestamp=$timestamp\nevent=$event\nsubject=$subject\ndescription=$description\nimportance=$importance\nlink=$link\n");
|
||||
if (($entity & 2)==2 || $mailtest) generate_email($event, clean_subject($subject), str_replace('<br>','. ',$description), $importance, $message, $recipients, $fqdnlink);
|
||||
if (($entity & 4)==4 && !$mailtest) { if (is_array($agents)) {foreach ($agents as $agent) {exec("TIMESTAMP='$timestamp' EVENT=".escapeshellarg($event)." SUBJECT=".escapeshellarg(clean_subject($subject))." DESCRIPTION=".escapeshellarg($description)." IMPORTANCE=".escapeshellarg($importance)." CONTENT=".escapeshellarg($message)." LINK=".escapeshellarg($fqdnlink)." bash ".$agent);};}};
|
||||
$cleanSubject = clean_subject($subject);
|
||||
$archiveData = [
|
||||
'timestamp' => $timestamp,
|
||||
'event' => $event,
|
||||
'subject' => $cleanSubject,
|
||||
'description' => $description,
|
||||
'importance' => $importance,
|
||||
];
|
||||
if ($message) $archiveData['message'] = str_replace('\n','<br>',$message);
|
||||
if (!$mailtest) file_put_contents($archive, build_ini_string($archiveData));
|
||||
if (($entity & 1)==1 && !$mailtest && !$noBrowser) {
|
||||
$unreadData = [
|
||||
'timestamp' => $timestamp,
|
||||
'event' => $event,
|
||||
'subject' => $cleanSubject,
|
||||
'description' => $description,
|
||||
'importance' => $importance,
|
||||
'link' => $link,
|
||||
];
|
||||
file_put_contents($unread, build_ini_string($unreadData));
|
||||
}
|
||||
if (($entity & 2)==2 || $mailtest) generate_email($event, $cleanSubject, str_replace('<br>','. ',$description), $importance, $message, $recipients, $fqdnlink);
|
||||
if (($entity & 4)==4 && !$mailtest) { if (is_array($agents)) {foreach ($agents as $agent) {exec("TIMESTAMP='$timestamp' EVENT=".escapeshellarg($event)." SUBJECT=".escapeshellarg($cleanSubject)." DESCRIPTION=".escapeshellarg($description)." IMPORTANCE=".escapeshellarg($importance)." CONTENT=".escapeshellarg($message)." LINK=".escapeshellarg($fqdnlink)." bash ".$agent);};}};
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
@@ -241,9 +291,12 @@ case 'get':
|
||||
$output[$i]['show'] = (fileperms($file) & 0x0FFF)==0400 ? 0 : 1;
|
||||
foreach ($fields as $field) {
|
||||
if (!$field) continue;
|
||||
[$key,$val] = array_pad(explode('=', $field),2,'');
|
||||
# limit the explode('=', …) used during reads to two pieces so values containing = remain intact
|
||||
[$key,$val] = array_pad(explode('=', $field, 2),2,'');
|
||||
if ($time) {$val = date($notify['date'].' '.$notify['time'], $val); $time = false;}
|
||||
$output[$i][trim($key)] = trim($val);
|
||||
# unescape the value before emitting JSON, so the browser UI
|
||||
# and any scripts calling `notify get` still see plain strings
|
||||
$output[$i][trim($key)] = ini_decode_value($val);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
@@ -139,15 +139,15 @@ hr {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type="url"],
|
||||
input[type="email"],
|
||||
input[type="date"],
|
||||
input[type="file"],
|
||||
textarea,
|
||||
.textarea {
|
||||
input[type="text"]:where(:not(.unapi *)),
|
||||
input[type="password"]:where(:not(.unapi *)),
|
||||
input[type="number"]:where(:not(.unapi *)),
|
||||
input[type="url"]:where(:not(.unapi *)),
|
||||
input[type="email"]:where(:not(.unapi *)),
|
||||
input[type="date"]:where(:not(.unapi *)),
|
||||
input[type="file"]:where(:not(.unapi *)),
|
||||
textarea:where(:not(.unapi *)),
|
||||
.textarea:where(:not(.unapi *)) {
|
||||
color: var(--text-color);
|
||||
font-family: clear-sans;
|
||||
font-size: 1.3rem;
|
||||
@@ -181,12 +181,12 @@ select:focus {
|
||||
}
|
||||
}
|
||||
|
||||
input[type="button"],
|
||||
input[type="reset"],
|
||||
input[type="submit"],
|
||||
button,
|
||||
button[type="button"],
|
||||
a.button {
|
||||
input[type="button"]:where(:not(.unapi *)),
|
||||
input[type="reset"]:where(:not(.unapi *)),
|
||||
input[type="submit"]:where(:not(.unapi *)),
|
||||
button:where(:not(.unapi *)),
|
||||
button[type="button"]:where(:not(.unapi *)),
|
||||
a.button:where(:not(.unapi *)) {
|
||||
font-family: clear-sans;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
@@ -250,48 +250,50 @@ input[type="number"]::-webkit-inner-spin-button {
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
input:focus[type="text"],
|
||||
input:focus[type="password"],
|
||||
input:focus[type="number"],
|
||||
input:focus[type="url"],
|
||||
input:focus[type="email"],
|
||||
input:focus[type="file"],
|
||||
textarea:focus {
|
||||
input:focus[type="text"]:where(:not(.unapi *)),
|
||||
input:focus[type="password"]:where(:not(.unapi *)),
|
||||
input:focus[type="number"]:where(:not(.unapi *)),
|
||||
input:focus[type="url"]:where(:not(.unapi *)),
|
||||
input:focus[type="email"]:where(:not(.unapi *)),
|
||||
input:focus[type="file"]:where(:not(.unapi *)),
|
||||
textarea:focus:where(:not(.unapi *)) {
|
||||
background-color: var(--focus-input-bg-color);
|
||||
outline: 0;
|
||||
}
|
||||
input:hover[type="button"],
|
||||
input:hover[type="reset"],
|
||||
input:hover[type="submit"],
|
||||
button:hover,
|
||||
button:hover[type="button"],
|
||||
a.button:hover {
|
||||
input:hover[type="button"]:where(:not(.unapi *)),
|
||||
input:hover[type="reset"]:where(:not(.unapi *)),
|
||||
input:hover[type="submit"]:where(:not(.unapi *)),
|
||||
button:hover:where(:not(.unapi *)),
|
||||
button:hover[type="button"]:where(:not(.unapi *)),
|
||||
a.button:hover:where(:not(.unapi *)) {
|
||||
color: var(--hover-button-text-color);
|
||||
background: var(--hover-button-background);
|
||||
}
|
||||
input[disabled],
|
||||
textarea[disabled] {
|
||||
input[disabled]:where(:not(.unapi *)),
|
||||
textarea[disabled]:where(:not(.unapi *)) {
|
||||
color: var(--text-color);
|
||||
border-bottom-color: var(--disabled-input-border-color);
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
input[type="button"][disabled],
|
||||
input[type="reset"][disabled],
|
||||
input[type="submit"][disabled],
|
||||
button[disabled],
|
||||
button[type="button"][disabled],
|
||||
a.button[disabled] input:hover[type="button"][disabled],
|
||||
input:hover[type="reset"][disabled],
|
||||
input:hover[type="submit"][disabled],
|
||||
button:hover[disabled],
|
||||
button:hover[type="button"][disabled],
|
||||
a.button:hover[disabled] input:active[type="button"][disabled],
|
||||
input:active[type="reset"][disabled],
|
||||
input:active[type="submit"][disabled],
|
||||
button:active[disabled],
|
||||
button:active[type="button"][disabled],
|
||||
a.button:active[disabled] {
|
||||
input[type="button"][disabled]:where(:not(.unapi *)),
|
||||
input[type="reset"][disabled]:where(:not(.unapi *)),
|
||||
input[type="submit"][disabled]:where(:not(.unapi *)),
|
||||
button[disabled]:where(:not(.unapi *)),
|
||||
button[type="button"][disabled]:where(:not(.unapi *)),
|
||||
a.button[disabled]:where(:not(.unapi *)),
|
||||
input:hover[type="button"][disabled]:where(:not(.unapi *)),
|
||||
input:hover[type="reset"][disabled]:where(:not(.unapi *)),
|
||||
input:hover[type="submit"][disabled]:where(:not(.unapi *)),
|
||||
button:hover[disabled]:where(:not(.unapi *)),
|
||||
button:hover[type="button"][disabled]:where(:not(.unapi *)),
|
||||
a.button:hover[disabled]:where(:not(.unapi *)),
|
||||
input:active[type="button"][disabled]:where(:not(.unapi *)),
|
||||
input:active[type="reset"][disabled]:where(:not(.unapi *)),
|
||||
input:active[type="submit"][disabled]:where(:not(.unapi *)),
|
||||
button:active[disabled]:where(:not(.unapi *)),
|
||||
button:active[type="button"][disabled]:where(:not(.unapi *)),
|
||||
a.button:active[disabled]:where(:not(.unapi *)) {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
color: var(--disabled-text-color);
|
||||
@@ -333,7 +335,7 @@ a.button:active[disabled] {
|
||||
input::-webkit-input-placeholder {
|
||||
color: var(--link-text-color);
|
||||
}
|
||||
select {
|
||||
select:where(:not(.unapi *)) {
|
||||
-webkit-appearance: none;
|
||||
font-family: clear-sans;
|
||||
font-size: 1.3rem;
|
||||
@@ -356,18 +358,18 @@ select {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
select option {
|
||||
select:where(:not(.unapi *)) option {
|
||||
color: var(--text-color);
|
||||
background-color: var(--mild-background-color);
|
||||
}
|
||||
select option:disabled {
|
||||
select:where(:not(.unapi *)) option:disabled {
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
select:focus {
|
||||
select:focus:where(:not(.unapi *)) {
|
||||
background-color: var(--focus-input-bg-color);
|
||||
outline: 0;
|
||||
}
|
||||
select[disabled] {
|
||||
select[disabled]:where(:not(.unapi *)) {
|
||||
color: var(--text-color);
|
||||
border-bottom-color: var(--disabled-border-color);
|
||||
opacity: 0.5;
|
||||
@@ -414,7 +416,7 @@ input.trim {
|
||||
width: 76px;
|
||||
min-width: 76px;
|
||||
}
|
||||
textarea {
|
||||
textarea:where(:not(.unapi *)) {
|
||||
resize: none;
|
||||
padding: 6px;
|
||||
border: 1px solid var(--textarea-border-color);
|
||||
@@ -951,7 +953,7 @@ div.title span img {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
table {
|
||||
table:where(:not(.unapi *)) {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-style: hidden;
|
||||
@@ -959,12 +961,12 @@ table {
|
||||
width: 100%;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
table thead td {
|
||||
table:where(:not(.unapi *)) thead td {
|
||||
line-height: 2.8rem;
|
||||
height: 2.8rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
table tbody td {
|
||||
table:where(:not(.unapi *)) tbody td {
|
||||
line-height: 2.6rem;
|
||||
height: 2.6rem;
|
||||
white-space: nowrap;
|
||||
@@ -2563,7 +2565,7 @@ div#title.ud {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.LanguageButton {
|
||||
.Theme--nav-top .LanguageButton {
|
||||
font-size: 12px !important;
|
||||
} /* Fix Switch Language Being Cut-Off */
|
||||
|
||||
@@ -2594,8 +2596,10 @@ div#title.ud {
|
||||
}
|
||||
|
||||
.usage-bar {
|
||||
position: relative;
|
||||
width: 6.4rem;
|
||||
position: absolute;
|
||||
bottom: .5rem;
|
||||
left: 50%;
|
||||
width: 14rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,6 @@ if [[ -x /etc/rc.d/rc.setterm ]]; then
|
||||
/etc/rc.d/rc.setterm
|
||||
fi
|
||||
|
||||
# Set the hostname:
|
||||
hostname $(cat /etc/HOSTNAME)
|
||||
|
||||
# Set the permissions on /var/log/dmesg according to whether the kernel
|
||||
# permits non-root users to access kernel dmesg information:
|
||||
if [[ -r /proc/sys/kernel/dmesg_restrict ]]; then
|
||||
@@ -246,7 +243,8 @@ fi
|
||||
# Start avahi:
|
||||
if [[ -x /etc/rc.d/rc.avahidaemon ]]; then
|
||||
/etc/rc.d/rc.avahidaemon start
|
||||
/etc/rc.d/rc.avahidnsconfd start
|
||||
# disable by default, users can start manually if needed
|
||||
# /etc/rc.d/rc.avahidnsconfd start
|
||||
fi
|
||||
|
||||
# Start Samba (a file/print server for Windows machines).
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
# run & log functions
|
||||
. /etc/rc.d/rc.runlog
|
||||
|
||||
# LimeTech - restore machine-id
|
||||
SRC="/boot/config/machine-id"
|
||||
DEST="/etc/machine-id"
|
||||
if [[ ! -f "$SRC" ]] || ! grep -Eq '^[0-9a-f]{32}$' "$SRC" ; then
|
||||
/usr/bin/dbus-uuidgen --ensure="$SRC"
|
||||
fi
|
||||
/usr/bin/install -m 0644 "$SRC" "$DEST"
|
||||
|
||||
# LimeTech - bind selected devices to vfio-pci
|
||||
/usr/local/sbin/vfio-pci 1>/var/log/vfio-pci 2>/var/log/vfio-pci-errors
|
||||
|
||||
@@ -217,9 +225,10 @@ if [[ -r /boot/config/ident.cfg ]]; then
|
||||
. <(/usr/bin/fromdos </boot/config/ident.cfg)
|
||||
NAME=${NAME//[^a-zA-Z\-\.0-9]/\-}
|
||||
fi
|
||||
/bin/echo "$NAME" >/etc/HOSTNAME
|
||||
/bin/hostname "$NAME"
|
||||
/bin/hostname -s >/etc/hostname
|
||||
/bin/echo "# Generated" >/etc/hosts
|
||||
/bin/echo "127.0.0.1 $NAME localhost" >>/etc/hosts
|
||||
/bin/echo "127.0.0.1 $NAME localhost" >>/etc/hosts
|
||||
|
||||
# LimeTech - restore the configured timezone
|
||||
if [[ $timeZone == custom ]]; then
|
||||
|
||||
@@ -31,7 +31,7 @@ CALLER="avahi"
|
||||
AVAHI="/usr/sbin/avahi-daemon"
|
||||
CONF="/etc/avahi/avahi-daemon.conf"
|
||||
HOSTS="/etc/hosts"
|
||||
NAME=$(</etc/HOSTNAME)
|
||||
NAME=$(hostname -s)
|
||||
|
||||
# run & log functions
|
||||
. /etc/rc.d/rc.runlog
|
||||
@@ -51,19 +51,19 @@ disable(){
|
||||
sed -ri "s/^#?(use-$1)=.*/\1=no/" $CONF
|
||||
}
|
||||
|
||||
# when starting avahidaemon, add name.local to the hosts file
|
||||
# add name.local to the hosts file
|
||||
add_local_to_hosts(){
|
||||
local OLD="^127\.0\.0\.1.*"
|
||||
local NEW="127.0.0.1 $NAME $NAME.local localhost"
|
||||
sed -i "s/$OLD/$NEW/gm;t" $HOSTS
|
||||
local NEW="127.0.0.1 $NAME $NAME.local localhost"
|
||||
sed -i "s/$OLD/$NEW/" $HOSTS
|
||||
return 0
|
||||
}
|
||||
|
||||
# when stopping avahidaemon, remove name.local from the hosts file
|
||||
# remove name.local from the hosts file
|
||||
remove_local_from_hosts(){
|
||||
local OLD="^127\.0\.0\.1.*"
|
||||
local NEW="127.0.0.1 $NAME localhost"
|
||||
sed -i "s/$OLD/$NEW/gm;t" $HOSTS
|
||||
local NEW="127.0.0.1 $NAME localhost"
|
||||
sed -i "s/$OLD/$NEW/" $HOSTS
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -113,7 +113,10 @@ avahid_restart(){
|
||||
}
|
||||
|
||||
avahid_reload(){
|
||||
$AVAHI --reload 2>/dev/null
|
||||
if avahid_running; then
|
||||
add_local_to_hosts
|
||||
$AVAHI --reload 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
avahid_update(){
|
||||
|
||||
@@ -243,6 +243,8 @@ container_add_route(){
|
||||
|
||||
docker_network_start(){
|
||||
log "Starting network..."
|
||||
# ensure br_netfilter modules is loaded, re: https://github.com/moby/moby/issues/48948
|
||||
modprobe br_netfilter
|
||||
# create list of possible custom networks
|
||||
EXCLUDE=; INCLUDE=$(ls --indicator-style=none $SYSTEM | awk '/^br[0-9]+/' ORS=' ')
|
||||
while IFS=$'\n' read -r NETWORK; do
|
||||
@@ -482,11 +484,11 @@ docker_network_start(){
|
||||
fi
|
||||
fi
|
||||
done
|
||||
# create IPv6 forward accept rule
|
||||
if [[ $IPV6_FORWARD == accept ]]; then
|
||||
ip6tables -P FORWARD ACCEPT
|
||||
log "created forward accept rule for IPv6 network"
|
||||
fi
|
||||
# # create IPv6 forward accept rule
|
||||
# if [[ $IPV6_FORWARD == accept ]]; then
|
||||
# ip6tables -P FORWARD ACCEPT
|
||||
# log "created forward accept rule for IPv6 network"
|
||||
# fi
|
||||
log "Network started."
|
||||
}
|
||||
|
||||
@@ -573,6 +575,9 @@ docker_service_start(){
|
||||
fi
|
||||
nohup $UNSHARE --propagation slave -- $DOCKER -p $DOCKER_PIDFILE $DOCKER_OPTS >>$DOCKER_LOG 2>&1 &
|
||||
wait_daemon
|
||||
# after docket started, continue to accept non-docker IPv6 traffic on br0
|
||||
ip6tables -P FORWARD ACCEPT
|
||||
# log "created forward accept rule for IPv6 network"
|
||||
if docker_running; then REPLY="Started"; else REPLY="Failed"; fi
|
||||
fi
|
||||
log "$DAEMON... $REPLY."
|
||||
|
||||
@@ -190,6 +190,26 @@ else
|
||||
log "Installing /boot/extra packages"
|
||||
( export -f log; find /boot/extra -maxdepth 1 -type f -exec sh -c 'upgradepkg --terse --install-new "$1" | log' -- "{}" \; )
|
||||
fi
|
||||
# Move any downloaded plugins in $CONFIG/plugins-nextboot to $CONFIG/plugins so they can be installed
|
||||
if [[ -d "$CONFIG/plugins-nextboot" ]]; then
|
||||
shopt -s nullglob
|
||||
for ITEM in "$CONFIG/plugins-nextboot"/*; do
|
||||
if [[ -f "$ITEM" && "$ITEM" == *.plg ]]; then
|
||||
# Move .plg files
|
||||
mv "$ITEM" "$CONFIG/plugins/" && log "Moved $(basename "$ITEM") from plugins-nextboot to plugins"
|
||||
elif [[ -d "$ITEM" ]]; then
|
||||
# Merge plugin directories
|
||||
PLUGIN_NAME=$(basename "$ITEM")
|
||||
DEST_DIR="$CONFIG/plugins/$PLUGIN_NAME"
|
||||
mkdir -p "$DEST_DIR"
|
||||
cp -r "$ITEM"/* "$DEST_DIR/" 2>/dev/null
|
||||
rm -rf "$ITEM"
|
||||
log "Moved plugin directory $PLUGIN_NAME and contents from plugins-nextboot to plugins"
|
||||
fi
|
||||
done
|
||||
shopt -u nullglob
|
||||
rmdir "$CONFIG/plugins-nextboot" 2>/dev/null;
|
||||
fi
|
||||
PRIORITY_PLUGINS=("dynamix.unraid.net.plg")
|
||||
# Install priority plugins first
|
||||
for PRIORITY_PLUGIN in "${PRIORITY_PLUGINS[@]}"; do
|
||||
|
||||
@@ -155,12 +155,17 @@ samba_stop(){
|
||||
REPLY="Already stopped"
|
||||
else
|
||||
REPLY="Stopped"
|
||||
# stop gracefully with SIGTERM the "master" smbd process first
|
||||
master=$(pgrep --ns $$ -o smbd)
|
||||
[[ -n "$master" ]] && run kill "$master"
|
||||
sleep 1
|
||||
# stop gracefully with SIGTERM
|
||||
run killall --ns $$ smbd nmbd wsdd2 winbindd
|
||||
samba_waitfor_shutdown
|
||||
if samba_running; then
|
||||
REPLY="Killed"
|
||||
# stop forcibly with SIGKILL
|
||||
[[ -n "$master" ]] && run kill -SIGKILL "$master"
|
||||
run killall --ns $$ -SIGKILL smbd nmbd wsdd2 winbindd
|
||||
samba_waitfor_shutdown
|
||||
fi
|
||||
|
||||
80
sbin/mover
80
sbin/mover
@@ -28,21 +28,21 @@ CFGPATH="/boot/config/shares"
|
||||
DEBUGGING=""
|
||||
|
||||
move() {
|
||||
find "$1" -depth 2>/dev/null | /usr/libexec/unraid/move $2 $DEBUGGING
|
||||
find "$1" -depth 2>/dev/null | /usr/libexec/unraid/move $DEBUGGING
|
||||
# second pass to clean up leftover empty directories
|
||||
find "$1" -depth -type d 2>/dev/null | /usr/libexec/unraid/move $2 $DEBUGGING
|
||||
find "$1" -depth -type d 2>/dev/null | /usr/libexec/unraid/move $DEBUGGING
|
||||
}
|
||||
|
||||
start() {
|
||||
if [ -f $PIDFILE ]; then
|
||||
if ps h $(cat $PIDFILE) | grep mover ; then
|
||||
echo "mover: already running"
|
||||
echo "mover: already running" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $$ >/var/run/mover.pid
|
||||
echo "mover: started"
|
||||
echo "mover: started" >&2
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
@@ -92,7 +92,7 @@ start() {
|
||||
for SHAREPATH in /mnt/$DISK/* ; do
|
||||
SHARE=$(basename "$SHAREPATH")
|
||||
if [[ -d "$SHAREPATH" && -f "$CFGPATH/$SHARE.cfg" ]]; then
|
||||
move "$SHAREPATH" "-e"
|
||||
move "$SHAREPATH"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -108,44 +108,7 @@ start() {
|
||||
done
|
||||
|
||||
rm -f $PIDFILE
|
||||
echo "mover: finished"
|
||||
}
|
||||
|
||||
empty() {
|
||||
DISK="$1"
|
||||
|
||||
if [ -f $PIDFILE ]; then
|
||||
if ps h $(cat $PIDFILE) | grep mover ; then
|
||||
echo "mover: already running"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $$ >/var/run/mover.pid
|
||||
echo "mover: started"
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
# we can only empty share directories
|
||||
for SHAREPATH in /mnt/$DISK/* ; do
|
||||
SHARE=$(basename "$SHAREPATH")
|
||||
if [[ -d "$SHAREPATH" && -f "$CFGPATH/$SHARE.cfg" ]]; then
|
||||
move "$SHAREPATH" "-e"
|
||||
fi
|
||||
done
|
||||
|
||||
# output list of files which could not be moved
|
||||
# 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
|
||||
echo "mover: finished"
|
||||
echo "mover: finished" >&2
|
||||
}
|
||||
|
||||
killtree() {
|
||||
@@ -170,43 +133,18 @@ stop() {
|
||||
|
||||
# display usage and then exit
|
||||
usage() {
|
||||
echo "Usage: $0 start [-e <disk_name>]"
|
||||
echo " $0 stop|status"
|
||||
echo " <disk_name> must match pattern 'disk[0-9]*' and /mnt/<disk_name> must be a mountpoint"
|
||||
echo "Usage: $0 start|stop|status"
|
||||
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 ! mountpoint --nofollow /mnt/$1 > /dev/null 2>&1; then
|
||||
echo "Error: nothing mounted at /mnt/$1"
|
||||
usage
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
if [ "$#" -ne 1 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
if [ -z "$2" ]; then
|
||||
start
|
||||
elif [ "$2" == "-e" ]; then
|
||||
if [ -z "$3" ]; then
|
||||
usage
|
||||
else
|
||||
validate_disk "$3"
|
||||
empty "$3"
|
||||
fi
|
||||
else
|
||||
usage
|
||||
fi
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
|
||||
Reference in New Issue
Block a user