Merge branch 'master' into master

This commit is contained in:
dlandon
2024-04-12 08:04:31 -05:00
committed by GitHub
76 changed files with 877 additions and 1279 deletions

View File

@@ -1203,7 +1203,7 @@ SSL certificate. Use this URL to access your server:
Note that use of a self-signed SSL certificate will generate a browser
warning.
Select **Strict** to enable *exclusive* use of an Unraid.net SSL
Select **Strict** to enable *exclusive* use of a myunraid.net SSL
certificate for https access (see **Provision** below). Note that a DNS
server must be reachable.
@@ -1267,10 +1267,10 @@ To adjust URLs or redirects, see the help text for "Use SSL/TLS".
:end
:mgmt_certificate_expiration_help:
**Provision** may be used to install a *free* Unraid.net SSL Certificate from
**Provision** may be used to install a *free* myunraid.net SSL Certificate from
[Let's Encrypt](https://letsencrypt.org/).
The Unraid.net SSL certificate can be used in two ways. First,
The myunraid.net SSL certificate can be used in two ways. First,
having the certificate present enables your server to respond to an
alternate URL of the form:
@@ -1284,24 +1284,20 @@ set to `*.<hash>.myunraid.net` thus validating the https connection.
You may enable this URL exclusively on your LAN by setting **Use
SSL/TLS** to **Strict**.
The second use for an Unraid.net certificate is to enable secure
remote access available through the My Servers plugin feature. Note
The second use for a myunraid.net certificate is to enable secure
remote access available through the Unraid Connect plugin feature. Note
that it is possible to use secure remote access in conjunction with
insecure local access.
After an Unraid.net SSL Certificate has been installed, two
background services are activated while the server is signed in to unraid.net:
- *updatedns* - This starts 30 seconds after server reboot has completed and contacts the Lime Technology
DNS service to register the servers local IP address. Thereafter it wakes up every 10 minutes in case
the local IP address has changed.
After a myunraid.net SSL Certificate has been installed, a
background service is activated:
- *renewcert* - This starts 60 seconds after server reboot has completed and contacts the Lime Technology
certificate renewal service to determine if your Unraid.net SSL certificate needs to be renewed.
certificate renewal service to determine if your myunraid.net SSL certificate needs to be renewed.
Thereafter it wakes up every 24 hours. If within 30 days of expiration, a new certificate is automatically
provisioned and downloaded to your server.
**Delete** may be used to delete the Unraid.net certificate file.
**Delete** may be used to delete the myunraid.net certificate file.
**nginx certificate handling details**
@@ -1309,7 +1305,7 @@ nginx makes use of two certificate files stored on the USB flash boot device:<br
- a self-signed certificate: `config/ssl/certs/<server-name>_unraid_bundle.pem`
- an Unraid.net certificate: `config/ssl/certs/certificate_bundle.pem`
- a myunraid.net certificate: `config/ssl/certs/certificate_bundle.pem`
The self-signed SSL certificate file is automatically created when nginx
starts; and re-created if the server hostname or local TLD is changed.
@@ -1493,7 +1489,7 @@ This will increase write performance but might possibly decrease read performanc
Let the server act as a central syslog server and collect syslog messages from other systems.
The server can listen on UDP, TCP or both with a selectable port number.
Syslog information is stored per IP address. That is every system gets its own syslog file.
Syslog information is stored either per IP address or per hostname. That is every system gets its own syslog file.
:end
:syslog_local_folder_help:
@@ -1501,6 +1497,15 @@ Select the share folder where the syslogs will be stored.
It is recommended that you use a share located on the cache drive to prevent array disk spinups.
:end
:syslog_remote_system_identifier_help:
Select the identifier for the remote system (used in the logfile name).
* "IP Address" uses the IP address (IPv4 or IPv6) of the sending system.
* "Hostname (from syslog message)" uses the hostname included in each syslog message.
* "Hostname (from DNS reverse lookup)" performs a DNS reverse lookup for the sending IP and uses the result.
:end
:syslog_local_rotation_help:
By default LOG rotation is disabled and will create a single LOG file of unlimited size.
@@ -2493,7 +2498,7 @@ If set to 'Yes' the bash history will persist reboots, set to 'No' to disable.
:WOL_intro_help:
This page allows the setup to start/resume and stop VMs, Containters(Docker and LXC) using WOL magic packets
It does setup wake up of the Unraid server.
It does not setup wake up of the Unraid server.
The process will look for the defined mac address defined within the service(VM or container) with the exception of dockers. Dockers do not have a mac address until they are running so you will need to define a user mac address to allow you to start them
@@ -2508,7 +2513,7 @@ When the enable and shutdown is set if the service is running when the next WOL
If set to yes Unraidwold daemon is set to run.
:end
:WOL_docker_help:
:WOL_run_docker_help:
If set to yes when wake on lan packets are received checks are carrried out for dockers otherwise dockers will be ignored.
:end

View File

@@ -111,6 +111,7 @@ function LockButton() {
}
function loadlist(init) {
timers.docker = setTimeout(function(){$('div.spinner.fixed').show('slow');},500);
docker = [];
$.get('/plugins/dynamix.docker.manager/include/DockerContainers.php',function(d) {
clearTimeout(timers.docker);
var data = d.split(/\0/);

View File

@@ -748,6 +748,7 @@ _(Categories)_:
: <input type="hidden" name="contCategory">
<select id="catSelect" size="1" multiple="multiple" style="display:none" onchange="prepareCategory();">
<optgroup label="_(Categories)_">
<option value="AI:">_(AI)_</option>
<option value="Backup:">_(Backup)_</option>
<option value="Cloud:">_(Cloud)_</option>
<option value="Crypto:">_(Crypto Currency)_</option>

View File

@@ -176,7 +176,7 @@ class DockerTemplates {
}
// if after above we don't have a valid url, check for GitLab
if (empty($github_api['url'])) {
$source = file_get_contents($url);
$source = $this->download_url($url);
// the following should always exist for GitLab Community Edition or GitLab Enterprise Edition
if (preg_match("/<meta content='GitLab (Community|Enterprise) Edition' name='description'>/", $source) > 0) {
$parse = parse_url($url);
@@ -721,7 +721,7 @@ class DockerClient {
$fp = stream_socket_client('unix:///var/run/docker.sock', $errno, $errstr);
if ($fp === false) {
echo "Couldn't create socket: [$errno] $errstr";
return null;
return [];
}
$protocol = $unchunk ? 'HTTP/1.0' : 'HTTP/1.1';
$out = "$method {$api}{$url} $protocol\r\nHost:127.0.0.1\r\nConnection:Close\r\n";
@@ -915,6 +915,7 @@ class DockerClient {
$c['Volumes'] = $info['HostConfig']['Binds'];
$c['Created'] = $this->humanTiming($ct['Created']);
$c['NetworkMode'] = $ct['HostConfig']['NetworkMode'];
$c['Manager'] = $info['Config']['Labels']['net.unraid.docker.managed'] ?? false;
[$net, $id] = array_pad(explode(':',$c['NetworkMode']),2,'');
$c['CPUset'] = $info['HostConfig']['CpusetCpus'];
$c['BaseImage'] = $ct['Labels']['BASEIMAGE'] ?? false;

View File

@@ -120,20 +120,25 @@ foreach ($containers as $ct) {
switch ($updateStatus) {
case 0:
echo "<span class='green-text' style='white-space:nowrap;'><i class='fa fa-check fa-fw'></i> "._('up-to-date')."</span>";
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
break;
case 1:
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('update ready')."</span></div>";
echo "<a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('apply update')."</span></a>";
break;
case 2:
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('rebuild ready')."</span></div>";
echo "<a class='exec'><span style='white-space:nowrap;'><i class='fa fa-recycle fa-fw'></i> "._('rebuilding')."</span></a>";
break;
default:
echo "<span class='orange-text' style='white-space:nowrap;'><i class='fa fa-unlink'></i> "._('not available')."</span>";
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
if ($ct['Manager'] == "dockerman")
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
break;
case 1:
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('update ready')."</span></div>";
if ($ct['Manager'] == "dockerman")
echo "<a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('apply update')."</span></a>";
else
echo "<span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('update available')."</span>";
break;
case 2:
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('rebuild ready')."</span></div>";
echo "<a class='exec'><span style='white-space:nowrap;'><i class='fa fa-recycle fa-fw'></i> "._('rebuilding')."</span></a>";
break;
default:
echo "<span class='orange-text' style='white-space:nowrap;'><i class='fa fa-unlink'></i> "._('not available')."</span>";
if ($ct['Manager'] == "dockerman")
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
break;
}
echo "<div class='advanced'><i class='fa fa-info-circle fa-fw'></i> ".compress(_($version),12,0)."</div></td>";
echo "<td>{$ct['NetworkMode']}</td>";

View File

@@ -14,8 +14,9 @@
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
require_once "$docroot/webGui/include/Wrappers.php";
$ncsi = exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -o 'OK'")=='OK';
$ncsi = check_network_connectivity();
$DockerTemplates = new DockerTemplates();
if ($ncsi) $DockerTemplates->downloadTemplates();
$DockerTemplates->getAllInfo($ncsi,$ncsi);

View File

@@ -270,6 +270,8 @@ function xmlToCommand($xml, $create_paths=false) {
$Mode = strval($config['Mode']);
if ($confType != "device" && !strlen($containerConfig)) continue;
if ($confType == "path") {
if ( ! trim($hostConfig) || ! trim($containerConfig) )
continue;
$Volumes[] = escapeshellarg($hostConfig).':'.escapeshellarg($containerConfig).':'.escapeshellarg($Mode);
if (!file_exists($hostConfig) && $create_paths) {
@mkdir($hostConfig, 0777, true);

View File

@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, Bergware International.
* Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
*
* This program is free software; you can redistribute it and/or
@@ -16,6 +16,7 @@
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
// this command will set the $notify array
extract(parse_plugin_cfg('dynamix', true));
// Multi-language support
@@ -43,7 +44,7 @@ if (!isset($check)) {
$DockerTemplates->getAllInfo(true);
echo " Done.";
} else {
$notify = "$docroot/webGui/scripts/notify";
$script = "$docroot/webGui/scripts/notify";
$var = (array)@parse_ini_file("/var/local/emhttp/var.ini");
$server = strtoupper(_var($var,'NAME','tower'));
$output = _var($notify,'docker_notify');
@@ -59,7 +60,7 @@ if (!isset($check)) {
$event = str_replace("&apos;","'",_("Docker")." - $name [$new]");
$subject = str_replace("&apos;","'",sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = str_replace("&apos;","'",sprintf(_("A new version of %s is available"),$name));
exec("$notify -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Docker' -x");
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Docker' -x");
}
}
}

View File

@@ -30,8 +30,7 @@ if (!document.getElementsByTagName(modalsWebComponent).length) {
$i18nHost.appendChild($modals);
}
</script>
<?
echo "
<unraid-i18n-host>
<unraid-user-profile server='" . $serverState->getServerStateJson() . "'></unraid-user-profile>
</unraid-i18n-host>";
<unraid-user-profile server="<?= $serverState->getServerStateJsonForHtmlAttr() ?>"></unraid-user-profile>
</unraid-i18n-host>

View File

@@ -34,11 +34,20 @@ class ServerState
protected $webguiGlobals;
private $var;
private $flashbackupCfg;
private $apiKey = '';
private $apiVersion = '';
private $avatar = '';
private $email = '';
private $extraOrigins = [];
private $flashBackupActivated = '';
private $hasRemoteApikey = false;
private $registeredTime = '';
private $username = '';
private $connectPluginInstalled = '';
private $connectPluginVersion;
private $configErrorEnum = [
"error" => 'UNKNOWN_ERROR',
"ineligible" => 'INELIGIBLE',
"invalid" => 'INVALID',
"nokeyserver" => 'NO_KEY_SERVER',
"withdrawn" => 'WITHDRAWN',
@@ -58,9 +67,9 @@ class ServerState
public $host = 'unknown';
public $combinedKnownOrigins = [];
public $nginxCfg;
public $flashbackupStatus;
public $registered;
public $nginxCfg = [];
public $flashbackupStatus = [];
public $registered = false;
public $myServersMiniGraphConnected = false;
public $keyfileBase64 = '';
@@ -78,11 +87,42 @@ class ServerState
// echo "<pre>" . json_encode($this->webguiGlobals, JSON_PRETTY_PRINT) . "</pre>";
$this->var = (array)parse_ini_file('state/var.ini');
$this->nginxCfg = parse_ini_file('/var/local/emhttp/nginx.ini');
$this->nginxCfg = @parse_ini_file('/var/local/emhttp/nginx.ini') ?? [];
$this->flashbackupCfg = '/var/local/emhttp/flashbackup.ini';
$this->flashbackupStatus = (file_exists($this->flashbackupCfg)) ? @parse_ini_file($this->flashbackupCfg) : [];
$this->osVersion = $this->var['version'];
$this->osVersionBranch = trim(@exec('plugin category /var/log/plugins/unRAIDServer.plg') ?? 'stable');
$caseModelFile = '/boot/config/plugins/dynamix/case-model.cfg';
$this->caseModel = file_exists($caseModelFile) ? file_get_contents($caseModelFile) : '';
$this->rebootDetails = new RebootDetails();
$this->keyfileBase64 = empty($this->var['regFILE']) ? null : @file_get_contents($this->var['regFILE']);
if ($this->keyfileBase64 !== false) {
$this->keyfileBase64 = @base64_encode($this->keyfileBase64);
$this->keyfileBase64UrlSafe = str_replace(['+', '/', '='], ['-', '_', ''], trim($this->keyfileBase64));
}
$this->updateOsCheck = new UnraidOsCheck();
$this->updateOsIgnoredReleases = $this->updateOsCheck->getIgnoredReleases();
$this->updateOsNotificationsEnabled = !empty(@$this->getWebguiGlobal('notify', 'unraidos'));
$this->updateOsResponse = $this->updateOsCheck->getUnraidOSCheckResult();
$this->setConnectValues();
}
/**
* Retrieve the value of a webgui global setting.
*/
public function getWebguiGlobal(string $key, string $subkey = null) {
if (!$subkey) {
return _var($this->webguiGlobals, $key, '');
}
$keyArray = _var($this->webguiGlobals, $key, []);
return _var($keyArray, $subkey, '');
}
private function setConnectValues() {
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net')) {
$this->connectPluginInstalled = 'dynamix.unraid.net.plg';
}
@@ -93,11 +133,29 @@ class ServerState
$this->connectPluginInstalled .= '_installFailed';
}
// exit early if the plugin is not installed
if (!$this->connectPluginInstalled) {
return;
}
$this->connectPluginVersion = file_exists('/var/log/plugins/dynamix.unraid.net.plg')
? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.plg 2>/dev/null'))
: (file_exists('/var/log/plugins/dynamix.unraid.net.staging.plg')
? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.staging.plg 2>/dev/null'))
: 'base-' . $this->var['version']);
$this->getMyServersCfgValues();
$this->getConnectKnownOrigins();
$this->getFlashBackupStatus();
}
private function getFlashBackupStatus() {
$flashbackupCfg = '/var/local/emhttp/flashbackup.ini';
$this->flashbackupStatus = (file_exists($flashbackupCfg)) ? @parse_ini_file($flashbackupCfg) : [];
$this->flashBackupActivated = empty($this->flashbackupStatus['activated']) ? '' : 'true';
}
private function getMyServersCfgValues() {
/**
* @todo can we read this from somewhere other than the flash? Connect page uses this path and /boot/config/plugins/dynamix.my.servers/myservers.cfg…
* - $myservers_memory_cfg_path ='/var/local/emhttp/myservers.cfg';
@@ -122,15 +180,17 @@ class ServerState
$this->myServersFlashCfg['remote']['dynamicRemoteAccessType'] = "DISABLED";
}
$this->osVersion = $this->var['version'];
$this->osVersionBranch = trim(@exec('plugin category /var/log/plugins/unRAIDServer.plg') ?? 'stable');
$this->apiKey = $this->myServersFlashCfg['upc']['apikey'] ?? '';
$this->apiVersion = $this->myServersFlashCfg['api']['version'] ?? '';
$this->avatar = (!empty($this->myServersFlashCfg['remote']['avatar']) && $this->connectPluginInstalled) ? $this->myServersFlashCfg['remote']['avatar'] : '';
$this->email = $this->myServersFlashCfg['remote']['email'] ?? '';
$this->hasRemoteApikey = !empty($this->myServersFlashCfg['remote']['apikey']);
$this->registered = !empty($this->myServersFlashCfg['remote']['apikey']) && $this->connectPluginInstalled;
$this->registeredTime = $this->myServersFlashCfg['remote']['regWizTime'] ?? '';
$this->username = $this->myServersFlashCfg['remote']['username'] ?? '';
}
$caseModelFile = '/boot/config/plugins/dynamix/case-model.cfg';
$this->caseModel = file_exists($caseModelFile) ? file_get_contents($caseModelFile) : '';
$this->rebootDetails = new RebootDetails();
private function getConnectKnownOrigins() {
/**
* Allowed origins warning displayed when the current webGUI URL is NOT included in the known lists of allowed origins.
* Include localhost in the test, but only display HTTP(S) URLs that do not include localhost.
@@ -145,6 +205,11 @@ class ServerState
$combinedOrigins = $allowedOrigins . "," . $extraOrigins; // combine the two strings for easier searching
$combinedOrigins = str_replace(" ", "", $combinedOrigins); // replace any spaces with nothing
$hostNotKnown = stripos($combinedOrigins, $this->host) === false; // check if the current host is in the combined list of origins
if ($extraOrigins) {
$this->extraOrigins = explode(",", $extraOrigins);
}
if ($hostNotKnown) {
$this->combinedKnownOrigins = explode(",", $combinedOrigins);
@@ -161,29 +226,8 @@ class ServerState
}
}
}
$this->keyfileBase64 = empty($this->var['regFILE']) ? null : @file_get_contents($this->var['regFILE']);
if ($this->keyfileBase64 !== false) {
$this->keyfileBase64 = @base64_encode($this->keyfileBase64);
$this->keyfileBase64UrlSafe = str_replace(['+', '/', '='], ['-', '_', ''], trim($this->keyfileBase64));
}
$this->updateOsCheck = new UnraidOsCheck();
$this->updateOsIgnoredReleases = $this->updateOsCheck->getIgnoredReleases();
$this->updateOsNotificationsEnabled = !empty(@$this->getWebguiGlobal('notify', 'unraidos'));
$this->updateOsResponse = $this->updateOsCheck->getUnraidOSCheckResult();
}
/**
* Retrieve the value of a webgui global setting.
*/
public function getWebguiGlobal(string $key, string $subkey = null) {
if (!$subkey) {
return _var($this->webguiGlobals, $key, '');
}
$keyArray = _var($this->webguiGlobals, $key, []);
return _var($keyArray, $subkey, '');
}
/**
* Retrieve the server information as an associative array
*
@@ -192,13 +236,13 @@ class ServerState
public function getServerState()
{
$serverState = [
"apiKey" => $this->myServersFlashCfg['upc']['apikey'] ?? '',
"apiVersion" => $this->myServersFlashCfg['api']['version'] ?? '',
"avatar" => (!empty($this->myServersFlashCfg['remote']['avatar']) && $this->connectPluginInstalled) ? $this->myServersFlashCfg['remote']['avatar'] : '',
"apiKey" => $this->apiKey,
"apiVersion" => $this->apiVersion,
"avatar" => $this->avatar,
"caseModel" => $this->caseModel,
"config" => [
'valid' => ($this->var['configValid'] === 'yes'),
'error' => isset($this->configErrorEnum[$this->var['configValid']]) ? $this->configErrorEnum[$this->var['configValid']] : 'UNKNOWN_ERROR',
'error' => isset($this->configErrorEnum[$this->var['configValid']]) ? $this->configErrorEnum[$this->var['configValid']] : null,
],
"connectPluginInstalled" => $this->connectPluginInstalled,
"connectPluginVersion" => $this->connectPluginVersion,
@@ -207,35 +251,35 @@ class ServerState
"date" => @$this->getWebguiGlobal('display', 'date') ?? '',
"time" => @$this->getWebguiGlobal('display', 'time') ?? '',
],
"description" => $this->var['COMMENT'] ? htmlspecialchars($this->var['COMMENT']) : '',
"description" => $this->var['COMMENT'] ? htmlspecialchars($this->var['COMMENT'], ENT_HTML5, 'UTF-8') : '',
"deviceCount" => $this->var['deviceCount'],
"email" => $this->myServersFlashCfg['remote']['email'] ?? '',
"email" => $this->email,
"expireTime" => 1000 * (($this->var['regTy'] === 'Trial' || strstr($this->var['regTy'], 'expired')) ? $this->var['regTm2'] : 0),
"extraOrigins" => explode(',', $this->myServersFlashCfg['api']['extraOrigins'] ?? ''),
"extraOrigins" => $this->extraOrigins,
"flashProduct" => $this->var['flashProduct'],
"flashVendor" => $this->var['flashVendor'],
"flashBackupActivated" => empty($this->flashbackupStatus['activated']) ? '' : 'true',
"flashBackupActivated" => $this->flashBackupActivated,
"guid" => $this->var['flashGUID'],
"hasRemoteApikey" => !empty($this->myServersFlashCfg['remote']['apikey']),
"hasRemoteApikey" => $this->hasRemoteApikey,
"internalPort" => _var($_SERVER, 'SERVER_PORT'),
"keyfile" => $this->keyfileBase64UrlSafe,
"lanIp" => ipaddr(),
"locale" => (!empty($_SESSION) && $_SESSION['locale']) ? $_SESSION['locale'] : 'en_US',
"model" => $this->var['SYS_MODEL'],
"name" => htmlspecialchars($this->var['NAME']),
"model" => $this->var['SYS_MODEL'] ? htmlspecialchars($this->var['SYS_MODEL'], ENT_HTML5, 'UTF-8') : '',
"name" => htmlspecialchars($this->var['NAME'], ENT_HTML5, 'UTF-8'),
"osVersion" => $this->osVersion,
"osVersionBranch" => $this->osVersionBranch,
"protocol" => _var($_SERVER, 'REQUEST_SCHEME'),
"rebootType" => $this->rebootDetails->getRebootType(),
"regDev" => @(int)$this->var['regDev'] ?? 0,
"regDevs" => @(int)$this->var['regDevs'] ?? 0,
"regGen" => @(int)$this->var['regGen'],
"regGuid" => @$this->var['regGUID'] ?? '',
"regTo" => @htmlspecialchars($this->var['regTo']) ?? '',
"regTo" => @htmlspecialchars($this->var['regTo'], ENT_HTML5, 'UTF-8') ?? '',
"regTm" => $this->var['regTm'] ? @$this->var['regTm'] * 1000 : '', // JS expects milliseconds
"regTy" => @$this->var['regTy'] ?? '',
"regExp" => $this->var['regExp'] ? @$this->var['regExp'] * 1000 : '', // JS expects milliseconds
"registered" => $this->registered,
"registeredTime" => $this->myServersFlashCfg['remote']['regWizTime'] ?? '',
"registeredTime" => $this->registeredTime,
"site" => _var($_SERVER, 'REQUEST_SCHEME') . "://" . _var($_SERVER, 'HTTP_HOST'),
"state" => strtoupper(empty($this->var['regCheck']) ? $this->var['regTy'] : $this->var['regCheck']),
"theme" => [
@@ -249,8 +293,8 @@ class ServerState
],
"ts" => time(),
"uptime" => 1000 * (time() - round(strtok(exec("cat /proc/uptime"), ' '))),
"username" => $this->myServersFlashCfg['remote']['username'] ?? '',
"wanFQDN" => $this->nginxCfg['NGINX_WANFQDN'] ?? '',
"username" => $this->username,
"wanFQDN" => @$this->nginxCfg['NGINX_WANFQDN'] ?? '',
];
if ($this->combinedKnownOrigins) {
@@ -273,11 +317,21 @@ class ServerState
}
/**
* Retrieve the server information as a JSON string
* Retrieve the server information as JSON
*
* @return string A JSON string containing server information.
* @return string
*/
public function getServerStateJson() {
return json_encode($this->getServerState());
}
/**
* Retrieve the server information as JSON string with converted special characters to HTML entities
*
* @return string
*/
public function getServerStateJsonForHtmlAttr() {
$json = json_encode($this->getServerState());
return htmlspecialchars($json, ENT_QUOTES, 'UTF-8');
}
}

View File

@@ -72,13 +72,13 @@ class WebComponentTranslations
'<p>To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.</p>' => '<p>' . _('To continue using Unraid OS you may purchase a license key.') . ' ' . _('Alternately, you may request a Trial extension.') . '</p>',
'<p>To support more storage devices as your server grows, click Upgrade Key.</p>' => '<p>' . _('To support more storage devices as your server grows, click Upgrade Key.') . '</p>',
'<p>You have used all your Trial extensions. To continue using Unraid OS you may purchase a license key.</p>' => '<p>' . _('You have used all your Trial extensions.') . ' ' . _('To continue using Unraid OS you may purchase a license key.') . '</p>',
'<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>' => '<p>' . _('Your **Trial** key includes all the functionality and device support of a **Pro** key') . '</p><p>' . _('After your **Trial** has reached expiration, your server *still functions normally* until the next time you Stop the array or reboot your server') . '</p><p>' . _('At that point you may either purchase a license key or request a *Trial* extension.') . '</p>',
'<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>' => '<p>' . _('Your **Trial** key includes all the functionality and device support of an **Unleashed** key') . '</p><p>' . _('After your **Trial** has reached expiration, your server *still functions normally* until the next time you Stop the array or reboot your server') . '</p><p>' . _('At that point you may either purchase a license key or request a *Trial* extension.') . '</p>',
'<p>Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>If you do not have a backup copy of your license key file you may attempt to recover your key.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>' => '<p>' . _('Your license key file is corrupted or missing.') . ' ' . _('The key file should be located in the /config directory on your USB Flash boot device') . '</p><p>' . _('If you do not have a backup copy of your license key file you may attempt to recover your key with your Unraid.net account') . '</p><p>' . _('If this was an expired Trial installation, you may purchase a license key.') . '</p>',
'<p>Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>You may attempt to recover your key with your Unraid.net account.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>' => '<p>' . _('Your license key file is corrupted or missing.') . ' ' . _('The key file should be located in the /config directory on your USB Flash boot device') . '</p><p>' . _('If you do not have a backup copy of your license key file you may attempt to recover your key with your Unraid.net account') . '</p><p>' . _('If this was an expired Trial installation, you may purchase a license key.') . '</p>',
'<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of a Pro Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class="list-disc pl-16px"><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>' => '<p>' . _('Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key.') . ' ' . _('A <em>Trial</em> key provides all the functionality of a Pro Registration key.') . '</p><p>' . _('Registration keys are bound to your USB Flash boot device serial number (GUID).') . ' ' . _('Please use a high quality name brand device at least 1GB in size.') . '</p><p>' . _('Note: USB memory card readers are generally not supported because most do not present unique serial numbers.') . '</p><p>' . _('*Important:*') . '</p><ul class="list-disc pl-16px"><li>' . _('Please make sure your server time is accurate to within 5 minutes') . '</li><li>' . _('Please make sure there is a DNS server specified') . '</li>>' . '</ul>',
'<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class="list-disc pl-16px"><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>' => '<p>' . _('Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key.') . ' ' . _('A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.') . '</p><p>' . _('Registration keys are bound to your USB Flash boot device serial number (GUID).') . ' ' . _('Please use a high quality name brand device at least 1GB in size.') . '</p><p>' . _('Note: USB memory card readers are generally not supported because most do not present unique serial numbers.') . '</p><p>' . _('*Important:*') . '</p><ul class="list-disc pl-16px"><li>' . _('Please make sure your server time is accurate to within 5 minutes') . '</li><li>' . _('Please make sure there is a DNS server specified') . '</li>>' . '</ul>',
'<p>Your Trial key requires an internet connection.</p><p><a href="/Settings/NetworkSettings" class="underline">Please check Settings > Network</a></p>' => '<p>' . _('Your Trial key requires an internet connection') . '</p><p><a href="/Settings/NetworkSettings" class="underline">' . _('Please check Settings > Network') . '</a></p>',
'<p>Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.</p>' => '<p>' . _('Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.') . '</p>',
'A Trial key provides all the functionality of a Pro Registration key' => _('A Trial key provides all the functionality of a Pro Registration key'),
'A Trial key provides all the functionality of an Unleashed Registration key' => _('A Trial key provides all the functionality of an Unleashed Registration key'),
'Acklowledge that you have made a Flash Backup to enable this action' => _('Acklowledge that you have made a Flash Backup to enable this action'),
'ago' => _('ago'),
'All you need is an active internet connection, an Unraid.net account, and the Connect plugin. Get started by installing the plugin.' => _('All you need is an active internet connection, an Unraid.net account, and the Connect plugin.') . ' ' . _('Get started by installing the plugin.'),
@@ -339,7 +339,7 @@ class WebComponentTranslations
'You may still update to releases dated prior to your update expiration date.' => _('You may still update to releases dated prior to your update expiration date.'),
'You\'re one step closer to enhancing your Unraid experience' => _('You\'re one step closer to enhancing your Unraid experience'),
'Your {0} Key has been replaced!' => sprintf(_('Your %s Key has been replaced!'), '{0}'),
'Your free Trial key provides all the functionality of a Pro Registration key' => _('Your free Trial key provides all the functionality of a Pro Registration key'),
'Your free Trial key provides all the functionality of an Unleashed Registration key' => _('Your free Trial key provides all the functionality of an Unleashed Registration key'),
'Your Trial has expired' => _('Your Trial has expired'),
'Your Trial key has been extended!' => _('Your Trial key has been extended!'),
];

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@
"css": [
"_nuxt/unraid-components.client-fad7c220.css"
],
"file": "_nuxt/unraid-components.client-40074634.js",
"file": "_nuxt/unraid-components.client-cd1b3939.js",
"isEntry": true,
"src": ".nuxt/nuxt-custom-elements/entries/unraid-components.client.mjs"
}

View File

@@ -11,6 +11,9 @@
*/
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
// Invoke the plugin command with indicated method
function plugin($method, $arg = '') {
global $docroot;
@@ -27,7 +30,7 @@ function language($method, $arg = '') {
function check_plugin($arg, &$ncsi) {
// Get network connection status indicator (NCSI)
if ($ncsi===null) $ncsi = exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -o 'OK'");
if ($ncsi===null) $ncsi = check_network_connectivity();
return $ncsi ? plugin('check',$arg) : false;
}

View File

@@ -1,6 +1,6 @@
<?php
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, 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,
@@ -108,6 +108,7 @@ class UnraidOsCheck
function _($text) {return $text;}
}
// this command will set the $notify array
extract(parse_plugin_cfg('dynamix', true));
$var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
@@ -115,7 +116,7 @@ class UnraidOsCheck
$params = [];
$params['branch'] = plugin('category', self::PLG_PATH, 'stable');
$params['current_version'] = plugin('version', self::PLG_PATH) ?: _var($var,'version');
if (_var($var,'regExp')) $params['update_exp'] = date('m-d-Y', _var($var,'regExp')*1);
if (_var($var,'regExp')) $params['update_exp'] = date('Y-m-d', _var($var,'regExp')*1);
$defaultUrl = self::BASE_RELEASES_URL;
// pass a param of altUrl to use the provided url instead of the default
$parsedAltUrl = (array_key_exists('altUrl',$_GET) && $_GET['altUrl']) ? $_GET['altUrl'] : null;
@@ -123,21 +124,12 @@ class UnraidOsCheck
if ($parsedAltUrl) $params['altUrl'] = $parsedAltUrl;
$urlbase = $parsedAltUrl ?? $defaultUrl;
$url = $urlbase.'?'.http_build_query($params);
$response = "";
// use error handler to convert warnings from file_get_contents to errors so they can be captured
function warning_as_error($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
$url = $urlbase.'?'.http_build_query($params);
$curlinfo = [];
$response = http_get_contents($url,[],$curlinfo);
if (array_key_exists('error', $curlinfo)) {
$response = json_encode(array('error' => $curlinfo['error']), JSON_PRETTY_PRINT);
}
set_error_handler("warning_as_error");
try {
$response = file_get_contents($url);
} catch (Exception $e) {
$response = json_encode(array('error' => $e->getMessage()), JSON_PRETTY_PRINT);
}
restore_error_handler();
$responseMutated = json_decode($response, true);
if (!$responseMutated) {
$response = json_encode(array('error' => 'Invalid response from '.$urlbase), JSON_PRETTY_PRINT);
@@ -159,14 +151,17 @@ class UnraidOsCheck
// send notification if a newer version is available and not ignored
$isNewerVersion = array_key_exists('isNewer',$responseMutated) ? $responseMutated['isNewer'] : false;
$isReleaseIgnored = in_array($responseMutated['version'], $this->getIgnoredReleases());
$isReleaseIgnored = array_key_exists('version',$responseMutated) ? in_array($responseMutated['version'], $this->getIgnoredReleases()) : false;
if ($responseMutated && $isNewerVersion && !$isReleaseIgnored) {
$output = _var($notify,'plugin');
$server = strtoupper(_var($var,'NAME','server'));
$newver = (array_key_exists('version',$responseMutated) && $responseMutated['version']) ? $responseMutated['version'] : 'unknown';
$script = '/usr/local/emhttp/webGui/scripts/notify';
exec("$script -e ".escapeshellarg("System - Unraid [$newver]")." -s ".escapeshellarg("Notice [$server] - Version update $newver")." -d ".escapeshellarg("A new version of Unraid is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
$event = "System - Unraid [$newver]";
$subject = "Notice [$server] - Version update $newver";
$description = "A new version of Unraid is available";
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
exit(0);

View File

@@ -44,7 +44,7 @@ function write(...$messages){
}
write(_("Checking connectivity")." ...\n");
if (exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -om1 'OK'")) {
if (check_network_connectivity()) {
$check = popen('plugin checkall','r');
while (!feof($check)) write(fgets($check));
pclose($check);

View File

@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, 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,
@@ -15,6 +15,7 @@
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
// this command will set the $notify array
extract(parse_plugin_cfg('dynamix', true));
// Multi-language support

View File

@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, 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,
@@ -15,6 +15,7 @@
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
// this command will set the $notify array
extract(parse_plugin_cfg('dynamix',true));
// Multi-language support

View File

@@ -254,10 +254,24 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist,state ,fstype){
if (data.html) box.find('#targetsnapimages').html(data.html);
},'json');
}
var memorydump = "no";
document.getElementById("targetsnaprmv").checked = true;
document.getElementById("targetsnaprmvmeta").checked = true;
document.getElementById("targetsnapkeep").checked = true;
document.getElementById("targetsnapfspc").checked = true;
if (fstype == "ZFS") {
box.find('#targetsnaprmv').prop('disabled',true);
box.find('#targetsnaprmvmeta').prop('disabled',true);
}
if (state != "running") {
box.find('#memoryline').prop('hidden',true);
box.find('#targetsnapmem').prop('hidden',true);
box.find('#targetsnapmem').prop('checked',false);
} else {
box.find('#memoryline').prop('hidden',false);
box.find('#targetsnapmem').prop('hidden',false);
box.find('#targetsnapmem').prop('checked',true);
}
box.dialog({
title: optiontext,
height: 'auto',
@@ -285,10 +299,11 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist,state ,fstype){
if (opt == "create") {
free = box.find('#targetsnapfspc').prop('checked') ? 'yes' : 'no';
fstypeuse = box.find('#targetsnapfstype').prop('checked') ? 'yes' : 'no';
memorydump = box.find('#targetsnapmem').prop('checked') ? 'yes' : 'no';
if (fstypeuse == "no") fstype ="QEMU";
desc = box.find("#targetsnapdesc").prop('value');
}
ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid, snapshotname:target, remove:remove, free:free, removemeta:removemeta, keep:keep, desc:desc, fstype:fstype}, "loadlist");
ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid, snapshotname:target, remove:remove, free:free, removemeta:removemeta, keep:keep, desc:desc, fstype:fstype,memorydump:memorydump}, "loadlist");
box.dialog('close');
},
"_(Cancel)_": function(){
@@ -496,13 +511,15 @@ $(function() {
<tr><td>_(VM Name)_:</td><td><label id="VMName"></label></td></tr>
<tr><td>_(Snapshot Name)_:</td><td><input type="text" id="targetsnap" autocomplete="off" spellcheck="false" value="--generate" onclick="this.select()">_(Check free space)_: <input type="checkbox" id="targetsnapfspc" checked></td></tr>
<tr><td>_(Description )_:</td><td><input type="text" id="targetsnapdesc" autocomplete="off" spellcheck="false" value="" onclick="this.select()"></td></tr>
<tr id="fstypeline"><td>_(FS Native Snapshot )_:</td><td><label id="fstype"></label><input type="checkbox" id="targetsnapfstype" checked>_(Unchecked will use QEMU External Snapshot)_</td></tr>
<tr id="memoryline"><td>_(Memory dump )_:</td><td><input type="checkbox" id="targetsnapmem" checked></td></tr>
<tr id="fstypeline"><td>_(FS Native Snapshot )_:</td><td><label id="fstype"></label><input type="checkbox" id="targetsnapfstype">_(Unchecked will use QEMU External Snapshot)_</td></tr>
</table>
</div>
<div id="templatesnapshotrevert" class="template">
_(VM Name)_: <label id="VMName"></label><br>
_(Snapshot Name)_: <input type="text" id="targetsnap" hidden><label id="targetsnapl"></label><br>
<tr id="fstypeline"><td>_(Snapshot Method)_:</td><td><label id="fstype"></label><br>
_(Remove Images)_: <input type="checkbox" id="targetsnaprmv" checked><br>
_(Remove Meta)_:<input type="checkbox" id="targetsnaprmvmeta" checked> <input type="checkbox" id="targetsnapkeep" hidden><label id="targetsnapimages"></label><br>
</div>

View File

@@ -21,7 +21,6 @@ $_SERVER['REQUEST_URI'] = 'vms';
require_once "$docroot/webGui/include/Translations.php";
$user_prefs = '/boot/config/plugins/dynamix.vm.manager/userprefs.cfg';
if (file_exists('/boot/config/plugins/dynamix.vm.manager/vmpreview')) $vmpreview = true ; else $vmpreview = false ;
$vms = $lv->get_domains();
if (empty($vms)) {
echo '<tr><td colspan="8" style="text-align:center;padding-top:12px">'._('No Virtual Machines installed').'</td></tr>';
@@ -53,7 +52,7 @@ foreach ($vms as $vm) {
$image = substr($icon,-4)=='.png' ? "<img src='$icon' class='img'>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
$arrConfig = domain_to_config($uuid);
$snapshots = getvmsnapshots($vm) ;
$cdroms = $lv->get_cdrom_stats($res) ;
$cdroms = $lv->get_cdrom_stats($res,true,true) ;
if ($state == 'running') {
$mem = $dom['memory']/1024;
} else {
@@ -110,7 +109,7 @@ foreach ($vms as $vm) {
}
unset($dom);
if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web" ; else $vmrcconsole = $domain_cfg["CONSOLE"] ;
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log),addslashes($fstype), $vmrcconsole,$vmpreview);
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log),addslashes($fstype), $vmrcconsole,false);
$kvm[] = "kvm.push({id:'$uuid',state:'$state'});";
switch ($state) {
case 'running':
@@ -215,7 +214,7 @@ foreach ($vms as $vm) {
$boot= $arrDisk["boot order"];
$serial = $arrDisk["serial"];
if ($boot < 1) $boot = _('Not set');
$reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($disk)." 2>/dev/null"));
$reallocation = trim(get_realvolume($disk));
if (!empty($reallocation)) $reallocationstr = "($reallocation)"; else $reallocationstr = "";
echo "<tr><td>$disk $reallocationstr</td><td>$serial</td><td>$bus</td>";
if ($state == 'shutoff') {
@@ -237,8 +236,10 @@ foreach ($vms as $vm) {
/* Display VM cdroms */
foreach ($cdroms as $arrCD) {
$tooltip = "";
$capacity = $lv->format_size($arrCD['capacity'], 0);
$allocation = $lv->format_size($arrCD['allocation'], 0);
if ($arrCD['spundown']) {$capacity = $allocation = "*"; $tooltip = "Drive spun down ISO volume is ".$arrCD['reallocation'];} else $tooltip = "ISO volume is ".$arrCD['reallocation'];
$disk = $arrCD['file'] ?? $arrCD['partition'] ?? "" ;
$dev = $arrCD['device'];
$bus = $arrValidDiskBuses[$arrCD['bus']] ?? 'VirtIO';
@@ -247,7 +248,7 @@ foreach ($vms as $vm) {
if ($disk != "" ) {
$title = _('Eject CD Drive');
$changemedia = "changemedia(\"{$uuid}\",\"{$dev}\",\"{$bus}\", \"--eject\")";
echo "<tr><td>$disk <a title='$title' href='#' onclick='$changemedia'> <i class='fa fa-eject'></i></a></td><td></td><td>$bus</td><td>$capacity</td><td>$allocation</td><td>$boot</td></tr>";
echo "<tr><td>$disk <a title='$title' href='#' onclick='$changemedia'> <i class='fa fa-eject'></i></a></td><td></td><td>$bus</td><td><span title='$tooltip' data-toggle='tooltip'>$capacity</span></td><td>$allocation</td><td>$boot</td></tr>";
} else {
$title = _('Insert CD');
$changemedia = "changemedia(\"{$uuid}\",\"{$dev}\",\"{$bus}\",\"--select\")";
@@ -282,7 +283,7 @@ foreach ($vms as $vm) {
$snapshotmemory = _(ucfirst($snapshot["memory"]["@attributes"]["snapshot"]));
$snapshotparent = $snapshot["parent"] ? $snapshot["parent"] : "None";
$snapshotdatetime = my_time($snapshot["creationtime"],"Y-m-d" )."<br>".my_time($snapshot["creationtime"],"H:i:s");
$snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"],$snapshot["method"],$vmpreview);
$snapmenu = sprintf("onclick=\"addVMSnapContext('%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,$snapshot["name"],$snapshot["method"]);
echo "<tr><td><span id='vmsnap-$uuid' $snapmenu class='hand'>$tab|__&nbsp;&nbsp;<i class='fa fa-clone'></i></span>&nbsp;",$snapshot["name"],"</td><td>$snapshotdesc</td><td><span class='inner' style='font-size:1.1rem;'>$snapshotdatetime</span></td><td>$snapshotstate</td><td>$snapshotparent</td><td>$snapshotmemory</td></tr>";
$tab .="&nbsp;&nbsp;&nbsp;&nbsp;";
}

View File

@@ -338,7 +338,7 @@ case 'snap-create':
case 'snap-create-external':
requireLibvirt();
$arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['desc'],$_REQUEST['free'],$_REQUEST['fstype']) ;
$arrResponse = vm_snapshot($domName,$_REQUEST['snapshotname'],$_REQUEST['desc'],$_REQUEST['free'],$_REQUEST['fstype'],$_REQUEST['memorydump']) ;
break;
case 'snap-images':

View File

@@ -1250,33 +1250,44 @@
return $tmp;
}
function get_cdrom_stats($domain, $sort=true) {
function get_cdrom_stats($domain, $sort=true,$spincheck = false) {
$unraiddisks = array_merge_recursive(@parse_ini_file('state/disks.ini',true)?:[], @parse_ini_file('state/devs.ini',true)?:[]);
$dom = $this->get_domain_object($domain);
$tmp = false;
$buses = $this->get_xpath($dom, '//domain/devices/disk[@device="cdrom"]/target/@bus', false);
$disks = $this->get_xpath($dom, '//domain/devices/disk[@device="cdrom"]/target/@dev', false);
$cds = $this->get_xpath($dom, '//domain/devices/disk[@device="cdrom"]/target/@dev', false);
$files = $this->get_xpath($dom, '//domain/devices/disk[@device="cdrom"]/source/@file', false);
$boot = $this->get_xpath($dom, '//domain/devices/disk[@device="cdrom"]/boot/@*', false);
$ret = [];
for ($i = 0; $i < $disks['num']; $i++) {
$tmp = libvirt_domain_get_block_info($dom, $disks[$i]);
for ($i = 0; $i < $cds['num']; $i++) {
$spundown = 0;
$reallocation = null;
if (isset($files[$i])) $reallocation = trim(get_realvolume($files[$i]));
if ($spincheck) {
if (isset($unraiddisks[$reallocation]['spundown']) && $unraiddisks[$reallocation]['spundown'] == 1) $spundown = 1; else $tmp = libvirt_domain_get_block_info($dom, $cds[$i]);
} else $tmp = libvirt_domain_get_block_info($dom, $cds[$i]);
if ($tmp) {
$tmp['bus'] = $buses[$i];
$tmp["boot order"] = $boot[$i] ?? "";
$tmp['reallocation'] = $reallocation;
$tmp['spundown'] = $spundown;
$ret[] = $tmp;
}
else {
$this->_set_last_error();
$ret[] = [
'device' => $disks[$i],
'device' => $cds[$i],
'file' => $files[$i],
'type' => '-',
'capacity' => '-',
'allocation' => '-',
'physical' => '-',
'bus' => $buses[$i]
'bus' => $buses[$i],
'reallocation' => $reallocation,
'spundown' => $spundown
];
}
}
@@ -1294,7 +1305,7 @@
}
unset($buses);
unset($disks);
unset($cds);
unset($files);
return $ret;

View File

@@ -1621,7 +1621,6 @@ private static $encoding = 'UTF-8';
$dom = $lv->domain_get_info($res);
$state = $lv->domain_state_translate($dom['state']);
$vmxml = $lv->domain_get_xml($res) ;
file_put_contents("/tmp/cloningxml" ,$vmxml) ; ## Remove before stable.
$storage = $lv->_get_single_xpath_result($vm, '//domain/metadata/*[local-name()=\'vmtemplate\']/@storage');
if (empty($storage)) $storage = "default" ;
# if VM running shutdown. Record was running.
@@ -1709,7 +1708,6 @@ private static $encoding = 'UTF-8';
write("addLog\0".htmlspecialchars("Creating new XML $clone"));
$xml = $lv->config_to_xml($config, true) ;
file_put_contents("/tmp/clonexml" ,$xml) ; ## Remove before stable.
$rtn = $lv->domain_define($xml) ;
return($rtn) ;
@@ -1868,7 +1866,7 @@ private static $encoding = 'UTF-8';
return true ;
}
function vm_snapshot($vm,$snapshotname, $snapshotdesc, $free = "yes", $method = "QEMU", $memorysnap = "yes") {
function vm_snapshot($vm,$snapshotname, $snapshotdescinput, $free = "yes", $method = "QEMU", $memorysnap = "yes") {
global $lv ;
#Get State
@@ -1883,7 +1881,7 @@ private static $encoding = 'UTF-8';
$diskspec = "" ;
$capacity = 0 ;
if ($snapshotname == "--generate") $name= "S" . date("YmdHis") ; else $name=$snapshotname ;
if ($snapshotdesc != "") $snapshotdesc = " --description '$snapshotdesc'" ;
if ($snapshotdescinput != "") $snapshotdesc = " --description '$snapshotdescinput'" ;
foreach($disks as $disk) {
$file = $disk["file"] ;
@@ -1915,7 +1913,7 @@ private static $encoding = 'UTF-8';
$cmdstr = "virsh snapshot-create-as '$vm' --name '$name' $snapshotdesc --atomic" ;
if ($state == "running") {
if ($state == "running" & $memorysnap == "yes") {
$cmdstr .= " --live ".$memspec.$diskspec ;
$capacity = $capacity + $memory ;
@@ -1934,7 +1932,6 @@ private static $encoding = 'UTF-8';
if (!empty($lv->domain_get_ovmf($res))) $nvram = $lv->nvram_create_snapshot($lv->domain_get_uuid($vm),$name) ;
$xmlfile = $dirpath."/".$name.".running" ;
file_put_contents("/tmp/xmltst", "$xmlfile" ) ;## Remove before stable.
if ($state == "running") exec("virsh dumpxml '$vm' > ".escapeshellarg($xmlfile),$outxml,$rtnxml) ;
$output= [] ;
@@ -1961,7 +1958,7 @@ private static $encoding = 'UTF-8';
$arrResponse = ['error' => substr($output[0],6) ] ;
} else {
$arrResponse = ['success' => true] ;
$ret = write_snapshots_database("$vm","$name",$state,$snapshotdesc,$method) ;
$ret = write_snapshots_database("$vm","$name",$state,$snapshotdescinput,$method) ;
#remove meta data
if ($ret != "noxml") $ret = $lv->domain_snapshot_delete($vm, "$name" ,2) ;
}
@@ -2017,7 +2014,6 @@ private static $encoding = 'UTF-8';
if ($method == "ZFS") $xml = $snapslist[$snap]['xml']; else $xml = custom::createXML('domain',$xmlobj)->saveXML();
if (!strpos($xml,'<vmtemplate xmlns="unraid"')) $xml=str_replace('<vmtemplate','<vmtemplate xmlns="unraid"',$xml);
if (!$dryrun) $new = $lv->domain_define($xml);
file_put_contents("/tmp/xmlrevert", "$xml" ) ;## Remove before stable.
if ($new) $arrResponse = ['success' => true] ; else $arrResponse = ['error' => $lv->get_last_error()] ;
}
@@ -2028,15 +2024,13 @@ private static $encoding = 'UTF-8';
if ($diskname == "hda" || $diskname == "hdb") continue ;
$path = $disk["source"]["@attributes"]["file"] ;
if (is_file($path) && $action == "yes") if (!$dryrun) unlink("$path") ;else echo "unlink $path\n";
file_put_contents("/tmp/rmvsnaps",$path,FILE_APPEND);
$item = array_search($path,$snapslist[$snap]['backing']["r".$diskname]) ;
$item++ ;
while($item > 0)
{
if (!isset($snapslist[$snap]['backing']["r".$diskname][$item])) break ;
$newpath = $snapslist[$snap]['backing']["r".$diskname][$item] ;
file_put_contents("/tmp/rmvsnaps",$newpath,FILE_APPEND);
if (is_file($newpath) && $action == "yes") if (!$dryrun) unlink("$newpath"); else echo "unlink $newpath\n";
if (is_file($newpath) && $action == "yes") if (!$dryrun) unlink("$newpath"); else echo "unlink $newpath\n";
$item++ ;
}
}
@@ -2094,7 +2088,7 @@ private static $encoding = 'UTF-8';
if (!$dryrun) shell_exec($fssnapcmd); else echo "$fssnapcmd\n";
}
if ($snapslist[$snap]['state'] == "running") {
if ($snapslist[$snap]['state'] == "running" || $snapslist[$snap]['state'] == "disk-snapshot") {
$xmlfile = $primarypath."/$snap.running" ;
$memoryfile = $primarypath."/memory$snap.mem" ;
# Set XML to saved XML
@@ -2102,14 +2096,16 @@ private static $encoding = 'UTF-8';
$xmlobj = custom::createArray('domain',$xml) ;
$xml = custom::createXML('domain',$xmlobj)->saveXML();
if (!strpos($xml,'<vmtemplate xmlns="unraid"')) $xml=str_replace('<vmtemplate','<vmtemplate xmlns="unraid"',$xml);
file_put_contents("/tmp/xmlrevert2", "$xml" ) ;## Remove before stable.
if (!$dryrun) $rtn = $lv->domain_define($xml) ;
# Restore Memory.
if (!$dryrun) $cmdrtn = exec("virsh restore --running ".escapeshellarg($memoryfile)) ;
if (!$dryrun && !$cmdrtn) unlink($xmlfile);
if (!$dryrun && !$cmdrtn) unlink($memoryfile);
if ($snapslist[$snap]['state'] == "running") {
if (!$dryrun) $cmdrtn = exec("virsh restore --running ".escapeshellarg($memoryfile)) ;
if (!$dryrun && !$cmdrtn) unlink($xmlfile);
if (!$dryrun && !$cmdrtn) unlink($memoryfile);
}
if ($snapslist[$snap]['state'] == "disk-snapshot") if (!$dryrun) unlink($xmlfile);
}

View File

@@ -62,7 +62,7 @@ function ajaxVMDispatchconsoleRV(params, spin){
}
},'json');
}
function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, fstype="QEMU",console="web", preview=false){
function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, fstype="QEMU",console="web",usage=false){
var opts = [];
var path = location.pathname;
var x = path.indexOf("?");
@@ -198,9 +198,9 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, f
}});
}
}
context.attach('#vm-'+uuid, opts);
if (usage) context.attach('#vmusage-'+uuid, opts); else context.attach('#vm-'+uuid, opts);
}
function addVMSnapContext(name, uuid, template, state, snapshotname, method, preview=false){
function addVMSnapContext(name, uuid, template, state, snapshotname, method){
var opts = [];
var path = location.pathname;
var x = path.indexOf("?");
@@ -211,7 +211,7 @@ function addVMSnapContext(name, uuid, template, state, snapshotname, method, pre
opts.push({text:_("Revert snapshot"), icon:"fa-fast-backward", action:function(e) {
e.preventDefault();
selectsnapshot(uuid, name, snapshotname, "revert",true) ;
selectsnapshot(uuid, name, snapshotname, "revert",true,state,method) ;
}});
if (method == "QEMU") {
opts.push({text:_("Block Commit"), icon:"fa-hdd-o", action:function(e) {
@@ -225,17 +225,12 @@ function addVMSnapContext(name, uuid, template, state, snapshotname, method, pre
e.preventDefault();
selectblock(uuid, name, snapshotname, "pull",true) ;
}});
if (preview) {
opts.push({text:_("Block Copy"), icon:"fa-stop", action:function(e) {
e.preventDefault();
ajaxVMDispatch({action:"domain-stop", uuid:uuid}, "loadlist");
}}); }
}
} else {
opts.push({text:_("Revert snapshot"), icon:"fa-fast-backward", action:function(e) {
e.preventDefault();
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
selectsnapshot(uuid, name, snapshotname, "revert",true) ;
selectsnapshot(uuid, name, snapshotname, "revert",true,state,method) ;
}});
}
opts.push({text:_("Remove snapshot"), icon:"fa-trash", action:function(e) {

View File

@@ -70,6 +70,7 @@ while (true) {
$ts = $time1 - $time0;
$echodata = "";
$running = 0;
ksort($vmusagestats);
foreach ($vmusagestats as $vm => $vmdata) {
if ($vmdata['state'] == 1) {

View File

@@ -0,0 +1,58 @@
#!/usr/bin/php
<?php
/* Copyright 2005-2024, Lime Technology
* Copyright 2024, Simon Fairweather
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
# Command for bash script /usr/libexec/virtiofsd
# eval exec /usr/bin/virtiofsd $(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/virtiofsd.php "$@")
$file = "/etc/libvirt/virtiofsd.opt";
$whole_cmd = '';
$long_options = ["fd:","print-capabilities","syslog","daemonize","rlimit-nofile","thread-pool-size:","socket-path:","socket-group:","help","version","source:"];
$short_options = "o:dfV";
$argoptions = getopt($short_options,$long_options);
array_shift($argv);
foreach ($argv as $i=>$arg) {
if (strpos($arg, "--fd") !== false) unset($argv[$i]) ;
if ($arg == "-o") { unset($argv[$i]);unset($argv[$i+1]); }
}
# Check if options file exists. Each option should be on a new line.
if (is_file($file)) $options = explode("\n",file_get_contents($file)) ; else $options = ['--syslog','--inode-file-handles=mandatory','--announce-submounts'];
$options[] = "--fd=".$argoptions['fd'];
if (isset($argoptions['o'])) {
$virtiofsoptions = explode(',',$argoptions["o"]);
foreach ($virtiofsoptions as $opt) {
$optsplit = explode('=',$opt);
switch ($optsplit[0]) {
case "source":
$options[] = "--shared-dir={$optsplit[1]}";
break;
case "cache":
$options[] = "--cache=never";
break;
case "xattr":
$options[] = "--xattr";
break;
case "sandbox":
$options[] = "--sandbox={$optsplit[1]}";
break;
}
}
}
$newargs = array_merge($options,$argv);
foreach($newargs as $arg) {
if ($arg != "") $whole_cmd.= escapeshellarg($arg).' ';
}
echo trim($whole_cmd);
?>

View File

@@ -545,6 +545,9 @@ window.onunload = function(){
<? elseif (_var($var,'configValid')=="invalid"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Too many attached devices. Please consider upgrading your)_ <a href="/Tools/Registration">_(registration key)_</a>.</td></tr>
<? elseif (_var($var,'configValid')=="ineligible"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Ineligible to run this version of Unraid OS. Please consider extending your)_ <a href="/Tools/Registration">_(registration key)_</a>.</td></tr>
<? elseif (_var($var,'configValid')=="nokeyserver"):?>
<tr><td><?status_indicator()?>**_(Stopped)_.**</td><td><input type="submit" name="cmdStart" value="_(Start)_" disabled></td>
<td>_(Cannot contact key-server. Please check your)_ <a href="/Settings/NetworkSettings">_(network settings)_</a>.</td></tr>

View File

@@ -1,5 +1,5 @@
Menu="Dashboard"
Nchan="wg_poller,update_1,update_2,update_3,ups_status:stop"
Nchan="wg_poller,update_1,update_2,update_3,ups_status:stop,vm_dashusage:stop"
---
<?PHP
/* Copyright 2005-2023, Lime Technology * Copyright 2012-2023, Bergware International.
@@ -73,6 +73,10 @@ $vdisk = exec("grep -Pom1 '^DOCKER_IMAGE_TYPE=\"\\K[^\"]+' /boot/config/docker.c
$dot = _var($display,'number','.,')[0];
$zfs = count(array_filter(array_column($disks,'fsType'),function($fs){return str_replace('luks:','',$fs??'')=='zfs';}));
$domain_cfgfile = "/boot/config/domain.cfg";
$domain_cfg = parse_ini_file($domain_cfgfile);
if (!isset($domain_cfg['USAGE'])) $vmusage = "N" ; else $vmusage = $domain_cfg['USAGE'];
// enable/disable graph elements by making hook script executable or not
chmod("$docroot/webGui/system/VM_usage",$libvirtd ? 0755 : 0644);
chmod("$docroot/webGui/system/ZFS_cache",$zfs ? 0755 : 0644);
@@ -275,10 +279,10 @@ foreach ($cpus as $pair) {
<a href='/Dashboard/Tools/Processes' title="_(View Running Processes)_"><i class='fa fa-fw fa-info-circle control'></i></a>
</td></tr><tr><td>
<span class='w26'><i class='ups fa fa-compress'></i>_(Usable size)_: <?=$ramsize?><br><i class='ups fa fa-expand'></i>_(Maximum size)_: <?="$memory_maximum $unit"?><?=$low?'*':''?></span>
<span class='w18 center'><a class='info hand none'>_(RAM usage)_<span>_(Percent of total used memory)_ (<?=$ramsize?>)</span></a></span>
<span class='w18 center'><a class='info hand none'>_(Flash device)_<span>_(Percent usage of flash usb device)_ (<?=$flashsize?>)</span></a></span>
<span class='w18 center'><a class='info hand none'>_(Log filesystem)_<span>_(Percent usage of LOG file system)_ (<?=$logsize?>)</span></a></span>
<span class='w18 center'><a class='info hand none'><?=$vdisk?><span><?=_("Percent usage of $vdisk")?> (<?=$dockersize?>)</span></a></span>
<span class='w18 center static'><a class='info hand none'>_(RAM usage)_<span>_(Percent of total used memory)_ (<?=$ramsize?>)</span></a></span>
<span class='w18 center static'><a class='info hand none'>_(Flash device)_<span>_(Percent usage of flash usb device)_ (<?=$flashsize?>)</span></a></span>
<span class='w18 center static'><a class='info hand none'>_(Log filesystem)_<span>_(Percent usage of LOG file system)_ (<?=$logsize?>)</span></a></span>
<span class='w18 center static'><a class='info hand none'><?=$vdisk?><span><?=_("Percent usage of $vdisk")?> (<?=$dockersize?>)</span></a></span>
</td></tr><tr><td>
<span class='w26'><legend>_(Legend)_</legend><span id='dynamic'></span></span>
<span class='w18 center'><div class='pie' id='sys0'><span class='sys0'></span><span class='var0'></span></div></span>
@@ -424,6 +428,13 @@ echo "</td></tr>";
<a href='/Dashboard/Settings/VMSettings' title="_(Go to VM settings)_"><i class='fa fa-fw fa-cog control'></i></a>
</td></tr>
</tbody>
<?if ($vmusage == "Y"):?>
<tbody id='vm_view_usage' title="_(Virtual Machines Usage)_" >
<tr><td><i class='icon-virtualization f32'></i><div class='section'>_(Virtual Machines Usage)_</div>
<a href='/Dashboard/Settings/VMSettings' title="_(Go to VM settings)_"><i class='fa fa-fw fa-cog control'></i></a>
</td></tr>
</tbody>
<?endif;?>
<?endif;?>
<tbody title="_(Shares Information)_"<?if ($group):?> class="mixed"<?endif;?>>
@@ -685,7 +696,8 @@ function hideShow() {
<tr><td>_(VM Name)_:</td><td><label id="VMName"></label></td></tr>
<tr><td>_(Snapshot Name)_:</td><td><input type="text" id="targetsnap" autocomplete="off" spellcheck="false" value="--generate" onclick="this.select()">_(Check free space)_:<input type="checkbox" id="targetsnapfspc" checked></td></tr>
<tr><td>_(Description)_:</td><td><input type="text" id="targetsnapdesc" autocomplete="off" spellcheck="false" value="" onclick="this.select()"></td></tr>
<tr id="fstypeline"><td>_(FS Native Snapshot )_:</td><td><label id="fstype"></label><input type="checkbox" id="targetsnapfstype" checked>_(Unchecked will use QEMU External Snapshot)_</td></tr>
<tr id="memoryline"><td>_(Memory dump )_:</td><td><input type="checkbox" id="targetsnapmem" checked></td></tr>
<tr id="fstypeline"><td>_(FS Native Snapshot )_:</td><td><label id="fstype"></label><input type="checkbox" id="targetsnapfstype" >_(Unchecked will use QEMU External Snapshot)_</td></tr>
</table>
</div>
@@ -932,7 +944,7 @@ function loadlist(init) {
$('#vms').is(':checked') ? $.cookie('my_vms','startedOnly',{expires:3650}) : $.removeCookie('my_vms');
});
}
$.post('/webGui/include/DashboardApps.php',{docker:'<?=$dockerd?>',vms:'<?=$libvirtd?>'},function(d) {
$.post('/webGui/include/DashboardApps.php',{docker:'<?=$dockerd?>',vms:'<?=$libvirtd?>',vmusage:'<?=$vmusage?>'},function(d) {
var data = d.split('\0');
$('#docker_view tr.updated').remove();
$('#docker_view').append(data[0]).hideMe();
@@ -944,6 +956,8 @@ function loadlist(init) {
var started_vms = $('#vm_view').find('span.outer.vms.started').length;
var stopped_vms = $('#vm_view').find('span.outer.vms.stopped').length;
var paused_vms = $('#vm_view').find('span.outer.vms.paused').length;
$('#vm_view_usage tr.useupdated').remove();
$('#vm_view_usage').append(data[2]).hideMe();
$('.apps.switch').html("_(Containers)_ -- _(Started)_: "+started_apps+", _(Stopped)_: "+stopped_apps+", _(Paused)_: "+paused_apps);
$('.vms.switch').html("_(VMs)_ -- _(Started)_: "+started_vms+", _(Stopped)_: "+stopped_vms+", _(Paused)_: "+paused_vms);
if ($.cookie('my_apps')!=null) $('span.apps.stopped').hide(0,noApps());
@@ -1209,7 +1223,7 @@ function addProperties() {
$('div#sys3').hover(function(){$('.sys3').hide();$('.var3').show();},function(){$('.sys3').show();$('.var3').hide();});
$('#current_time').hover(function(){$.post('/webGui/include/DashboardApps.php',{ntp:'ntp'},function(ntp){$('#current_time').prop('title',ntp+"\n_(Go to date and time settings)_");});});
// make truncated descriptions fully visible when hovering over them
$('span.w18').hover(
$('span.w18').not('.static').hover(
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {recall = $(this).next(); recover=recall.html(); recall.html('&nbsp;');}},
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {recall.html(recover); recall=null;}}
);
@@ -1380,7 +1394,7 @@ function VMClone(uuid, name){
});
dialogStyle();
}
function selectsnapshot(uuid, name ,snaps, opt, getlist, status,fstype){
function selectsnapshot(uuid, name ,snaps, opt, getlist, state,fstype){
box = $("#iframe-popup");
box.html($("#templatesnapshot"+opt).html());
const capopt = opt.charAt(0).toUpperCase() + opt.slice(1);
@@ -1394,7 +1408,21 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist, status,fstype){
var only = (opt == "remove") ? 0 : 1;
$.post("/plugins/dynamix.vm.manager/include/VMajax.php",{action:"snap-images",uuid:uuid,snapshotname:snaps,only:only},function(data){if (data.html) box.find('#targetsnapimages').html(data.html);},'json');
}
var memorydump = "no";
document.getElementById("targetsnapfspc").checked = true;
if (fstype == "ZFS") {
box.find('#targetsnaprmv').prop('disabled',true);
box.find('#targetsnaprmvmeta').prop('disabled',true);
}
if (state != "running") {
box.find('#memoryline').prop('hidden',true);
box.find('#targetsnapmem').prop('hidden',true);
box.find('#targetsnapmem').prop('checked',false);
} else {
box.find('#memoryline').prop('hidden',false);
box.find('#targetsnapmem').prop('hidden',false);
box.find('#targetsnapmem').prop('checked',true);
}
box.dialog({
title: optiontext,
height: 'auto',
@@ -1422,10 +1450,11 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist, status,fstype){
if (opt == "create") {
free = box.find('#targetsnapfspc').prop('checked') ? 'yes' : 'no';
desc = box.find("#targetsnapdesc").prop('value');
memorydump = box.find('#targetsnapmem').prop('checked') ? 'yes' : 'no';
fstypeuse = box.find('#targetsnapfstype').prop('checked') ? 'yes' : 'no';
if (fstypeuse == "no") fstype ="QEMU";
}
ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid, snapshotname:target, remove:remove, free:free, desc:desc, fstype:fstype}, "loadlist");
ajaxVMDispatch({action:"snap-" + opt +'-external', uuid:uuid, snapshotname:target, remove:remove, free:free, desc:desc, fstype:fstype,memorydump:memorydump}, "loadlist");
box.dialog('close');
},
"_(Cancel)_": function(){
@@ -1501,6 +1530,18 @@ function cpu_parse(msg) {
return parse;
}
<?if ($vmusage == "Y"):?>
var vmdashusage = new NchanSubscriber('/sub/vm_dashusage',{subscriber:'websocket'});
vmdashusage.on('message', function(msg){
var data = JSON.parse(msg);
for (const [vm, vmdata] of Object.entries(data)) {
for (const [displayitem, value] of Object.entries(vmdata)) {
$('#vmmetrics-'+displayitem + '-' + vm ).html(value);
}
}
});
<?endif;?>
var dashboard = new NchanSubscriber('/sub/cpuload,update1,update2,update3<?=$wireguard?",wireguard":""?>',{subscriber:'websocket'});
dashboard.on('message',function(msg,meta) {
switch (meta.id.channel()) {
@@ -1667,6 +1708,9 @@ $(function() {
dropdown('enter_view');
startup = false;
dashboard.start();
<?if ($vmusage == "Y"):?>
vmdashusage.start();
<?endif;?>
<?if ($apcupsd):?>
apcups.start();
<?endif;?>
@@ -1692,6 +1736,9 @@ $(function() {
window.onunload = function(){
dashboard.stop();
<?if ($vmusage == "Y"):?>
vmdashusage.stop();
<?endif;?>
<?if ($apcupsd):?>
apcups.stop();
<?endif;?>

View File

@@ -313,7 +313,14 @@ function setGlue(form,reset) {
{glue:'/',more:3,dev:1,type:'',min1:1,max1:4,min2:1,max2:128,min3:1,max3:4}, // highpoint
{glue:'' ,more:1,dev:1,type:'',min1:0,max1:15}, // hp cciss
{glue:'' ,more:0,dev:0,type:''}, // marvell
{glue:',',more:1,dev:1,type:'',min1:0,max1:127} // megaraid
{glue:',',more:1,dev:1,type:'',min1:0,max1:127}, // megaraid
{glue:',',more:1,dev:0,type:'input',min1:'',max1:'0xffffffff'}, // cypress
{glue:',',more:3,dev:0,type:'select',min1:'',max1:'p',min2:'',max2:'x',min3:1,max3:128},// jmicron ata
{glue:'' ,more:0,dev:0,type:''}, // prolific
{glue:'' ,more:0,dev:0,type:''}, // sunplus
{glue:'' ,more:0,dev:0,type:''}, // asmedia
{glue:'' ,more:0,dev:0,type:''}, // jmicron nvme
{glue:'' ,more:0,dev:0,type:''}, // realtek
];
var n = form.smType.selectedIndex>0 ? form.smType.selectedIndex-1 : <?=_var($var,'smIndex',0)?>;
var x = data[n]['more'];
@@ -603,7 +610,7 @@ _(Spin down delay)_:
_(File system status)_:
: <?=_(_var($disk,'fsStatus'))?>&nbsp;
<?$disabled = (_var($var,'fsState')=="Stopped" && !empty(_var($disk,'uuid'))) || (_var($var,'fsState')=="Started" && _var($disk,'fsStatus')=='Mounted') ? "disabled" : ""?>
<?$disabled = (_var($var,'fsState')=="Stopped" && !empty(_var($disk,'uuid'))) || (_var($var,'fsState')=="Started" && _var($disk,'fsType')!='auto') ? "disabled" : ""?>
<?if (diskType('Data') || (!isSubpool($name) && _var($disk,'slots',0)==1)):?>
_(File system type)_:
: <select id="diskFsType" name="diskFsType.<?=_var($disk,'idx',0)?>" onchange="changeFsType()" <?=$disabled?>>
@@ -1246,6 +1253,13 @@ _(SMART controller type)_:
<?=mk_option(_var($disk,'smType'), "-d cciss", "HP cciss")?>
<?=mk_option(_var($disk,'smType'), "-d marvell", "Marvell")?>
<?=mk_option(_var($disk,'smType'), "-d megaraid", "MegaRAID")?>
<?=mk_option(_var($disk,'smType'), "-d usbcypress", "Cypress ATACB")?>
<?=mk_option(_var($disk,'smType'), "-d usbjmicron", "JMicron ATA pass-through")?>
<?=mk_option(_var($disk,'smType'), "-d usbprolific", "Prolific ATA pass-through")?>
<?=mk_option(_var($disk,'smType'), "-d usbsunplus", "Sunplus ATA pass-through")?>
<?=mk_option(_var($disk,'smType'), "-d sntasmedia", "ASMedia NVMe pass-through")?>
<?=mk_option(_var($disk,'smType'), "-d sntjmicron", "JMicron NVMe pass-through")?>
<?=mk_option(_var($disk,'smType'), "-d sntrealtek", "Realtek NVMe pass-through")?>
</select>
<input type="text" name="smPort1" value="<?=_var($disk,'smPort1')?>" class="option"><select name="smPort1" class="narrow option" disabled></select>
<input type="text" name="smPort2" value="<?=_var($disk,'smPort2')?>" class="option"><select name="smPort2" class="narrow option" disabled></select>

View File

@@ -364,6 +364,13 @@ _(Default SMART controller type)_:
<?=mk_option(_var($var,'smType'), "-d cciss", "HP cciss")?>
<?=mk_option(_var($var,'smType'), "-d marvell", "Marvell")?>
<?=mk_option(_var($var,'smType'), "-d megaraid", "MegaRAID")?>
<?=mk_option(_var($var,'smType'), "-d usbcypress", "Cypress ATACB")?>
<?=mk_option(_var($var,'smType'), "-d usbjmicron", "JMicron ATA pass-through")?>
<?=mk_option(_var($var,'smType'), "-d usbprolific", "Prolific ATA pass-through")?>
<?=mk_option(_var($var,'smType'), "-d usbsunplus", "Sunplus ATA pass-through")?>
<?=mk_option(_var($var,'smType'), "-d sntasmedia", "ASMedia NVMe pass-through")?>
<?=mk_option(_var($var,'smType'), "-d sntjmicron", "JMicron NVMe pass-through")?>
<?=mk_option(_var($var,'smType'), "-d sntrealtek", "Realtek NVMe pass-through")?>
</select>
:disk_default_smart_controller_help:

View File

@@ -5,8 +5,8 @@ Icon="icon-key"
Tag="expeditedssl"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, 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,
@@ -75,7 +75,7 @@ if ($cert1Present) {
$cert1SelfSigned = ($cert1Subject == $cert1Issuer);
}
// unraid.net, myunraid.net LE cert. could potentially be user provided as well
// myunraid.net LE cert. could potentially be user provided as well
$cert2File = "/boot/config/ssl/certs/certificate_bundle.pem";
$cert2Present = file_exists("$cert2File");
if ($cert2Present) {
@@ -84,6 +84,8 @@ if ($cert2Present) {
$cert2Expires = exec("/usr/bin/openssl x509 -in $cert2File -noout -text | sed -n -e 's/^.*Not After : //p'");
$isWildcardCert = preg_match('/.*\.myunraid\.net$/', $cert2Subject);
$subject2URL = $cert2Subject;
$dnsValid = false;
$dnsRebindingProtection = false;
if ($isWildcardCert) {
exec("openssl x509 -checkend 2592000 -noout -in $cert2File 2>/dev/null", $arrout, $retval_expired);
if (!$addr) {
@@ -119,8 +121,8 @@ $http_port = _var($var,'PORT','80') != '80' ? ":{$var['PORT']}" : '';
$https_port = _var($var,'PORTSSL','443') != '443' ? ":{$var['PORTSSL']}" : '';
$http_ip_url = "http://"._var($nginx,'NGINX_LANIP')."{$http_port}/";
$https_ip_url = "https://"._var($nginx,'NGINX_LANIP')."{$https_port}/";
$http_ip6_url = "http://"._var($nginx,'NGINX_LANIP6')."{$http_port}/";
$https_ip6_url = "https://"._var($nginx,'NGINX_LANIP6')."{$https_port}/";
$http_ip6_url = "http://["._var($nginx,'NGINX_LANIP6')."]{$http_port}/";
$https_ip6_url = "https://["._var($nginx,'NGINX_LANIP6')."]{$https_port}/";
$http_mdns_url = "http://"._var($nginx,'NGINX_LANMDNS')."{$http_port}/";
$https_mdns_url = "https://"._var($nginx,'NGINX_LANMDNS')."{$https_port}/";
$https_fqdn_url = "https://"._var($nginx,'NGINX_LANFQDN')."{$https_port}/";
@@ -164,7 +166,6 @@ $cert_time_format = $display['date'].($display['date']!='%c' ? ', '.str_replac
$provisionlabel = $isWildcardCert ? _('Renew') : _('Provision');
$disabled_provision = $keyfile===false || ($isWildcardCert && $retval_expired===0) || !$addr ? 'disabled' : '';
$disabled_provision_msg = !$addr ? _('Ensure the primary network card eth0 has an IP address.') : '';
$disabled_updatedns = $keyfile!==false && $isWildcardCert ? '' : 'disabled';
$disabled_delete = $cert2Present && $var['USE_SSL']!='auto' ? '' : 'disabled';
$disabled_auto = $isWildcardCert && !$dnsRebindingProtection && $dnsValid ? '' : 'disabled';
@@ -189,23 +190,6 @@ function provisionHandler(event, form) { // provisions and renewals require bein
if (event.submitter.value === 'Renew') return true; // always allow renewals
};
function updateDNS(button) {
$(button).prop("disabled", true).html("<i class='fa fa-circle-o-notch fa-spin fa-fw'></i>_(Update DNS)_");
var failure = function(data) {
var status = data.status;
var obj = data.responseJSON;
var msg = "_(Sorry, an error occurred updating unraid.net DNS records)_. _(The error is)_: "+obj.error+".";
$(button).prop("disabled", false).html("_(Update DNS)_");
swal({title:"_(Oops)_",text:msg,type:"error",html:true,confirmButtonText:"_(Ok)_"});
};
var success = function(data) {
$(button).prop("disabled", false).html("_(Update DNS)_");
<?$text = _('Your local IP address %s has been updated for unraid.net')?>
swal({title:"",text:"<?=sprintf($text,$addr)?>",type:"success",html:true,confirmButtonText:"_(Ok)_"});
};
$.post("/webGui/include/UpdateDNS.php",success).fail(failure);
}
function checkPorts(form) {
var portsInUse = [<?=implode(',',$portsInUse)?>];
var range = [], list = [], duplicates = [];
@@ -359,12 +343,10 @@ _(Local access URLs)_:
$n = 0;
foreach($urls as $url) {
$msg = "";
$url0 = substr_count($url[0]??'',':')>3 ? preg_replace('#(://)(.+?)(:?\d*)/$#','$1[$2]$3/',$url[0]) : $url[0]; // IPv6 - IPv4 notation
$url1 = substr_count($url[1]??'',':')>3 ? preg_replace('#(://)(.+?)(:?\d*)/$#','$1[$2]$3/',$url[1]) : $url[1]; // IPv6 - IPv4 notation
if ($url[1]) $msg .= " "._("redirects to")." <a href='$url1'>$url1</a>";
if ($url[1]) $msg .= " "._("redirects to")." <a href='$url[1]'>$url[1]</a>";
if ($url[2]) $msg .= " "._("uses")." ".$url[2];
if ($url[3]) $msg .= "<span class='warning'> <i class='fa fa-warning fa-fw'></i> "._("is a self-signed certificate, ignore the browser's warning and proceed to the GUI")."</span>";
echo ($n ? "<dt>&nbsp;</dt><dd>" : ""),"<a href='$url0'>$url0</a>$msg",($n++ ? "</dd>" : "");
echo ($n ? "<dt>&nbsp;</dt><dd>" : ""),"<a href='$url[0]'>$url[0]</a>$msg",($n++ ? "</dd>" : "");
}?>
:mgmt_local_access_urls_help:
@@ -431,7 +413,7 @@ _(CA-signed certificate file)_:
<?endif;?>
&nbsp;
: <button type="submit" name="changePorts" value="Provision" <?=$disabled_provision?>><?=$provisionlabel?></button><button type="submit" name="changePorts" value="Delete" <?=$disabled_delete?> >_(Delete)_</button><!-- <button type="button" onclick="updateDNS(this)" <?=$disabled_updatedns?>>_(Update DNS)_</button> --><?=$disabled_provision_msg?>
: <button type="submit" name="changePorts" value="Provision" <?=$disabled_provision?>><?=$provisionlabel?></button><button type="submit" name="changePorts" value="Delete" <?=$disabled_delete?> >_(Delete)_</button><?=$disabled_provision_msg?>
:mgmt_certificate_expiration_help:

View File

@@ -18,8 +18,7 @@ Tag="folder-o"
<?
$width = [166,300];
function data_disks($disk) {
global $pools;
return (_var($disk,'type')=="Data" || (_var($disk,'type')=="Cache" && in_array(_var($disk,'name'),$pools))) && _var($disk,'status')=='DISK_OK';
return in_array(_var($disk,'type'),['Data','Cache']) && array_key_exists('exportable',$disk) && _var($disk,'fsStatus')=='Mounted';
}
?>
<script>

View File

@@ -101,7 +101,7 @@ function textsave(module,remove = false) {
if (data.modprobe == "") $('#text'+module).attr('hidden', true); else $('#text'+module).attr('rows', 3);
if (data.supportpage == true) {
if (data.support == true) {
document.getElementById("link" + module).innerHTML = "<a href='" + data.supporturl + "'target='_blank'><i title='"+_("Support page")_+"' class='fa fa-phone-square'></i></a>";
document.getElementById("link" + module).innerHTML = "<a href='" + data.supporturl + "'target='_blank'><i title='"+"_(Support page)_"+"' class='fa fa-phone-square'></i></a>";
}
}
}

View File

@@ -108,6 +108,15 @@ _(Local syslog folder)_:
:syslog_local_folder_help:
_(System identifier for logfile name)_:
: <select name="server_filename">
<?=mk_option($syslog['server_filename'], "syslog-%FROMHOST-IP%.log", _("IP Address"))?>
<?=mk_option($syslog['server_filename'], "syslog-%HOSTNAME%.log", _("Hostname (from syslog message)"))?>
<?=mk_option($syslog['server_filename'], "syslog-%FROMHOST%.log", _("Hostname (from DNS reverse lookup)"))?>
</select>
:syslog_remote_system_identifier_help:
_(Local syslog rotation)_:
: <select name="log_rotation" onchange="logOptions(this.value,'slow')">
<?=mk_option(_var($syslog,'log_rotation'), "", _("Disabled"))?>
@@ -146,7 +155,7 @@ _(Local syslog number of files)_:
</div>
_(Remote syslog server)_:
: <span class="span"><input type="text" name="remote_server" class="narrow" value="<?=_var($syslog,'remote_server')?>" maxlength="23" placeholder="_(name or ip address)_"></span>
: <span class="span"><input type="text" name="remote_server" class="narrow" value="<?=_var($syslog,'remote_server')?>" maxlength="50" placeholder="_(name or ip address)_"></span>
<select name="remote_protocol" class="narrow">
<?=mk_option(_var($syslog,'remote_protocol'), "udp", _("UDP"))?>
<?=mk_option(_var($syslog,'remote_protocol'), "tcp", _("TCP"))?>

View File

@@ -30,6 +30,8 @@ $(function() {
$globals = [];
$names = ['_SERVER','devs','disks','sec','sec_nfs','shares','users','var'];
foreach ($names as $name) $globals[$name] = $$name;
// show outgoing proxy information, is in the environment but not a superglobal
$globals['environment'] = ['http_proxy' => getenv('http_proxy'), 'no_proxy' => getenv('no_proxy')];
echo "<pre class='up'>",htmlspecialchars(print_r($globals,true)),"</pre>";
?>
<input type="button" value="_(Done)_" onclick="done()">

View File

@@ -1,121 +0,0 @@
Menu="UNRAID-OS"
Title="Wake On LAN"
Icon="fa-bell"
Tag="server"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
if (count($_POST)) {
$cfg = NULL ;
if ($_POST['#apply'] == "_(Save)_") {
foreach($_POST as $postkey=>$data) {
if ($postkey=="#apply") continue;
$keys = explode(";",$postkey);
if (count($keys) >1) $update_file[$keys[1]][$keys[2]][$keys[0]]=$data;
}
foreach($update_file as $type => $types) {
foreach($types as $name => $details) {
if ($details['user_mac'] == "") $details['user_mac'] = "None Defined";
if ($details['user_mac'] == "None Defined" && ($details['enable'] == "enable" || $details['enable'] == "shutdown")) unset($update_file[$type][$name]) ;
}
}
}
unset($_POST) ;
file_put_contents("/boot/config/wol.json",json_encode($update_file));
#echo '<meta http-equiv="refresh" content="0;url=/Tools">';
echo '<meta http-equiv="refresh" content="0">';
#unset($_SESSION['csrf_token']);
}
?>
<script src="/webGui/javascript/jquery.tablesorter.widgets.js"></script>
<script>
function showWOL(options, init = false) {
option = options;
if (init) {
$('#wolsearch').prop('disabled', true);
$.post('/webGui/include/WOL.php',{table:'t1load',option:"all"},function(data){
clearTimeout(timers.refresh);
filter = [];
$("#t1").trigger("destroy");
$('#t1').html(data.html);
$('#t1').tablesorter({
sortList: [[0,0]],
sortAppend: [[0,0]],
widgets: ['stickyHeaders','filter','zebra'],
widgetOptions: {
// on black and white, offset is height of #menu
// on azure and gray, offset is height of #header
stickyHeaders_offset: ($('#menu').height() < 50) ? $('#menu').height() : $('#header').height(),
filter_columnFilters: false,
zebra: ["normal-row","alt-row"]
}
});
$('div.spinner.fixed').hide('slow');
$('#wolsearch').prop('disabled', false);
$('#select').prop('disabled', false);
$('#rebuild').prop('disabled', data.init);
},"json");
} else {
filter = [];
filterWOL();
}
}
function filterWOL() {
var totalColumns = $('#t1')[0].config.columns;
var filter = [];
filter[totalColumns] = $('#wolsearch').val(); // this searches all columns
$('#t1').trigger('search', [filter]);
}
function showWOLupdate() {
$('#rebuild').prop('disabled', true);
$('#t1').html("");
$('#wolsearch').prop('disabled', true);
$('#select').prop('disabled', true);
$('div.spinner.fixed').show('slow');
$.post('/webGui/include/WOL.php',{table:'t1create',option:"all"},function(data){
$('#rebuild').prop('disabled', false);
showWOL("all",true);
$('div.spinner.fixed').hide('slow');
});
}
function maccreate(name) {
$.post('/webGui/include/WOL.php',{table:'macaddress'}, function( data ) {
if (data.mac) {
$('#'+name).val(data.mac);
}
},'json');
}
showWOL("all",true);
</script>
:WOL_intro_help:
<form autocomplete="off" onsubmit="return false;"><span><input class="t1 search" id="wolsearch" type="search" placeholder="Search..." onchange="filterWOL();"></span></form>
<pre><form name="WOL" id="WOL" method="POST" class="js-confirm-leave" >
<table name="t1"id='t1' class="t1 unraid tablesorter" >
<tr><td><div class="spinner"></div></td></tr></table></pre><br>
<input type="button" value="_(Done)_" onclick="done()">
<input type="submit" name="#apply" id='#apply' value="_(Save)_" >
</form>

View File

@@ -1,102 +0,0 @@
Menu="OtherSettings"
Type="xmenu"
Title="Wake on Lan Settings"
Icon="fa-bell"
Tag="share-alt"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
exec("ls --indicator-style=none /sys/class/net|grep -P '^eth[1-9][0-9]*$'",$ports);
$disabled = _var($var,'fsState')!='Stopped' ? 'disabled' : '';
$width = [166,300];
$file = '/boot/config/wol.cfg';
$current = parse_ini_file($file);
if (!isset($current['LOGFILE'])) $current['LOGFILE'] = "syslog"
?>
<script>
showStatus('pid','unraidwold');
</script>
<form markdown="1" name="WOLsettings" method="POST" action="/update.php" target="progressFrame" >
<input type="hidden" name="#file" value="<?=$file;?>">
<input type="hidden" name="#command" value="/webGui/scripts/WOL_action">
<input type="hidden" name="#arg[1]" value="load">
_(Enable Wake on Lan)_:
: <select name="WOLENABLED" >
<?=mk_option($current['WOLENABLED'], "no", _('No'))?>
<?=mk_option($current['WOLENABLED'], "yes", _('Yes'))?>
</select>
:WOL_enable_help:
_(Enable Docker actions)_:
: <select name="RUNDOCKER" >
<?=mk_option($current['RUNDOCKER'], "y", _('Yes'))?>
<?=mk_option($current['RUNDOCKER'], "n", _('No'))?>
</select>
:WOL_run_docker_help:
_(Enable LXC actions)_:
: <select name="RUNLXC" >
<?=mk_option($current['RUNLXC'], "y", _('Yes'))?>
<?=mk_option($current['RUNLXC'], "n", _('No'))?>
</select>
:WOL_run_LXC_help:
_(Enable VM actions)_:
: <select name="RUNVM" >
<?=mk_option($current['RUNVM'], "y", _('Yes'))?>
<?=mk_option($current['RUNVM'], "n", _('No'))?>
</select>
:WOL_run_VM_help:
_(Enable Shutdown actions)_:
: <select name="RUNSHUT" >
<?=mk_option($current['RUNSHUT'], "n", _('No'))?>
<?=mk_option($current['RUNSHUT'], "y", _('Yes'))?>
</select>
:WOL_run_shutdown_help:
_(Interface to listern on)_:
: <select id="INTERFACE" name="INTERFACE" >
<?=mk_option(_var($eth0,'INTERFACE'),'eth0','eth0','selected')?>
<?foreach ($ports as $port):?>
<?if (!locked('eth0',$port)) echo mk_option_check($current['INTERFACE'],$port,$port)?>
<?endforeach;?>
</select>
:WOL_interface_help:
_(Interface promiscuous mode)_:
: <select id="IFMODE" name="IFMODE" >
<?=mk_option($current['IFMODE'], "n", _('No'))?>
<?=mk_option($current['IFMODE'], "y", _('Yes'))?>
</select>
:WOL_promiscuous_mode_help:
_(Log file)_:
: <input name="LOGFILE" class="narrow"type="text" value=<?=$current['LOGFILE']?>>
:WOL_log_file_help:
&nbsp;
: <input type="submit" value="_(Apply)_" disabled><input type="button" value="_(Done)_" onclick="done()">
</form>

View File

@@ -1,3 +0,0 @@
#!/bin/bash
/usr/local/emhttp/plugins/dynamix/scripts/WOL_action load &

View File

@@ -1,3 +0,0 @@
#!/bin/bash
/usr/local/emhttp/plugins/dynamix/include/script/WOL_action stop &

View File

@@ -68,6 +68,8 @@ if ($_POST['docker']) {
}
echo "\0";
if ($_POST['vms']) {
$vmusage = $_POST['vmusage'];
$vmusagehtml = [];
$user_prefs = '/boot/config/plugins/dynamix.vm.manager/userprefs.cfg';
$vms = $lv->get_domains() ?: [];
if (file_exists($user_prefs)) {
@@ -78,6 +80,7 @@ if ($_POST['vms']) {
natcasesort($vms);
}
echo "<tr title='' class='updated'><td>";
$running = 0;
foreach ($vms as $vm) {
$res = $lv->get_domain_by_name($vm);
$uuid = libvirt_domain_get_uuid_string($res);
@@ -123,6 +126,7 @@ if ($_POST['vms']) {
$shape = 'play';
$status = 'started';
$color = 'green-text';
$running++;
break;
case 'paused':
case 'pmsuspended':
@@ -138,8 +142,30 @@ if ($_POST['vms']) {
}
$image = substr($icon,-4)=='.png' ? "<img src='$icon' class='img'>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
echo "<span class='outer solid vms $status'><span id='vm-$uuid' $menu class='hand'>$image</span><span class='inner'>$vm<br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span></span>";
if ($state == "running") {
#Build VM Usage array.
$menuusage = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm), addslashes($uuid), addslashes($template), $state, addslashes($vmrcurl), strtoupper($vmrcprotocol), addslashes($log),addslashes($fstype), $vmrcconsole,true);
$vmusagehtml[] = "<span class='outer solid vmsuse $status'><span id='vmusage-$uuid' $menuusage class='hand'>$image</span><span class='inner'>$vm<br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span>";
$vmusagehtml[] = "<br><br><span id='vmmetrics-gcpu-".$uuid."'>"._("Loading")."....</span>";
$vmusagehtml[] = "<br><span id='vmmetrics-hcpu-".$uuid."'>"._("Loading")."....</span>";
$vmusagehtml[] = "<br><span id='vmmetrics-mem-".$uuid."'>"._("Loading")."....</span>";
$vmusagehtml[] = "<br><span id='vmmetrics-disk-".$uuid."'>"._("Loading")."....</span>";
$vmusagehtml[] = "<br><span id='vmmetrics-net-".$uuid."'>"._("Loading")."....</span>";
$vmusagehtml[] = "</span>";
}
}
$none = count($vms) ? _('No running virtual machines') : _('No virtual machines defined');
echo "<span id='no_vms' style='display:none'>$none<br><br></span>";
echo "</td></tr>";
echo "\0";
echo "<tr title='' class='useupdated'><td>";
if ($vmusage == "Y") {
foreach ($vmusagehtml as $vmhtml) {
echo $vmhtml;
}
if (!count($vmusagehtml)) echo "<span id='no_usagevms'><br> "._('No running virtual machines')."<br></span>";
if ($running < 1 && count($vmusagehtml)) echo "<span id='no_usagevms'><br>". _('No running virtual machines')."<br></span>";
echo "</td></tr>";
}
}

View File

@@ -291,4 +291,13 @@ function my_mkdir($dirname,$permissions = 0777,$recursive = false) {
break;
}
}
function get_realvolume($path) {
if (strpos($path,"/mnt/user/",0) === 0)
$reallocation = trim(shell_exec("getfattr --absolute-names --only-values -n system.LOCATION ".escapeshellarg($path)." 2>/dev/null"));
else {
$realexplode = explode("/",str_replace("/mnt/","",$path));
$reallocation = $realexplode[0];
}
return $reallocation;
}
?>

View File

@@ -39,13 +39,13 @@ foreach ($files as $file) {
$c = 0;
foreach ($fields as $field) {
if ($c==5) break;
$text = $field ? explode('=',$field,2)[1] : "-";
$text = $field ? (explode('=',$field,2)[1]??"") : "-";
$tag = ($c<4) ? "" : " data='".str_replace(['alert','warning','normal'],['0','1','2'],$text)."'";
echo (!$c++) ? "<tr>".str_replace('*',$text,$td_).date($dynamix['notify']['date'].' '.$dynamix['notify']['time'],$text)."$_td" : "<td$tag>"._($text)."</td>";
}
echo "<td><a href='#' onclick='$(this).hide();$.post(\"/webGui/include/DeleteLogFile.php\",{log:\"$archive\"},function(){archiveList();});return false' title=\""._('Delete notification')."\"><i class='fa fa-trash-o'></i></a></td></tr>";
if ($extra) {
$text = explode('=',$field,2)[1];
$text = explode('=',$field,2)[1]??"";
echo "<tr class='tablesorter-childRow row$row'><td colspan='4'>$text</td><td></td></tr><tr class='tablesorter-childRow row$row'><td colspan='5'></td></tr>";
$row++;
}

View File

@@ -73,7 +73,7 @@ case 't1load':
} else {
$supporturl = $module['supporturl'];
$pluginname = $module['plugin'];
$supporthtml = "<span id='link$modname'><a href='$supporturl' target='_blank'><i title='"._("Support page $pluginname")."' class='fa fa-phone-square'></i></a></span>";
$supporthtml = "<span id='link$modname'><a href='$supporturl' target='_blank'><i title='"._("Support page")." $pluginname' class='fa fa-phone-square'></i></a></span>";
}
}
if (!empty($module["version"])) $version = " (".$module["version"].")"; else $version = "";

View File

@@ -1,6 +1,6 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, 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,
@@ -12,353 +12,15 @@
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
// add translations
$_SERVER['REQUEST_URI'] = 'settings';
require_once "$docroot/webGui/include/Translations.php";
function host_lookup_ip($host) {
$result = @dns_get_record($host, DNS_A);
$ip = $result ? _var($result[0],'ip') : '';
return($ip);
}
function rebindDisabled() {
global $isLegacyCert;
$rebindtesturl = $isLegacyCert ? "rebindtest.unraid.net" : "rebindtest.myunraid.net";
// DNS Rebind Protection - this checks the server but clients could still have issues
$validResponse = ["192.168.42.42", "fd42"];
$response = host_lookup_ip($rebindtesturl);
return in_array(explode('::',$response)[0], $validResponse);
}
function format_port($port) {
return ($port != 80 && $port != 443) ? ':'.$port : '';
}
function anonymize_host($host) {
global $anon;
if ($anon) {
$host = preg_replace('/.*\.myunraid\.net/', '*.hash.myunraid.net', $host);
$host = preg_replace('/.*\.unraid\.net/', 'hash.unraid.net', $host);
}
return $host;
}
function anonymize_ip($ip) {
global $anon;
if ($anon && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
$ip = "[redacted]";
}
return $ip;
}
function generate_internal_host($host, $ip) {
if (strpos($host,'.myunraid.net') !== false) {
$host = str_replace('*', str_replace('.', '-', $ip), $host);
}
return $host;
}
function generate_external_host($host, $ip) {
if (strpos($host,'.myunraid.net') !== false) {
$host = str_replace('*', str_replace('.', '-', $ip), $host);
} elseif (strpos($host,'.unraid.net') !== false) {
$host = "www.".$host;
}
return $host;
}
function verbose_output($httpcode, $result) {
global $cli, $verbose, $anon, $plgversion, $post, $var, $isRegistered, $myservers, $reloadNginx, $nginx, $isLegacyCert;
global $remoteaccess;
global $icon_warn, $icon_ok;
if (!$cli || !$verbose) return;
if ($anon) echo "(Output is anonymized, use '-vv' to see full details)".PHP_EOL;
echo "Unraid OS "._var($var,'version','???').((strpos($plgversion, "base-") === false) ? " with My Servers plugin version {$plgversion}" : '').PHP_EOL;
echo ($isRegistered) ? "{$icon_ok}Signed in to Unraid.net as {$myservers['remote']['username']}".PHP_EOL : "{$icon_warn}Not signed in to Unraid.net".PHP_EOL ;
echo "Use SSL is "._var($nginx,'NGINX_USESSL','No').PHP_EOL;
echo (rebindDisabled()) ? "{$icon_ok}Rebind protection is disabled" : "{$icon_warn}Rebind protection is enabled";
echo " for ".($isLegacyCert ? "unraid.net" : "myunraid.net").PHP_EOL;
if ($post) {
$wanip = trim(@file_get_contents("https://wanip4.unraid.net/"));
// check the data
$certhostname = _var($nginx,'NGINX_CERTNAME');
if ($certhostname) {
// $certhostname is $nginx['NGINX_CERTNAME'] (certificate_bundle.pem)
$certhostip = host_lookup_ip(generate_internal_host($certhostname, _var($post,'internalip')));
$certhosterr = ($certhostip != _var($post,'internalip'));
}
if (_var($post,'internalhostname') != $certhostname) {
// $post['internalhostname'] is $nginx['NGINX_LANMDNS'] (no cert, or Server_unraid_bundle.pem) || $nginx['NGINX_CERTNAME'] (certificate_bundle.pem)
$internalhostip = host_lookup_ip(generate_internal_host(_var($post,'internalhostname'), _var($post,'internalip')));
$internalhosterr = ($internalhostip != _var($post,'internalip'));
}
if (!empty($post['externalhostname'])) {
// $post['externalhostname'] is $nginx['NGINX_CERTNAME'] (certificate_bundle.pem)
$externalhostip = host_lookup_ip(generate_external_host($post['externalhostname'], $wanip));
$externalhosterr = ($externalhostip != $wanip);
}
// anonymize data. no caclulations can be done with this data beyond this point.
if ($anon) {
if (!empty($certhostip)) $certhostip = anonymize_ip($certhostip);
if (!empty($certhostname)) $certhostname = anonymize_host($certhostname);
if (!empty($internalhostip)) $internalhostip = anonymize_ip($internalhostip);
if (!empty($externalhostip)) $externalhostip = anonymize_ip($externalhostip);
if (!empty($wanip)) $wanip = anonymize_ip($wanip);
if (!empty($post['internalip'])) $post['internalip'] = anonymize_ip($post['internalip']);
if (!empty($post['internalhostname'])) $post['internalhostname'] = anonymize_host($post['internalhostname']);
if (!empty($post['externalhostname'])) $post['externalhostname'] = anonymize_host($post['externalhostname']);
if (!empty($post['externalport'])) $post['externalport'] = "[redacted]";
}
// always anonymize the keyfile
if (!empty($post['keyfile'])) $post['keyfile'] = "[redacted]";
// output notes
if (!empty($post['internalprotocol']) && !empty($post['internalhostname']) && !empty($post['internalport'])) {
$localurl = $post['internalprotocol']."://".generate_internal_host($post['internalhostname'], _var($post,'internalip')).format_port($post['internalport']);
echo 'Local Access url: '.$localurl.PHP_EOL;
if ($internalhostip) {
// $internalhostip will not be defined for .local domains, ok to skip
echo ($internalhosterr) ? $icon_warn : $icon_ok;
echo generate_internal_host($post['internalhostname'], _var($post,'internalip'))." resolves to {$internalhostip}";
echo ($internalhosterr) ? ", it should resolve to "._var($post,'internalip') : "";
echo PHP_EOL;
}
if ($certhostname) {
echo ($certhosterr) ? $icon_warn : $icon_ok;
echo generate_internal_host($certhostname, _var($post,'internalip')).' ';
echo ($certhostip) ? "resolves to {$certhostip}" : "does not resolve to an IP address";
echo ($certhosterr) ? ", it should resolve to "._var($post,'internalip') : "";
echo PHP_EOL;
}
if ($remoteaccess == 'yes' && !empty($post['externalprotocol']) && !empty($post['externalhostname']) && !empty($post['externalport'])) {
$remoteurl = $post['externalprotocol']."://".generate_external_host($post['externalhostname'], $wanip).format_port($post['externalport']);
echo 'Remote Access url: '.$remoteurl.PHP_EOL;
echo ($externalhosterr) ? $icon_warn : $icon_ok;
echo generate_external_host($post['externalhostname'], $wanip).' ';
echo ($externalhosterr) ? "does not resolve to an IP address" : "resolves to ".($externalhostip??'');
echo PHP_EOL;
}
if ($reloadNginx) {
echo "IP address changes were detected, nginx was reloaded".PHP_EOL;
}
}
// output post data
echo PHP_EOL.'Request:'.PHP_EOL;
echo @json_encode($post, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . PHP_EOL;
}
if ($result) {
echo "Response (HTTP $httpcode):".PHP_EOL;
$mutatedResult = is_array($result) ? json_encode($result) : $result;
echo @json_encode(@json_decode($mutatedResult, true), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . PHP_EOL;
}
}
/**
* @name response_complete
* @param {HTTP Response Status Code} $httpcode https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
* @param {String|Array} $result - strings are assumed to be encoded JSON. Arrays will be encoded to JSON.
* @param {String} $cli_success_msg
*/
function response_complete($httpcode, $result, $cli_success_msg='') {
global $cli, $verbose;
$mutatedResult = is_array($result) ? json_encode($result) : $result;
if ($cli) {
if ($verbose) verbose_output($httpcode, $result);
$json = @json_decode($mutatedResult,true);
if (!empty($json['error'])) {
echo 'Error: '.$json['error'].PHP_EOL;
exit(1);
}
exit($cli_success_msg.PHP_EOL);
}
header('Content-Type: application/json');
http_response_code($httpcode);
exit((string)$mutatedResult);
}
require_once "$docroot/webGui/include/Wrappers.php";
// This is a stub, does nothing but return success
my_logger("This is a stub and should not be called", "UpdateDNS");
$cli = php_sapi_name()=='cli';
$verbose = $anon = false;
if ($cli && ($argc > 1) && $argv[1] == "-v") {
$verbose = true;
$anon = true;
}
if ($cli && ($argc > 1) && $argv[1] == "-vv") {
$verbose = true;
}
$var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
$nginx = (array)@parse_ini_file('/var/local/emhttp/nginx.ini');
$is69 = version_compare(_var($var,'version'),"6.9.9","<");
$reloadNginx = false;
$dnserr = false;
$icon_warn = "⚠️ ";
$icon_ok = "✅ ";
$myservers_flash_cfg_path='/boot/config/plugins/dynamix.my.servers/myservers.cfg';
$myservers = (array)@parse_ini_file($myservers_flash_cfg_path,true);
// ensure some vars are defined here so we don't have to test them later
if (empty($myservers['remote']['apikey'])) {
$myservers['remote']['apikey'] = "";
}
if (empty($myservers['remote']['wanaccess'])) {
$myservers['remote']['wanaccess'] = "no";
}
if (empty($myservers['remote']['wanport'])) {
$myservers['remote']['wanport'] = 443;
}
// remoteaccess, externalport
if ($cli) {
$remoteaccess = empty($nginx['NGINX_WANFQDN']) ? 'no' : 'yes';
$externalport = $myservers['remote']['wanport'];
} else {
$remoteaccess = _var($_POST,'remoteaccess','no');
$externalport = intval(_var($_POST,'externalport',443));
if ($remoteaccess != 'yes') {
$remoteaccess = 'no';
}
if ($externalport < 1 || $externalport > 65535) {
$externalport = 443;
}
if ($myservers['remote']['wanaccess'] != $remoteaccess) {
// update the wanaccess ini value
$orig = file_exists($myservers_flash_cfg_path) ? parse_ini_file($myservers_flash_cfg_path,true) : [];
if (!$orig) {
$orig = ['remote' => $myservers['remote']];
}
$orig['remote']['wanaccess'] = $remoteaccess;
$text = '';
foreach ($orig as $section => $block) {
$pairs = "";
foreach ($block as $key => $value) if (strlen($value)) $pairs .= "$key=\"$value\"\n";
if ($pairs) $text .= "[$section]\n".$pairs;
}
if ($text) file_put_contents($myservers_flash_cfg_path, $text);
// need nginx reload
$reloadNginx = true;
}
exit("success".PHP_EOL);
}
$isRegistered = !empty($myservers['remote']['username']);
// protocols, hostnames, ports
$internalprotocol = 'http';
$internalport = _var($nginx,'NGINX_PORT');
$internalhostname = _var($nginx,'NGINX_LANMDNS');
$externalprotocol = 'https';
// keyserver will expand *.hash.myunraid.net or add www to hash.unraid.net as needed
$externalhostname = _var($nginx,'NGINX_CERTNAME');
$isLegacyCert = preg_match('/.*\.unraid\.net$/', _var($nginx,'NGINX_CERTNAME'));
$isWildcardCert = preg_match('/.*\.myunraid\.net$/', _var($nginx,'NGINX_CERTNAME'));
$internalip = _var($nginx,'NGINX_LANIP');
if (_var($nginx,'NGINX_USESSL')=='yes') {
// When NGINX_USESSL is 'yes' in 6.9, it could be using either Server_unraid_bundle.pem or certificate_bundle.pem
// When NGINX_USESSL is 'yes' in 6.10, it is is using Server_unraid_bundle.pem
$internalprotocol = 'https';
$internalport = _var($nginx,'NGINX_PORTSSL');
if ($is69 && _var($nginx,'NGINX_CERTNAME')) {
// this is from certificate_bundle.pem
$internalhostname = _var($nginx,'NGINX_CERTNAME');
}
}
if (_var($nginx,'NGINX_USESSL')=='auto') {
// NGINX_USESSL cannot be 'auto' in 6.9, it is either 'yes' or 'no'
// When NGINX_USESSL is 'auto' in 6.10, it is using certificate_bundle.pem
$internalprotocol = 'https';
$internalport = _var($nginx,'NGINX_PORTSSL');
// keyserver will expand *.hash.myunraid.net as needed
$internalhostname = _var($nginx,'NGINX_CERTNAME');
}
// My Servers version
$plgversion = file_exists("/var/log/plugins/dynamix.unraid.net.plg") ? trim(exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.plg 2>/dev/null'))
: (file_exists("/var/log/plugins/dynamix.unraid.net.staging.plg") ? trim(exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.staging.plg 2>/dev/null'))
: 'base-'._var($var,'version'));
// only proceed when when signed in or when legacy unraid.net SSL certificate exists
if (!$isRegistered && !$isLegacyCert) {
response_complete(406, ['error' => _('Nothing to do')]);
}
// keyfile
$keyfile = empty($var['regFILE']) ? false : @file_get_contents($var['regFILE']);
if ($keyfile === false) {
response_complete(406, ['error' => _('Registration key required')]);
}
$keyfile = @base64_encode($keyfile);
// build post array
$post = [
'keyfile' => $keyfile,
'plgversion' => $plgversion
];
if ($isLegacyCert) {
// sign in not required to maintain local ddns for unraid.net cert
// enable local ddns regardless of use_ssl value
$post['internalip'] = $internalip;
// if host.unraid.net does not resolve to the internalip and DNS Rebind Protection is disabled, disable caching
if (host_lookup_ip(generate_internal_host(_var($nginx,'NGINX_CERTNAME'), $post['internalip'])) != $post['internalip'] && rebindDisabled()) $dnserr = true;
}
if ($isRegistered) {
// if signed in, send data needed to maintain My Servers Dashboard
$post['internalhostname'] = $internalhostname;
$post['internalport'] = $internalport;
$post['internalprotocol'] = $internalprotocol;
$post['remoteaccess'] = $remoteaccess;
$post['servercomment'] = _var($var,'COMMENT');
$post['servername'] = _var($var,'NAME');
if ($isWildcardCert) {
// keyserver needs the internalip to generate the local access url
$post['internalip'] = $internalip;
}
if ($remoteaccess == 'yes') {
// include wanip in the cache file so we can track if it changes
$post['_wanip'] = trim(@file_get_contents("https://wanip4.unraid.net/"));
$post['externalhostname'] = $externalhostname;
$post['externalport'] = $externalport;
$post['externalprotocol'] = $externalprotocol;
// if wanip.hash.myunraid.net or www.hash.unraid.net does not resolve to the wanip, disable caching
if (host_lookup_ip(generate_external_host($post['externalhostname'], $post['_wanip'])) != $post['_wanip']) $dnserr = true;
}
}
// if remoteaccess is enabled in 6.10.0-rc3+ and WANIP has changed since nginx started, reload nginx
if (_var($post,'_wanip') != _var($nginx,'NGINX_WANIP') && version_compare(_var($var,'version'),"6.10.0-rc2",">")) $reloadNginx = true;
// if remoteaccess is currently disabled (perhaps because a wanip was not available when nginx was started)
// BUT the system is configured to have it enabled AND a wanip is now available
// then reload nginx
if ($remoteaccess == 'no' && _var($nginx,'NGINX_WANACCESS') == 'yes' && !empty(trim(@file_get_contents("https://wanip4.unraid.net/")))) $reloadNginx = true;
if ($reloadNginx) {
exec("/etc/rc.d/rc.nginx reload &>/dev/null");
}
// maxage is 36 hours
$maxage = 36*60*60;
if ($dnserr || $verbose) $maxage = 0;
$datafile = "/tmp/UpdateDNS.txt";
$datafiletmp = "/tmp/UpdateDNS.txt.new";
$dataprev = @file_get_contents($datafile) ?: '';
$datanew = implode("\n",$post)."\n";
if ($datanew == $dataprev && (time()-filemtime($datafile) < $maxage)) {
response_complete(204, null, _('No change to report'));
}
file_put_contents($datafiletmp,$datanew);
rename($datafiletmp, $datafile);
// do not submit the wanip, it will be captured from the submission if needed for remote access
unset($post['_wanip']);
// report necessary server details to limetech for DNS updates
$ch = curl_init('https://keys.lime-technology.com/account/server/register');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ( ($result === false) || ($httpcode != "200") ) {
// delete cache file to retry submission on next run
@unlink($datafile);
response_complete($httpcode ?? "500", ['error' => $error]);
}
response_complete($httpcode, $result, _('success'));
header('Content-Type: application/json');
http_response_code(204);
exit(0);
?>

View File

@@ -1,125 +0,0 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
global $lv;
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/SysDriversHelpers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
// add translations
$_SERVER['REQUEST_URI'] = 'tools';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt.php";
function macbyte($val) {
if ($val < 16)
return '0'.dechex($val);
return dechex($val);
}
$arrEntries = [];
$vms = $lv->get_domains() ?:[];
sort($vms,SORT_NATURAL);
foreach($vms as $vm){
$arrEntries['VM'][$vm]['interfaces'] = $lv->get_nic_info($vm);
$arrEntries['VM'][$vm]['name'] = $vm;
}
$DockerClient = new DockerClient();
$containers = $DockerClient->getDockerJSON("/containers/json?all=1");
foreach($containers as $ct)
$arrEntries['Docker'][substr($ct["Names"][0],1)] = [
'interfaces' => ['0 '=> ['mac' => isset($ct["NetworkSettings"]["Networks"]["bridge"]["MacAddress"]) ? $ct["NetworkSettings"]["Networks"]["bridge"]["MacAddress"] : ""]],
'name' => substr($ct["Names"][0],1),
];
$lxc = explode("\n",shell_exec("lxc-ls -1")) ;
$lxcpath = trim(shell_exec("lxc-config lxc.lxcpath"));
foreach ($lxc as $lxcname) {
if ($lxcname == "") continue;
$value = explode("=",shell_exec("cat $lxcpath/$lxcname/config | grep 'hwaddr'"));
$arrEntries['LXC'][$lxcname]['interfaces'][0]['mac'] = trim($value[1]);
$arrEntries['LXC'][$lxcname]['name'] = $lxcname;
}
if (is_file("/boot/config/wol.json")) $user_mac = json_decode(file_get_contents("/boot/config/wol.json"),true); else $user_mac = [];
foreach($arrEntries as $key => $data) {
$type=$key;
foreach($data as $data2){
$name=$data2['name'];
if (isset($user_mac[$type][$name])) {
$name=$name;
$arrEntries[$type][$name]['enable'] = $user_mac[$type][$name]['enable'];
$arrEntries[$type][$name]['user_mac'] = strtolower($user_mac[$type][$name]['user_mac']);
} else {
$arrEntries[$type][$name]['enable'] = 'enable';
$arrEntries[$type][$name]['user_mac'] = 'None Defined';
}
}
}
switch ($_POST['table']) {
case 't1load':
$arrMacs = $arrEntries;
$html = "<thead><tr><th>"._('Service')."</th><th>"._('Name')."</th><th>"._('Mac Address')."</th><th>"._('Enabled')."</th><th>"._('User Mac Address')."</th></tr></thead>";
$html .= "<tbody>";
ksort($arrMacs);
foreach($arrMacs as $systype => $m) {
foreach($m as $macaddr) {
if ($systype == "") continue;
$html .= "<tr id='row$systype'>";
$macs = "";
foreach($macaddr['interfaces'] as $intdetail)
{
$macs .= " {$intdetail['mac']}" ;
}
$html .= "<td>$systype</td>";
$selecttypename="enable;".$systype.";".$macaddr['name'];
$mactypename=htmlspecialchars("user_mac;".$systype.";".$macaddr['name']);
$mactypeid=htmlspecialchars("user_mac".$systype."".$macaddr['name']);
$user_mac_str = '<input type="text" name="'.$mactypename.'" id="'.$mactypeid.'" class="narrow" value="'.htmlspecialchars($macaddr['user_mac']).'" title="'._("random mac, you can supply your own").'" /><a><i onclick="maccreate(\''.$mactypeid.'\')" class="fa fa-refresh mac_generate" title="re-generate random mac address"></i></a>';
$html .= "<td>{$macaddr['name']}</td><td id=\"status$systype\">$macs</td><td>";
$html .="<select name='$selecttypename' class='audio narrow'>";
$html .= mk_option($macaddr["enable"] , "disable", _("Disabled"));
$html .= mk_option($macaddr["enable"] , "enable", _("Enabled"));
$html .= mk_option($macaddr["enable"] , "shutdown", _("Enabled and Shutdown"));
$html .= "</select></td><td>".$user_mac_str."</td></tr>";
$text = "";
}
}
if (count($arrMacs) < 1) {
$html .= "<tr id='row'>";
$html .= "<td></td><td></td><td>"._("No Entries")."</td><td></td><td></td></tr>";
}
$html .= "</tbody>";
$rtn = array();
$rtn['html'] = $html;
echo json_encode($rtn);
break;
case "macaddress":
$seed = 1;
$prefix = '62:00:00';
$prefix .=':'.macbyte(($seed * rand()) % 256).':'.macbyte(($seed * rand()) % 256).':'.macbyte(($seed * rand()) % 256);
echo json_encode(['mac' => $prefix]);
break;
}
?>

View File

@@ -1,180 +0,0 @@
#!/usr/bin/php
<?php
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/Custom.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
function getContainerStats($container, $option) {
exec("lxc-info " . $container, $content);
foreach($content as $index => $string) {
if (strpos($string, $option) !== FALSE)
return trim(explode(':', $string)[1]);
}
}
$mac = strtolower($argv[1]);
$libvirtd_running = is_file('/var/run/libvirt/libvirtd.pid') ;
$dockerd_running = is_file('/var/run/dockerd.pid');
$lxc_ls_exist = is_file('/usr/bin/lxc-ls');
#$RUNDOCKER = $RUNLXC = $RUNVM = true;
extract(parse_ini_file("/boot/config/wol.cfg")) ;
if (!isset($RUNLXC)) $RUNLXC = "y";
if (!isset($RUNVM)) $RUNVM = "y";
if (!isset($RUNDocker)) $RUNDocker = "y";
if (!isset($RUNSHUT)) $RUNSHUT = "n";
$arrEntries = [] ;
if ($libvirtd_running && $RUNVM == "y") {
$vms = $lv->get_domains();
sort($vms,SORT_NATURAL);
foreach($vms as $vm){
$arrEntries['VM'][$vm]['interfaces'] = $lv->get_nic_info($vm);
$arrEntries['VM'][$vm]['name'] = $vm;
}
}
if ($dockerd_running && $RUNDOCKER == "y") {
$DockerClient = new DockerClient();
$containers = $DockerClient->getDockerJSON("/containers/json?all=1");
foreach($containers as $container)
$arrEntries['Docker'][ substr($container["Names"][0],1) ] = [
'interfaces' => ['0' => ['mac' => isset($container["NetworkSettings"]["Networks"]["bridge"]["MacAddress"]) ? $container["NetworkSettings"]["Networks"]["bridge"]["MacAddress"]:""]],
'name' => substr($container["Names"][0],1),
'state' => $container["State"],
];
}
if ($lxc_ls_exist && $RUNLXC == "y") {
$lxc = explode("\n",shell_exec("lxc-ls -1")) ;
$lxcpath = trim(shell_exec("lxc-config lxc.lxcpath"));
foreach ($lxc as $lxcname) {
if ($lxcname == "") continue;
$values = explode("=",shell_exec("cat $lxcpath/$lxcname/config | grep 'hwaddr'"));
$arrEntries['LXC'][$lxcname]['interfaces'][0]['mac'] = trim($values[1]);
$arrEntries['LXC'][$lxcname]['name'] = $lxcname;
}
}
if (is_file("/boot/config/wol.json")) $user_mac = json_decode(file_get_contents("/boot/config/wol.json"),true); else $user_mac = [];
foreach($arrEntries as $typekey => $typedata)
{
foreach($typedata as $typeEntry){
$name=$typeEntry['name'];
if (isset($user_mac[$typekey][$name])) {
$name=$name;
$arrEntries[$typekey][$name]['enable'] = $user_mac[$typekey][$name]['enable'];
$arrEntries[$typekey][$name]['user_mac'] = strtolower($user_mac[$typekey][$name]['user_mac']);
} else {
$arrEntries[$typekey][$name]['enable'] = "enable";
$arrEntries[$typekey][$name]['user_mac'] = 'None Defined';
}
}
}
$mac_list=[];
foreach($arrEntries as $type => $detail)
{
foreach($detail as $name => $entryDetail)
{
foreach($entryDetail['interfaces'] as $interfaces)
{
$interfaces['mac'] = strtolower($interfaces['mac']);
if($interfaces['mac'] == "" && $entryDetail['user_mac'] == "None Defined") continue;
if (isset($entryDetail['state'])) $state = $entryDetail['state']; else $state = "";
if (isset($entryDetail['enable']) && !$entryDetail['enable'] ) $enable = false; else $enable = true;
if ($entryDetail['user_mac'] != "None Defined") {
$mac_list[$entryDetail['user_mac']] = [
'type' => $type,
'name' => $name,
'state' => $state,
'enable' => $entryDetail['enable'],
];
}
if ($interfaces['mac'] != "") {
$mac_list[$interfaces['mac']] = [
'type' => $type,
'name' => $name,
'state' => $state,
'enable' => $entryDetail['enable'],
];
}
}
}
}
$found = array_key_exists($mac,$mac_list);
if ($found && $mac_list[$mac]['enable'] != "disable") {
echo _("Found"). " " . $mac . " " .$mac_list[$mac]['type'] . " " . $mac_list[$mac]['name'];
switch ($mac_list[$mac]['type']) {
case "VM":
if ($libvirtd_running && $RUNVM == "y") {
$res = $lv->get_domain_by_name($mac_list[$mac]['name']);
$dom = $lv->domain_get_info($res);
$state = $lv->domain_state_translate($dom['state']);
switch ($state) {
case 'running':
if ($RUNSHUT == "y" && $mac_list[$mac]['enable'] == "shutdown") $lv->domain_shutdown("{$mac_list[$mac]['name']}");
break;
case 'paused':
case 'pmsuspended':
$lv->domain_resume("{$mac_list[$mac]['name']}");
break;
default:
$lv->domain_start("{$mac_list[$mac]['name']}");
}
}
break;
case "LXC":
if ($lxc_ls_exist && $RUNLXC == "y") {
$state = getContainerStats($mac_list[$mac]['name'], "State");
switch ($state) {
case 'RUNNING':
if ($RUNSHUT == "y" && $mac_list[$mac]['enable'] == "shutdown") shell_exec("lxc-stop {$mac_list[$mac]['name']}");
break;
case 'FROZEN':
shell_exec("lxc-unfreeze {$mac_list[$mac]['name']}");
break;
default:
shell_exec("lxc-start {$mac_list[$mac]['name']}");
}
}
break;
case "Docker":
if ($dockerd_running && $RUNDOCKER == "y") {
switch ($mac_list[$mac]['state']) {
case "running":
if ($RUNSHUT == "y" && $mac_list[$mac]['enable'] == "shutdown") shell_exec("docker stop {$mac_list[$mac]['name']}");
break;
case "exited":
case "created":
shell_exec("docker start {$mac_list[$mac]['name']}");
break;
case "paused":
shell_exec("docker unpause {$mac_list[$mac]['name']}");
}
}
break;
}
} else {
if ($mac_list[$mac]['enable'] == "disable") echo $mac . " " . _(" has not been actioned as set to disabled");
else echo _("Not Found"). " " . $mac . " " . _("ignoring or Maybe actions disabled for type(Docker/VM/LXC)");
}
?>

View File

@@ -149,4 +149,56 @@ function my_date($fmt, $time) {
function my_logger($message, $logger='webgui') {
exec('logger -t '.escapeshellarg($logger).' -- '.escapeshellarg($message));
}
// Original PHP code by Chirp Internet: www.chirpinternet.eu
// Please acknowledge use of this code by including this header.
// https://www.the-art-of-web.com/php/http-get-contents/
// Modified for Unraid
/**
* Fetches URL and returns content
* @param string $url The URL to fetch
* @param array $opts Array of options to pass to curl_setopt()
* @param array $getinfo Empty array passed by reference, will contain results of curl_getinfo and curl_error
* @return string|false $out The fetched content
*/
function http_get_contents(string $url, array $opts = [], array &$getinfo = NULL) {
$ch = curl_init();
if(isset($getinfo)) {
curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($ch, CURLOPT_TIMEOUT, 45);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_REFERER, "");
curl_setopt($ch, CURLOPT_FAILONERROR, true);
if(is_array($opts) && $opts) {
foreach($opts as $key => $val) {
curl_setopt($ch, $key, $val);
}
}
$out = curl_exec($ch);
if(isset($getinfo)) {
$getinfo = curl_getinfo($ch);
}
if (curl_errno($ch)) {
$msg = curl_error($ch) . " {$url}";
if(isset($getinfo)) {
$getinfo['error'] = $msg;
}
my_logger($msg, "http_get_contents");
}
return $out;
}
/**
* Detect network connectivity via Network Connectivity Status Indicator
* @return bool
*/
function check_network_connectivity(): bool {
$url = 'http://www.msftncsi.com/ncsi.txt';
$out = http_get_contents($url);
return ($out=="Microsoft NCSI");
}
?>

View File

@@ -39,4 +39,8 @@ if ($_SERVER['SCRIPT_NAME'] != '/login.php' && $_SERVER['SCRIPT_NAME'] != '/auth
if ($var['csrf_token'] != $_POST['csrf_token']) csrf_terminate("wrong");
unset($_POST['csrf_token']);
}
$proxy_cfg = (array)@parse_ini_file('/var/local/emhttp/proxy.ini',true);
putenv('http_proxy='.((array_key_exists('http_proxy', $proxy_cfg)) ? $proxy_cfg['http_proxy'] : ''));
putenv('https_proxy='.((array_key_exists('https_proxy', $proxy_cfg)) ? $proxy_cfg['https_proxy'] : ''));
putenv('no_proxy='.((array_key_exists('http_proxy', $proxy_cfg)) ? $proxy_cfg['no_proxy'] : ''));
?>

View File

@@ -13,6 +13,7 @@
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/Wrappers.php";
// add translations
$_SERVER['REQUEST_URI'] = 'settings';
@@ -425,11 +426,10 @@ case 'public':
$ip = _var($_POST,'#ip');
$v4 = _var($_POST,'#prot')!='6';
$v6 = _var($_POST,'#prot')!='';
$context = stream_context_create(['https'=>['timeout'=>12]]);
$int_ipv4 = $v4 ? (preg_match("/^$validIP4$/",$ip) ? $ip : (@dns_get_record($ip,DNS_A)[0]['ip'] ?: '')) : '';
$ext_ipv4 = $v4 ? (@file_get_contents('https://wanip4.unraid.net',false,$context) ?: '') : '';
$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'] ?: '')) : '';
$ext_ipv6 = $v6 ? (@file_get_contents('https://wanip6.unraid.net',false,$context) ?: '') : '';
$ext_ipv6 = $v6 ? (http_get_contents('https://wanip6.unraid.net') ?: '') : '';
echo "$int_ipv4;$ext_ipv4;$int_ipv6;$ext_ipv6";
break;
case 'addtunnel':

View File

@@ -78,7 +78,7 @@ function device_info(&$disk,$online) {
$help .= "<br>"._("Click to spin $action device");
}
$status = "<a class='info'><i ".($ctrl?"id='dev-$named' ":"")."class='fa fa-$orb orb $color-orb'$ctrl></i><span>$help</span></a>";
$link = (($parity || $data || $pool) && !str_contains($disk_status,'_NP')) || $name=='flash' || in_array($name,$pools) || $type=='New'
$link = (($parity || $data || $pool) && $disk_status!='DISK_NP') || $name=='flash' || in_array($name,$pools) || $type=='New'
? "<a href=\"".htmlspecialchars("/Main/Settings/$source?name=$name")."\">$fancy</a>"
: $fancy;
return $view.$status.$link;

View File

@@ -20,10 +20,12 @@ require_once "$docroot/webGui/include/publish.php";
while (true) {
$echo = shell_exec("$notify get");
$md5_new = md5($echo,true);
if ($md5_new !== $md5_old) {
publish('notify', $echo);
$md5_old = $md5_new;
if ( $echo ) {
$md5_new = md5($echo,true);
if ($md5_new !== $md5_old) {
publish('notify', $echo);
$md5_old = $md5_new;
}
}
sleep(3);
}

View File

@@ -0,0 +1,91 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2024, Lime Technology
* Copyright 2024-2024, Simon Fairweather.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = '/usr/local/emhttp';
$varroot = '/var/local/emhttp';
$md5_old = -1;
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/publish.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
global $vmusagestats;
exec("/etc/rc.d/rc.libvirt status >/dev/null",$dummy,$libvirtd);
$libvirtd = $libvirtd==0;
if (!$libvirtd) return;
extract(parse_plugin_cfg('dynamix',true));
get_vm_usage_stats();
sleep(1);
// add translations
$_SERVER['REQUEST_URI'] = 'dashboard';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
// remember current language
$locale_init = $locale;
function update_translation($locale) {
global $docroot,$language;
$language = [];
if ($locale) {
$text = "$docroot/languages/$locale/translations.txt";
if (file_exists($text)) {
$store = "$docroot/languages/$locale/translations.dot";
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = unserialize(file_get_contents($store));
}
$text = "$docroot/languages/$locale/dashboard.txt";
if (file_exists($text)) {
$store = "$docroot/languages/$locale/dashboard.dot";
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = array_merge($language,unserialize(file_get_contents($store)));
}
}
}
$domain_cfgfile = "/boot/config/domain.cfg";
$domain_cfg = parse_ini_file($domain_cfgfile);
if (isset($domain_cfg['USAGE']) && $domain_cfg['USAGE'] != 'Y' ) return;
if (!isset($domain_cfg['USAGETIMER'])) $timer = 3 ; else $timer = $domain_cfg['USAGETIMER'];
while (true) {
extract(parse_plugin_cfg('dynamix',true));
if (_var($display,'locale') != $locale_init) {
$locale_init = _var($display,'locale');
update_translation($locale_init);
}
get_vm_usage_stats();
$echo = [];
foreach ($vmusagestats as $vm => $vmdata) {
if ($vmdata['state'] == 1) {
$vmencode = str_replace(" "," ",$vm);
$vmencode = $lv->domain_get_uuid($vm);
$echo[$vmencode ]['gcpu'] = "<span class='advanced'>"._("Guest CPU").": <span class='cpug-".$vm."'>".$vmdata['cpuguest']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuguest']."%;'></span><span></span></div></span>";
$echo[$vmencode ]['hcpu'] = "<span class='advanced'>"._("Host CPU").": <span class='cpug-".$vm."'>".$vmdata['cpuhost']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuhost']."%;'></span><span></span></div></span>";
$echo[$vmencode ]['mem'] .= "<span>Mem: ".my_scale($vmdata['mem'],$unit)."$unit / ".my_scale($vmdata['maxmem'],$unit)."$unit</span>";
$echo[$vmencode ]['disk'] .= "<span>Disk: "._("Rd").": ".my_scale($vmdata['rdrate'],$unit)."$unit/s "._("Wr").": ".my_scale($vmdata['wrrate'],$unit)."$unit/s</span>";
$echo[$vmencode ]['net'] .= "<span>Net: "._("RX").": ".my_scale($vmdata['rxrate'],$unit)."$unit/s "._("TX").": ".my_scale($vmdata['txrate'],$unit)."$unit/s</span>";
}
}
$echo = json_encode($echo);
$md5_new = md5($echo,true);
if ($md5_new !== $md5_old) {
$md5_old = publish('vm_dashusage',$echo)!==false ? $md5_new : -1;
}
sleep($timer);
}
?>

View File

@@ -1,93 +0,0 @@
#!/bin/bash
#
# script: WOL_action
#
# Startup script for unraidwold
#
# Simon Fairweather - Initial Script October 2023
DAEMON="unraidwold"
BINARY="/usr/local/bin/unraidwold"
# run & log functions
. /etc/rc.d/rc.runlog
. /boot/config/wol.cfg
unraidwold_running(){
sleep 0.1
ps axc | grep -q ' unraidwold'
}
unraidwold_start(){
log "Starting $DAEMON..."
local REPLY
if unraidwold_running; then
REPLY="Already started"
else
nohup $BINARY $1 $2 $3 $4 $5 $6 > /dev/null &
fi
log "$DAEMON... $REPLY."
}
unraidwold_stop(){
log "Stopping $DAEMON..."
local REPLY
if ! unraidwold_running; then
REPLY="Already stopped"
else
killall -TERM $DAEMON
if ! unraidwold_running; then REPLY="Stopped"; else REPLY="Failed"; fi
fi
log "$DAEMON... $REPLY."
}
unraidwold_status(){
if unraidwold_running; then
echo "$DAEMON is currently running."
else
echo "$DAEMON is not running."
exit 1
fi
}
unraidwold_load()
{
if unraidwold_running; then
unraidwold_stop
fi
sleep 1
if [ "$WOLENABLED" == "yes" ]; then
interfacemode=""
logoptions=""
if [ "$IFMODE" == "y" ]; then
interfacemode="--promiscuous"
fi
if [ "$LOGFILE" != "syslog" ]; then
logoptions="--log $LOGFILE"
fi
unraidwold_start --interface $INTERFACE $interfacemode $logoptions
fi
}
case "$1" in
'start')
unraidwold_start $2 $3 $4 $5 $6
;;
'stop')
unraidwold_stop
;;
'status')
unraidwold_status
;;
'load')
unraidwold_load
;;
*)
echo "Usage: $BDAEMON start|stop|restart|status"
exit 1
esac
exit 0

View File

@@ -25,6 +25,7 @@ $zip = $all ? ($argv[2]??'') : ($argv[1]??'');
$cli = empty($zip);
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/Wrappers.php";
$folders = ['/boot','/boot/config','/boot/config/plugins','/boot/syslinux','/var/log','/var/log/plugins','/boot/extra','/var/log/packages','/var/lib/pkgtools/packages','/tmp'];
@@ -115,16 +116,6 @@ function maskIP($file) {
// anonymize full IPv6 addresses
run("sed -ri 's/([\"\[ ]([0-9a-f]{1,4}:){4})(([0-9a-f]{1,4}:){3}|:)([0-9a-f]{1,4})([/\" .]|$)/\\1XXXX:XXXX:XXXX:\\5\\6/g' ".escapeshellarg($file)." 2>/dev/null");
}
function cache_only($disk) {
return _var($disk,'type')=='Cache';
}
function cache_filter($disks) {
return array_filter($disks,'cache_only');
}
function pools_filter($disks) {
return array_unique(array_map('prefix',array_keys(cache_filter($disks))));
}
function download_url($url, $path="", $bg=false, $timeout=15) {
$ch = curl_init();
curl_setopt_array($ch,[
@@ -661,6 +652,13 @@ if (file_exists($dhcplog)) {
if (!$all) maskIP($log);
}
// copy phplog
$phplog = "/var/log/phplog";
if (file_exists($phplog)) {
$log = "/$diag/logs/phplog.txt";
run("todos <$phplog >".escapeshellarg($log));
}
// copy graphql-api.log
$graphql = "/var/log/graphql-api.log";
if (file_exists($graphql)) {

View File

@@ -35,7 +35,7 @@ if [[ -n $local_server ]]; then
sed -ri "\$a\\\$InputUDPServerBindRuleset remote\n\\\$UDPServerRun ${server_port:-514}" $ETC
[[ $server_protocol == udp ]] && sed -ri 's/^(\$ModLoad imtcp)/#\1/;/^\$InputTCPServerBindRuleset remote$/d;/^\$InputTCPServerRun [0-9]+$/d' $ETC
fi
sed -ri "/^\\\$template remote,.*$/d;/^#\\\$UDPServerRun [0-9]+.*$/a\\\$template remote,\"${server_folder:-/mnt/user/system}/syslog-%FROMHOST-IP%.log\"" $ETC
sed -ri "/^\\\$template remote,.*$/d;/^#\\\$UDPServerRun [0-9]+.*$/a\\\$template remote,\"${server_folder:-/mnt/user/system}/${server_filename:-syslog-%FROMHOST-IP%.log}\"" $ETC
else
sed -ri '/^\$RuleSet remote$/d;/^\$FileOwner nobody$/d;/^\$FileGroup users$/d;/^\$FileCreateMode 06[46][46]$/d;/^\$IncludeConfig \/etc\/rsyslog\.d\/\*\.conf # remote$/d;/^\*\.\* \?remote$/d;/^\$template remote,".*"$/d;/^\$Input(TCP|UDP)ServerBindRuleset remote$/d;/^\$(InputTCP|UDP)ServerRun [0-9]+$/d;s/^#?\$(ModLoad imtcp|ModLoad imudp)/#\$\1/' $ETC
fi

View File

@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, 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,
@@ -21,6 +21,7 @@ $disks = (array)@parse_ini_file("/var/local/emhttp/disks.ini",true);
require_once "$docroot/webGui/include/CustomMerge.php";
$script = "$docroot/webGui/scripts/notify";
// this command will set the $notify array
extract(parse_plugin_cfg("dynamix",true));
$output = _var($notify,'report');
$server = strtoupper(_var($var,'NAME','tower'));
@@ -178,7 +179,8 @@ $warn = ($error0 || $error3) ? "alert" : (($error1 || $error2) ? "warning" : "no
$stat = $warn=="normal" ? "[PASS]" : "[FAIL]";
$info = "Array has $size disk".($size==1 ? "" : "s").($parity ? " ({$word}parity".($pools ? " & pools)" : ")") : ($pools ? " ({$word}pools)" : ""));
$message = implode('\n', $data);
exec("$script -s ".escapeshellarg("Notice [$server] - array health report $stat")." -d ".escapeshellarg("$info")." -m ".escapeshellarg("$message")." -i ".escapeshellarg("$warn $output")." -l '/Main'");
$subject = "Notice [$server] - array health report $stat";
exec("$script -s ".escapeshellarg($subject)." -d ".escapeshellarg("$info")." -m ".escapeshellarg("$message")." -i ".escapeshellarg("$warn $output")." -l '/Main'");
exit(0);
?>

View File

@@ -1,11 +0,0 @@
table#t1{margin-top:0;font-family:clear-sans}
table#t1 thead tr th{font-weight:bold}
table#t1 tbody tr td{padding:4px 20px 4px 0;margin:0;text-align:left;white-space:normal}
table#t1 tbody tr td:nth-child(1){width:10%}
table#t1 tbody tr td:nth-child(2){width:30%}
table#t1 tbody tr td:nth-child(3){width:15%}
table#t1 tbody tr td:nth-child(4){width:20%}
table#t1 tbody tr td:nth-child(5){width:25%;padding-right:0}
table.t1.tablesorter .filtered{display:none}
.tablesorter-header-inner{font-family:clear-sans;font-weight:bold}
#macform .mac_generate{cursor:pointer;margin-left:-5px;color:#08C;font-size:1.3rem;transform:translate(0px, 2px)}

View File

@@ -122,7 +122,9 @@ table.unraid thead tr:first-child>td{font-size:1.2rem;text-transform:uppercase;l
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(0,0,0,0.05)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.legacy{table-layout:auto!important}
table.legacy thead td{line-height:normal;height:auto;padding:7px 0}
table.legacy tbody td{line-height:normal;height:auto;padding:5px 0}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}

View File

@@ -120,7 +120,9 @@ table.unraid tbody tr:nth-child(even){background-color:#212121}
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(255,255,255,0.1)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.legacy{table-layout:auto!important}
table.legacy thead td{line-height:normal;height:auto;padding:7px 0}
table.legacy tbody td{line-height:normal;height:auto;padding:5px 0}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}

View File

@@ -122,7 +122,9 @@ table.unraid thead tr:first-child>td{font-size:1.2rem;text-transform:uppercase;l
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(255,255,255,0.05)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.legacy{table-layout:auto!important}
table.legacy thead td{line-height:normal;height:auto;padding:7px 0}
table.legacy tbody td{line-height:normal;height:auto;padding:5px 0}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}

View File

@@ -120,7 +120,9 @@ table.unraid tbody tr:nth-child(even){background-color:#ededed}
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(0,0,0,0.1)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.legacy{table-layout:auto!important}
table.legacy thead td{line-height:normal;height:auto;padding:7px 0}
table.legacy tbody td{line-height:normal;height:auto;padding:5px 0}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}

View File

@@ -1,4 +1,4 @@
.jGrowl{position:fixed;font-size:1.3rem}
.jGrowl{position:fixed;font-size:1.3rem;z-index:10001}
.jGrowl.top-left{left:10px;top:90px}
.jGrowl.top-right{right:10px;top:90px}
.jGrowl.bottom-left{left:10px;bottom:24px}

View File

@@ -1,4 +1,4 @@
.jGrowl{position:fixed;font-size:1.3rem}
.jGrowl{position:fixed;font-size:1.3rem;z-index:10001}
.jGrowl.top-left{left:10px;top:130px}
.jGrowl.top-right{right:10px;top:130px}
.jGrowl.bottom-left{left:10px;bottom:24px}

View File

@@ -1,4 +1,4 @@
.jGrowl{position:fixed;font-size:1.3rem}
.jGrowl{position:fixed;font-size:1.3rem;z-index:10001}
.jGrowl.top-left{left:10px;top:90px}
.jGrowl.top-right{right:10px;top:90px}
.jGrowl.bottom-left{left:10px;bottom:24px}

View File

@@ -1,4 +1,4 @@
.jGrowl{position:fixed;font-size:1.3rem}
.jGrowl{position:fixed;font-size:1.3rem;z-index:10001}
.jGrowl.top-left{left:10px;top:130px}
.jGrowl.top-right{right:10px;top:130px}
.jGrowl.bottom-left{left:10px;bottom:24px}

View File

@@ -50,7 +50,7 @@ fi
# initrd has already done so):
if [[ -d /run ]]; then
if ! /bin/grep -wq "tmpfs /run tmpfs" /proc/mounts; then
/sbin/mount -v -n -t tmpfs tmpfs /run -o mode=0755,size=32M,nodev,nosuid,noexec
/sbin/mount -v -n -t tmpfs tmpfs /run -o mode=0755,size=128M,nodev,nosuid,noexec
fi
fi

View File

@@ -42,7 +42,8 @@ SYSTEM="/sys/devices/system/cpu"
# provides power savings on Intel processors while avoiding the ramp-up lag
# present when using the powersave governor (which is the default if ondemand
# is requested on these machines):
if [[ $(cat $SYSTEM/cpu0/cpufreq/scaling_driver 2>/dev/null) == intel_pstate ]]; then
if [[ $(cat $SYSTEM/cpu0/cpufreq/scaling_driver 2>/dev/null) == intel_pstate ||
$(cat $SYSTEM/cpu0/cpufreq/scaling_driver 2>/dev/null) == amd-pstate-epp ]]; then
SCALING_GOVERNOR="performance"
fi

View File

@@ -501,7 +501,7 @@ docker_container_start(){
log "$CONTAINER: $OUT" &
else
run container_add_route $CONTAINER
log "$CONTAINER: started succesfully!" &
log "$CONTAINER: started successfully!" &
if [[ $WAIT -gt 0 ]]; then
log "$CONTAINER: wait $WAIT seconds" &
sleep $WAIT
@@ -566,7 +566,7 @@ docker_service_stop(){
run ip link del docker0
fi
REPLY="Stopped"
# signal succesful stop
# signal successful stop
TIMER=-1
else
# show waiting message

View File

@@ -69,7 +69,10 @@
# Adapted by Bergware for use in Unraid OS - December 2023
# - remove leading zeros in IPv4 and IPv6 addresses
# Bergware - modified for Unraid OS, December 2023
# Adapted by Bergware for use in Unraid OS - February 2024
# - revised bond interface creation for linux kernel 6.6.14 and later point releases
# Bergware - modified for Unraid OS, February 2024
###########
# LOGGING #
@@ -175,8 +178,8 @@ bond_up(){
for BONDIF in ${BONDNICS[$i]}; do
if [[ -e $SYSTEM/$BONDIF ]]; then
[[ -z $PRIMARY ]] && PRIMARY=$BONDIF
run ip link set $BONDIF down
run ip link set $BONDIF master ${BONDNAME[$i]} up type bond_slave
run ip link set $BONDIF up
run ip link set $BONDIF master ${BONDNAME[$i]} down type bond_slave
else
[[ $DEBUG_ETH_UP == yes ]] && log "interface $BONDIF not present, can't add to ${BONDNAME[$i]}"
fi

View File

@@ -237,6 +237,7 @@ libvirtd_start(){
check_processor
modprobe -a $MODULE $MODULES
echo 0 > /sys/module/kvm/parameters/report_ignored_msrs
libvirtd -d -l $LIBVIRTD_OPTS
log "$DAEMON... Started."
}

View File

@@ -14,6 +14,28 @@
# run & log functions
. /etc/rc.d/rc.runlog
# import CA proxy
UnProxy=/boot/config/proxy.cfg
CAProxy=/boot/config/plugins/community.applications/proxy.cfg
if [[ -f "${CAProxy}" && ! -f "${UnProxy}" ]]; then
proxy=
port=
. "${CAProxy}"
# proxy and port were set by the previous command
if [[ -n "${proxy}" && -n "${port}" ]]; then
cat << EOF > "${UnProxy}"
proxy_active="1"
proxy_url_1="${proxy}:${port}"
proxy_name_1="Imported from CA"
EOF
fi
mv "${CAProxy}" "${CAProxy}.bak"
fi
# load proxy environment vars so it is used for plugin updates and the go script
/usr/local/sbin/set_proxy
[[ -x /etc/profile.d/proxy.sh ]] && . /etc/profile.d/proxy.sh
# irqbalance daemon distributes interrupts over processors and cores
# if [[ -x /usr/sbin/irqbalance ]]; then
# /usr/sbin/irqbalance
@@ -69,7 +91,7 @@ rm -f /boot/plugins/unRAIDServer.plg
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"
OBSOLETE="vfio.pci dynamix.wireguard dynamix.ssd.trim dynamix.file.manager gui.search unlimited-width proxy.editor"
for PLUGIN in $OBSOLETE; do
if [[ -e $CONFIG/plugins/$PLUGIN.plg ]]; then
log "moving obsolete plugin $PLUGIN.plg to $CONFIG/plugins-error"
@@ -166,6 +188,20 @@ if [[ $PERSISTENT_BASH_HISTORY == 1 ]]; then
ln -s /boot/config/history/bash_history /root/.bash_history
fi
# cleanup the 'go' script
# delete emhttp line with trailing & (could also have leading env vars), add new line with just call to emhttp
GOTEST1='^[^#]*/usr/local/sbin/emhttp.*&$'
if grep -q "$GOTEST1" $CONFIG/go; then
cp $CONFIG/go $CONFIG/go-
sed -i "s@$GOTEST1@/usr/local/sbin/emhttp@g" $CONFIG/go
fi
# delete lines added by ProxyEditor
GOTEST2='# Added by ProxyEditor'
if grep -q "$GOTEST2" $CONFIG/go; then
[[ ! -f $CONFIG/go- ]] && cp $CONFIG/go $CONFIG/go-
sed -i "/$GOTEST2/d" $CONFIG/go
fi
# Invoke the 'go' script
if [[ -f $CONFIG/go ]]; then
log "Starting go script"

View File

@@ -686,8 +686,6 @@ nginx_reload(){
if nginx_check; then
log "Reloading $DAEMON configuration..."
kill -HUP $(cat $PID)
# update DNS
php -f /usr/local/emhttp/webGui/include/UpdateDNS.php
sleep 3
else
log "Invalid configuration, $DAEMON not reloaded"
@@ -705,8 +703,6 @@ nginx_renew(){
build_ssl
# start unconditionally
$NGINX -c $CONF 2>/dev/null
# update DNS
php -f /usr/local/emhttp/webGui/include/UpdateDNS.php
}
nginx_update(){

View File

@@ -1,7 +1,9 @@
#!/bin/bash
DISABLE="no"
source /boot/config/domain.cfg
if [ $DISABLE == "yes" ]
then
exit ;
printf '\n%s\n' "Start/autostart is disabled in VM settings." >&2 ## Send message to stderr.
exit 1 ;
fi
eval exec /usr/bin/qemu-system-x86_64 $(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/qemu.php "$@")

View File

@@ -154,3 +154,132 @@ if ($proxy_active != "0") {
remove_proxy_from_generated_files();
}
?>
=======
#!/bin/bash
#
# script: set_proxy
#
# Copyright 2005-2024, Lime Technology
#
# call this script (/usr/local/sbin/set_proxy) when /boot/config/proxy.cfg changes
# proxy.cfg is the source of all proxy information
CFG=/boot/config/proxy.cfg
# these files are generated by this script based on the data in proxy.cfg
PROXY_SH=/etc/profile.d/proxy.sh
PROXY_INI=/usr/local/emhttp/state/proxy.ini
# random file extension for atomic writes
RND=$RANDOM
VERBOSE=
[[ "$1" == "-v" ]] && VERBOSE=1
# global vars defined later
proxy_active=
proxy_url=
no_proxy=
# write environment variables to /etc/profile.d/proxy.sh
set_proxy_sh() {
local FILE
FILE="${PROXY_SH}"
cat <<EOF >"${FILE}.${RND}"
#!/bin/bash
# Do not edit. This file is autogenerated by /usr/local/sbin/set_proxy
export http_proxy="${proxy_url}"
export https_proxy="${proxy_url}"
export no_proxy="${no_proxy}"
EOF
chmod 755 "${FILE}.${RND}"
mv "${FILE}.${RND}" "${FILE}"
}
# clear environment variables from /etc/profile.d/proxy.sh
unset_proxy_sh() {
local FILE
FILE="${PROXY_SH}"
cat <<EOF >"${FILE}.${RND}"
#!/bin/bash
# Do not edit. This file is autogenerated by /usr/local/sbin/set_proxy
unset http_proxy
unset https_proxy
unset no_proxy
EOF
chmod 755 "${FILE}.${RND}"
mv "${FILE}.${RND}" "${FILE}"
}
set_proxy_ini() {
local FILE
FILE="${PROXY_INI}"
cat <<EOF >"${FILE}.${RND}"
http_proxy="${proxy_url}"
https_proxy="${proxy_url}"
no_proxy="${no_proxy}"
EOF
chmod 644 "${FILE}.${RND}"
mv "${FILE}.${RND}" "${FILE}"
}
# clear /usr/local/emhttp/state/proxy.ini
unset_proxy_ini() {
# vars are empty now, call set_proxy_ini to add empty vars to proxy.ini
set_proxy_ini
}
# generate proxy files
add_proxy_to_generated_files_and_exit() {
echo "generating proxy files"
set_proxy_sh
set_proxy_ini
[[ -n "${VERBOSE}" ]] && display_generated_files
exit 0
}
# remove proxy info from all generated files and exit
remove_proxy_from_generated_files_and_exit() {
echo "removing proxy info from generated files"
unset_proxy_sh
unset_proxy_ini
[[ -n "${VERBOSE}" ]] && display_generated_files
exit 0
}
# when verbose mode enabled
display_generated_files() {
echo
display_generated_file "${PROXY_SH}"
display_generated_file "${PROXY_INI}"
}
# when verbose mode enabled
display_generated_file() {
local FILE
FILE=$1
echo "${FILE}"
[[ -f "${FILE}" ]] && cat "${FILE}" || echo "file does not exist"
echo
}
# if no proxy config, remove proxy info from all generated files and exit
[[ ! -f "${CFG}" ]] && remove_proxy_from_generated_files_and_exit
# read current proxy information from /boot/config/proxy.cfg
# shellcheck source=/dev/null
. <(/usr/bin/fromdos <"${CFG}")
# determine proxy information
proxy_url_var="proxy_url_${proxy_active:=}"
proxy_url="${!proxy_url_var}"
# if no active proxies, remove proxy info from all generated files and exit
if [[ "${proxy_active:=0}" == "0" || "${proxy_url}" == "" ]]; then
remove_proxy_from_generated_files_and_exit
fi
# proxies are defined, write generated files
no_proxy="127.0.0.1,localhost"
add_proxy_to_generated_files_and_exit

2
sbin/virtiofsd Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
eval exec /usr/bin/virtiofsd $(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/virtiofsd.php "$@")