#!/usr/bin/php -q '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 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,$pools,$customShares,$unraid_vars; 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) $text = str_replace(end(explode("-",$unraid_vars['regGUID'])),"...",$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 $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() { $var = parse_ini_file('/var/local/emhttp/var.ini'); extract(parse_ini_file('/var/local/emhttp/network.ini',true)); $nginx = parse_ini_file('/var/local/emhttp/nginx.ini'); $internalip = $internalip_priv = $internalip_msg = $eth0['IPADDR:0']; $dnsserver = $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['LOCAL_TLD'] ? '': '[blank] (FYI - a blank TLD can cause issues for Mac and Linux clients)'; $isLegacyCert = preg_match('/.*\.unraid\.net$/', $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['NAME']}\n"; $urls .= "Local TLD: {$var['LOCAL_TLD']}{$host_tld_msg}\n"; $urls .= "HTTP port: {$var['PORT']}\n"; $urls .= "HTTPS port: {$var['PORTSSL']}\n"; $urls .= "Internal IP: {$internalip_msg}\n"; $urls .= "DNS 1: {$dnsserver}\n"; $urls .= "USE SSL: {$var['USE_SSL']}\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['NAME']; $host_tld = $var['LOCAL_TLD'] ? ".{$var['LOCAL_TLD']}" : ''; $expected_host = "{$host_name}{$host_tld}"; $http_port = $var['PORT'] != 80 ? ":{$var['PORT']}" : ''; $https_port = $var['PORTSSL'] != 443 ? ":{$var['PORTSSL']}" : ''; $https_1_cert = "{$var['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 = ''; switch($var['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 ($var['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}", $var['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}", $var['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 ($var['USE_SSL'] != "no") { $telnet_disabled = ($var['USE_TELNET'] == "no") ? " (disabled)" : ""; $ssh_disabled = ($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 ($var['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 '{$var['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]}"; } // global variables $disks = (array)@parse_ini_file("$get/disks.ini",true); $pools = pools_filter($disks); // don't anonymize system share names $vardomain = (array)@parse_ini_file('/boot/config/domain.cfg'); $vardocker = (array)@parse_ini_file('/boot/config/docker.cfg'); $showshares = []; $customShares = ''; $showshares[] = current(array_slice(explode('/',$vardomain['IMAGE_FILE']),3,1)); $showshares[] = current(array_slice(explode('/',$vardomain['DOMAINDIR']),3,1)); $showshares[] = current(array_slice(explode('/',$vardomain['MEDIADIR']),3,1)); $showshares[] = current(array_slice(explode('/',$vardomain['DISKDIR']),3,1)); $showshares[] = current(array_slice(explode('/',$vardocker['DOCKER_IMAGE_FILE']),3,1)); $showshares[] = current(array_slice(explode('/',$vardocker['DOCKER_APP_CONFIG_PATH']),3,1)); $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'); $unraid_vars = parse_ini_file('/var/local/emhttp/var.ini'); 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("$get/*.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("$get/cpuload.ini")."\r\n"; $diskload = (array)@file("$get/diskload.ini"); $loadTxt = []; foreach ($diskload as $loadLine) { $load = explode('=',$loadLine); foreach ($disks as $disk) { if ($load[0]==$disk['device']) { $loadTxt[] = "{$disk['device']} ({$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 ".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"); // 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 = (array)@parse_ini_file("$get/shares.ini", true); foreach ($shares as $share) { $name = $share['name']; if (!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) $disks = (array)@parse_ini_file("$get/disks.ini", true); include_once "$docroot/webGui/include/CustomMerge.php"; include_once "$docroot/webGui/include/Wrappers.php"; run("ls -l /dev/disk/by-id/[asun]* 2>/dev/null|sed '/-part/d;s|^.*/by-id/[^-]*-||;s|-> ../../||;s|:|-|'", $devices); foreach ($devices as $device) { list($name,$port) = explode(' ',$device); $diskName = ''; $type = ''; foreach ($disks as $find) { if ($find['device']==$port) { $diskName = $find['name']; $type = get_value($find,'smType',''); get_ctlr_options($type, $find); $port = $find['smDevice'] ?? $port; break; } } $port = port_name($port); $status = $find['status'] == "DISK_OK" ? "" : " - {$find['status']}"; run("smartctl -x $type ".escapeshellarg("/dev/$port")." 2>/dev/null|todos >".escapeshellarg("/$diag/smart/$name-$date $diskName ($port)$status.txt")); } // create pool btrfs information foreach ($pools as $pool) { if (strpos($disks[$pool]['fsType']??'','btrfs')!==false) { 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")); } } // create installed plugin information $pluginList = json_decode(download_url("https://raw.githubusercontent.com/Squidly271/AppFeed/master/pluginList.json"),true); 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 && ! $pluginList[$plgURL] && basename($plugin) !== "unRAIDServer.plg") $installedPlugins .= " (Unknown to Community Applications)"; if ($pluginList[$plgURL]['blacklist']) $installedPlugins .= " (Blacklisted)"; if ($pluginList[$plgURL]['deprecated'] || ($pluginList[$plgURL]['dmax'] && version_compare($pluginList[$plgURL]['dmax'],$unraid['version'],"<"))) $installedPlugins .= " (Deprecated)"; if ($pluginList[$plgURL]['version'] > $plgVer) $installedPlugins .= " (Update available: {$pluginList[$plgURL]['version']})"; elseif ($pluginList[$plgURL]) $installedPlugins .= " (Up to date)"; if ($pluginList[$plgURL]['max'] && version_compare($pluginList[$plgURL]['max'],$unraid['version'],"<")) $installedPlugins .= " (Incompatible)"; if ($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[^\"]+' ".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) { $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 thrid party system diagnostics run("ls -lA /boot/config/plugins/*/packages/*/ 2>/dev/null|todos >>".escapeshellarg("/$diag/system/thirdparty_packages.txt")); if (is_dir("/dev/dri/")) run("ls -lA /sys/class/drm/*/device/driver 2>/dev/null|todos >>".escapeshellarg("/$diag/system/drm.txt")); else null; if (is_dir("/dev/dvb/")) run("ls -lA /sys/class/dvb/*/device/driver 2>/dev/null|todos >>".escapeshellarg("/$diag/system/dvb.txt")); else null; 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; // 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_',''); ?>