Merge remote-tracking branch 'upstream/master'

This commit is contained in:
bergware
2023-06-02 22:20:24 +02:00
700 changed files with 6850 additions and 68606 deletions

View 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

View 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

View 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

View 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

View 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

View 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_','');
?>

View 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"

View 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);
?>

View 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);
?>

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

View 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");
?>

View 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_','');
?>

View 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);
?>

View 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);
?>

View 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_','');
?>

View 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);
?>

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

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

View 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

View 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

View 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

View 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);
}
?>

View 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);
?>

View 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"

View 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// /, }

View 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_','');
?>

View 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);
?>

View 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)"],["","&#169;","&#174;","&#8482;"],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);
?>

View 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

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

View File

@@ -0,0 +1,3 @@
#!/bin/bash
POLLER=/usr/local/emhttp/webGui/scripts/upnp_poller
[[ -z $(pgrep -f $POLLER) ]] && $POLLER

View 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

View 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);
?>

View 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

View 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