From eda411c0be1b1b8720b52d75f65fd81517efd6f2 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Mon, 24 Aug 2026 18:51:36 -0400 Subject: [PATCH] Give the PHP layer somewhere to record what it swallowed --- Plugin/unraid/api/board.php | 2 +- Plugin/unraid/api/recent.php | 2 +- Plugin/unraid/api/rsync_win_arrays.php | 2 +- Plugin/unraid/include/config.php | 50 ++++++++++++++++++++++++++ Plugin/unraid/include/scheduler.php | 2 +- Plugin/unraid/pages/scheduler.php | 2 +- 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Plugin/unraid/api/board.php b/Plugin/unraid/api/board.php index cd67585..f9e5958 100644 --- a/Plugin/unraid/api/board.php +++ b/Plugin/unraid/api/board.php @@ -170,7 +170,7 @@ if (is_dir(LOG_DIR)) { if ($lastErr !== null) $errors[] = ['script' => $script, 'line' => $lastErr, 'ts' => (int)$lf->getMTime()]; } - } catch (Exception $e) {} + } catch (Exception $e) { vv_log_error('api/board.php', 'log walk failed: ' . $e->getMessage()); } usort($errors, fn($a, $b) => $b['ts'] - $a['ts']); } $out['errors'] = array_slice($errors, 0, 20); diff --git a/Plugin/unraid/api/recent.php b/Plugin/unraid/api/recent.php index bf43e03..4df4a27 100644 --- a/Plugin/unraid/api/recent.php +++ b/Plugin/unraid/api/recent.php @@ -76,7 +76,7 @@ try { 'dur' => isset($d['end']) ? max(0, (int)$d['end'] - (int)$d['start']) : 0, ]; } -} catch (Exception $e) {} +} catch (Exception $e) { vv_log_error('api/recent.php', 'run-log walk failed: ' . $e->getMessage()); } usort($runs, fn($a, $b) => $b['start'] - $a['start']); echo json_encode(['ok' => true, 'runs' => array_slice($runs, 0, 24)]); diff --git a/Plugin/unraid/api/rsync_win_arrays.php b/Plugin/unraid/api/rsync_win_arrays.php index 425dbf8..9ebb074 100644 --- a/Plugin/unraid/api/rsync_win_arrays.php +++ b/Plugin/unraid/api/rsync_win_arrays.php @@ -123,7 +123,7 @@ if ($action === 'list_scripts') { $label = str_replace('_', ' ', basename($rel, '.sh')); $groups[$folder][] = ['id' => $rel, 'label' => $label]; } - } catch (Exception $e) {} + } catch (Exception $e) { vv_log_error('api/rsync_win_arrays.php', 'script tree walk failed: ' . $e->getMessage()); } ksort($groups); foreach ($groups as &$g) usort($g, fn($a, $b) => strcmp($a['id'], $b['id'])); echo json_encode(['ok' => true, 'groups' => $groups]); diff --git a/Plugin/unraid/include/config.php b/Plugin/unraid/include/config.php index 21b8603..6dff82a 100644 --- a/Plugin/unraid/include/config.php +++ b/Plugin/unraid/include/config.php @@ -160,6 +160,56 @@ define('LOG_DIR', '/var/log/varaverk'); define('CUSTOM_SCRIPTS_DIR', $_vv_cfg['CUSTOM_SCRIPTS_DIR'] ?? '/boot/config/plugins/user.scripts/Varaverk/Scripts'); unset($_vv_cfg); +// ── Recording a failure this layer chose to survive ─────────────────────────────────────────── +// The bash half writes a log for every run. The PHP half had nowhere to write at all — no +// error_log() call in 107 files — so an endpoint that caught an exception and carried on left no +// trace anywhere. Five directory walks do exactly that, and an unreadable directory renders as a +// legitimately empty result: the scheduler's script library shows "no scripts" whether you have +// none or the tree could not be read. +// +// This is the smallest thing that fixes it. It does not change any response contract; it records +// what was swallowed so the operator can find out why a page went empty. +// +// It lives in config.php because that is the one file every layer reaches — api/, include/ and +// pages/ all load it, several of them without ever loading common.php. +define('VV_PHP_LOG', LOG_DIR . '/php.log'); +define('VV_PHP_LOG_MAX', 262144); // 256KB. /var/log is tmpfs on Unraid — this is RAM, so a + // repeating fault must not be able to grow without bound. + +/** + * Record a failure that was caught and survived. Never throws, never echoes, never affects the + * response — a logger that can break a request is worse than no logger. + */ +function vv_log_error(string $where, string $message): void { + try { + if (!is_dir(LOG_DIR)) return; + // Timestamps are local, matching every bash log — see "The clock this layer runs on". + $line = date('Y-m-d H:i:s') . ' ' . $where . ' ' + . preg_replace('/\s+/', ' ', trim($message)) . "\n"; + + $size = @filesize(VV_PHP_LOG); + if ($size !== false && $size > VV_PHP_LOG_MAX) { + // Drop the older half rather than the whole file, so a fault that repeats every poll + // still leaves recent context instead of a log that empties itself at random. + $keep = @file_get_contents(VV_PHP_LOG, false, null, (int)(VV_PHP_LOG_MAX / 2)); + if ($keep !== false) { + // Resume on a line boundary — slicing at a byte offset lands mid-line, and a + // half-written first entry is worse than one fewer entry. + $nl = strpos($keep, "\n"); + $keep = $nl === false ? '' : substr($keep, $nl + 1); + @file_put_contents(VV_PHP_LOG, $keep, LOCK_EX); + } + } + $isNew = !file_exists(VV_PHP_LOG); + @file_put_contents(VV_PHP_LOG, $line, FILE_APPEND | LOCK_EX); + // Do not inherit the ambient umask. php-fpm's is loose enough to create this 0666, and a + // world-writable file under /var/log is the kind of small thing that is never noticed. + if ($isNew) @chmod(VV_PHP_LOG, 0644); + } catch (Throwable $_) { + return; // Deliberately silent: this is the last place an error should propagate from. + } +} + define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db'); // ── The two install layouts ─────────────────────────────────────────────────── diff --git a/Plugin/unraid/include/scheduler.php b/Plugin/unraid/include/scheduler.php index 945c46b..16cbe92 100644 --- a/Plugin/unraid/include/scheduler.php +++ b/Plugin/unraid/include/scheduler.php @@ -468,7 +468,7 @@ function vv_script_library(): array { if (in_array($rel, $orchIds) || isset($confMap[$rel]) || isset($cardIds[$rel]) || isset($schedule[$rel])) continue; $library[] = ['id' => $rel, 'label' => vv_pretty_label(basename($rel, '.sh'))]; } - } catch (Exception $e) {} + } catch (Exception $e) { vv_log_error('include/scheduler.php', 'script library walk failed: ' . $e->getMessage()); } usort($library, fn($a, $b) => strcmp($a['id'], $b['id'])); return $library; } diff --git a/Plugin/unraid/pages/scheduler.php b/Plugin/unraid/pages/scheduler.php index 027401f..70ac45f 100644 --- a/Plugin/unraid/pages/scheduler.php +++ b/Plugin/unraid/pages/scheduler.php @@ -126,7 +126,7 @@ try { if (in_array($_parts[0], $_repoSkip)) continue; $_repoFiles[] = $_rel; } -} catch (Exception $_re) {} +} catch (Exception $_re) { vv_log_error('pages/scheduler.php', 'repo file walk failed: ' . $_re->getMessage()); } sort($_repoFiles); // Docs tree: README and Manual with their per-module children