true]; // ── Active locks ────────────────────────────────────────────────────────── $lockDir = '/tmp/unraid_locks'; $locks = []; if (is_dir($lockDir)) { foreach (glob($lockDir . '/*.lock') ?: [] as $lf) { $name = basename($lf, '.lock'); $age = time() - (int)filemtime($lf); $content = trim(file_get_contents($lf) ?: ''); // content is "PID:scriptname" — extract PID $pid = preg_match('/^(\d+)/', $content, $pm) ? $pm[1] : ''; // Skip if PID is still alive (it's legitimately running) if ($pid && file_exists("/proc/$pid")) continue; $locks[] = ['name' => $name, 'file' => basename($lf), 'age' => $age]; } } $out['locks'] = $locks; // ── Recent errors ────────────────────────────────────────────────────────── $errors = []; if (is_dir(LOG_DIR)) { $cutoff = time() - 7 * 86400; // only logs touched in last 7 days foreach (glob(LOG_DIR . '/*.log') ?: [] as $lf) { if (filemtime($lf) < $cutoff) continue; $script = basename($lf, '.log'); $lines = array_slice(@file($lf) ?: [], -200); $lastErr = null; foreach (array_reverse($lines) as $raw) { // Strip ANSI escape codes $clean = preg_replace('/\033\[[0-9;]*[mK]/', '', rtrim($raw)); if (!$clean) continue; if (preg_match('/\[(?:ERROR|WARN|CRITICAL|FAILED)\]/i', $clean) || preg_match('/\b(?:ERROR|CRITICAL|FAILED):\s/i', $clean) || str_contains($clean, '✗') || (str_contains($clean, '⚠') && !str_contains($clean, '♥'))) { $lastErr = mb_substr($clean, 0, 220); break; } } if ($lastErr !== null) { $errors[] = ['script' => $script, 'line' => $lastErr, 'ts' => (int)filemtime($lf)]; } } usort($errors, fn($a, $b) => $b['ts'] - $a['ts']); } $out['errors'] = array_slice($errors, 0, 20); // ── Partner reachability ─────────────────────────────────────────────────── $cacheFile = '/tmp/vv_partner_cache.json'; $cacheTtl = 30; $partnerData = null; if (file_exists($cacheFile) && (time() - (int)filemtime($cacheFile)) < $cacheTtl) { $partnerData = json_decode(file_get_contents($cacheFile), true); } else { $confRaw = vv_read_conf_raw('master.conf'); preg_match('/^\s*HOST1(?:_NAME)?\s*=\s*["\']?(\S+?)["\']?\s*(?:#.*)?$/m', $confRaw, $m1); preg_match('/^\s*HOST2(?:_NAME)?\s*=\s*["\']?(\S+?)["\']?\s*(?:#.*)?$/m', $confRaw, $m2); $host1 = $m1[1] ?? ''; $host2 = $m2[1] ?? ''; $mine = vv_get_hostname(); $partnerHost = null; if ($mine && $host1 && strcasecmp($mine, $host1) === 0) $partnerHost = $host2; if ($mine && $host2 && strcasecmp($mine, $host2) === 0) $partnerHost = $host1; if ($partnerHost) { $start = microtime(true); $result = shell_exec('ping -c1 -W1 ' . escapeshellarg($partnerHost) . ' 2>&1'); $elapsed = (int)round((microtime(true) - $start) * 1000); $reached = str_contains((string)$result, '1 received') || str_contains((string)$result, '1 packets received'); $partnerData = [ 'host' => $partnerHost, 'reachable' => $reached, 'latency' => $reached ? $elapsed : null, ]; @file_put_contents($cacheFile, json_encode($partnerData)); } } $out['partner'] = $partnerData; echo json_encode($out);