mirror of
https://github.com/unraid/webgui.git
synced 2026-01-06 01:29:54 -06:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
21
emhttp/plugins/dynamix/scripts/agent
Executable file
21
emhttp/plugins/dynamix/scripts/agent
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
run="/boot/config/plugins/dynamix/notifications/agents"
|
||||
idle="/boot/config/plugins/dynamix/notifications/agents-disabled"
|
||||
|
||||
case $1 in
|
||||
enable)
|
||||
[[ ! -d $run ]] && mkdir -p $run
|
||||
[[ -f $idle/$2 ]] && mv -f $idle/$2 $run/$2
|
||||
;;
|
||||
disable)
|
||||
[[ ! -d $idle ]] && mkdir -p $idle
|
||||
[[ -f $run/$2 ]] && mv -f $run/$2 $idle/$2
|
||||
;;
|
||||
delete)
|
||||
[[ -f $run/$2 ]] && rm -f $run/$2
|
||||
;;
|
||||
test)
|
||||
[[ -f $run/$2 ]] && /bin/bash $run/$2
|
||||
;;
|
||||
esac
|
||||
14
emhttp/plugins/dynamix/scripts/bootmode
Executable file
14
emhttp/plugins/dynamix/scripts/bootmode
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# usage: bootmode 0|1
|
||||
# 0 = legacy mode
|
||||
# 1 = UEFI mode
|
||||
|
||||
cd /boot
|
||||
case $1 in
|
||||
0) OLD=EFI; NEW=EFI-; ;;
|
||||
1) OLD=EFI-; NEW=EFI; ;;
|
||||
*) OLD=EFI; NEW=EFI-; ;;
|
||||
esac
|
||||
if [[ -d $OLD && ! -d $NEW ]]; then
|
||||
mv $OLD $NEW
|
||||
fi
|
||||
19
emhttp/plugins/dynamix/scripts/btrfs_balance
Executable file
19
emhttp/plugins/dynamix/scripts/btrfs_balance
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
# btrfs_balance start <dev> <options>
|
||||
# btrfs_balance status <dev>
|
||||
# btrfs_balance cancel <dev>
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
options=$3
|
||||
[[ -z "${options// }" ]] && options="--full-balance"
|
||||
exec /sbin/btrfs balance start $options $2 &>/dev/null &
|
||||
;;
|
||||
'status')
|
||||
/sbin/btrfs balance status $2
|
||||
/sbin/btrfs balance status $2 | grep -q running
|
||||
;;
|
||||
'cancel')
|
||||
/sbin/btrfs balance cancel $2 &>/dev/null
|
||||
;;
|
||||
esac
|
||||
28
emhttp/plugins/dynamix/scripts/btrfs_check
Executable file
28
emhttp/plugins/dynamix/scripts/btrfs_check
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# btrfs_check start <dev> <id> <options>
|
||||
# btrfs_check status <dev> <id>
|
||||
# btrfs_check cancel <dev> <id>
|
||||
|
||||
# by default btrfs-check outputs to /var/lib/btrfs
|
||||
mkdir -p /var/lib/btrfs
|
||||
case "$1" in
|
||||
'start')
|
||||
exec /sbin/btrfs check $4 $2 &> /var/lib/btrfs/check.status.$3 &
|
||||
;;
|
||||
'status')
|
||||
if [ -f /var/lib/btrfs/check.status.$3 ]; then
|
||||
cat /var/lib/btrfs/check.status.$3
|
||||
else
|
||||
echo "Not available"
|
||||
fi;
|
||||
# establish retval of this script: 0 running, 1 not running
|
||||
pgrep -f "/sbin/btrfs check .*$2" >/dev/null
|
||||
;;
|
||||
'cancel')
|
||||
pkill -f "/sbin/btrfs_check.*$2"
|
||||
while pgrep -f "/sbin/btrfs check .*$2" >/dev/null ; do
|
||||
sleep 1
|
||||
done
|
||||
echo "Cancelled" >> /var/lib/btrfs/check.status.$3
|
||||
;;
|
||||
esac
|
||||
23
emhttp/plugins/dynamix/scripts/btrfs_scrub
Executable file
23
emhttp/plugins/dynamix/scripts/btrfs_scrub
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# btrfs_scrub start <dev> <options>
|
||||
# btrfs_scrub status <dev>
|
||||
# btrfs_scrub cancel <dev>
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
# one might wonder: why exec the scrub command, passing -B and put in background, vs just
|
||||
# executing scrub without -B and without explicit background? It's because it handles case
|
||||
# where user adds -B themselves as one of the options, which would hang webGui process until
|
||||
# scrub completes, which would not be good. (btrfs balance does not have -B option btw)
|
||||
exec /sbin/btrfs scrub start -B $3 $2 &>/dev/null &
|
||||
;;
|
||||
'status')
|
||||
# first output whatever the status is to stdout
|
||||
/sbin/btrfs scrub status $2
|
||||
# establish retval of this script: 0 running, 1 not running
|
||||
/sbin/btrfs scrub status $2 | grep -q running
|
||||
;;
|
||||
'cancel')
|
||||
/sbin/btrfs scrub cancel $2 &>/dev/null
|
||||
;;
|
||||
esac
|
||||
696
emhttp/plugins/dynamix/scripts/diagnostics
Executable file
696
emhttp/plugins/dynamix/scripts/diagnostics
Executable file
@@ -0,0 +1,696 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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 called from webgui:
|
||||
// anonymous: diagnostics '/usr/local/emhttp/tower-diagnostics-20230130-1546.zip'
|
||||
// all data: diagnostics -a '/usr/local/emhttp/tower-diagnostics-20230130-1546.zip'
|
||||
// if called from cli:
|
||||
// anonymous: diagnostics
|
||||
// all data: diagnostics -a
|
||||
$opt = getopt('a',['all']);
|
||||
$all = isset($opt['a']) || isset($opt['all']);
|
||||
$zip = $all ? ($argv[2]??'') : ($argv[1]??'');
|
||||
$cli = empty($zip);
|
||||
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
$folders = ['/boot','/boot/config','/boot/config/plugins','/boot/syslinux','/var/log','/var/log/plugins','/boot/extra','/var/log/packages','/var/lib/pkgtools/packages','/tmp'];
|
||||
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
|
||||
// global variables
|
||||
$path = "/var/local/emhttp";
|
||||
$var = @parse_ini_file("$path/var.ini") ?: [];
|
||||
$disks = @parse_ini_file("$path/disks.ini",true) ?: [];
|
||||
$pools = pools_filter($disks);
|
||||
|
||||
require_once "$docroot/webGui/include/CustomMerge.php";
|
||||
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/diagnostics?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
function run($cmd, &$save=null) {
|
||||
global $cli,$diag;
|
||||
// output command for display
|
||||
write($cmd);
|
||||
// execute command with timeout of 30s
|
||||
exec("timeout -s9 30 $cmd", $save);
|
||||
return implode("\n",$save);
|
||||
}
|
||||
function newline($file) {
|
||||
exec("echo|todos >>".escapeshellarg($file));
|
||||
}
|
||||
function shareDisks($share) {
|
||||
return str_replace(',',', ',exec("shopt -s dotglob; getfattr --no-dereference --absolute-names --only-values -n system.LOCATIONS ".escapeshellarg("/mnt/user/$share")." 2>/dev/null") ?: "");
|
||||
}
|
||||
function anonymize($text, $select) {
|
||||
global $all,$var,$pools,$customShares;
|
||||
if ($all) return $text;
|
||||
switch ($select) {
|
||||
case 1:
|
||||
// remove any stray references to the GUID that may wind up in .ini files (notably Unassigned Devices)
|
||||
$guid = explode('-',_var($var,'regGUID'));
|
||||
$text = str_replace(end($guid),"...",$text);
|
||||
$rows = explode("\n", $text);
|
||||
$pool = implode('|',$pools) ?: 'cache';
|
||||
$regex = "/\b((disk|$pool|parity|cpu|eth|dev)[0-9]+)|(smart|flash|flashbackup|$pool|parity|cpu{$customShares})\b/";
|
||||
foreach ($rows as &$row) {
|
||||
if (!preg_match($regex, $row)) {
|
||||
$row = preg_replace("/^(\s*\[\S).*(\S\])( => Array)$/","$1..$2$3",$row);
|
||||
$row = preg_replace("/^(\s*\[(cachePool|name|nameOrig|comment|flashGUID|regGUID|regTo|readList|writeList|csrf_token|NGINX_DEFAULTURL|NGINX_CERTNAME|NGINX_LANFQDN|NGINX_WANFQDN|NGINX_WANIP)\] => \S).*(\S)$/","$1..$3",$row);
|
||||
}
|
||||
}
|
||||
return implode("\n", $rows);
|
||||
case 2:
|
||||
// anonymize share configuration name - make it unique
|
||||
$name = basename($text,'.cfg');
|
||||
if (!in_array($name,$pools)) {
|
||||
$len = strlen($name);
|
||||
if ($len>2) {
|
||||
$dash = str_repeat('-',$len-2);
|
||||
$name = preg_replace("/^(\S).*(\S)/","$1$dash$2",$name);
|
||||
$i = 1;
|
||||
while (file_exists(dirname($text)."/$name.cfg")) {$name = substr($name,0,$len)." ($i)"; $i++;}
|
||||
}
|
||||
}
|
||||
return dirname($text)."/$name.cfg";
|
||||
}
|
||||
}
|
||||
function prefix($key) {
|
||||
return preg_replace('/\d+$/','',$key);
|
||||
}
|
||||
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,[
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_FRESH_CONNECT => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_TIMEOUT => $timeout,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true
|
||||
]);
|
||||
$out = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
if ($path) file_put_contents($path,$out);
|
||||
return $out ?: false;
|
||||
}
|
||||
function geturls_certdetails($file, $hostname, $ip="") {
|
||||
// called by the geturls() function
|
||||
// best to ensure the file exists before calling this function
|
||||
if (!file_exists($file)) return ['', '', ''];
|
||||
// read the cert
|
||||
$data = null;
|
||||
exec("/usr/bin/openssl x509 -noout -subject -nameopt multiline -in ".escapeshellarg($file), $data);
|
||||
$data = implode("\n", $data);
|
||||
// determine cn
|
||||
preg_match('/ *commonName *= (.*)/', $data, $matches);
|
||||
$cn = trim($matches[1]??'');
|
||||
if (strpos($cn, ".myunraid.net") !== false) {
|
||||
$type = 'myunraid.net';
|
||||
$iphost = str_replace('.','-',$ip);
|
||||
// anonymize ip portion of hostname if not a private ip
|
||||
$priv_iphost = (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) ? "ipaddress" : $iphost;
|
||||
// anonymize myunraid.net hash
|
||||
$cn_priv = preg_replace('/\*\.([a-f0-9]*)\.myunraid\.net/', "{$priv_iphost}.hash.myunraid.net", $cn);
|
||||
// replace wildcard with ip
|
||||
$cn = str_replace('*', $iphost, $cn);
|
||||
} elseif (strpos($cn, ".unraid.net") !== false) {
|
||||
$type = 'unraid.net';
|
||||
// anonymize unraid.net hash
|
||||
$cn_priv = preg_replace('/.*\.unraid\.net/', 'hash.unraid.net', $cn);
|
||||
} else {
|
||||
// replace wildcard with hostname
|
||||
$cn = str_replace('*', $hostname, $cn);
|
||||
$cn_priv = $cn;
|
||||
if (strpos($data, "Self-signed") !== false){
|
||||
$type = 'self-signed';
|
||||
} else {
|
||||
$type = 'user-provided';
|
||||
}
|
||||
}
|
||||
return [$cn, $cn_priv, $type];
|
||||
}
|
||||
function geturls_checkhost($host, $hostpriv, $expectedip, $dnsserver) {
|
||||
// called by the geturls() function
|
||||
// dns lookups will fail if there is no TLD or if it is ".local", so skip it
|
||||
if (strpos($host, '.') === false || strpos($host, '.local') !== false) return '';
|
||||
$result = @dns_get_record($host, DNS_A);
|
||||
$ip = ($result) ? $result[0]['ip'] : '';
|
||||
if ($ip == '') return " ERROR: When using DNS server {$dnsserver}, the host {$hostpriv} does not resolve.\n";
|
||||
if ($ip != $expectedip) {
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) $ip = "[redacted]";
|
||||
if (filter_var($expectedip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) $expectedip = "[redacted]";
|
||||
return " ERROR: When using DNS server {$dnsserver}, {$hostpriv} resolves to {$ip}. It should resolve to {$expectedip}\n";
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function geturls() {
|
||||
global $var,$path;
|
||||
extract(@parse_ini_file("$path/network.ini",true));
|
||||
$nginx = @parse_ini_file("$path/nginx.ini");
|
||||
$internalip = $internalip_priv = $internalip_msg = _var($eth0,'IPADDR:0');
|
||||
$dnsserver = _var($eth0,'DNS_SERVER1');
|
||||
if (filter_var($internalip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
$internalip_priv = "ipaddress";
|
||||
$internalip_msg = "redacted (FYI - this system has a routable IP address, ensure it is behind a firewall)";
|
||||
}
|
||||
$host_tld_msg = _var($var,'LOCAL_TLD') ? '' : '[blank] (FYI - a blank TLD can cause issues for Mac and Linux clients)';
|
||||
$isLegacyCert = preg_match('/.*\.unraid\.net$/',_var($nginx,'NGINX_CERTNAME'));
|
||||
$rebindtesturl = $isLegacyCert ? "rebindtest.unraid.net" : "rebindtest.myunraid.net";
|
||||
$rebindtestdomain = $isLegacyCert ? "unraid.net" : "myunraid.net";
|
||||
$rebindip = "192.168.42.42";
|
||||
$rebindtest_ip = exec("host -4 $rebindtesturl 2>/dev/null|sed -n 's/.*has address //p'");
|
||||
$rebind_msg = ($rebindtest_ip != $rebindip) ? "is enabled, $rebindtestdomain urls will not work" : "is disabled, $rebindtestdomain urls will work";
|
||||
// show raw data from config files
|
||||
$urls = '';
|
||||
$urls .= "Server Name: "._var($var,'NAME','tower')."\n";
|
||||
$urls .= "Local TLD: "._var($var,'LOCAL_TLD').$host_tld_msg."\n";
|
||||
$urls .= "HTTP port: "._var($var,'PORT',80)."\n";
|
||||
$urls .= "HTTPS port: "._var($var,'PORTSSL',443)."\n";
|
||||
$urls .= "Internal IP: {$internalip_msg}\n";
|
||||
$urls .= "DNS 1: {$dnsserver}\n";
|
||||
$urls .= "USE SSL: "._var($var,'USE_SSL','no')."\n";
|
||||
$urls .= "DNS Rebinding Protection {$rebind_msg} on this network\n\n";
|
||||
$urls .= "Available URLs:\n (the URL marked with an asterisk is the primary url for this server)\n";
|
||||
// calculate variables
|
||||
$cert_path = "/boot/config/ssl/certs/";
|
||||
$host_name = _var($var,'NAME','tower');
|
||||
$host_tld = _var($var,'LOCAL_TLD') ? ".{$var['LOCAL_TLD']}" : '';
|
||||
$use_ssl = _var($var,'USE_SSL','no');
|
||||
$http_port = _var($var,'PORT',80)!=80 ? ":{$var['PORT']}" : '';
|
||||
$https_port = _var($var,'PORTSSL',443)!=443 ? ":{$var['PORTSSL']}" : '';
|
||||
$https_1_cert = "{$host_name}_unraid_bundle.pem";
|
||||
$https_2_cert = 'certificate_bundle.pem';
|
||||
$http_primary = $https_1_primary = $https_2_primary = $http_msg = $https_1_msg = $https_2_msg = '';
|
||||
$expected_host = "{$host_name}{$host_tld}";
|
||||
switch ($use_ssl) {
|
||||
case "no":
|
||||
$http_primary = '*';
|
||||
break;
|
||||
case "yes":
|
||||
$https_1_primary = '*';
|
||||
$http_msg = "\n (this will redirect to the primary url)";
|
||||
break;
|
||||
case "auto":
|
||||
$http_msg = "\n (this will redirect to the primary url)";
|
||||
$https_1_msg = "\n (this will redirect to the primary url)";
|
||||
$https_2_primary = '*';
|
||||
break;
|
||||
}
|
||||
// calculate http ip url
|
||||
$http_ip_url = "http://{$internalip_priv}{$http_port}";
|
||||
$urls .= "HTTP IP url: {$http_ip_url}{$http_msg}\n";
|
||||
// calculate http url
|
||||
$http_url = "http://{$expected_host}{$http_port}";
|
||||
$urls .= "{$http_primary}HTTP url: {$http_url}{$http_msg}\n";
|
||||
$urls .= geturls_checkhost($expected_host, $expected_host, $internalip, $dnsserver);
|
||||
// calculate https url - self-signed or user-provided in tower_unraid_bundle.pem
|
||||
// this is available when USE_SSL != no, and the certificate file exists
|
||||
if ($use_ssl != "no" && file_exists("{$cert_path}{$https_1_cert}")) {
|
||||
[$https_1_host, $https_1_hostpriv, $https_1_type] = geturls_certdetails("{$cert_path}{$https_1_cert}", $host_name);
|
||||
$https_1_url = "https://{$https_1_hostpriv}{$https_port}";
|
||||
$urls .= "{$https_1_primary}HTTPS url 1 ($https_1_type): {$https_1_url}{$https_1_msg}\n";
|
||||
$urls .= geturls_checkhost($https_1_host, $https_1_hostpriv, $internalip, $dnsserver);
|
||||
if (strtolower($https_1_host) != strtolower($expected_host)) {
|
||||
$urls .= " ERROR: the certificate Subject CN in {$https_1_cert} should be {$expected_host}\n";
|
||||
}
|
||||
} else {
|
||||
// add a note that this url is not configured
|
||||
$urls .= "HTTPS url 1 (undefined): https://{$expected_host}{$https_port}\n (this url is not configured, it will not work)\n";
|
||||
$urls .= geturls_checkhost($expected_host, $expected_host, $internalip, $dnsserver);
|
||||
}
|
||||
// calculate https url for certificate_bundle.pem
|
||||
// this is available if the certificate file exists, regardless of the USE_SSL setting
|
||||
// this is usually a (my)unraid.net LE cert, but it can also be a user-provided cert
|
||||
if (file_exists("{$cert_path}{$https_2_cert}")) {
|
||||
[$https_2_host, $https_2_hostpriv, $https_2_type] = geturls_certdetails("{$cert_path}{$https_2_cert}", $host_name, $internalip);
|
||||
$https_2_url = "https://{$https_2_hostpriv}{$https_port}";
|
||||
$urls .= "{$https_2_primary}HTTPS url 2 ({$https_2_type}): {$https_2_url}{$https_2_msg}\n";
|
||||
$urls .= geturls_checkhost($https_2_host, $https_2_hostpriv, $internalip, $dnsserver);
|
||||
if (strpos($https_2_host, ".unraid.net") === false && strpos($https_2_host, ".myunraid.net") === false && strtolower($https_2_host) != strtolower($expected_host)) {
|
||||
$urls .= " ERROR: the certificate Subject CN in {$https_2_cert} should be {$expected_host}\n";
|
||||
}
|
||||
}
|
||||
if ($use_ssl != "no") {
|
||||
$telnet_disabled = _var($var,'USE_TELNET')=="no" ? " (disabled)" : "";
|
||||
$ssh_disabled = _var($var,'USE_SSH')=="no" ? " (disabled)" : "";
|
||||
$urls .= "\nTip: if DNS goes down and you lose access to the webgui, use telnet{$telnet_disabled}, ";
|
||||
$urls .= "ssh{$ssh_disabled}, or a local keyboard/monitor to run:\n";
|
||||
$urls .= " use_ssl no\n";
|
||||
$urls .= "to enable 'HTTP IP url' and make 'HTTP url' the primary url for the system. ";
|
||||
if ($use_ssl == "auto") {
|
||||
$urls .= "Or:\n";
|
||||
$urls .= " use_ssl yes\n";
|
||||
$urls .= "to make 'HTTPS url 1' the primary.";
|
||||
}
|
||||
$urls .= "\nOnce DNS has been restored, navigate to Settings -> Management Access and set 'Use SSL' back to '$use_ssl'\n";
|
||||
}
|
||||
// get a list of the certificate files on the flash drive
|
||||
$dirlisting[0] = "{$cert_path}";
|
||||
if (file_exists($cert_path)) {
|
||||
exec("ls -l ".escapeshellarg($cert_path), $dirlisting);
|
||||
} else {
|
||||
$dirlisting[1] = "Directory not found";
|
||||
}
|
||||
$urls .= "\n\n".implode("\n", $dirlisting)."\n";
|
||||
return str_replace("\n", "\r\n", $urls);
|
||||
}
|
||||
|
||||
// diagnostics start
|
||||
run("mkdir -p /boot/logs");
|
||||
|
||||
if ($cli) {
|
||||
// script is called from CLI
|
||||
echo "Starting diagnostics collection... ";
|
||||
$server = isset($var['NAME']) ? str_replace(' ','_',strtolower($var['NAME'])) : 'tower';
|
||||
$date = date('Ymd-Hi');
|
||||
$diag = "$server-diagnostics-$date";
|
||||
$zip = "/boot/logs/$diag.zip";
|
||||
} else {
|
||||
// script is called from GUI
|
||||
$diag = basename($zip, '.zip');
|
||||
$split = explode('-', $diag);
|
||||
$date = "{$split[2]}-{$split[3]}";
|
||||
}
|
||||
|
||||
// don't anonymize system share names
|
||||
$vardomain = @parse_ini_file('/boot/config/domain.cfg') ?: [];
|
||||
$vardocker = @parse_ini_file('/boot/config/docker.cfg') ?: [];
|
||||
$showshares = [];
|
||||
$customShares = '';
|
||||
if (!empty($vardomain['IMAGE_FILE'])) $showshares[] = current(array_slice(explode('/',$vardomain['IMAGE_FILE']),3,1));
|
||||
if (!empty($vardomain['DOMAINDIR'])) $showshares[] = current(array_slice(explode('/',$vardomain['DOMAINDIR']),3,1));
|
||||
if (!empty($vardomain['MEDIADIR'])) $showshares[] = current(array_slice(explode('/',$vardomain['MEDIADIR']),3,1));
|
||||
if (!empty($vardomain['DISKDIR'])) $showshares[] = current(array_slice(explode('/',$vardomain['DISKDIR']),3,1));
|
||||
if (!empty($vardocker['DOCKER_IMAGE_FILE'])) $showshares[] = current(array_slice(explode('/',$vardocker['DOCKER_IMAGE_FILE']),3,1));
|
||||
if (!empty($vardocker['DOCKER_APP_CONFIG_PATH'])) $showshares[] = current(array_slice(explode('/',$vardocker['DOCKER_APP_CONFIG_PATH']),3,1));
|
||||
if (!empty($vardocker['DOCKER_HOME'])) $showshares[] = current(array_slice(explode('/',$vardocker['DOCKER_HOME']),3,1));
|
||||
|
||||
foreach ($showshares as $showme) if ($showme) $customShares .= "|$showme";
|
||||
|
||||
// create folder structure
|
||||
run("mkdir -p ".escapeshellarg("/$diag/system")." ".escapeshellarg("/$diag/config")." ".escapeshellarg("/$diag/logs")." ".escapeshellarg("/$diag/shares")." ".escapeshellarg("/$diag/smart")." ".escapeshellarg("/$diag/qemu")." ".escapeshellarg("/$diag/xml"));
|
||||
|
||||
// get utilization of running processes
|
||||
run("top -bn1 -o%CPU 2>/dev/null|todos >".escapeshellarg("/$diag/system/top.txt"));
|
||||
|
||||
// make Unraid version reference
|
||||
$unraid = parse_ini_file('/etc/unraid-version');
|
||||
file_put_contents("/$diag/unraid-".$unraid['version'].".txt",$unraid['version']."\r\n");
|
||||
|
||||
// add bz*.sha256 values
|
||||
run("tail /boot/bz*.sha256 >> ".escapeshellarg("/$diag/unraid-".$unraid['version'].".txt"));
|
||||
|
||||
// copy ini variables
|
||||
foreach (glob("$path/*.ini") as $file) {
|
||||
$ini = basename($file,".ini");
|
||||
// skip users file in anonymized mode
|
||||
if ($all || $ini != "users") file_put_contents("/$diag/system/vars.txt",preg_replace(["/\n/","/^Array/"],["\r\n",$ini],anonymize(print_r(parse_ini_file($file,true),true),1)),FILE_APPEND);
|
||||
}
|
||||
|
||||
// Create loads.txt
|
||||
$cpuload = run("uptime")." Cores: ".run("nproc")."\r\n".(string)@file_get_contents("$path/cpuload.ini")."\r\n";
|
||||
$loadTxt = [];
|
||||
if (file_exists("$path/diskload.ini")){
|
||||
$diskload = file("$path/diskload.ini");
|
||||
foreach ($diskload as $loadLine) {
|
||||
$load = explode('=',$loadLine);
|
||||
foreach ($disks as $disk) {
|
||||
if ($load[0]==_var($disk,'device')) {
|
||||
$loadTxt[] = _var($disk,'device')." ("._var($disk,'name').")=".trim($load[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file_put_contents("/$diag/system/loads.txt",$cpuload.implode("\r\n",$loadTxt));
|
||||
|
||||
// individual commands execution (suppress errors)
|
||||
run("lscpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/lscpu.txt"));
|
||||
run("lsscsi -vgl 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsscsi.txt"));
|
||||
run("lspci -knn 2>/dev/null|todos >".escapeshellarg("/$diag/system/lspci.txt"));
|
||||
run("lsusb 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsusb.txt"));
|
||||
run("free -mth 2>/dev/null|todos >".escapeshellarg("/$diag/system/memory.txt"));
|
||||
run("ps -auxf --sort=-pcpu 2>/dev/null|todos >".escapeshellarg("/$diag/system/ps.txt"));
|
||||
run("lsof -Pni 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsof.txt"));
|
||||
run("lsmod|sort 2>/dev/null|todos >".escapeshellarg("/$diag/system/lsmod.txt"));
|
||||
run("df -h 2>/dev/null|todos >".escapeshellarg("/$diag/system/df.txt"));
|
||||
run("ifconfig -a -s 2>/dev/null|grep -Po '^(eth|bond)[0-9]+'", $ports);
|
||||
run("dmidecode -qt2|awk -F: '/^\tManufacturer:/{m=\$2};/^\tProduct Name:/{p=\$2} END{print m\" -\"p}' 2>/dev/null|todos >".escapeshellarg("/$diag/system/motherboard.txt"));
|
||||
run("dmidecode -qt0 2>/dev/null|todos >>".escapeshellarg("/$diag/system/motherboard.txt"));
|
||||
run("cat /proc/meminfo 2>/dev/null|todos >".escapeshellarg("/$diag/system/meminfo.txt"));
|
||||
run("dmidecode --type 17 2>/dev/null|todos >>".escapeshellarg("/$diag/system/meminfo.txt"));
|
||||
|
||||
// create ethernet information information (suppress errors)
|
||||
foreach ($ports as $port) {
|
||||
run("ethtool ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt"));
|
||||
file_put_contents("/$diag/system/ethtool.txt", "\r\n", FILE_APPEND);
|
||||
run("ethtool -i ".escapeshellarg($port)." 2>/dev/null|todos >>".escapeshellarg("/$diag/system/ethtool.txt"));
|
||||
file_put_contents("/$diag/system/ethtool.txt", "--------------------------------\r\n", FILE_APPEND);
|
||||
}
|
||||
run("ifconfig -a 2>/dev/null|todos >".escapeshellarg("/$diag/system/ifconfig.txt"));
|
||||
|
||||
// create system information (suppress errors)
|
||||
run("find /sys/kernel/iommu_groups/ -type l 2>/dev/null|sort -V|todos >".escapeshellarg("/$diag/system/iommu_groups.txt"));
|
||||
run("todos </proc/cmdline >".escapeshellarg("/$diag/system/cmdline.txt"));
|
||||
|
||||
// create folder structure listing
|
||||
$dest = "/$diag/system/folders.txt";
|
||||
foreach ($folders as $folder) {
|
||||
if (is_dir($folder)) run("echo -ne ".escapeshellarg("\r\n$folder\r\n")." >>".escapeshellarg($dest).";ls -l ".escapeshellarg($folder)."|todos >>".escapeshellarg("$dest")); else run("echo -ne ".escapeshellarg("\r\n$folder\r\nfolder does not exist\r\n")." >>".escapeshellarg("$dest"));
|
||||
}
|
||||
|
||||
// copy configuration files (suppress errors)
|
||||
run("cp /boot/config/*.{cfg,conf,dat} ".escapeshellarg("/$diag/config")." 2>/dev/null");
|
||||
run("cp /boot/config/go ".escapeshellarg("/$diag/config/go.txt")." 2>/dev/null");
|
||||
|
||||
// anonymize go file
|
||||
if (!$all)
|
||||
run("sed -i -e '/password/c ***line removed***' -e '/user/c ***line removed***' -e '/pass/c ***line removed***' ".escapeshellarg("/$diag/config/go.txt"));
|
||||
|
||||
// anonymize configuration files
|
||||
if (!$all)
|
||||
run("sed -ri 's/^((disk|flash)(Read|Write)List.*=\")[^\"]+/\\1.../' ".escapeshellarg("/$diag/config/*.cfg")." 2>/dev/null");
|
||||
|
||||
// include listening interfaces
|
||||
run("$docroot/webGui/scripts/show_interfaces ip|tr -d ' '|tr '#' ' '|tr ',' '\n' >".escapeshellarg("/$diag/config/listen.txt"));
|
||||
|
||||
// copy share information (anonymize if applicable)
|
||||
$files = glob("/boot/config/shares/*.cfg");
|
||||
$shareDisk = [];
|
||||
foreach ($files as $file) {
|
||||
$dest = "/$diag/shares/".basename($file);
|
||||
$share = basename($file,'.cfg');
|
||||
if (!in_array($share,$showshares)) $dest = anonymize($dest,2);
|
||||
@copy($file, $dest);
|
||||
if (!$all) run("sed -ri 's/^(share(Comment|ReadList|WriteList)=\")[^\"]+/\\1.../' ".escapeshellarg($dest)." 2>/dev/null");
|
||||
$home = shareDisks($share);
|
||||
$home = $home ? "# Share exists on $home\r\n" : "# Share does not exist\r\n";
|
||||
$shareDisk[] = str_pad(basename($dest,'.cfg'),34).str_pad(exec("grep -m1 'shareUseCache' ".escapeshellarg($file)),24).$home;
|
||||
file_put_contents($dest,$home.file_get_contents($dest));
|
||||
}
|
||||
file_put_contents("/$diag/shares/shareDisks.txt",implode($shareDisk));
|
||||
|
||||
// create default user shares information
|
||||
$shares = @parse_ini_file("$path/shares.ini", true) ?: [];
|
||||
foreach ($shares as $share) {
|
||||
$name = _var($share,'name');
|
||||
if ($name && !in_array("/boot/config/shares/$name.cfg",$files)) {
|
||||
$home = shareDisks($name);
|
||||
$home = $home ? "# Share exists on $home\r\n" : "# Share does not exist\r\n";
|
||||
$file = anonymize("/$diag/shares/$name.cfg",2);
|
||||
file_put_contents($file,"# This share has default settings.\r\n".$home);
|
||||
file_put_contents("/$diag/shares/shareDisks.txt",str_pad(basename($file,'.cfg'),34).str_pad('shareUseCache="no"',24).$home,FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
// copy pools information (anonymize)
|
||||
$files = glob("/boot/config/pools/*.cfg");
|
||||
@mkdir("/$diag/config/pools");
|
||||
foreach ($files as $file) {
|
||||
$dest = anonymize("/$diag/config/pools/".basename($file),2);
|
||||
@copy($file,$dest);
|
||||
}
|
||||
|
||||
// copy modprobe information
|
||||
$files = glob("/boot/config/modprobe.d/*.conf");
|
||||
if ($files) {
|
||||
@mkdir("/$diag/config/modprobe.d");
|
||||
foreach ($files as $file) {
|
||||
$dest = "/$diag/config/modprobe.d/".basename($file);
|
||||
@copy($file,$dest);
|
||||
}
|
||||
}
|
||||
|
||||
// copy docker information (if existing)
|
||||
$max = 1*1024*1024; //=1MB
|
||||
$docker = "/var/log/docker.log";
|
||||
if (file_exists($docker)) {
|
||||
$log = "/$diag/logs/docker";
|
||||
run("todos <$docker >".escapeshellarg("$log.txt"));
|
||||
if (filesize($docker)>=$max) {
|
||||
run("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
run("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
// create SMART reports (suppress errors)
|
||||
run("ls -l /dev/disk/by-id/[asun]* 2>/dev/null|sed '/-part/d;s|^.*/by-id/[^-]*-||;s|-> ../../||;s|:|-|'", $devices);
|
||||
foreach ($devices as $device) {
|
||||
[$name,$port] = array_pad(explode(' ',$device),2,'');
|
||||
$diskName = $type = '';
|
||||
foreach ($disks as $find) {
|
||||
if (_var($find,'device')==$port) {
|
||||
$diskName = _var($find,'name');
|
||||
$type = get_value($find,'smType','');
|
||||
get_ctlr_options($type, $find);
|
||||
$port = _var($find,'smDevice',$port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$port = port_name($port);
|
||||
$status = _var($find,'status')=="DISK_OK" ? "" : " - "._var($find,'status');
|
||||
run("smartctl -x $type ".escapeshellarg("/dev/$port")." 2>/dev/null|todos >".escapeshellarg("/$diag/smart/$name-$date $diskName ($port)$status.txt"));
|
||||
}
|
||||
|
||||
// create btrfs pool information
|
||||
foreach ($pools as $pool) {
|
||||
if (strpos(_var($disks[$pool],'fsType'),'btrfs')!==false) {
|
||||
$dev = _var($disks[$pool],'device');
|
||||
run("echo 'Pool: $pool'|todos >>".escapeshellarg("/$diag/system/btrfs-usage.txt"));
|
||||
run("/sbin/btrfs filesystem usage -T /mnt/$pool 2>/dev/null|todos >>".escapeshellarg("/$diag/system/btrfs-usage.txt"));
|
||||
newline("/$diag/system/btrfs-usage.txt");
|
||||
run("/sbin/btrfs filesystem show /dev/{$dev}p1 2>/dev/null|todos >>".escapeshellarg("/$diag/system/btrfs-usage.txt"));
|
||||
newline("/$diag/system/btrfs-usage.txt");
|
||||
}
|
||||
}
|
||||
|
||||
// create zfs pool information
|
||||
run("/usr/sbin/zpool status 2>/dev/null|todos >>".escapeshellarg("/$diag/system/zfs-info.txt"));
|
||||
newline("/$diag/system/zfs-info.txt");
|
||||
// sometimes hangs, temporary disabled
|
||||
//run("/usr/sbin/zpool import 2>/dev/null|todos >>".escapeshellarg("/$diag/system/zfs-info.txt"));
|
||||
|
||||
// create installed plugin information
|
||||
$pluginList = json_decode(download_url("https://raw.githubusercontent.com/Squidly271/AppFeed/master/pluginList.json"),true);
|
||||
$installedPlugins = "";
|
||||
if (!$pluginList) $installedPlugins = "Could not download current plugin versions\r\n\r\n";
|
||||
$plugins = glob("/var/log/plugins/*.plg");
|
||||
foreach ($plugins as $plugin) {
|
||||
$plgVer = run("plugin version ".escapeshellarg($plugin));
|
||||
$plgURL = run("plugin pluginURL ".escapeshellarg($plugin));
|
||||
$installedPlugins .= basename($plugin)." - $plgVer";
|
||||
if ($pluginList && empty($pluginList[$plgURL]) && basename($plugin) !== "unRAIDServer.plg")
|
||||
$installedPlugins .= " (Unknown to Community Applications)";
|
||||
if (!empty($pluginList[$plgURL]['blacklist']))
|
||||
$installedPlugins .= " (Blacklisted)";
|
||||
if (!empty($pluginList[$plgURL]['deprecated']) || (!empty($pluginList[$plgURL]['dmax']) && version_compare($pluginList[$plgURL]['dmax'],$unraid['version'],"<")))
|
||||
$installedPlugins .= " (Deprecated)";
|
||||
if (!empty($pluginList[$plgURL]['version']) && $pluginList[$plgURL]['version'] > $plgVer)
|
||||
$installedPlugins .= " (Update available: {$pluginList[$plgURL]['version']})";
|
||||
elseif (!empty($pluginList[$plgURL]))
|
||||
$installedPlugins .= " (Up to date)";
|
||||
if (!empty($pluginList[$plgURL]['max']) && version_compare($pluginList[$plgURL]['max'],$unraid['version'],"<"))
|
||||
$installedPlugins .= " (Incompatible)";
|
||||
if (!empty($pluginList[$plgURL]['min']) && version_compare($pluginList[$plgURL]['min'],$unraid['version'],">"))
|
||||
$installedPlugins .= " (Incompatible)";
|
||||
$installedPlugins .= "\r\n";
|
||||
}
|
||||
$installedPlugins = $installedPlugins ?: "No additional Plugins Installed";
|
||||
file_put_contents("/$diag/system/plugins.txt",$installedPlugins);
|
||||
|
||||
// determine urls
|
||||
file_put_contents("/$diag/system/urls.txt",geturls());
|
||||
|
||||
// copy libvirt information (if existing)
|
||||
$libvirtd = "/var/log/libvirt/libvirtd.log";
|
||||
if (file_exists($libvirtd)) {
|
||||
$log = "/$diag/logs/libvirt";
|
||||
run("todos <$libvirtd >".escapeshellarg("$log.txt"));
|
||||
if (filesize($libvirtd)>=$max) {
|
||||
run("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
run("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
// copy VMs information (if existing)
|
||||
$qemu = glob("/var/log/libvirt/qemu/*.log*");
|
||||
if ($qemu) {
|
||||
foreach ($qemu as $file) {
|
||||
$log = "/$diag/qemu/".basename($file,'.log');
|
||||
run("todos <".escapeshellarg($file)." >".escapeshellarg("$log.txt"));
|
||||
if (filesize($file)>=$max) {
|
||||
run("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
run("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file_put_contents("/$diag/qemu/no qemu log files","");
|
||||
}
|
||||
|
||||
// copy VM XML config files
|
||||
run("cp /etc/libvirt/qemu/*.xml ".escapeshellarg("/$diag/xml")." 2>/dev/null");
|
||||
|
||||
// anonymize MAC OSK info
|
||||
$all_xml = glob("/$diag/xml/*.xml");
|
||||
foreach ($all_xml as $xml) {
|
||||
run("sed -ri 's/(,osk=).+/\\1.../' ".escapeshellarg("$xml")." 2>/dev/null");
|
||||
run("sed -ri 's/(passwd=).+/\\1.../' ".escapeshellarg("$xml")." 2>/dev/null");
|
||||
}
|
||||
|
||||
// copy syslog information (anonymize if applicable)
|
||||
$max = 2*1024*1024; //=2MB
|
||||
foreach (glob("/var/log/syslog*") as $file) {
|
||||
$log = "/$diag/logs/".basename($file);
|
||||
run("todos <".escapeshellarg($file)." >".escapeshellarg("$log.txt"));
|
||||
if (!$all) {
|
||||
unset($titles,$rows);
|
||||
run("grep -Po 'file: \K[^\"\\x27]+' ".escapeshellarg("$log.txt")." 2>/dev/null|sort|uniq", $titles);
|
||||
run("sed -ri 's|\b\S+@\S+\.\S+\b|email@removed.com|;s|\b(username\|password)([=:])\S+\b|\\1\\2xxx|;s|(GUID: \S)\S+(\S) |\\1..\\2 |;s|(moving \"\S\|\"/mnt/user/\S).*(\S)\"|\\1..\\2\"|' ".escapeshellarg("$log.txt"));
|
||||
run("sed -ri 's|(server: ).+(\.(my)?unraid\.net(:[0-9]+)?,)|\\1hash\\2|;s|(host: \").+(\.(my)?unraid\.net(:[0-9]+)?\")|\\1hash\\2|;s|(referrer: \"https?://).+(\.(my)?unraid\.net)|\\1hash\\2|' ".escapeshellarg("$log.txt"));
|
||||
foreach ($titles as $mover) {
|
||||
if (!$mover) continue;
|
||||
$title = "/{$mover[0]}..".substr($mover,-1)."/...";
|
||||
run("sed -i 's/".str_replace("/","\/",$mover)."/".str_replace("/","\/",$title)."/g' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
//run("sed -ri 's|(file: [.>cr].*)[ /]$mover/.*$|\\1 file: $title|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
}
|
||||
run("grep -n ' cache_dirs: -' ".escapeshellarg("$log.txt")." 2>/dev/null|cut -d: -f1", $rows);
|
||||
for ($i = 0; $i < count($rows); $i += 2) for ($row = $rows[$i]+1; $row < $rows[$i+1]; $row++) run("sed -ri '$row s|(cache_dirs: \S).*(\S)|\\1..\\2|' ".escapeshellarg("$log.txt")." 2>/dev/null");
|
||||
}
|
||||
// replace consecutive repeated lines in syslog
|
||||
run("awk -i inplace '{if(s!=substr(\$0,17)){if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\";print;x=0}else{x++}s=substr(\$0,17)}END{if(x>0)print\"### [PREVIOUS LINE REPEATED \"x\" TIMES] ###\\r\"}' ".escapeshellarg("$log.txt"));
|
||||
// remove SHA256 hashes
|
||||
run("sed -ri 's/(SHA256:).+[^\s\b]/SHA256:***REMOVED***/gm' $log.txt");
|
||||
// truncate syslog if too big
|
||||
if (basename($file)=='syslog' && filesize($file)>=$max) run("tail -n 200 ".escapeshellarg("$log.txt")." >".escapeshellarg("$log.last200.txt"));
|
||||
run("truncate -s '<$max' ".escapeshellarg("$log.txt"));
|
||||
}
|
||||
|
||||
// copy dhcplog
|
||||
$dhcplog = "/var/log/dhcplog";
|
||||
if (file_exists($dhcplog)) {
|
||||
$log = "/$diag/logs/dhcplog.txt";
|
||||
run("todos <$dhcplog >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// copy graphql-api.log
|
||||
$graphql = "/var/log/graphql-api.log";
|
||||
if (file_exists($graphql)) {
|
||||
$log = "/$diag/logs/graphql-api.txt";
|
||||
run("todos <$graphql >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// copy vfio-pci log
|
||||
$vfiopci = "/var/log/vfio-pci";
|
||||
if (file_exists($vfiopci)) {
|
||||
$log = "/$diag/logs/vfio-pci.txt";
|
||||
run("todos <$vfiopci >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// copy wg-quick log
|
||||
$wgquick = '/var/log/wg-quick.log';
|
||||
if (file_exists($wgquick)) {
|
||||
$log = "/$diag/logs/wg-quick.txt";
|
||||
run("todos <$wgquick >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// generate unraid-api.txt
|
||||
if (file_exists("/usr/local/sbin/unraid-api")) {
|
||||
$log = "/$diag/system/unraid-api.txt";
|
||||
run("unraid-api report | todos >".escapeshellarg($log));
|
||||
}
|
||||
|
||||
// generate testparm.txt
|
||||
$testparm = run("testparm -s 2>/dev/null");
|
||||
if (!$all)
|
||||
$testparm = preg_replace("/(?<=\[.)(.*)(?=.\])|(?<=(write list = .)|(comment = .)|(valid users = .)|(path = \/mnt\/addons\/.)|(path = \/mnt\/remotes\/.)|(path = \/mnt\/disks\/.)|(path = \/mnt\/user\/.)).*(?=.)/","...",$testparm);
|
||||
file_put_contents("/$diag/system/testparm.txt",str_replace("\n","\r\n",$testparm));
|
||||
|
||||
// BEGIN - third party plugins diagnostics
|
||||
// list third party packages in /boot/config/plugins/*/packages/
|
||||
run("ls -lA /boot/config/plugins/*/packages/*/ 2>/dev/null|todos >>".escapeshellarg("/$diag/system/thirdparty_packages.txt"));
|
||||
|
||||
// list va-api compatible devices
|
||||
if (is_dir("/dev/dri/")) run("ls -lA /sys/class/drm/*/device/driver 2>/dev/null|todos >>".escapeshellarg("/$diag/system/drm.txt")); else null;
|
||||
|
||||
// list dvb adapters
|
||||
if (is_dir("/dev/dvb/")) run("ls -lA /sys/class/dvb/*/device/driver 2>/dev/null|todos >>".escapeshellarg("/$diag/system/dvb.txt")); else null;
|
||||
|
||||
// generate nvidia diagnostics
|
||||
if (is_file("/usr/bin/nvidia-smi")) run("/usr/bin/nvidia-smi --query 2>/dev/null|todos >>".escapeshellarg("/$diag/system/nvidia-smi.txt")); else null;
|
||||
|
||||
// generate lxc diagnostics
|
||||
if (is_dir("/boot/config/plugins/lxc")) {
|
||||
run("mkdir -p ".escapeshellarg("/$diag/lxc"));
|
||||
$lxc_path = run("cat /boot/config/plugins/lxc/lxc.conf 2>/dev/null| cut -d '=' -f2");
|
||||
run("tail -n +1 $lxc_path/*/config 2>/dev/null|todos >>".escapeshellarg("/$diag/lxc/container_configurations.txt"));
|
||||
run("cat /boot/config/plugins/lxc/plugin.cfg 2>/dev/null|todos >>".escapeshellarg("/$diag/lxc/plugin.cfg"));
|
||||
run("cat /boot/config/plugins/lxc/lxc.conf 2>/dev/null|todos >>".escapeshellarg("/$diag/lxc/lxc.conf"));
|
||||
run("cat /boot/config/plugins/lxc/default.conf 2>/dev/null|todos >>".escapeshellarg("/$diag/lxc/default.conf"));
|
||||
run("lxc-checkconfig 2>/dev/null|todos >>".escapeshellarg("/$diag/lxc/checkconfig.txt"));
|
||||
} else {
|
||||
null;
|
||||
}
|
||||
|
||||
// generate iscsi target diagnostics
|
||||
if (is_file("/boot/config/plugins/iSCSIgui.plg")) {
|
||||
run("mkdir -p ".escapeshellarg("/$diag/iscsi"));
|
||||
run("targetcli ls 2>/dev/null|todos >>".escapeshellarg("/$diag/iscsi/iscsi-target.txt"));
|
||||
} else {
|
||||
null;
|
||||
}
|
||||
|
||||
// generate iscsi initiator diagnostics
|
||||
if (is_file("/boot/config/plugins/iscsi-initiator.plg")) {
|
||||
run("mkdir -p ".escapeshellarg("/$diag/iscsi"));
|
||||
run("cat /boot/config/plugins/iscsi-initiator/initiatorname.cfg 2>/dev/null|todos >>".escapeshellarg("/$diag/iscsi/initiatorname.cfg"));
|
||||
run("cat /boot/config/plugins/iscsi-initiator/iscsid.conf 2>/dev/null|todos >>".escapeshellarg("/$diag/iscsi/iscsid.conf"));
|
||||
run("cat /boot/config/plugins/iscsi-initiator/targets.cfg 2>/dev/null|todos >>".escapeshellarg("/$diag/iscsi/targets.cfg"));
|
||||
} else {
|
||||
null;
|
||||
}
|
||||
// END - third party plugins diagnostics
|
||||
|
||||
// create resulting zip file and remove temp folder
|
||||
run("zip -qmr ".escapeshellarg($zip)." ".escapeshellarg("/$diag"));
|
||||
if ($cli) {
|
||||
echo "done.\nZIP file '$zip' created.\n";
|
||||
} else {
|
||||
copy($zip,"/boot/logs/".basename($zip));
|
||||
}
|
||||
|
||||
// signal we are DONE
|
||||
write('_DONE_','');
|
||||
?>
|
||||
29
emhttp/plugins/dynamix/scripts/disk_size
Executable file
29
emhttp/plugins/dynamix/scripts/disk_size
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# usage: disk_size <disk-name> <output-file>
|
||||
|
||||
# Creates an "ini" output file suitable for php parse_ini_function which describes
|
||||
# the size of <user-share> takes up on the selected disk.
|
||||
# Since this uses the 'du' command, could take awhile.
|
||||
|
||||
disk="$1"
|
||||
output="/var/local/emhttp/$disk.$2"
|
||||
total=0;
|
||||
|
||||
echo "Computing share usage for $disk..."
|
||||
rm -f "$output"
|
||||
|
||||
function check {
|
||||
folder="/mnt/$2/$1"
|
||||
if [[ -e "$folder" ]] ; then
|
||||
echo "calculating $1 usage..."
|
||||
size=$(du -sb "$folder"|cut -f1)
|
||||
echo "$1=$size" >>"$output"
|
||||
total=$(($total + $size))
|
||||
fi
|
||||
}
|
||||
while IFS=$'\n' read -r share; do
|
||||
[[ -d $share ]] && check "$(basename "$share")" "$disk"
|
||||
done <<< $(ls -vd /mnt/user/*)
|
||||
echo "share.total=$total" >>"$output"
|
||||
echo "total disk usage: $total"
|
||||
17
emhttp/plugins/dynamix/scripts/emcmd
Executable file
17
emhttp/plugins/dynamix/scripts/emcmd
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
require_once "/usr/local/emhttp/webGui/include/publish.php";
|
||||
|
||||
function emhttp_command($cmd) {
|
||||
$var = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
|
||||
$cmd .= "&csrf_token=".($var['csrf_token']??'');
|
||||
return curl_socket("/var/run/emhttpd.socket", "http://localhost/update", $cmd);
|
||||
}
|
||||
|
||||
$error = !empty($argv[1]) ? emhttp_command($argv[1]) : '';
|
||||
if ($error) {
|
||||
echo "$error\n";
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
?>
|
||||
179
emhttp/plugins/dynamix/scripts/feedback
Executable file
179
emhttp/plugins/dynamix/scripts/feedback
Executable file
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = '';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$var = parse_ini_file('state/var.ini');
|
||||
$unraid = parse_ini_file('/etc/unraid-version');
|
||||
$keyfile = trim(base64_encode(@file_get_contents($var['regFILE'])));
|
||||
$width = in_array($display['theme'],['azure','gray']) ? '98.4%' : '100%';
|
||||
|
||||
$style = ["<style>"];
|
||||
$style[] = "div.spinner.fixed{z-index:100000}";
|
||||
$style[] = "div#control_panel{position:absolute;left:0;right:0;top:0;padding-top:8px;line-height:24px;white-space:nowrap}";
|
||||
$style[] = "div.divide{text-align:center;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}";
|
||||
$style[] = "div.divide label:first-child{margin-left:0}";
|
||||
$style[] = "div.divide label{margin-left:2%;cursor:pointer}";
|
||||
$style[] = "div.allpanels{display:none;position:absolute;left:0;right:0;top:40px;bottom:0;overflow:auto}";
|
||||
$style[] = "div#footer_panel{position:absolute;left:0;right:0;bottom:0;height:30px;line-height:30px;text-align:center}";
|
||||
$style[] = "textarea.feedback{width:$width;height:530px;margin:0;resize:none}";
|
||||
$style[] = "@media (max-width:960px){textarea.feedback{height:330px}}";
|
||||
$style[] = "@media (max-height:768px){textarea.feedback{height:330px}}";
|
||||
$style[] = "input.submit[type=button]{margin-right:0;float:right}";
|
||||
$style[] = "input.submit[type=email]{margin-top:10px;float:left}";
|
||||
$style[] = "p.note,label.note{font-size:1.1rem!important;display:block}";
|
||||
$style[] = "p.success{text-align:center!important;margin-top:20px}";
|
||||
$style[] = "span.spacer{margin:0 4px}";
|
||||
$style[] = "</style>";
|
||||
|
||||
$html = ["<div>"];
|
||||
$html[] = "<div id='control_panel' class='divide'>";
|
||||
$html[] = "<label for='optFeatureRequest'><input type='radio' name='mode' id='optFeatureRequest' value='featurerequest' checked='checked'/> "._('Product Suggestion')."</label>";
|
||||
$html[] = "<label for='optBugReport'><input type='radio' name='mode' id='optBugReport' value='bugreport'/> "._('Bug Report')."</label>";
|
||||
$html[] = "<label for='optComment'><input type='radio' name='mode' id='optComment' value='comment'/> "._('Other Comment')."</label>";
|
||||
$html[] = "<hr>";
|
||||
$html[] = "</div>";
|
||||
$html[] = "<div id='thanks_panel' class='allpanels'></div>";
|
||||
$html[] = "<div id='featurerequest_panel' class='allpanels'>";
|
||||
$html[] = "<textarea id='featureDescription' class='feedback' placeholder=\""._('Please summarize your suggestion here').".\"></textarea>";
|
||||
$html[] = "<br>";
|
||||
$html[] = "<input type='email' id='featureEmail' class='submit' autocomplete='off' spellcheck='false' placeholder=\""._('Contact Email Address').' ('._('optional').")\"><input type='button' id='featureSubmit' class='submit' value=\""._('Submit')."\"/>";
|
||||
$html[] = "</div>";
|
||||
$html[] = "<div id='bugreport_panel' class='allpanels'>";
|
||||
$html[] = "<textarea id='bugDescription' class='feedback'></textarea>";
|
||||
$html[] = "<input type='email' id='bugEmail' class='submit' autocomplete='off' spellcheck='false' placeholder=\""._('Contact Email Address').". ("._('optional').")\"><input type='button' id='bugSubmit' class='submit' value=\""._('Submit')."\"/>";
|
||||
$html[] = "<label class='note' for='anonymize'><input type='checkbox' id='anonymize' value='1' />"._('Anonymize diagnostics (may make troubleshooting more difficult)')."</label>";
|
||||
$html[] = "<p class='note'><b>"._('NOTE').":</b> <i>"._('Submission of this bug report will automatically send your system diagnostics to Lime Technology').".</i></p>";
|
||||
$html[] = "</div>";
|
||||
$html[] = "<div id='comment_panel' class='allpanels'>";
|
||||
$html[] = "<textarea id='commentDescription' class='feedback' placeholder=\""._('Type your question or comment to Lime Technology here').".\"></textarea>";
|
||||
$html[] = "<br>";
|
||||
$html[] = "<input type='email' id='commentEmail' class='submit' autocomplete='off' spellcheck='false' placeholder=\""._('Contact Email Address')." ("._('optional').")\"><input type='button' id='commentSubmit' class='submit' value=\""._('Submit')."\"/>";
|
||||
$html[] = "</div>";
|
||||
$html[] = "<div id='footer_panel'>";
|
||||
$html[] = "<a href='https://unraid.net' target='_blank'>"._('Website')."</a><span class='spacer blue-text'>|</span>";
|
||||
$html[] = "<a href='https://forums.unraid.net' target='_blank'>"._('Forum')."</a><span class='spacer blue-text'>|</span>";
|
||||
$html[] = "<a href='https://docs.unraid.net/' target='_blank'>"._('Docs')."</a>";
|
||||
$html[] = "</div>";
|
||||
$html[] = "</div>";
|
||||
|
||||
$script = ["<script>"];
|
||||
$script[] = "var inkeyfile = '$keyfile';";
|
||||
$script[] = "var unraid_osversion = '{$unraid['version']}';";
|
||||
$script[] = "var unraid_timestamp = ".time().";";
|
||||
$script[] = "var inpageurl = window.top.location.href;";
|
||||
|
||||
$script[] = "function featurerequest_reset() {";
|
||||
$script[] = " \$('#featureDescription').val('');";
|
||||
$script[] = " \$('#featureEmail').val('');";
|
||||
$script[] = "}";
|
||||
|
||||
$script[] = "function bugreport_reset() {";
|
||||
$script[] = " \$('#bugDescription').val(\""._('Bug Description').": \\n\\n\\n\\n"._('How to reproduce').": \\n\\n\\n\\n"._('Expected results').": \\n\\n\\n\\n"._('Actual results').": \\n\\n\\n\\n"._('Other information').": \\n\");";
|
||||
$script[] = " \$('#bugEmail').val('');";
|
||||
$script[] = "}";
|
||||
|
||||
$script[] = "function comment_reset() {";
|
||||
$script[] = " \$('#commentDescription').val('');";
|
||||
$script[] = " \$('#commentEmail').val('');";
|
||||
$script[] = "}";
|
||||
|
||||
$script[] = "function form_submit(url, params, panel, diagnostics) {";
|
||||
$script[] = " panel.find('textarea,input').prop('disabled',true);";
|
||||
$script[] = " \$('div.spinner.fixed').show();";
|
||||
$script[] = " if (diagnostics) {";
|
||||
$script[] = " var anonymize = \$('#anonymize').is(':checked') ? '1' : '';";
|
||||
$script[] = " \$.get('/webGui/include/Feedback.php',{getdiagnostics:1,anonymize:anonymize},function(data) {";
|
||||
$script[] = " params.diagnostics = data;";
|
||||
$script[] = " form_submit(url, params, panel);";
|
||||
$script[] = " }).fail(function() {";
|
||||
$script[] = " \$('div.spinner.fixed').hide();";
|
||||
$script[] = " panel.fadeOut('fast').find('textarea,input').prop('disabled', false);";
|
||||
$script[] = " var failure_message = \"<p class='red-text' style='text-align:center;'>"._('Sorry, an error occurred')." ("._('Unable to generate system diagnostics')." "._('Please try again later').".</p>\";";
|
||||
$script[] = " \$('#thanks_panel').html(failure_message).fadeIn('fast');";
|
||||
$script[] = " });";
|
||||
$script[] = " return;";
|
||||
$script[] = " }";
|
||||
$script[] = " params.timestamp = unraid_timestamp;";
|
||||
$script[] = " params.osversion = unraid_osversion;";
|
||||
$script[] = " params.keyfile = inkeyfile;";
|
||||
$script[] = " params.pageurl = inpageurl;";
|
||||
$script[] = " \$.post(url,params,function(data) {";
|
||||
$script[] = " \$('div.spinner.fixed').hide();";
|
||||
$script[] = " if (data.error) {";
|
||||
$script[] = " var failure_message = \"<p class='red-text' style='text-align:center;'>"._('Sorry, an error occurred').". "._('Please try again later').".</p>\";";
|
||||
$script[] = " \$('#thanks_panel').html(failure_message).fadeIn('fast');";
|
||||
$script[] = " } else {";
|
||||
$script[] = " data.message = data.message || '';";
|
||||
$script[] = " var url_parts = url.split('/');";
|
||||
$script[] = " var success_message = '<div style=\"text-align:center\"><h2 style=\"color:#4f8a10!important\">"._("Thank You")."!</h2><img src=\"/webGui/images/feedback_'+url_parts[4]+'.png\"/><p class=\"success\">'+data.message+'</p></div>';";
|
||||
$script[] = " \$('#thanks_panel').html(success_message).fadeIn('fast', function() {";
|
||||
$script[] = " var resetfunction = window[url_parts[4]+'_reset'];";
|
||||
$script[] = " if (typeof resetfunction !== 'undefined' && \$.isFunction(resetfunction)) {";
|
||||
$script[] = " resetfunction();";
|
||||
$script[] = " }";
|
||||
$script[] = " });";
|
||||
$script[] = " }";
|
||||
$script[] = " }).fail(function(jqXHR, textStatus, errorThrown) {";
|
||||
$script[] = " if (jqXHR.responseJSON && jqXHR.responseJSON.error) {";
|
||||
$script[] = " errorThrown = jqXHR.responseJSON.error;";
|
||||
$script[] = " }";
|
||||
$script[] = " var failure_message = \"<p class='red-text' style='text-align:center;'>"._('Sorry, an error occurred').". "._('Please try again later').".</p>\";";
|
||||
$script[] = " \$('#thanks_panel').html(failure_message).fadeIn('fast');";
|
||||
$script[] = " }).always(function() {";
|
||||
$script[] = " \$('#spinner_image').fadeOut('fast');";
|
||||
$script[] = " panel.fadeOut('fast').find('textarea,input').prop('disabled', false);";
|
||||
$script[] = " });";
|
||||
$script[] = "}";
|
||||
|
||||
$script[] = "\$(function() {";
|
||||
$script[] = " \$('#control_panel input[type=radio]').click(function() {";
|
||||
$script[] = " var showPanel = '#'+\$('#control_panel input[type=radio]:checked').val()+'_panel';";
|
||||
$script[] = " $('.allpanels').not(showPanel).fadeOut('fast');";
|
||||
$script[] = " var loadfunction = window[\$('#control_panel input[type=radio]:checked').val()+'_load'];";
|
||||
$script[] = " if (typeof loadfunction !== 'undefined' && \$.isFunction(loadfunction)) {";
|
||||
$script[] = " loadfunction();";
|
||||
$script[] = " } else {";
|
||||
$script[] = " \$(showPanel).fadeIn('fast');";
|
||||
$script[] = " }";
|
||||
$script[] = " });";
|
||||
$script[] = " \$('#featureSubmit').click(function featureSubmitClick(){";
|
||||
$script[] = " if (\$('#featureDescription').val() === '') return;";
|
||||
$script[] = " form_submit('https://keys.lime-technology.com/feedback/featurerequest',{description:\$('#featureDescription').val(),email:\$('#featureEmail').val()},\$('#featurerequest_panel'));";
|
||||
$script[] = " });";
|
||||
$script[] = " \$('#bugSubmit').click(function bugSubmitClick(){";
|
||||
$script[] = " if (\$('#bugDescription').val() === '') return;";
|
||||
$script[] = " form_submit('https://keys.lime-technology.com/feedback/bugreport',{description:\$('#bugDescription').val(),email:\$('#bugEmail').val()},\$('#bugreport_panel'),true);";
|
||||
$script[] = " });";
|
||||
$script[] = " \$('#commentSubmit').click(function commentSubmitClick(){";
|
||||
$script[] = " if (\$('#commentDescription').val() === '') return;";
|
||||
$script[] = " form_submit('https://keys.lime-technology.com/feedback/comment',{description:\$('#commentDescription').val(),email:\$('#commentEmail').val()},\$('#comment_panel'));";
|
||||
$script[] = " });";
|
||||
$script[] = " featurerequest_reset();";
|
||||
$script[] = " bugreport_reset();";
|
||||
$script[] = " comment_reset();";
|
||||
$script[] = " \$('#optFeatureRequest').click();";
|
||||
$script[] = "});";
|
||||
$script[] = "</script>";
|
||||
|
||||
echo implode($style),implode($html),implode($script);
|
||||
?>
|
||||
46
emhttp/plugins/dynamix/scripts/flash_backup
Executable file
46
emhttp/plugins/dynamix/scripts/flash_backup
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
$var = file_exists('/var/local/emhttp/var.ini') ? parse_ini_file('/var/local/emhttp/var.ini') : [];
|
||||
$dir = ['system','appdata','isos','domains'];
|
||||
$out = ['prev','previous'];
|
||||
|
||||
$server = isset($var['NAME']) ? str_replace(' ','_',strtolower($var['NAME'])) : 'tower';
|
||||
$mydate = date('Ymd-Hi');
|
||||
$backup = "$server-flash-backup-$mydate.zip";
|
||||
|
||||
$used = exec("df /boot|awk 'END{print $3}'") * 1.5;
|
||||
$free = exec("df /|awk 'END{print $4}'");
|
||||
if ($free > $used) $zip = "/$backup"; else {
|
||||
foreach ($dir as $share) {
|
||||
if (!is_dir("/mnt/user/$share")) continue;
|
||||
$free = exec("df /mnt/user/$share|awk 'END{print $4}'");
|
||||
if ($free > $used) {$zip = "/mnt/user/$share/$backup"; break;}
|
||||
}
|
||||
}
|
||||
if (isset($zip)) {
|
||||
chdir("/boot");
|
||||
foreach (glob("*",GLOB_NOSORT+GLOB_ONLYDIR) as $folder) {
|
||||
if (in_array($folder,$out)) continue;
|
||||
exec("zip -qr ".escapeshellarg($zip)." ".escapeshellarg($folder));
|
||||
}
|
||||
foreach (glob("*",GLOB_NOSORT) as $file) {
|
||||
if (is_dir($file)) continue;
|
||||
exec("zip -q ".escapeshellarg($zip)." ".escapeshellarg($file));
|
||||
}
|
||||
symlink($zip,"$docroot/$backup");
|
||||
echo $backup;
|
||||
}
|
||||
?>
|
||||
17
emhttp/plugins/dynamix/scripts/ftpusers
Executable file
17
emhttp/plugins/dynamix/scripts/ftpusers
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/php -q
|
||||
<?php
|
||||
// usage: ftpusers 0|1 ['user1 user2 .. ']
|
||||
// 0 = disable, 1 = enable FTP server daemon
|
||||
// the list of users must be within quotes, ie, as one argument
|
||||
// if no user(s) specified, deletes the config file
|
||||
|
||||
$config_file = "/boot/config/vsftpd.user_list";
|
||||
if (isset($argv[2]))
|
||||
file_put_contents($config_file, implode("\n", explode(' ', trim($argv[2])))."\n");
|
||||
else
|
||||
@unlink($config_file);
|
||||
|
||||
$state = !empty($argv[1]) ? "'s/^#\(ftp.*vsftpd\)\$/\\1/'" : "'s/^\(ftp.*vsftpd\)\$/#\\1/'";
|
||||
exec("sed -i $state /etc/inetd.conf");
|
||||
exec("killall -HUP inetd");
|
||||
?>
|
||||
49
emhttp/plugins/dynamix/scripts/install_key
Executable file
49
emhttp/plugins/dynamix/scripts/install_key
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
|
||||
$url = rawurldecode($argv[1]??'');
|
||||
$host = parse_url($url)['host'];
|
||||
|
||||
if (in_array($host,['keys.lime-technology.com','lime-technology.com'])) {
|
||||
$key_file = basename($url);
|
||||
write("Downloading $keyfile ...\n");
|
||||
exec("/usr/bin/wget -q -O ".escapeshellarg("/boot/config/$key_file")." ".escapeshellarg($url), $output, $return_var);
|
||||
if ($return_var === 0) {
|
||||
if (parse_ini_file('/var/local/emhttp/var.ini')['mdState'] == 'STARTED') {
|
||||
write("Installing ... Please Stop array to complete key installation.\n");
|
||||
} else {
|
||||
write("Installed ...\n");
|
||||
}
|
||||
} else {
|
||||
write("ERROR: $return_var\n");
|
||||
}
|
||||
} else {
|
||||
write("ERROR, bad or missing key file URL: $url\n");
|
||||
}
|
||||
write('_DONE_','');
|
||||
?>
|
||||
331
emhttp/plugins/dynamix/scripts/monitor
Executable file
331
emhttp/plugins/dynamix/scripts/monitor
Executable file
@@ -0,0 +1,331 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
// Multi-language handling
|
||||
if (!function_exists('_')) {
|
||||
function _($text) {return $text;}
|
||||
}
|
||||
// Exit when settings are not yet initialized
|
||||
if (!file_exists("/var/local/emhttp/var.ini")) exit;
|
||||
|
||||
$var = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
|
||||
$devs = @parse_ini_file("/var/local/emhttp/devs.ini",true) ?: [];
|
||||
$disks = @parse_ini_file("/var/local/emhttp/disks.ini",true) ?: [];
|
||||
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/webGui/include/Preselect.php";
|
||||
require_once "$docroot/webGui/include/CustomMerge.php";
|
||||
|
||||
extract(parse_plugin_cfg("dynamix",true));
|
||||
|
||||
$notify = "$docroot/webGui/scripts/notify";
|
||||
$ram = "/var/local/emhttp/monitor.ini";
|
||||
$rom = "/boot/config/plugins/dynamix/monitor.ini";
|
||||
$saved = @parse_ini_file($ram,true);
|
||||
$high1 = _var($display,'critical',0);
|
||||
$high2 = _var($display,'warning',0);
|
||||
$server = strtoupper(_var($var,'NAME','tower'));
|
||||
$pools = pools_filter($disks);
|
||||
$errors = [];
|
||||
$top = 120;
|
||||
|
||||
function check_temp(&$disk,$text,$info) {
|
||||
global $notify,$saved,$server,$display,$top;
|
||||
$name = _var($disk,'name');
|
||||
$temp = _var($disk,'temp','*');
|
||||
$max = _var($disk,'maxTemp') ? $disk['maxTemp'] : (_var($display,'max') ? $display['max'] : 0);
|
||||
$hot = _var($disk,'hotTemp') ? $disk['hotTemp'] : (_var($display,'hot') ? $display['hot'] : 0);
|
||||
$warn = exceed($temp,$max,$top) ? 'alert' : (exceed($temp,$hot,$top) ? 'warning' : false);
|
||||
$item = 'temp';
|
||||
$last = $saved[$item][$name] ?? 0;
|
||||
if ($warn) {
|
||||
if ($temp>$last) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text temperature")." -s ".escapeshellarg(ucfirst($warn)." [$server] - $text ".($warn=='alert'?'overheated (':'is hot (').my_temp($temp).")")." -d ".escapeshellarg("$info")." -i \"$warn\" 2>/dev/null");
|
||||
$saved[$item][$name] = max($max,$temp);
|
||||
}
|
||||
} else {
|
||||
if ($last && $temp<=$top) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text message")." -s ".escapeshellarg("Notice [$server] - $text returned to normal temperature")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
function check_smart(&$disk,$port,$text,$info) {
|
||||
global $notify,$saved,$server,$numbers;
|
||||
$name = _var($disk,'name');
|
||||
$select = get_value($disk,'smSelect',0);
|
||||
$level = get_value($disk,'smLevel',1);
|
||||
$events = explode('|',get_value($disk,'smEvents',$numbers));
|
||||
$type = get_value($disk,'smType','');
|
||||
get_ctlr_options($type, $disk);
|
||||
$file = "/var/local/emhttp/smart/$name";
|
||||
exec("awk 'NR>7{print $1,$2,$4,$6,$9,$10}' ".escapeshellarg($file)." 2>/dev/null", $codes);
|
||||
$item = 'smart';
|
||||
foreach ($codes as $code) {
|
||||
if (!$code || !is_numeric($code[0])) continue;
|
||||
[$id,$class,$value,$thres,$when,$raw] = my_explode(' ',$code,7);
|
||||
$fail = strpos($when,'FAILING_NOW')!==false;
|
||||
if (!$fail && !in_array($id,$events)) continue;
|
||||
$word = str_replace(['_',' (-)'],[' ',''],strtolower("$class ($when)"));
|
||||
$ack = "$name.ack";
|
||||
switch ($select) {
|
||||
case 0:
|
||||
$attr = "$name.$id";
|
||||
$last = ($saved[$item][$attr] ?? 0)*$level;
|
||||
if ($raw>0 || $fail) {
|
||||
if ($raw>$last) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text SMART health [$id]")." -s ".escapeshellarg("Warning [$server] - $word is $raw")." -d ".escapeshellarg("$info")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$attr] = $raw;
|
||||
unset($saved[$item][$ack]);
|
||||
}
|
||||
} else {
|
||||
if ($last>0) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text SMART message [$id]")." -s ".escapeshellarg("Notice [$server] - $word returned to normal value")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$attr]);
|
||||
unset($saved[$item][$ack]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
$attr = "$name.{$id}n";
|
||||
$last = $saved[$item][$attr] ?? 255;
|
||||
if (($thres>0 && $value<=$thres*$level) || $fail) {
|
||||
if ($value*($value>$thres?$level:1)<$last) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text SMART health [$id]")." -s ".escapeshellarg("Warning [$server] - $word is $value")." -d ".escapeshellarg("$info")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$attr] = $value;
|
||||
unset($saved[$item][$ack]);
|
||||
}
|
||||
} else {
|
||||
if ($last<255) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text SMART message [$id]")." -s ".escapeshellarg("Notice [$server] - $word returned to normal value")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$attr]);
|
||||
unset($saved[$item][$ack]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
function check_usage(&$disk,$used,$text,$info) {
|
||||
global $notify,$saved,$server,$display;
|
||||
if ($used == -1) return;
|
||||
$name = _var($disk,'name');
|
||||
$critical = _var($disk,'critical') ? $disk['critical'] : (_var($display,'critical') ? $display['critical'] : 0);
|
||||
$warning = _var($disk,'warning') ? $disk['warning'] : (_var($display,'warning') ? $display['warning'] : 0);
|
||||
$warn = exceed($used,$critical) ? 'alert' : (exceed($used,$warning) ? 'warning' : false);
|
||||
$item = 'used';
|
||||
$last = $saved[$item][$name] ?? 0;
|
||||
if ($warn) {
|
||||
if ($used>$last) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text disk utilization")." -s ".escapeshellarg(ucfirst($warn)." [$server] - $text is ".($warn=='alert'?'low on space':'high on usage')." ({$used}%)")." -d ".escapeshellarg("$info")." -i \"$warn\" 2>/dev/null");
|
||||
$saved[$item][$name] = max($critical,$used);
|
||||
}
|
||||
} else {
|
||||
if ($last && $used<=100) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text message")." -s ".escapeshellarg("Notice [$server] - $text returned to normal utilization level")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check array devices
|
||||
foreach ($disks as $disk) {
|
||||
$name = _var($disk,'name');
|
||||
if ($name=='flash' || substr(_var($disk,'status'),-3)=='_NP') continue;
|
||||
$text = my_disk($name).(in_array($name,$pools)||$name=='parity'?' disk':'');
|
||||
$device = _var($disk,'device');
|
||||
$info = !empty($disk['id']) ? "{$disk['id']} ($device)" : "No device identification ($device)";
|
||||
// process disk temperature notifications
|
||||
check_temp($disk,$text,$info);
|
||||
// process disk SMART notifications
|
||||
check_smart($disk,port_name($disk['smDevice'] ?? $device),$text,$info);
|
||||
// process disk usage notifications
|
||||
check_usage($disk,_var($disk,'fsSize',0)>0?100-round(100*_var($disk,'fsFree',0)/$disk['fsSize']):-1,$text,$info);
|
||||
// process disk operation notifications
|
||||
$warn = strtok(_var($disk,'color'),'-');
|
||||
$item = 'disk';
|
||||
$last = $saved[$item][$name] ?? '';
|
||||
switch ($warn) {
|
||||
case 'red':
|
||||
if ($warn!=$last) {
|
||||
if (_var($var,'fsState')!='Stopped') {
|
||||
$status = strtolower(str_replace(['NP_','_'],['',' '],_var($disk,'status')));
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text error")." -s ".escapeshellarg("Alert [$server] - $text in error state ($status)")." -d ".escapeshellarg("$info")." -i \"alert\" 2>/dev/null");
|
||||
}
|
||||
$saved[$item][$name] = $warn;
|
||||
}
|
||||
break;
|
||||
case 'yellow':
|
||||
if ($warn!=$last) {
|
||||
if (_var($var,'fsState')!='Stopped') {
|
||||
$status = $name=='parity' ? "parity-sync in progress" : " is being reconstructed and is available for normal operation";
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text message")." -s ".escapeshellarg("Notice [$server] - $text, $status")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
}
|
||||
$saved[$item][$name] = $warn;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($last) {
|
||||
if (_var($var,'fsState')!='Stopped') {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text message")." -s ".escapeshellarg("Notice [$server] - $text returned to normal operation")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
}
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
break;}
|
||||
// count disk errors
|
||||
if (_var($disk,'numErrors',0)>0) $errors[] = "$text - $info (errors {$disk['numErrors']})";
|
||||
// check file system of cache pool
|
||||
$item = 'pool';
|
||||
if (in_array($name,$pools) && strpos(_var($disk,'fsType'),'btrfs')!==false && _var($disk,'uuid')!=="") {
|
||||
$attr = 'missing';
|
||||
if (exec("/sbin/btrfs filesystem show "._var($disk,'uuid')." 2>/dev/null|grep -c 'missing'")>0) {
|
||||
if (empty($saved[$item][$attr])) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text message")." -s ".escapeshellarg("Warning [$server] - Cache pool BTRFS missing device(s)")." -d ".escapeshellarg("$info")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$attr] = 1;
|
||||
}
|
||||
} elseif (isset($saved[$item][$attr])) unset($saved[$item][$attr]);
|
||||
$attr = "profile-$name";
|
||||
if (exec("/sbin/btrfs filesystem df /mnt/$name 2>/dev/null|grep -c '^Data'")>1) {
|
||||
if (empty($saved[$item][$attr])) {
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $text message")." -s ".escapeshellarg("Warning [$server] - $pool pool BTRFS too many profiles (You can ignore this warning when a pool balance operation is in progress)")." -d ".escapeshellarg("$info")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$attr] = 1;
|
||||
}
|
||||
} elseif (isset($saved[$item][$attr])) unset($saved[$item][$attr]);
|
||||
}
|
||||
}
|
||||
|
||||
// check unassigned devices
|
||||
foreach ($devs as $dev) {
|
||||
$name = _var($dev,'name','no name');
|
||||
$id = _var($dev,'id');
|
||||
$port = port_name($name);
|
||||
$text = "device $name";
|
||||
$info = !empty($id) ? "$id ($name)": "No device identification ($name)";
|
||||
// process disk temperature notifications
|
||||
check_temp($dev,$text,$info);
|
||||
// process disk SMART notifications
|
||||
check_smart($dev,$port,$text,$info);
|
||||
}
|
||||
|
||||
// report array read errors
|
||||
$item = 'array';
|
||||
$name = 'errors';
|
||||
$last = $saved[$item][$name] ?? 0;
|
||||
$warn = count($errors);
|
||||
$info = "Array has $warn disk".($warn==1 ? "" : "s")." with read errors";
|
||||
if ($warn>0) {
|
||||
if ($warn<>$last) {
|
||||
$message = implode('\n', $errors);
|
||||
exec("$notify -l '/Main' -e \"Unraid array errors\" -s ".escapeshellarg("Warning [$server] - array has errors")." -d ".escapeshellarg("$info")." -m ".escapeshellarg("$message")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$name] = $warn;
|
||||
}
|
||||
} else {
|
||||
if ($last) {
|
||||
exec("$notify -l '/Main' -e \"Unraid array errors\" -s ".escapeshellarg("Notice [$server] - array turned good")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// process parity check, parity sync and data-rebuild notifications
|
||||
$name = 'parity';
|
||||
$last = $saved[$item][$name] ?? '';
|
||||
if ($var['mdResyncPos']) {
|
||||
if (!$last) {
|
||||
$action = preg_split('/\s+/',$var['mdResyncAction']);
|
||||
switch ($action[0]) {
|
||||
case 'recon': $last = $action[1]=='P' ? 'Parity-Sync' : 'Data-Rebuild'; break;
|
||||
case 'check': $last = count($action)>1 ? 'Parity-Check' : 'Read-Check'; break;
|
||||
case 'clear': $last = 'Disk-Clear'; break;
|
||||
default : $last = '';
|
||||
}
|
||||
$info = "Size: ".my_scale($var['mdResyncSize']*1024,$unit)." $unit";
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $last")." -s ".escapeshellarg("Notice [$server] - $last started")." -d ".escapeshellarg("$info")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$name] = $last;
|
||||
}
|
||||
} else {
|
||||
if ($last) {
|
||||
[$date,$duration,$speed,$status,$error,$action,$size] = last_parity_log();
|
||||
$info = $status==0 ? "Duration: ".my_check($duration, $speed) : ($status==-4 ? "Canceled" : "Error code: $status");
|
||||
$level = ($status==0 && $var['sbSyncErrs']==0) ? "normal" : "warning";
|
||||
exec("$notify -l '/Main' -e ".escapeshellarg("Unraid $last")." -s ".escapeshellarg("Notice [$server] - $last finished ($error errors)")." -d ".escapeshellarg("$info")." -i \"$level\" 2>/dev/null");
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// check read-write status of USB flash drive
|
||||
$name = 'flash';
|
||||
$last = $saved[$item][$name] ?? '';
|
||||
$warn = exec("grep -Pom1 '/boot \S+ \K\S{2}' /proc/mounts");
|
||||
$info = "{$disks['flash']['id']} ({$disks['flash']['device']})";
|
||||
if ($warn!="rw") {
|
||||
if ($warn!=$last) {
|
||||
exec("$notify -l '/Main' -e \"USB flash drive failure\" -s ".escapeshellarg("Alert [$server] - USB drive is not read-write")." -d ".escapeshellarg("$info")." -i \"alert\" 2>/dev/null");
|
||||
$saved[$item][$name] = $warn;
|
||||
}
|
||||
} else {
|
||||
if ($last) {
|
||||
exec("$notify -l '/Main' -e \"USB flash drive operation\" -s ".escapeshellarg("Notice [$server] - USB drive returned to normal operation")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// check docker image disk utilization
|
||||
system('mountpoint -q /var/lib/docker', $retval);
|
||||
if ($retval===0 && exec("df /var/lib/docker|grep -Po '^/dev/\Kloop'")) {
|
||||
$item = 'system';
|
||||
$name = 'docker';
|
||||
$last = $saved[$item][$name] ?? '';
|
||||
if (file_exists("/boot/config/docker.cfg")) {
|
||||
$cfg = parse_ini_file("/boot/config/docker.cfg");
|
||||
$info = "Docker utilization of image file {$cfg['DOCKER_IMAGE_FILE']}";
|
||||
} else
|
||||
$info = "Docker image file not specified";
|
||||
|
||||
$warn = exec("df /var/lib/docker|awk '/^\//{print $5*1}'");
|
||||
if ($warn>=$high1 && $high1>0) {
|
||||
if ($warn>$last) {
|
||||
exec("$notify -l '/Docker' -e \"Docker critical image disk utilization\" -s ".escapeshellarg("Alert [$server] - Docker image disk utilization of {$warn}%")." -d ".escapeshellarg("$info")." -i \"alert\" 2>/dev/null");
|
||||
$saved[$item][$name] = $warn;
|
||||
}
|
||||
} elseif ($warn>=$high2 && $high2>0) {
|
||||
if ($warn>$last) {
|
||||
exec("$notify -l '/Docker' -e \"Docker high image disk utilization\" -s ".escapeshellarg("Warning [$server] - Docker image disk utilization of {$warn}%")." -d ".escapeshellarg("$info")." -i \"warning\" 2>/dev/null");
|
||||
$saved[$item][$name] = $warn;
|
||||
}
|
||||
} else {
|
||||
if ($last) {
|
||||
exec("$notify -l '/Docker' -e \"Docker image disk utilization\" -s ".escapeshellarg("Notice [$server] - Docker image disk utilization returned to normal level")." -d ".escapeshellarg("$info")." 2>/dev/null");
|
||||
unset($saved[$item][$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save new status
|
||||
if ($saved) {
|
||||
$text = '';
|
||||
foreach ($saved as $item => $block) {
|
||||
if ($block) $text .= "[$item]\n";
|
||||
foreach ($block as $key => $value) $text .= "$key=\"$value\"\n";
|
||||
}
|
||||
if ($text) {
|
||||
if ($text != @file_get_contents($ram)) file_put_contents($ram, $text);
|
||||
if (!file_exists($rom) || exec("diff -q $ram $rom")) file_put_contents($rom, $text);
|
||||
} else {
|
||||
delete_file($ram,$rom);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
?>
|
||||
118
emhttp/plugins/dynamix/scripts/netconfig
Executable file
118
emhttp/plugins/dynamix/scripts/netconfig
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$set = $ifname = $argv[1];
|
||||
$run = $set != 'none';
|
||||
$ini = parse_ini_file('/var/local/emhttp/network.ini',true); ksort($ini,SORT_NATURAL);
|
||||
$cfg = '/boot/config/network.cfg';
|
||||
|
||||
function ifname($name) {
|
||||
global $old;
|
||||
if (!$name) return;
|
||||
for ($i=0; $i<$old['SYSNICS']; $i++) {
|
||||
$nics = $old['BONDNICS'][$i] ?? $old['BRNICS'][$i] ?? '';
|
||||
if (strpos("$nics ","$name ")!==false) return $old['IFNAME'][$i];
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
function bond_nics(&$bond,$nic) {
|
||||
$bond['BONDNICS'] = str_replace(',',' ',$bond['BONDNICS']);
|
||||
return explode(' ',preg_replace("/$nic ?/","",$bond['BONDNICS']));
|
||||
}
|
||||
function bridge_nics(&$bridge,$nic) {
|
||||
$bridge['BRNICS'] = str_replace(',',' ',$bridge['BRNICS']);
|
||||
return explode(' ',preg_replace("/$nic ?/","",$bridge['BRNICS']));
|
||||
}
|
||||
|
||||
// stop interface with existing (old) configuration
|
||||
// don't execute when only interface description has changed
|
||||
if ($run) {
|
||||
$old = [];
|
||||
if (file_exists($cfg)) {
|
||||
$old = parse_ini_string(preg_replace(['/^#/m',"/\r/m"],[';',''],file_get_contents($cfg)));
|
||||
if (isset($old['SYSNICS'])) {
|
||||
// new syntax
|
||||
$ifname = ifname($set);
|
||||
} else {
|
||||
// legacy syntax
|
||||
if ($set=='eth0') $ifname = $old['BRIDGING']=='yes' ? ($old['BRNAME'] ?? 'br0') : ($old['BONDING']=='yes' ? ($old['BONDNAME'] ?? 'bond0') : $set);
|
||||
}
|
||||
}
|
||||
exec("/etc/rc.d/rc.inet1 ".escapeshellarg("{$ifname}_stop")." >/dev/null");
|
||||
if ($ini[$set]['BONDING']=='yes') {
|
||||
// release additional NICs in bond
|
||||
foreach (bond_nics($ini[$set],$set) as $nic) {
|
||||
if (isset($old['SYSNICS'])) $nic = ifname($nic);
|
||||
if ($nic && $nic!=$ifname) exec("/etc/rc.d/rc.inet1 ".escapeshellarg("{$nic}_stop")." >/dev/null");
|
||||
}
|
||||
} elseif ($ini[$set]['BRIDGING']=='yes') {
|
||||
// release additional NICs in bridge
|
||||
foreach (bridge_nics($ini[$set],$set) as $nic) {
|
||||
if (isset($old['SYSNICS'])) $nic = ifname($nic);
|
||||
if ($nic && $nic!=$ifname) exec("/etc/rc.d/rc.inet1 ".escapeshellarg("{$nic}_stop")." >/dev/null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create configuration file for all available interfaces
|
||||
$i = 0; $new = []; $new[] = "# Generated settings:";
|
||||
foreach ($ini as $name => $port) {
|
||||
$bonding = $port['BONDING']=='yes';
|
||||
$bridging = $port['BRIDGING']=='yes';
|
||||
if ($bonding && in_array($name,bond_nics($port,$name))) continue;
|
||||
if ($bridging && in_array($name,bridge_nics($port,$name))) continue;
|
||||
$trunk = $port['TYPE']=='trunk';
|
||||
$j = 0; $x0 = 0;
|
||||
$iface = $bridging ? $port['BRNAME'] : ($bonding ? $port['BONDNAME'] : $name);
|
||||
$new[] = "IFNAME[$i]=\"$iface\"";
|
||||
if ($set==$name) $ifname = $iface;
|
||||
foreach ($port as $key => $val) {
|
||||
if (!strlen($val)) continue;
|
||||
if (preg_match('/^(TYPE|BONDING$|BRIDGING)/',$key)) continue;
|
||||
if (!$bonding && preg_match('/^(BONDING_MODE|BONDING_MIIMON|BONDNICS|BONDNAME)/',$key)) continue;
|
||||
if (!$bridging && preg_match('/^(BRSTP|BRFD|BRNICS|BRNAME)/',$key)) continue;
|
||||
[$item,$x] = array_pad(explode(':',$key,2),2,0);
|
||||
if ($trunk && $x>0 && preg_match('/^(VLANID|USE_DHCP6?|IPADDR6?|NETMASK6?|GATEWAY6?|METRIC|PRIVACY6|DESCRIPTION|PROTOCOL)/',$key)) {
|
||||
if ($x0 != $x) {$x0 = $x; $j++;}
|
||||
$vlan = ",$j]";
|
||||
} else $vlan = '';
|
||||
if (!$vlan && preg_match('/^VLANID/',$key)) continue;
|
||||
if ($item=='DHCP_KEEPRESOLV') $DHCP_KEEPRESOLV = $val;
|
||||
if ($item=='DHCP6_KEEPRESOLV') $DHCP6_KEEPRESOLV = $val;
|
||||
if ($item=='PROTOCOL') $PROTOCOL = $val;
|
||||
if ($item=='USE_DHCP') $USE_DHCP = $val;
|
||||
if ($item=='USE_DHCP6') $USE_DHCP6 = $val;
|
||||
if (in_array($item,['IPADDR','NETMASK','GATEWAY']) && $USE_DHCP!='no') continue;
|
||||
if (in_array($item,['IPADDR6','NETMASK6','GATEWAY6']) && $USE_DHCP6!='no') continue;
|
||||
if (preg_match('/^DNS_SERVER/',$key) && isset($DHCP_KEEPRESOLV) && $DHCP_KEEPRESOLV=='no') continue;
|
||||
if (preg_match('/^DNS6_SERVER/',$key) && isset($DHCP6_KEEPRESOLV) && $DHCP6_KEEPRESOLV=='no') continue;
|
||||
if ($item=='PRIVACY6' && $PROTOCOL=='ipv4') continue;
|
||||
if ($item=='METRIC' && $PROTOCOL=='ipv6') continue;
|
||||
if ($item=='METRIC6' && $PROTOCOL=='ipv4') continue;
|
||||
$new[] = $item.(preg_match('/^(DNS6?_SERVER|DHCP6?_KEEPRESOLV)/',$key)?'':'['.$i.($vlan?'':']')).$vlan."=\"$val\"";
|
||||
}
|
||||
if ($trunk) $new[] = "VLANS[$i]=\"".($j+1)."\"";
|
||||
$i++;
|
||||
}
|
||||
$new[] = "SYSNICS=\"$i\"";
|
||||
|
||||
file_put_contents($cfg,implode("\r\n",$new)."\r\n");
|
||||
// start interface with updated (new) configuration
|
||||
// don't execute when only interface description has changed
|
||||
if ($run) {
|
||||
exec("/etc/rc.d/rc.inet1 {$ifname}_start >/dev/null 2>&1");
|
||||
exec("/usr/local/sbin/create_network_ini $ifname >/dev/null 2>&1 &");
|
||||
}
|
||||
exit(0);
|
||||
?>
|
||||
77
emhttp/plugins/dynamix/scripts/newperms
Executable file
77
emhttp/plugins/dynamix/scripts/newperms
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/php -q
|
||||
<?
|
||||
# Copyright 2005-2023, Lime Technology
|
||||
#
|
||||
# Usage: newperms [dir] [owner] [group]
|
||||
# Recursively changes the ownership and permissions of the directory and all files/subdirs
|
||||
# within the directory.
|
||||
# If no arguments given, operates on /mnt/cache and /mnt/disk*, setting owner:group to nobody:users
|
||||
# default owner is 'nobody', default group is 'users'
|
||||
|
||||
# This was created to fix ownership/permissions when upgrading to Unraid version 5.
|
||||
|
||||
# With corrections suggested by forum member Stokkes
|
||||
|
||||
# Here's a breakdown of chmod "u-x,go-rwx,go+u,ugo+X"
|
||||
# u-x Clear the 'x' bit in the user permissions (leaves rw as-is)
|
||||
# go-rwx Clear the 'rwx' bits in both the group and other permissions
|
||||
# go+u Copy the user permissions to group and other
|
||||
# ugo+X Set the 'x' bit for directories in user, group, and other
|
||||
|
||||
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
|
||||
if ($nchan) unset($argv[$argc-1]); // remove nchan parameter
|
||||
|
||||
function write(...$messages){
|
||||
global $nchan;
|
||||
if ($nchan) {
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
} else {
|
||||
foreach ($messages as $message) echo $message;
|
||||
}
|
||||
}
|
||||
function process($path) {
|
||||
global $argv;
|
||||
$owner = $argv[2] ?? 'nobody';
|
||||
$group = $argv[3] ?? 'users';
|
||||
if (is_dir($path) && preg_match('/^\/mnt\/.+/',$path)) {
|
||||
write("Processing: $path\n", "... chmod -R u-x,go-rwx,go+u,ugo+X $path\n");
|
||||
exec("chmod -R u-x,go-rwx,go+u,ugo+X ".escapeshellarg($path));
|
||||
write("... chown -R $owner:$group $path\n");
|
||||
exec("chown -R $owner:$group ".escapeshellarg($path));
|
||||
write("... sync\n");
|
||||
exec("sync");
|
||||
write("\n");
|
||||
} else write("Error: expecting absolute folder path starting with /mnt\n");
|
||||
}
|
||||
|
||||
$startTime = time();
|
||||
|
||||
if ($argv[1]??'') {
|
||||
$paths = array_filter(explode('*',rawurldecode($argv[1])));
|
||||
foreach ($paths as $path) process($path);
|
||||
} else {
|
||||
$disks = parse_ini_file("/var/local/emhttp/disks.ini",true);
|
||||
foreach ($disks as $disk => $prop) {
|
||||
if (is_dir("/mnt/$disk") && !in_array($prop['status'],['DISK_NP_DSBL','DISK_NP'])) process("/mnt/$disk");
|
||||
}
|
||||
}
|
||||
|
||||
$time = time()-$startTime;
|
||||
$hours = floor($time/3600);
|
||||
$mins = floor($time/60)%60;
|
||||
$secs = floor($time%60);
|
||||
|
||||
write("Completed, elapsed time: ".sprintf('%02d:%02d:%02d', $hours, $mins, $secs)."\n");
|
||||
if ($nchan) write('_DONE_','');
|
||||
?>
|
||||
252
emhttp/plugins/dynamix/scripts/notify
Executable file
252
emhttp/plugins/dynamix/scripts/notify
Executable file
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
* Copyright 2012, Andrew Hamer-Adams, http://www.pixeleyes.co.nz.
|
||||
*
|
||||
* 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
require_once "$docroot/webGui/include/Encryption.php";
|
||||
|
||||
function usage() {
|
||||
echo <<<EOT
|
||||
notify [-e "event"] [-s "subject"] [-d "description"] [-i "normal|warning|alert"] [-m "message"] [-x] [-t] [-b] [add]
|
||||
create a notification
|
||||
use -e to specify the event
|
||||
use -s to specify a subject
|
||||
use -d to specify a short description
|
||||
use -i to specify the severity
|
||||
use -m to specify a message (long description)
|
||||
use -l to specify a link (clicking the notification will take you to that location)
|
||||
use -x to create a single notification ticket
|
||||
use -r to specify recipients and not use default
|
||||
use -t to force send email only (for testing)
|
||||
use -b to NOT send a browser notification
|
||||
all options are optional
|
||||
|
||||
notify init
|
||||
Initialize the notification subsystem.
|
||||
|
||||
notify smtp-init
|
||||
Initialize sendmail configuration (ssmtp in our case).
|
||||
|
||||
notify get
|
||||
Output a json-encoded list of all the unread notifications.
|
||||
|
||||
notify archive file
|
||||
Move file from 'unread' state to 'archive' state.
|
||||
|
||||
EOT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function generate_email($event, $subject, $description, $importance, $message, $recipients, $fqdnlink) {
|
||||
global $ssmtp;
|
||||
$rcpt = $ssmtp['RcptTo'];
|
||||
if (!$recipients)
|
||||
$to = implode(',', explode(' ', trim($rcpt)));
|
||||
else
|
||||
$to = $recipients;
|
||||
if (empty($to)) return;
|
||||
$subj = "{$ssmtp['Subject']}$subject";
|
||||
$headers = [];
|
||||
$headers[] = "MIME-Version: 1.0";
|
||||
$headers[] = "X-Mailer: PHP/".phpversion();
|
||||
$headers[] = "Content-type: text/plain; charset=utf-8";
|
||||
$headers[] = "From: {$ssmtp['root']}";
|
||||
$headers[] = "Reply-To: {$ssmtp['root']}";
|
||||
if (($importance == "warning" || $importance == "alert") && $ssmtp['SetEmailPriority']=="True") {
|
||||
$headers[] = "X-Priority: 1 (highest)";
|
||||
$headers[] = "X-Mms-Priority: High";
|
||||
}
|
||||
$headers[] = "";
|
||||
$body = [];
|
||||
if (!empty($fqdnlink)) {
|
||||
$body[] = "Link: $fqdnlink";
|
||||
$body[] = "";
|
||||
}
|
||||
$body[] = "Event: $event";
|
||||
$body[] = "Subject: $subject";
|
||||
$body[] = "Description: $description";
|
||||
$body[] = "Importance: $importance";
|
||||
if (!empty($message)) {
|
||||
$body[] = "";
|
||||
foreach (explode('\n',$message) as $line)
|
||||
$body[] = $line;
|
||||
}
|
||||
$body[] = "";
|
||||
return mail($to, $subj, implode("\n", $body), implode("\n", $headers));
|
||||
}
|
||||
|
||||
function safe_filename($string) {
|
||||
$special_chars = ["?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}"];
|
||||
$string = trim(str_replace($special_chars, "", $string));
|
||||
$string = preg_replace('~[^0-9a-z -_]~i', '', $string);
|
||||
$string = preg_replace('~[- ]~i', '_', $string);
|
||||
return trim($string);
|
||||
}
|
||||
|
||||
// start
|
||||
if ($argc == 1) exit(usage());
|
||||
|
||||
extract(parse_plugin_cfg("dynamix",true));
|
||||
|
||||
$path = _var($notify,'path','/tmp/notifications');
|
||||
$unread = "$path/unread";
|
||||
$archive = "$path/archive";
|
||||
$agents_dir = "/boot/config/plugins/dynamix/notifications/agents";
|
||||
if (is_dir($agents_dir)) {
|
||||
$agents = [];
|
||||
foreach (array_diff(scandir($agents_dir), ['.','..']) as $p) {
|
||||
if (file_exists("{$agents_dir}/{$p}")) $agents[] = "{$agents_dir}/{$p}";
|
||||
}
|
||||
} else {
|
||||
$agents = NULL;
|
||||
}
|
||||
|
||||
switch ($argv[1][0]=='-' ? 'add' : $argv[1]) {
|
||||
case 'init':
|
||||
$files = glob("$unread/*.notify", GLOB_NOSORT);
|
||||
foreach ($files as $file) if (!is_readable($file)) chmod($file,0666);
|
||||
break;
|
||||
|
||||
case 'smtp-init':
|
||||
@mkdir($unread,0755,true);
|
||||
@mkdir($archive,0755,true);
|
||||
$conf = [];
|
||||
$conf[] = "# Generated settings:";
|
||||
$conf[] = "Root={$ssmtp['root']}";
|
||||
$domain = strtok($ssmtp['root'],'@');
|
||||
$domain = strtok('@');
|
||||
$conf[] = "rewriteDomain=$domain";
|
||||
$conf[] = "FromLineOverride=YES";
|
||||
$conf[] = "Mailhub={$ssmtp['server']}:{$ssmtp['port']}";
|
||||
$conf[] = "UseTLS={$ssmtp['UseTLS']}";
|
||||
$conf[] = "UseSTARTTLS={$ssmtp['UseSTARTTLS']}";
|
||||
if ($ssmtp['AuthMethod'] != "none") {
|
||||
$conf[] = "AuthMethod={$ssmtp['AuthMethod']}";
|
||||
$conf[] = "AuthUser={$ssmtp['AuthUser']}";
|
||||
$conf[] = "AuthPass=".base64_decrypt($ssmtp['AuthPass']);
|
||||
}
|
||||
$conf[] = "";
|
||||
file_put_contents("/etc/ssmtp/ssmtp.conf", implode("\n", $conf));
|
||||
break;
|
||||
|
||||
case 'cron-init':
|
||||
@mkdir($unread,0755,true);
|
||||
@mkdir($archive,0755,true);
|
||||
$text = empty($notify['status']) ? "" : "# Generated array status check schedule:\n{$notify['status']} $docroot/plugins/dynamix/scripts/statuscheck &> /dev/null\n\n";
|
||||
parse_cron_cfg("dynamix", "status-check", $text);
|
||||
$text = empty($notify['unraidos']) ? "" : "# Generated Unraid OS update check schedule:\n{$notify['unraidos']} $docroot/plugins/dynamix.plugin.manager/scripts/unraidcheck &> /dev/null\n\n";
|
||||
parse_cron_cfg("dynamix", "unraid-check", $text);
|
||||
$text = empty($notify['version']) ? "" : "# Generated plugins version check schedule:\n{$notify['version']} $docroot/plugins/dynamix.plugin.manager/scripts/plugincheck &> /dev/null\n\n";
|
||||
parse_cron_cfg("dynamix", "plugin-check", $text);
|
||||
$text = empty($notify['system']) ? "" : "# Generated system monitoring schedule:\n{$notify['system']} $docroot/plugins/dynamix/scripts/monitor &> /dev/null\n\n";
|
||||
parse_cron_cfg("dynamix", "monitor", $text);
|
||||
$text = empty($notify['docker_update']) ? "" : "# Generated docker monitoring schedule:\n{$notify['docker_update']} $docroot/plugins/dynamix.docker.manager/scripts/dockerupdate check &> /dev/null\n\n";
|
||||
parse_cron_cfg("dynamix", "docker-update", $text);
|
||||
$text = empty($notify['language_update']) ? "" : "# Generated languages version check schedule:\n{$notify['language_update']} $docroot/plugins/dynamix.plugin.manager/scripts/languagecheck &> /dev/null\n\n";
|
||||
parse_cron_cfg("dynamix", "language-check", $text);
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
$event = 'Unraid Status';
|
||||
$subject = 'Notification';
|
||||
$description = 'No description';
|
||||
$importance = 'normal';
|
||||
$message = $recipients = $link = $fqdnlink = '';
|
||||
$timestamp = time();
|
||||
$ticket = $timestamp;
|
||||
$mailtest = false;
|
||||
$overrule = false;
|
||||
$noBrowser = false;
|
||||
|
||||
$options = getopt("l:e:s:d:i:m:r:xtb");
|
||||
foreach ($options as $option => $value) {
|
||||
switch ($option) {
|
||||
case 'e':
|
||||
$event = $value;
|
||||
break;
|
||||
case 's':
|
||||
$subject = $value;
|
||||
break;
|
||||
case 'd':
|
||||
$description = $value;
|
||||
break;
|
||||
case 'i':
|
||||
$importance = strtok($value,' ');
|
||||
$overrule = strtok(' ');
|
||||
break;
|
||||
case 'm':
|
||||
$message = $value;
|
||||
break;
|
||||
case 'r':
|
||||
$recipients = $value;
|
||||
break;
|
||||
case 'x':
|
||||
$ticket = 'ticket';
|
||||
break;
|
||||
case 't':
|
||||
$mailtest = true;
|
||||
break;
|
||||
case 'b':
|
||||
$noBrowser = true;
|
||||
break;
|
||||
case 'l':
|
||||
$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
|
||||
$link = $value;
|
||||
$fqdnlink = (strpos($link,"http") === 0) ? $link : $nginx['NGINX_DEFAULTURL'].$link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$unread = "{$unread}/".safe_filename("{$event}-{$ticket}.notify");
|
||||
$archive = "{$archive}/".safe_filename("{$event}-{$ticket}.notify");
|
||||
if (file_exists($archive)) break;
|
||||
$entity = $overrule===false ? $notify[$importance] : $overrule;
|
||||
if (!$mailtest) file_put_contents($archive,"timestamp=$timestamp\nevent=$event\nsubject=$subject\ndescription=$description\nimportance=$importance\n".($message ? "message=".str_replace('\n','<br>',$message)."\n" : ""));
|
||||
if (($entity & 1)==1 && !$mailtest && !$noBrowser) file_put_contents($unread,"timestamp=$timestamp\nevent=$event\nsubject=$subject\ndescription=$description\nimportance=$importance\nlink=$link\n");
|
||||
if (($entity & 2)==2 || $mailtest) if (!generate_email($event, $subject, str_replace('<br>','. ',$description), $importance, $message, $recipients, $fqdnlink)) exit(1);
|
||||
if (($entity & 4)==4 && !$mailtest) { if (is_array($agents)) {foreach ($agents as $agent) {exec("TIMESTAMP='$timestamp' EVENT=".escapeshellarg($event)." SUBJECT=".escapeshellarg($subject)." DESCRIPTION=".escapeshellarg($description)." IMPORTANCE=".escapeshellarg($importance)." CONTENT=".escapeshellarg($message)." LINK=".escapeshellarg($fqdnlink)." bash ".$agent);};}};
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
$output = [];
|
||||
$json = [];
|
||||
$files = glob("$unread/*.notify", GLOB_NOSORT);
|
||||
usort($files, function($a,$b){return filemtime($a)-filemtime($b);});
|
||||
$i = 0;
|
||||
foreach ($files as $file) {
|
||||
$fields = file($file,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
|
||||
$time = true;
|
||||
$output[$i]['file'] = basename($file);
|
||||
$output[$i]['show'] = (fileperms($file) & 0x0FFF)==0400 ? 0 : 1;
|
||||
foreach ($fields as $field) {
|
||||
if (!$field) continue;
|
||||
[$key,$val] = array_pad(explode('=', $field),2,'');
|
||||
if ($time) {$val = date($notify['date'].' '.$notify['time'], $val); $time = false;}
|
||||
$output[$i][trim($key)] = trim($val);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
echo json_encode($output, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
|
||||
case 'archive':
|
||||
if ($argc != 3) exit(usage());
|
||||
$file = $argv[2];
|
||||
if (strpos(realpath("$unread/$file"),$unread.'/')===0) @unlink("$unread/$file");
|
||||
break;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
?>
|
||||
39
emhttp/plugins/dynamix/scripts/parity_control
Executable file
39
emhttp/plugins/dynamix/scripts/parity_control
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$stamps = '/var/tmp/stamps.ini';
|
||||
$mdcmd = '/usr/local/sbin/mdcmd';
|
||||
|
||||
function mdstat($key) {
|
||||
return exec("grep -Pom1 '^$key=\K.+' /proc/mdstat");
|
||||
}
|
||||
// Only act on active parity-check operation
|
||||
if (mdstat('mdResyncPos')>0 && substr(mdstat('mdResyncAction'),0,7)=='check P') {
|
||||
switch ($argv[1]) {
|
||||
case 'pause':
|
||||
if (mdstat('mdResync')>0) {
|
||||
if (!file_exists($stamps)) file_put_contents($stamps,mdstat('sbSynced'));
|
||||
file_put_contents($stamps,','.time(),FILE_APPEND);
|
||||
exec("$mdcmd nocheck pause");
|
||||
}
|
||||
break;
|
||||
case 'resume':
|
||||
if (mdstat('mdResync')==0) {
|
||||
file_put_contents($stamps,','.time(),FILE_APPEND);
|
||||
exec("$mdcmd check resume");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
71
emhttp/plugins/dynamix/scripts/parity_history
Executable file
71
emhttp/plugins/dynamix/scripts/parity_history
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'main';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$month = [' Jan '=>'-01-',' Feb '=>'-02-',' Mar '=>'-03-',' Apr '=>'-04-',' May '=>'-05-',' Jun '=>'-06-',' Jul '=>'-07-',' Aug '=>'-08-',' Sep '=>'-09-',' Oct '=>'-10-',' Nov '=>'-11-',' Dec '=>'-12-'];
|
||||
|
||||
function this_plus($val, $word, $last) {
|
||||
return $val>0 ? (($val||$last)?($val.' '.$word.($last?'':', ')):'') : '';
|
||||
}
|
||||
function this_duration($time) {
|
||||
if (!$time) return 'Unavailable';
|
||||
$days = floor($time/86400);
|
||||
$hmss = $time-$days*86400;
|
||||
$hour = floor($hmss/3600);
|
||||
$mins = floor($hmss/60)%60;
|
||||
$secs = $hmss%60;
|
||||
return this_plus($days,_('day'),($hour|$mins|$secs)==0).this_plus($hour,_('hr'),($mins|$secs)==0).this_plus($mins,_('min'),$secs==0).this_plus($secs,_('sec'),true);
|
||||
}
|
||||
$log = "/boot/config/parity-checks.log";
|
||||
$head = "<table style='margin-top:10px;background-color:inherit'><tr style='font-weight:bold'><td>"._('Action')."</td><td>"._('Date')."</td><td>"._('Size')."</td><td>"._('Duration')."</td><td>"._('Speed')."</td><td>"._('Status')."</td><td>"._('Errors')."</td></tr>";
|
||||
$list = [];
|
||||
|
||||
if (file_exists($log)) {
|
||||
$handle = fopen($log, 'r');
|
||||
while (($row = fgets($handle))!==false) {
|
||||
[$date,$duration,$speed,$status,$error,$action,$size] = my_explode('|',$row,7);
|
||||
$action = preg_split('/\s+/',$action);
|
||||
switch ($action[0]) {
|
||||
case 'recon': $action = in_array($action[1],['P','Q']) ? _('Parity-Sync') : _('Data-Rebuild'); break;
|
||||
case 'check': $action = count($action)>1 ? _('Parity-Check') : _('Read-Check'); break;
|
||||
case 'clear': $action = _('Disk-Clear'); break;
|
||||
default : $action = '-'; break;
|
||||
}
|
||||
$date = str_replace(' ',', ',strtr(str_replace(' ',' 0',$date),$month));
|
||||
$date .= ' ('._(date('l',strtotime($date)),0).')';
|
||||
$size = $size ? my_scale($size*1024,$unit,-1)." $unit" : '-';
|
||||
$duration = this_duration($duration);
|
||||
// handle both old and new speed notation
|
||||
$speed = $speed ? (is_numeric($speed) ? my_scale($speed,$unit,1)." $unit/s" : $speed) : _('Unavailable');
|
||||
$status = $status==0 ? _('OK') : ($status==-4 ? _('Canceled') : $status);
|
||||
$list[] = "<tr><td>$action</td><td>$date</td><td>$size</td><td>$duration</td><td>$speed</td><td>$status</td><td>$error</td></tr>";
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
if ($list) {
|
||||
$list = array_reverse($list);
|
||||
} else {
|
||||
$list[] = "<tr><td colspan='7' style='text-align:center;padding-top:12px'>"._('No parity check history present')."!</td></tr>";
|
||||
}
|
||||
echo $head,implode($list),"</table>";
|
||||
?>
|
||||
25
emhttp/plugins/dynamix/scripts/reiserfs_check
Executable file
25
emhttp/plugins/dynamix/scripts/reiserfs_check
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# reiserfs_check start <dev> <id> <options>
|
||||
# reiserfs_check status <dev> <id>
|
||||
# reiserfs_check cancel <dev>
|
||||
|
||||
# using /var/lib because that's where btrfs puts status
|
||||
mkdir -p /var/lib/reiserfs
|
||||
case "$1" in
|
||||
'start')
|
||||
# using /var/lib because that's where btrfs puts status
|
||||
exec /sbin/reiserfsck $2 --yes --quiet $4 &> /var/lib/reiserfs/check.status.$3 &
|
||||
;;
|
||||
'status')
|
||||
if [ -f /var/lib/reiserfs/check.status.$3 ]; then
|
||||
cat /var/lib/reiserfs/check.status.$3
|
||||
else
|
||||
echo "Not available"
|
||||
fi;
|
||||
pgrep -f "/sbin/reiserfsck $2" >/dev/null
|
||||
;;
|
||||
'cancel')
|
||||
pkill -f "/sbin/reiserfsck $2"
|
||||
echo "Cancelled" >> /var/lib/reiserfs/check.status.$3
|
||||
;;
|
||||
esac
|
||||
10
emhttp/plugins/dynamix/scripts/reload_services
Executable file
10
emhttp/plugins/dynamix/scripts/reload_services
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
SERVICES="sshd avahidaemon samba rpc nfsd ntpd nginx"
|
||||
|
||||
for cmd in $SERVICES; do
|
||||
[[ $cmd == $1 ]] && option=renew || option=reload
|
||||
if /etc/rc.d/rc.$cmd update; then
|
||||
/etc/rc.d/rc.$cmd $option >/dev/null 2>&1
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
94
emhttp/plugins/dynamix/scripts/rsyslog_config
Executable file
94
emhttp/plugins/dynamix/scripts/rsyslog_config
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
|
||||
CFG=/boot/config/rsyslog.conf
|
||||
ETC=/etc/rsyslog.conf
|
||||
USB=/boot/config/rsyslog.local
|
||||
LOG=/etc/logrotate.d/rsyslog.local
|
||||
|
||||
# read settings
|
||||
source /boot/config/rsyslog.cfg
|
||||
|
||||
# read $var
|
||||
source /var/local/emhttp/var.ini
|
||||
[[ $fsState == Started ]] || h='#'
|
||||
|
||||
# create local ruleset
|
||||
if ! grep -q '^\$RuleSet local$' $ETC; then
|
||||
sed -ri '/^# Include all config files in/a$RuleSet local' $ETC
|
||||
sed -ri '/^#?\*\.\* @@?.*:[0-9]+$/a$DefaultRuleset local' $ETC
|
||||
fi
|
||||
|
||||
# local syslog server
|
||||
if [[ -n $local_server ]]; then
|
||||
if ! grep -q '^\$RuleSet remote$' $ETC; then
|
||||
sed -ri '$a\$RuleSet remote\n\$FileOwner nobody\n\$FileGroup users\n$FileCreateMode 0666\n\$IncludeConfig /etc/rsyslog.d/*.conf # remote\n*.* ?remote' $ETC
|
||||
fi
|
||||
# enable/disable local logging
|
||||
sed -ri "s/^#?(\*\.\* \?remote)$/$h\1/" $ETC
|
||||
if [[ $server_protocol == tcp || $server_protocol == both ]]; then
|
||||
sed -ri '/^\$InputTCPServerBindRuleset remote$/d;/^\$InputTCPServerRun [0-9]+$/d;s/^#?(\$ModLoad imtcp)/\1/' $ETC
|
||||
sed -ri "\$a\\\$InputTCPServerBindRuleset remote\n\\\$InputTCPServerRun ${server_port:-514}" $ETC
|
||||
[[ $server_protocol == tcp ]] && sed -ri 's/^(\$ModLoad imudp)/#\1/;/^\$InputUDPServerBindRuleset remote$/d;/^\$UDPServerRun [0-9]+$/d' $ETC
|
||||
fi
|
||||
if [[ $server_protocol == udp || $server_protocol == both ]]; then
|
||||
sed -ri '/^\$InputUDPServerBindRuleset remote$/d;/^\$UDPServerRun [0-9]+$/d;s/^#?(\$ModLoad imudp)/\1/' $ETC
|
||||
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
|
||||
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
|
||||
|
||||
# remote syslog server
|
||||
if [[ -n $remote_server ]]; then
|
||||
[[ $remote_protocol == udp ]] && com='@' || com='@@'
|
||||
sed -ri "s/^#?(\*\.\*) @@?.*:[0-9]+$/\1 $com$remote_server:${remote_port:-514}/" $ETC
|
||||
else
|
||||
sed -ri 's/^#?(\*\.\* @@?.*:[0-9]+)$/#\1/' $ETC
|
||||
fi
|
||||
|
||||
# mirror syslog to flash
|
||||
if [[ -n $syslog_flash ]]; then
|
||||
if ! grep -q '^\$template flash,' $ETC; then
|
||||
sed -ri '/^#\$UDPServerRun [0-9]+.*$/a$template flash,"/boot/logs/syslog"' $ETC
|
||||
sed -ri '/^\*\.debug .*syslog$/a*.debug ?flash' $ETC
|
||||
fi
|
||||
else
|
||||
sed -ri '/^\$template flash,"\/boot\/logs\/syslog"$/d;/^\*\.debug \?flash/d' $ETC
|
||||
fi
|
||||
|
||||
# copy conf to flash (read settings on reboot)
|
||||
todos <$ETC >$CFG
|
||||
|
||||
# keep local logging disabled at startup
|
||||
# disk mount and disk unmount events are used to start/stop logging
|
||||
sed -ri 's/^(\*\.\* \?remote)/#\1/' $CFG
|
||||
|
||||
# update syslog rotation
|
||||
if [[ -n $local_server ]]; then
|
||||
if [[ -n $log_rotation ]]; then
|
||||
cat <<- EOF > $LOG
|
||||
$server_folder/*.log {
|
||||
su nobody users
|
||||
missingok
|
||||
create 0666 nobody users
|
||||
size $log_size
|
||||
rotate $log_files
|
||||
sharedscripts
|
||||
postrotate
|
||||
/bin/kill -HUP \$(cat /var/run/rsyslogd.pid 2>/dev/null) 2>/dev/null || true
|
||||
endscript
|
||||
}
|
||||
EOF
|
||||
chmod 644 $LOG
|
||||
# keep copy on flash
|
||||
cp -f $LOG $USB
|
||||
else
|
||||
# clean up
|
||||
rm -f $LOG $USB
|
||||
fi
|
||||
fi
|
||||
|
||||
# update syslog daemon
|
||||
/etc/rc.d/rc.rsyslogd restart &> /dev/null
|
||||
42
emhttp/plugins/dynamix/scripts/run_cmd
Executable file
42
emhttp/plugins/dynamix/scripts/run_cmd
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/webGui/include/ColorCoding.php";
|
||||
|
||||
$last = "\e[0m";
|
||||
$color = ['text' => $last, 'error' => "\e[91m", 'warn' => "\e[93m", 'system' => "\e[96m", 'array' => "\e[92m", 'login' => "\e[95m"];
|
||||
$call = ['tail','docker','grep'];
|
||||
|
||||
if (!in_array($argv[1]??'',$call)) exit;
|
||||
|
||||
$dummy = array_shift($argv);
|
||||
$files = array_pop($argv);
|
||||
array_multisort(array_map('filemtime',($logs = glob($files,GLOB_NOSORT))),SORT_ASC,$logs);
|
||||
if (empty($logs)) $logs = [$files];
|
||||
|
||||
foreach ($color as $cat => $val) echo $val,$cat,$last," "; echo "\n\n";
|
||||
foreach ($logs as $log) {
|
||||
$cmd = popen(implode(' ',$argv).' '.escapeshellarg($log),'r');
|
||||
while (($line = fgets($cmd))!==false) {
|
||||
$lead = $last;
|
||||
foreach ($match as $type) foreach ($type['text'] as $text) if (preg_match("/$text/i",$line)) {
|
||||
$lead = $color[$type['class']];
|
||||
break 2;
|
||||
}
|
||||
echo $lead,rtrim($line,"\n"),$last,"\n";
|
||||
}
|
||||
pclose($cmd);
|
||||
}
|
||||
?>
|
||||
73
emhttp/plugins/dynamix/scripts/select_case
Executable file
73
emhttp/plugins/dynamix/scripts/select_case
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'dashboard';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$boot = "/boot/config/plugins/dynamix";
|
||||
$file = $argv[1];
|
||||
$cmodel = is_file("$boot/$file") ? file_get_contents("$boot/$file") : '';
|
||||
|
||||
$style = ["<style>"];
|
||||
$style[] = "div.case-list{float:left;padding:10px;margin:0 45px 64px 0;height:128px;width:128px;text-align:center;cursor:pointer}";
|
||||
$style[] = "div.case-list span{position:relative;top:64px;width:auto;max-width:128px;height:128px;font-size:128px}";
|
||||
$style[] = "div.case-list span.fa{top:24px;max-width:80px;font-size:80px}";
|
||||
$style[] = "div.case-list:hover{color:#f0000c}";
|
||||
$style[] = "div.case-name{position:relative;top:74px;font-family:clear-sans!important}";
|
||||
$style[] = "div.custom-name{position:relative;top:10px;font-family:clear-sans!important}";
|
||||
$style[] = "</style>";
|
||||
|
||||
$script = ["<script>"];
|
||||
$script[] = "function selectDone() {";
|
||||
$script[] = " \$('.sweet-alert').hide('fast').removeClass('nchan');";
|
||||
$script[] = " swal.close();";
|
||||
$script[] = " getCase();";
|
||||
$script[] = "}";
|
||||
$script[] = "function setCase(model) {";
|
||||
$script[] = " \$.post('/webGui/include/SelectCase.php',{mode:'set',file:'$file',model:model},function(){selectDone();});";
|
||||
$script[] = "}";
|
||||
$script[] = "function importFile(file) {";
|
||||
$script[] = " if (file.name.split('.').pop().toLowerCase() != 'png') return;";
|
||||
$script[] = " var reader = new FileReader();";
|
||||
$script[] = " reader.readAsDataURL(file);";
|
||||
$script[] = " reader.onload = function(e){\$.post('/webGui/include/SelectCase.php',{mode:'file',file:'$file',data:e.target.result},function(){selectDone();})};";
|
||||
$script[] = "}";
|
||||
$script[] = "</script>";
|
||||
|
||||
$html = ["<div>"];
|
||||
$cases = file("$docroot/webGui/styles/default-cases.css",FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($cases as $case) if (substr($case,0,6)=='.case-') $models[] = substr($case,1,strpos($case,':')-1);
|
||||
natsort($models);
|
||||
for ($i=0; $i < count($models); $i++) {
|
||||
$model = $models[$i];
|
||||
$name = substr($model,5);
|
||||
$title = str_replace('3u-avs-10-4','3u-avs-10/4',$name);
|
||||
$select = $name==$cmodel ? 'color:#e68a00' : '';
|
||||
$html[] = "<div id='$name' class='case-list' style='$select' onclick='setCase(\"$name\")'><span class='$model'></span><div class='case-name'>$title</div></div>";
|
||||
}
|
||||
$select = $cmodel=='case-model.png' ? 'color:#e68a00' : '';
|
||||
$html[] = "<div id='Custom' class='case-list' style='$select' onclick='$(\"input#file\").trigger(\"click\")'><span class='fa fa-file-image-o'></span><div class='custom-name'>"._('custom image')."</div></div>";
|
||||
$html[] = "</div></div>";
|
||||
$html[] = "<input type='file' id='file' accept='.png' onchange='importFile(this.files[0])' style='display:none'>";
|
||||
$html[] = "</div>";
|
||||
|
||||
echo implode($style),implode($script),implode($html);
|
||||
?>
|
||||
33
emhttp/plugins/dynamix/scripts/share_size
Executable file
33
emhttp/plugins/dynamix/scripts/share_size
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# usage: share_size <user-share> <output-file>
|
||||
|
||||
# Creates an "ini" output file suitable for php parse_ini_function which describes
|
||||
# the size <user-share> takes up on the cache disk and each data disk. Since this
|
||||
# uses the 'du' command, could take awhile.
|
||||
|
||||
share="$1"
|
||||
output="/var/local/emhttp/$share.$2"
|
||||
pools=${3//,/ }
|
||||
total=0;
|
||||
|
||||
echo "Computing disk usage for $share..."
|
||||
rm -f "$output"
|
||||
|
||||
function check {
|
||||
if [[ -e "$1/$2" ]] ; then
|
||||
echo "calculating $1 usage..."
|
||||
size=$(du -sb "$1/$2"|cut -f1)
|
||||
echo "$(basename "$1")=$size" >>"$output"
|
||||
total=$(($total + $size))
|
||||
fi
|
||||
}
|
||||
|
||||
for pool in $pools; do
|
||||
check "/mnt/$pool" "$share"
|
||||
done;
|
||||
while IFS=$'\n' read -r disk; do
|
||||
check "$disk" "$share"
|
||||
done <<< $(ls -vd /mnt/disk[0-9]*)
|
||||
echo "disk.total=$total" >>"$output"
|
||||
echo "total disk usage: $total"
|
||||
99
emhttp/plugins/dynamix/scripts/show_interfaces
Executable file
99
emhttp/plugins/dynamix/scripts/show_interfaces
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
|
||||
NETWORK_INI="/var/local/emhttp/network.ini"
|
||||
SYSTEM="/sys/class/net"
|
||||
EXTRA="/boot/config/network-extra.cfg"
|
||||
|
||||
link() {
|
||||
grep -Pom1 "^$1=\"\K[^\"]+" $NETWORK_INI.eth
|
||||
}
|
||||
|
||||
zero() {
|
||||
data=;
|
||||
for i in ${!bind[@]}; do
|
||||
[[ ${bind[$i]} == $1 ]] && data=$1
|
||||
done
|
||||
echo $data
|
||||
}
|
||||
|
||||
show() {
|
||||
case $# in
|
||||
1) ip addr show to $1 2>/dev/null|grep -Pom1 '^\d+: \K[^:]+';;
|
||||
2) ip addr show $1 $2 2>/dev/null|grep -Pom1 'inet6? \K[^\/]+';;
|
||||
3) ip $1 addr show $2 $3 2>/dev/null|grep -Pom1 'inet6? \K[^\/]+';;
|
||||
esac
|
||||
}
|
||||
|
||||
remove() {
|
||||
[[ $# -eq 0 ]] && return
|
||||
for i in ${!bind[@]}; do
|
||||
[[ ${bind[$i]} == $1 ]] && unset 'bind[i]'
|
||||
done
|
||||
}
|
||||
|
||||
extra() {
|
||||
source <(/usr/bin/fromdos <$EXTRA)
|
||||
for net in $include_interfaces; do
|
||||
if [[ -z ${net//[^.:]} ]]; then
|
||||
# net is an interface name, validate
|
||||
[[ -n $(show dev $net) && -z $(zero $net) ]] && bind+=($net)
|
||||
else
|
||||
# net is an IP address, convert to name
|
||||
net=$(show $net)
|
||||
[[ -n $net && -z $(zero $net) ]] && bind+=($net)
|
||||
fi
|
||||
done
|
||||
for net in $exclude_interfaces; do
|
||||
if [[ -z ${net//[^.:]} ]]; then
|
||||
# net is an interface name, remove
|
||||
remove $net
|
||||
else
|
||||
# net is an IP address, convert to name and remove
|
||||
remove $(show $net)
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
bind=();
|
||||
if [[ -f $NETWORK_INI ]]; then
|
||||
# get interface and vlan configurations
|
||||
for eth in $(grep -Po '^\[\K[^\]]+' $NETWORK_INI); do
|
||||
# main interface
|
||||
if [[ -e $SYSTEM/$eth ]]; then
|
||||
sed -n "/^\[$eth\]/,/^\[eth/p" $NETWORK_INI >$NETWORK_INI.eth
|
||||
net=$eth
|
||||
[[ $(link BONDING) == yes ]] && net=${eth/eth/bond}
|
||||
[[ $(link BRIDGING) == yes ]] && net=${eth/eth/br}
|
||||
net4=$(link IPADDR:0)
|
||||
net6=$(link IPADDR6:0)
|
||||
[[ -n $net4 || -n $net6 ]] && bind+=($net)
|
||||
if [[ $(link TYPE) == trunk ]]; then
|
||||
# vlan interface
|
||||
for vlan in $(grep -Po '^VLANID:\K\d+' $NETWORK_INI.eth); do
|
||||
net4=$(link IPADDR:$vlan)
|
||||
net6=$(link IPADDR6:$vlan)
|
||||
[[ -n $net4 || -n $net6 ]] && bind+=($net.$vlan)
|
||||
done
|
||||
fi
|
||||
fi
|
||||
done
|
||||
# add active WG tunnels
|
||||
for wg in $(wg show interfaces); do
|
||||
net4=$(show -4 dev $wg)
|
||||
net6=$(show -6 dev $wg)
|
||||
[[ -n $net4 || -n $net6 ]] && bind+=($wg)
|
||||
done
|
||||
# add user defined interfaces
|
||||
[[ -f $EXTRA ]] && extra
|
||||
# remove temporary file
|
||||
rm -f $NETWORK_INI.eth
|
||||
fi
|
||||
bind=${bind[@]}
|
||||
if [[ $1 == ip ]]; then
|
||||
ip=()
|
||||
for net in $bind; do
|
||||
ip+=("$net#[$(show -4 dev $net)#$(show -6 dev $net)]")
|
||||
done
|
||||
bind=${ip[@]}
|
||||
fi
|
||||
echo ${bind// /, }
|
||||
89
emhttp/plugins/dynamix/scripts/ssd_trim
Executable file
89
emhttp/plugins/dynamix/scripts/ssd_trim
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// cron operation
|
||||
if ($argc==2 && $argv[1]=='cron') {
|
||||
// trim btrfs, xfs
|
||||
echo shell_exec("fstrim -va 2>/dev/null");
|
||||
// trim zfs
|
||||
zfs_trim(false);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'settings';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
|
||||
function is_hdd($disk) {
|
||||
$disk = explode('/',$disk);
|
||||
$disk = preg_replace('/^(sd[a-z]+|nvme[0-9]+n1)p?1$/','$1',end($disk));
|
||||
return file_get_contents("/sys/block/$disk/queue/rotational")==1;
|
||||
}
|
||||
|
||||
function zfs_info($name) {
|
||||
$trim = preg_replace('/(.$)/',' $1',exec("zfs list -Ho used $name"))."iB";
|
||||
$bytes = exec("zfs list -Hpo used $name");
|
||||
exec("zpool list -vHP $name|grep -Po '^\s+\K/\S+'",$devs);
|
||||
foreach ($devs as &$dev) if (is_hdd($dev)) $dev = '';
|
||||
return "/mnt/$name: $trim ($bytes bytes) trimmed on ".implode(', ',array_filter($devs));
|
||||
}
|
||||
|
||||
function zfs_trim($write) {
|
||||
if (!file_exists('/proc/spl/kstat/zfs/arcstats')) return;
|
||||
exec("zfs list -d0 -Ho name",$pools);
|
||||
foreach ($pools as $name) {
|
||||
if ($write) {
|
||||
write("/mnt/$name: ... <i class='fa fa-spin fa-circle-o-notch'></i>\r");
|
||||
if (exec("zpool trim -w $name 2>&1")=='') write(zfs_info($name)."\r","\n"); else write("\r");
|
||||
} else {
|
||||
if (exec("zpool trim -w $name 2>&1")=='') echo zfs_info($name)."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write(_("TRIM operation started")."\n","\n","\n");
|
||||
// trim btrfs, xfs
|
||||
exec("findmnt -lnt btrfs,xfs -o target,source|awk '\$2!~\"\\\\[\"{print \$1,\$2}'",$mounts);
|
||||
foreach ($mounts as $mount) {
|
||||
[$target,$source] = explode(' ',$mount);
|
||||
if (is_hdd($source)) continue;
|
||||
write("$target: ... <i class='fa fa-spin fa-circle-o-notch'></i>\r");
|
||||
$trim = exec("fstrim -v $target 2>/dev/null");
|
||||
if ($trim) write("$trim on $source\r","\n"); else write("\r");
|
||||
}
|
||||
// trim zfs
|
||||
zfs_trim(true);
|
||||
write(_("Finished")."\n",'_DONE_','');
|
||||
?>
|
||||
182
emhttp/plugins/dynamix/scripts/statuscheck
Executable file
182
emhttp/plugins/dynamix/scripts/statuscheck
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$var = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
|
||||
$disks = @parse_ini_file("/var/local/emhttp/disks.ini",true) ?: [];
|
||||
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
require_once "$docroot/webGui/include/CustomMerge.php";
|
||||
|
||||
$script = "$docroot/webGui/scripts/notify";
|
||||
extract(parse_plugin_cfg("dynamix",true));
|
||||
$output = _var($notify,'report');
|
||||
$server = strtoupper(_var($var,'NAME','tower'));
|
||||
$data = [];
|
||||
$parity = $pools = false;
|
||||
$error0 = $error1 = $error2 = $error3 = 0;
|
||||
|
||||
function plus($val, $word, $last) {
|
||||
return $val>0 ? (($val || $last) ? ($val.' '.$word.($val!=1?'s':'').($last ?'':', ')) : '') : '';
|
||||
}
|
||||
function my_temp($value) {
|
||||
global $display;
|
||||
if ($value=='*') return ' - standby';
|
||||
$unit = _var($display,'unit','C');
|
||||
return ' - active '.($unit=='F' ? round(9/5*$value+32) : str_replace('.',_var($display,'number','.,')[0], $value)).' '.$unit;
|
||||
}
|
||||
function my_disk($name) {
|
||||
return ucfirst(preg_replace('/(\d+)$/',' $1',$name));
|
||||
}
|
||||
function my_scale($value, &$unit, $precision = NULL) {
|
||||
global $display;
|
||||
$scale = _var($display,'scale',-1);
|
||||
$number = _var($display,'number','.,');
|
||||
$units = ['B','KB','MB','GB','TB','PB'];
|
||||
if ($scale==0 && $precision===NULL) {
|
||||
$unit = '';
|
||||
return number_format($value, 0, $number[0], ($value>=10000 ? $number[1] : ''));
|
||||
} else {
|
||||
$base = $value ? floor(log($value, 1000)) : 0;
|
||||
if ($scale>0 && $base>$scale) $base = $scale;
|
||||
$value = round($value/pow(1000, $base), $precision===NULL ? 2 : $precision);
|
||||
if ($value>=1000 && $scale<0) { $value = 1; $base++; }
|
||||
$unit = $units[$base];
|
||||
return number_format($value, $precision===NULL ? (($value-intval($value)==0 || $value>=100) ? 0 : ($value>=10 ? 1 : 2)) : $precision, $number[0], ($value>=10000 ? $number[1] : ''));
|
||||
}
|
||||
}
|
||||
function my_check($time,$speed) {
|
||||
if (!$time) return 'unavailable (no parity-check entries logged)';
|
||||
$days = floor($time/86400);
|
||||
$hmss = $time-$days*86400;
|
||||
$hour = floor($hmss/3600);
|
||||
$mins = floor($hmss/60)%60;
|
||||
$secs = $hmss%60;
|
||||
return plus($days,'day',($hour|$mins|$secs)==0).plus($hour,'hour',($mins|$secs)==0).plus($mins,'minute',$secs==0).plus($secs,'second',true).". Average speed: $speed";
|
||||
}
|
||||
function my_time($time) {
|
||||
global $display;
|
||||
$date = my_date(_var($display,'date').(_var($display,'date')!='%c' ? ", "._var($display,'time') : ""), $time);
|
||||
$now = new DateTime("@".intval(time()/86400)*86400);
|
||||
$last = new DateTime("@".intval($time/86400)*86400);
|
||||
$days = date_diff($last,$now)->format('%a');
|
||||
switch (true) {
|
||||
case ($days<0):
|
||||
return $date;
|
||||
case ($days==0):
|
||||
return "$date (today)";
|
||||
case ($days==1):
|
||||
return "$date (yesterday)";
|
||||
default:
|
||||
return "$date ($days days ago)";
|
||||
}
|
||||
}
|
||||
function my_clock($time) {
|
||||
if (!$time) return 'less than a minute';
|
||||
$days = floor($time/1440);
|
||||
$hour = floor($time/60)%24;
|
||||
$mins = $time%60;
|
||||
return plus($days,'day',($hour|$mins)==0).plus($hour,'hour',$mins==0).plus($mins,'minute',true);
|
||||
}
|
||||
|
||||
function my_array(&$disk) {
|
||||
global $data,$display,$error0,$error1,$error2,$error3;
|
||||
$name = _var($disk,'name');
|
||||
$max = $disk['maxTemp'] ?? $display['max'] ?? 0;
|
||||
$hot = $disk['hotTemp'] ?? $display['hot'] ?? 0;
|
||||
if (strpos(_var($disk,'status'),'_NP')!==false) return false;
|
||||
$temp = _var($disk,'temp','*');
|
||||
if ($max>0 && $temp>=$max) {
|
||||
$fail = ' (disk is overheated';
|
||||
$error0++;
|
||||
} elseif ($hot>0 && $temp>=$hot) {
|
||||
$fail = ' (disk is hot';
|
||||
$error1++;
|
||||
} else {
|
||||
$fail = '';
|
||||
}
|
||||
if (_var($disk,'numErrors',0)>0) {
|
||||
if ($fail) $fail .= ', '; else $fail = ' (';
|
||||
$fail .= 'disk has read errors';
|
||||
$error2++;
|
||||
}
|
||||
if ($fail) $fail .= ')';
|
||||
$status = $fail ? ' [NOK]' : ' [OK]';
|
||||
$color = strtok(_var($disk,'color'),'-');
|
||||
if ($color=='red'||$color=='yellow') {$error3++; $status = ' ['.str_replace(['NP_','_'],['',' '],_var($disk,'status')).']';}
|
||||
$info = _var($disk,'id')." ("._var($disk,'device').")";
|
||||
if ($info==" ()") $info = 'No device identification present';
|
||||
$data[] = my_disk($name)." - $info".my_temp($temp).$fail.$status;
|
||||
return true;
|
||||
}
|
||||
|
||||
// generate report of array devices
|
||||
foreach ($disks as $disk) if (_var($disk,'type')=='Parity') $parity |= my_array($disk);
|
||||
foreach ($disks as $disk) if (_var($disk,'type')=='Data') my_array($disk);
|
||||
foreach ($disks as $disk) if (_var($disk,'type')=='Cache') $pools |= my_array($disk);
|
||||
|
||||
$size = count($data);
|
||||
|
||||
// generate parity report
|
||||
$data[] = '';
|
||||
$mdResync = _var($var,'mdResync',0);
|
||||
$action = preg_split('/\s+/',_var($var,'mdResyncAction'));
|
||||
if ($mdResync>0) {
|
||||
$mdResyncPos = _var($var,'mdResyncPos',0);
|
||||
$mdResyncDb = _var($var,'mdResyncDb',0);
|
||||
$mdResyncDt = _var($var,'mdResyncDt',0);
|
||||
switch ($action[0]) {
|
||||
case "recon": $mode = $action[1]=='P' ? 'Parity-Sync' : 'Data-Rebuild'; break;
|
||||
case "check": $mode = count($action)>1 ? 'Parity-Check' : 'Read-Check'; break;
|
||||
case "clear": $mode = 'Disk-Clear'; break;
|
||||
default : $mode = 'Unknown'; break;
|
||||
}
|
||||
$data[] = $mode." in progress.";
|
||||
$data[] = "Total size: ".my_scale($mdResync*1024, $unit)." $unit";
|
||||
$data[] = "Elapsed time: ".my_clock(floor((time()-_var($var,'sbUpdated',0))/60));
|
||||
$data[] = "Current position: ".my_scale($mdResyncPos*1024, $unit)." $unit (".number_format(($mdResyncPos/($mdResync/100+1)),1,$unraid['display']['number'][0],'')." %)";
|
||||
$data[] = "Estimated speed: ".my_scale($mdResyncDb/$mdResyncDt*1024, $unit, 1)." $unit/sec";
|
||||
$data[] = "Estimated finish: ".my_clock(round(((($mdResyncDt*(($mdResync-$mdResyncPos)/($mdResyncDb/100+1)))/100)/60),0));
|
||||
$data[] = "Sync errors ".(_var($var,'mdResyncCorr',0)==0 ? 'detected: ' : 'corrected: ')._var($var,'sbSyncErrs',0);
|
||||
} else {
|
||||
$sbSynced = _var($var,'sbSynced',0);
|
||||
$sbSynced2 = _var($var,'sbSynced2',0);
|
||||
$sbSyncErrs = _var($var,'sbSyncErrs',0);
|
||||
if (_var($var,'sbSyncExit',0)!=0) {
|
||||
$data[] = "Last check incomplete on ".my_time($sbSynced2).", finding $sbSyncErrs error".($sbSyncErrs==1?'.':'s.');
|
||||
$data[] = "Error code: ".$var['sbSyncExit'];
|
||||
} elseif ($sbSynced==0) {
|
||||
$data[] = "Parity has not been checked yet";
|
||||
} elseif ($sbSynced2>0) {
|
||||
if ($action[0]=='recon') {
|
||||
$data[] = $action[1]=='P' ? 'Parity is invalid' : 'Data-Rebuild is invalid';
|
||||
} else {
|
||||
$data[] = 'Parity is valid';
|
||||
}
|
||||
$duration = Max($sbSynced2-$sbSynced,1);
|
||||
$speed = my_scale(_var($var,'mdResyncSize',0)*1024/$duration,$unit,1)." $unit/s";
|
||||
$data[] = "Last checked on ".my_time($sbSynced2).", finding $sbSyncErrs error".($sbSyncErrs==1?'.':'s.');
|
||||
$data[] = "Duration: ".my_check($duration,$speed);
|
||||
}
|
||||
}
|
||||
|
||||
$word = $size==1 ? "" : "including ";
|
||||
$warn = ($error0 || $error3) ? "alert" : (($error1 || $error2) ? "warning" : "normal");
|
||||
$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'");
|
||||
|
||||
exit(0);
|
||||
?>
|
||||
191
emhttp/plugins/dynamix/scripts/system_information
Executable file
191
emhttp/plugins/dynamix/scripts/system_information
Executable file
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = '';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
function dmidecode($key, $n, $all=true) {
|
||||
$entries = array_filter(explode($key,shell_exec("dmidecode -qt$n")?:''));
|
||||
$properties = [];
|
||||
foreach ($entries as $entry) {
|
||||
$property = [];
|
||||
foreach (explode("\n",$entry) as $line) if (strpos($line,': ')!==false) {
|
||||
[$key,$value] = my_explode(': ',trim($line));
|
||||
$property[$key] = $value;
|
||||
}
|
||||
$properties[] = $property;
|
||||
}
|
||||
return $all ? $properties : $properties[0]??null;
|
||||
}
|
||||
|
||||
$var = @parse_ini_file('state/var.ini') ?: [];
|
||||
$model = _var($var,'SYS_MODEL',_('N/A'));
|
||||
$board = dmidecode('Base Board Information',2,0);
|
||||
$bios = dmidecode('BIOS Information',0,0);
|
||||
$cpu = dmidecode('Processor Information',4,0);
|
||||
$cpumodel = str_ireplace(["Processor","(C)","(R)","(TM)"],["","©","®","™"],exec("grep -Pom1 '^model name\s+:\s*\K.+' /proc/cpuinfo") ?: $cpu['Version']);
|
||||
$kernel = exec("uname -srm");
|
||||
$openssl = exec("openssl version|cut -d' ' -f2");
|
||||
|
||||
$board['Manufacturer'] = (empty($board['Manufacturer'])) ? _('Unknown') : $board['Manufacturer'];
|
||||
$board['Product Name'] = (empty($board['Product Name'])) ? "" : $board['Product Name'];
|
||||
$board['Version'] = (empty($board['Version'])) ? "" : _('Version')." ".$board['Version'];
|
||||
$board['Serial Number'] = (empty($board['Serial Number'])) ? "" : _('s/n')." ".$board['Serial Number'];
|
||||
$bios['Vendor'] = (empty($bios['Vendor'])) ? "" : $bios['Vendor'];
|
||||
$bios['Version'] = (empty($bios['Version'])) ? "" : _('Version')." ".$bios['Version'];
|
||||
$bios['Release Date'] = (empty($bios['Release Date'])) ? "" : _('Dated')." ".$bios['Release Date'];
|
||||
$cpu['Current Speed'] = (strpos($cpumodel,'@')===false && !empty($cpu['Current Speed'])) ? " @ {$cpu['Current Speed']}" : "";
|
||||
|
||||
// Check for Intel VT-x (vmx) or AMD-V (svm) cpu virtualization support
|
||||
// If either kvm_intel or kvm_amd are loaded then Intel VT-x (vmx) or AMD-V (svm) cpu virtualization support was found
|
||||
$strLoadedModules = shell_exec("/etc/rc.d/rc.libvirt test");
|
||||
|
||||
// Check for Intel VT-x (vmx) or AMD-V (svm) cpu virtualization support
|
||||
$strCPUInfo = file_get_contents('/proc/cpuinfo');
|
||||
|
||||
if (!empty($strLoadedModules)) {
|
||||
// Yah! CPU and motherboard supported and enabled in BIOS
|
||||
$hvm = _('Enabled');
|
||||
} else {
|
||||
$hvm = '<a href="https://docs.unraid.net/unraid-os/manual/vm-management#system-preparation" target="_blank">';
|
||||
if (strpos($strCPUInfo,'vmx')===false && strpos($strCPUInfo, 'svm')===false) {
|
||||
// CPU doesn't support virtualization
|
||||
$hvm .= _('Not Available');
|
||||
} else {
|
||||
// Motherboard either doesn't support virtualization or BIOS has it disabled
|
||||
$hvm .= _('Disabled');
|
||||
}
|
||||
$hvm .= '</a>';
|
||||
}
|
||||
|
||||
// Check for any IOMMU Groups
|
||||
$iommu_groups = shell_exec("find /sys/kernel/iommu_groups/ -type l");
|
||||
|
||||
if (!empty($iommu_groups)) {
|
||||
// Yah! CPU and motherboard supported and enabled in BIOS
|
||||
$iommu = _('Enabled');
|
||||
} else {
|
||||
$iommu = '<a href="https://docs.unraid.net/unraid-os/manual/vm-management#determining-hvmiommu-hardware-support" target="_blank">';
|
||||
if (strpos($strCPUInfo,'vmx')===false && strpos($strCPUInfo, 'svm')===false) {
|
||||
// CPU doesn't support virtualization so iommu would be impossible
|
||||
$iommu .= _('Not Available');
|
||||
} else {
|
||||
// Motherboard either doesn't support iommu or BIOS has it disabled
|
||||
$iommu .= _('Disabled');
|
||||
}
|
||||
$iommu .= '</a>';
|
||||
}
|
||||
|
||||
$cache_installed = [];
|
||||
$cache_devices = dmidecode('Cache Information',7);
|
||||
foreach ($cache_devices as $device) $cache_installed[] = $device['Socket Designation'].": ".str_replace(['kB','B'],['KB','iB'],$device['Installed Size']);
|
||||
|
||||
/*
|
||||
Memory Device (16) will get us each ram chip. By matching on MB it'll filter out Flash/Bios chips
|
||||
Sum up all the Memory Devices to get the amount of system memory installed. Convert MB to GB
|
||||
Physical Memory Array (16) usually one of these for a desktop-class motherboard but higher-end xeon motherboards
|
||||
might have two or more of these. The trick is to filter out any Flash/Bios types by matching on GB
|
||||
Sum up all the Physical Memory Arrays to get the motherboard's total memory capacity
|
||||
Extract error correction type, if none, do not include additional information in the output
|
||||
If maximum < installed then roundup maximum to the next power of 2 size of installed. E.g. 6 -> 8 or 12 -> 16
|
||||
*/
|
||||
$sizes = ['MB','GB','TB'];
|
||||
$memory_type = $ecc = '';
|
||||
$memory_installed = $memory_maximum = 0;
|
||||
$memory_devices = dmidecode('Memory Device',17);
|
||||
$modules = 0;
|
||||
foreach ($memory_devices as $device) {
|
||||
if (empty($device['Type']) || $device['Type']=='Unknown') continue;
|
||||
[$size, $unit] = my_explode(' ',$device['Size']);
|
||||
$base = array_search($unit,$sizes);
|
||||
if ($base!==false) $memory_installed += $size*pow(1024,$base);
|
||||
if (!$memory_type) $memory_type = $device['Type'];
|
||||
$modules++;
|
||||
}
|
||||
$memory = $modules > 1 ? "<span class='link blue-text' onclick=\"$('tr.ram').toggle()\">"._('Memory').":</span>" : _('Memory').':';
|
||||
$memory_array = dmidecode('Physical Memory Array',16);
|
||||
foreach ($memory_array as $device) {
|
||||
[$size, $unit] = my_explode(' ',$device['Maximum Capacity']);
|
||||
$base = array_search($unit,$sizes);
|
||||
if ($base>=1) $memory_maximum += $size*pow(1024,$base);
|
||||
if (!$ecc && isset($device['Error Correction Type']) && $device['Error Correction Type']!='None') $ecc = $device['Error Correction Type']." ";
|
||||
}
|
||||
if ($memory_installed >= 1024) {
|
||||
$memory_installed = round($memory_installed/1024);
|
||||
$memory_maximum = round($memory_maximum/1024);
|
||||
$unit = 'GiB';
|
||||
} else $unit = 'MiB';
|
||||
|
||||
// If maximum < installed then roundup maximum to the next power of 2 size of installed. E.g. 6 -> 8 or 12 -> 16
|
||||
$low = $memory_maximum < $memory_installed;
|
||||
if ($low) $memory_maximum = pow(2,ceil(log($memory_installed)/log(2)));
|
||||
|
||||
$style = "<style>table.info{margin-top:10px;background-color:inherit} table.info td:first-child{width:20%;font-weight:bold;padding-left:10px} tr.ram,tr.port{display:none} span.link{text-decoration:underline;cursor:pointer}</style>";
|
||||
|
||||
$list = [];
|
||||
$list[] = "<table class='info'><tr><td>"._('Model').":</td><td>$model</td></tr>";
|
||||
$list[] = "<tr><td>".('M/B').":</td><td>{$board['Manufacturer']} {$board['Product Name']} {$board['Version']} {$board['Serial Number']}</td></tr>";
|
||||
$list[] = "<tr><td>"._('BIOS').":</td><td>{$bios['Vendor']} {$bios['Version']} {$bios['Release Date']}</td></tr>";
|
||||
$list[] = "<tr><td>"._('CPU').":</td><td>$cpumodel {$cpu['Current Speed']}</td></tr>";
|
||||
$list[] = "<tr><td>"._('HVM').":</td><td>$hvm</td></tr>";
|
||||
$list[] = "<tr><td>"._('IOMMU').":</td><td>$iommu</td></tr>";
|
||||
$list[] = "<tr><td>"._('Cache').":</td><td>".implode(', ',$cache_installed)."</td></tr>";
|
||||
$list[] = "<tr><td>$memory</td><td>$memory_installed $unit $memory_type $ecc("._('max. installable capacity')." $memory_maximum $unit".($low?'*':'').")</td></tr>";
|
||||
|
||||
foreach ($memory_devices as $device) {
|
||||
if (empty($device['Type']) || $device['Type']=='Unknown') continue;
|
||||
$size = preg_replace('/( .)B$/','$1iB',_var($device,'Size',0));
|
||||
$list[] = "<tr class='ram'><td></td><td>".$device['Locator'].": "._var($device,'Manufacturer')." "._var($device,'Part Number').", $size "._var($device,'Type')." @ "._var($device,'Configured Memory Speed')."</td></tr>";
|
||||
}
|
||||
|
||||
exec("ls --indicator-style=none /sys/class/net|grep -Po '^(bond|eth)\d+$'",$sPorts);
|
||||
$i = 0;
|
||||
$network = count($sPorts) > 1 ? "<span class='link blue-text' onclick=\"$('tr.port').toggle()\">"._('Network').":</span>" : _('Network').':';
|
||||
foreach ($sPorts as $port) {
|
||||
$int = "/sys/class/net/$port";
|
||||
$mtu = file_get_contents("$int/mtu");
|
||||
$link = @file_get_contents("$int/carrier")==1;
|
||||
$name = $i ? "" : $network;
|
||||
$more = $i++ ? "port" : "";
|
||||
if (substr($port,0,4)=='bond') {
|
||||
if ($link) {
|
||||
$bond_mode = str_replace('Bonding Mode: ','',file("/proc/net/bonding/$port",FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)[1]);
|
||||
$list[] = "<tr class='$more'><td>$name</td><td>$port: $bond_mode, mtu $mtu</td></tr>";
|
||||
} else {
|
||||
$list[] = "<tr class='$more'><td>$name</td><td>$port: "._("bond down")."</td></tr>";
|
||||
}
|
||||
} else {
|
||||
if ($link) {
|
||||
$speed = file_get_contents("$int/speed");
|
||||
$duplex = file_get_contents("$int/duplex");
|
||||
$list[] = "<tr class='$more'><td>$name</td><td>$port: $speed Mbps, $duplex duplex, mtu $mtu</td></tr>";
|
||||
} else {
|
||||
$list[] = "<tr class='$more'><td>$name</td><td>$port: "._("interface down")."</td></tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$list[] = "<tr><td>"._('Kernel').":</td><td>$kernel</td></tr>";
|
||||
$list[] = "<tr><td>"._('OpenSSL').":</td><td>$openssl</td></tr>";
|
||||
$list[] = "<tr><td>"._('Uptime').":</td><td><span class='uptime'></span></td></tr></table>";
|
||||
|
||||
echo $style,implode($list);
|
||||
?>
|
||||
80
emhttp/plugins/dynamix/scripts/update_access
Executable file
80
emhttp/plugins/dynamix/scripts/update_access
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
|
||||
CONF=/etc/ssh/sshd_config
|
||||
INET=/etc/inetd.conf
|
||||
SERV=/etc/services
|
||||
|
||||
# read settings
|
||||
if [[ -a /boot/config/ident.cfg ]]; then
|
||||
source <(/usr/bin/fromdos < /boot/config/ident.cfg)
|
||||
fi
|
||||
|
||||
# preset default values
|
||||
[[ -z $BIND_MGT ]] && BIND_MGT=no
|
||||
[[ -z $USE_TELNET ]] && USE_TELNET=yes
|
||||
[[ -z $PORTTELNET ]] && PORTTELNET=23
|
||||
[[ -z $USE_SSH ]] && USE_SSH=yes
|
||||
[[ -z $PORTSSH ]] && PORTSSH=22
|
||||
[[ -z $USE_UPNP ]] && USE_UPNP=no
|
||||
|
||||
# get management IP addresses
|
||||
if [[ $BIND_MGT == yes ]]; then
|
||||
ETH=eth0
|
||||
[[ -e /sys/class/net/bond0 ]] && ETH=bond0
|
||||
[[ -e /sys/class/net/br0 ]] && ETH=br0
|
||||
IPV4=$(ip -4 addr show $ETH|awk '/inet /{gsub(/\/.+$/,"",$2);print $2;exit}')
|
||||
IPV6=$(ip -6 addr show $ETH noprefixroute|awk '/inet6 /{gsub(/\/.+$/,"",$2);print $2;exit}')
|
||||
[[ -z $IPV6 ]] && IPV6=$(ip -6 addr show $ETH scope global permanent|awk '/inet6 /{gsub(/\/.+$/,"",$2);print $2;exit}')
|
||||
fi
|
||||
|
||||
# update SSH listening port
|
||||
if [[ $PORTSSH == 22 ]]; then
|
||||
sed -ri 's/^#?Port [0-9]+$/#Port 22/' $CONF
|
||||
else
|
||||
sed -ri "s/^#?Port [0-9]+\$/Port ${PORTSSH}/" $CONF
|
||||
fi
|
||||
|
||||
# bind/unbind SSH service
|
||||
if [[ -n $IPV4 ]]; then
|
||||
sed -ri "s/^#?(ListenAddress) 0.0.0.0\$/\1 ${IPV4}/" $CONF
|
||||
else
|
||||
sed -ri 's/^#?(ListenAddress) [0-9]{1,3}\..+$/#\1 0.0.0.0/' $CONF
|
||||
fi
|
||||
if [[ -n $IPV6 ]]; then
|
||||
sed -ri "s/^#?(ListenAddress) ::\$/\1 ${IPV6}/" $CONF
|
||||
else
|
||||
sed -ri 's/^#?(ListenAddress) [A-Fa-f0-9]{1,4}:.+$/#\1 ::/' $CONF
|
||||
fi
|
||||
|
||||
# enable/disable SSH service
|
||||
if [[ $USE_SSH == yes ]]; then
|
||||
if [[ -r /var/run/sshd.pid ]]; then
|
||||
/etc/rc.d/rc.sshd restart >/dev/null
|
||||
else
|
||||
/etc/rc.d/rc.sshd start >/dev/null
|
||||
fi
|
||||
else
|
||||
/etc/rc.d/rc.sshd stop >/dev/null
|
||||
fi
|
||||
|
||||
# enable/disable UPnP function
|
||||
if [[ $USE_UPNP == yes ]]; then
|
||||
[[ ! -x /usr/bin/upnpc ]] && chmod +x /usr/bin/upnpc
|
||||
else
|
||||
[[ -x /usr/bin/upnpc ]] && chmod -x /usr/bin/upnpc
|
||||
fi
|
||||
|
||||
# update TELNET listening port
|
||||
sed -ri "s/^(telnet\s+)[0-9]+\/(tcp|udp)\$/\1${PORTTELNET}\/\2/" $SERV
|
||||
|
||||
# bind/unbind TELNET service
|
||||
if [[ -n $IPV4 ]]; then
|
||||
BIND="$IPV4:"
|
||||
fi
|
||||
# enable/disable TELNET service
|
||||
if [[ $USE_TELNET == yes ]]; then
|
||||
sed -ri "s/^#?(.+:)?(telnet\s.+telnetd\$)/${BIND}\2/" $INET
|
||||
else
|
||||
sed -ri 's/^#?(.+:)?(telnet\s.+telnetd$)/#\2/' $INET
|
||||
fi
|
||||
/etc/rc.d/rc.inetd restart >/dev/null
|
||||
44
emhttp/plugins/dynamix/scripts/upnp_poller
Executable file
44
emhttp/plugins/dynamix/scripts/upnp_poller
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
while :; do
|
||||
if [[ -x /usr/bin/upnpc ]]; then
|
||||
UPNP=/var/tmp/upnp
|
||||
XML=$(cat $UPNP 2>/dev/null)
|
||||
LINK=eth0
|
||||
[[ -e /sys/class/net/bond0 ]] && LINK=bond0
|
||||
[[ -e /sys/class/net/br0 ]] && LINK=br0
|
||||
if [[ -n $XML ]]; then
|
||||
# validate XML
|
||||
timeout 6 stdbuf -o0 upnpc -u $XML -m $LINK -l 2>&1|grep -qm1 'refused'
|
||||
[[ $? -ne 1 ]] && XML=
|
||||
fi
|
||||
if [[ -z $XML ]]; then
|
||||
# obtain XML
|
||||
GW=$(ip -4 route list default dev $LINK|awk '{print $3}')
|
||||
DESC=$(timeout 12 stdbuf -o0 upnpc -m $LINK -l 2>/dev/null|grep -Po 'desc: \K.+')
|
||||
for URL in $DESC; do
|
||||
IP=${URL#*://}
|
||||
if [[ ${IP%:*} == $GW ]]; then
|
||||
XML=$URL
|
||||
echo -n $XML >$UPNP
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [[ -n $XML ]]; then
|
||||
# upnp on router is enabled, get active tunnels
|
||||
TUNNEL=$(wg show interfaces)
|
||||
UPNP=$(timeout 6 stdbuf -o0 upnpc -u $XML -m $LINK -l 2>/dev/null|grep -Po "WireGuard-\Kwg[0-9]+"|tr '\n' ' ')
|
||||
for WG in $TUNNEL; do
|
||||
if [[ -z $(grep -Pom1 'UPNP:0="\K.[^"]+' /etc/wireguard/$WG.cfg) && ! ${UPNP[@]} =~ "$WG " ]]; then
|
||||
# port forwarding is closed; re-open it
|
||||
IP=$(ip -4 addr show dev $LINK|grep -Pom1 'inet \K.[^/]+')
|
||||
PORT=$(wg show $WG listen-port)
|
||||
upnpc -u $XML -m $LINK -e "WireGuard-$WG" -a $IP $PORT $PORT udp >/dev/null 2>&1
|
||||
[[ $? -eq 0 ]] && logger -t upnpc "Added port $PORT/udp" || logger -t upnpc "Failed to add port $PORT/udp"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
# loop every 3 minutes
|
||||
sleep 180
|
||||
done &
|
||||
3
emhttp/plugins/dynamix/scripts/upnp_poller_start
Executable file
3
emhttp/plugins/dynamix/scripts/upnp_poller_start
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
POLLER=/usr/local/emhttp/webGui/scripts/upnp_poller
|
||||
[[ -z $(pgrep -f $POLLER) ]] && $POLLER
|
||||
56
emhttp/plugins/dynamix/scripts/upnp_port
Executable file
56
emhttp/plugins/dynamix/scripts/upnp_port
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# upnp_port add link descr ip port port udp
|
||||
# upnp_port del link port udp
|
||||
|
||||
# exit if not executable
|
||||
[[ ! -x /usr/bin/upnpc ]] && exit 0
|
||||
|
||||
LINK=$2
|
||||
UPNP=/var/tmp/upnp
|
||||
XML=$(cat $UPNP 2>/dev/null)
|
||||
|
||||
if [[ -n $XML ]]; then
|
||||
# validate XML
|
||||
timeout 6 stdbuf -o0 upnpc -u $XML -m $LINK -l 2>&1|grep -qm1 'refused'
|
||||
[[ $? -ne 1 ]] && XML=
|
||||
fi
|
||||
if [[ -z $XML ]]; then
|
||||
# obtain XML
|
||||
GW=$(ip -4 route list default dev $LINK|awk '{print $3}')
|
||||
DESC=$(timeout 12 stdbuf -o0 upnpc -m $LINK -l 2>/dev/null|grep -Po 'desc: \K.+')
|
||||
for URL in $DESC; do
|
||||
IP=${URL#*://}
|
||||
if [[ ${IP%:*} == $GW ]]; then
|
||||
XML=$URL
|
||||
echo -n $XML >$UPNP
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
# exit when no XML
|
||||
[[ -z $XML ]] && exit 0
|
||||
|
||||
WG=$(wg show interfaces)
|
||||
POLLER=/usr/local/emhttp/webGui/scripts/upnp_poller
|
||||
|
||||
case $1 in
|
||||
add)
|
||||
upnpc -u $XML -m $LINK -e $3 -a $4 $5 $6 $7 >/dev/null 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
[[ -n $WG ]] && at -M -f ${POLLER}_start now 2>/dev/null
|
||||
logger -t upnpc "Added port $5/$7"
|
||||
else
|
||||
logger -t upnpc "Failed to add port $5/$7"
|
||||
fi
|
||||
;;
|
||||
del)
|
||||
upnpc -u $XML -m $LINK -d $3 $4 >/dev/null 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
[[ -z $WG && -n $(pgrep -f $POLLER) ]] && pkill -f $POLLER
|
||||
logger -t upnpc "Deleted port $3/$4"
|
||||
else
|
||||
logger -t upnpc "Failed to delete port $3/$4"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
71
emhttp/plugins/dynamix/scripts/wg_config
Executable file
71
emhttp/plugins/dynamix/scripts/wg_config
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/php -q
|
||||
<?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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'settings';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$file = $argv[1];
|
||||
$path = realpath('/etc/wireguard'.($argv[2]??''));
|
||||
$root = '/boot/config/wireguard';
|
||||
|
||||
$style = ["<style>"];
|
||||
$style[] = "pre h2{text-decoration:underline}";
|
||||
$style[] = "input#download{margin:0 0 10px 0}";
|
||||
$style[] = "pre.config{font-family:bitstream;margin:0;border:none}";
|
||||
$style[] = "img{display:block;margin:20px 0}";
|
||||
$style[] = "img:hover{transform:scale(1.1)}";
|
||||
$style[] = "</style>";
|
||||
|
||||
$script = ["<script>"];
|
||||
$script[] = "function config_cleanUp(id,file) {";
|
||||
$script[] = " if (document.hasFocus()) {";
|
||||
$script[] = " $('#'+id).val(\""._('Download')."\").prop('disabled',false);";
|
||||
$script[] = " $.post('/webGui/include/Download.php',{cmd:'delete',file:file});";
|
||||
$script[] = " } else {";
|
||||
$script[] = " setTimeout(function(){config_cleanUp(id,file);},1000);";
|
||||
$script[] = " }";
|
||||
$script[] = "}";
|
||||
$script[] = "function config_download(id,source,file) {";
|
||||
$script[] = " $('#'+id).val(\""._('Downloading')."...\").prop('disabled',true);";
|
||||
$script[] = " $.post('/webGui/include/Download.php',{cmd:'save',source:source+'.conf',file:file,opts:'qj'},function(){";
|
||||
$script[] = " $.post('/webGui/include/Download.php',{cmd:'save',source:source+'.png',file:file,opts:'qj'},function(zip){";
|
||||
$script[] = " location = zip;";
|
||||
$script[] = " setTimeout(function(){config_cleanUp(id,file);},1000);";
|
||||
$script[] = " });";
|
||||
$script[] = " });";
|
||||
$script[] = "}";
|
||||
$script[] = "</script>";
|
||||
|
||||
$html = [];
|
||||
$html[] = "<h2>".($argv[2] ? _('Remote peer configuration') : _('Local server configuration'))."</h2>";
|
||||
$html[] = "<div><input type='button' id='download' value=\""._('Download')."\" onclick=\"config_download(this.id,'$path/$file','$file.zip')\"></div>";
|
||||
$html[] = "<pre class='config'>";
|
||||
$html[] = @file_get_contents("$path/$file.conf");
|
||||
$html[] = "\n";
|
||||
$html[] = "</pre>";
|
||||
if (is_file("$path/$file.png")) {
|
||||
@unlink("$docroot/$file.png");
|
||||
symlink("$path/$file.png", "$docroot/$file.png");
|
||||
$html[] = "<img src=\"/$file.png?v=".filemtime("$path/$file.png")."\">";
|
||||
}
|
||||
|
||||
echo implode($style),implode($script),implode($html);
|
||||
?>
|
||||
23
emhttp/plugins/dynamix/scripts/xfs_check
Executable file
23
emhttp/plugins/dynamix/scripts/xfs_check
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# xfs_check start <dev> <id> <options>
|
||||
# xfs_check status <dev> <id>
|
||||
# xfs_check cancel <dev>
|
||||
|
||||
# using /var/lib because that's where btrfs puts status
|
||||
mkdir -p /var/lib/xfs
|
||||
case "$1" in
|
||||
'start')
|
||||
exec /sbin/xfs_repair $4 $2 &> /var/lib/xfs/check.status.$3 &
|
||||
;;
|
||||
'status')
|
||||
if [ -f /var/lib/xfs/check.status.$3 ]; then
|
||||
cat /var/lib/xfs/check.status.$3
|
||||
else
|
||||
echo "Not available"
|
||||
fi;
|
||||
pgrep -f "/sbin/xfs_repair.*$2" >/dev/null
|
||||
;;
|
||||
'cancel')
|
||||
pkill -f "/sbin/xfs_repair.*$2"
|
||||
;;
|
||||
esac
|
||||
23
emhttp/plugins/dynamix/scripts/zfs_scrub
Executable file
23
emhttp/plugins/dynamix/scripts/zfs_scrub
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# zfs_scrub start <pool>
|
||||
# zfs_scrub clear <pool>
|
||||
# zfs_scrub status <pool>
|
||||
# zfs_scrub cancel <pool>
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
exec /usr/sbin/zpool scrub $2 2>/dev/null
|
||||
;;
|
||||
'clear')
|
||||
exec /usr/sbin/zpool clear $2 2>/dev/null
|
||||
;;
|
||||
'status')
|
||||
# first output whatever the status is to stdout
|
||||
/usr/sbin/zpool status -P $2
|
||||
# establish retval of this script: 0 running, 1 not running
|
||||
/usr/sbin/zpool status -P $2 | grep -q 'scrub in progress'
|
||||
;;
|
||||
'cancel')
|
||||
/usr/sbin/zpool scrub -s $2 2>/dev/null
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user