From 57d55c647c3088419086841f4cec084c3e386065 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Sun, 24 May 2026 17:51:31 -0400 Subject: [PATCH] varaverk: replace User Scripts dependency with native job tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run_job.sh: new wrapper called by scheduler cron for every job. Writes /var/log/varaverk/.json (status/start/end/exit/pid) and /var/log/varaverk/.log (latest run output, overwritten). Maps exit 0→ok, 1→warn, 2+→error so watchdog "took action" runs show warn not error. scheduler: cron now calls bash run_job.sh instead of inline bash -c. Added vv_job_stat_path(). LOG_DIR moved to config.php (shared). monitor: vv_scripts_status() now reads /var/log/varaverk/*.json and */*.json — no User Scripts tmpScripts dependency at all. Stale running detection via /proc/ check. Returns error_count in addition to running/ok/warn counts. pages/monitor.php: added error pill (red ✗), error icon/color in list, duration shown next to age for each script entry. --- .../plugins/varaverk/include/config.php | 1 + .../plugins/varaverk/include/monitor.php | 51 +++++++++---------- .../plugins/varaverk/include/scheduler.php | 13 ++--- .../emhttp/plugins/varaverk/pages/monitor.php | 11 ++-- .../local/emhttp/plugins/varaverk/run_job.sh | 39 ++++++++++++++ 5 files changed, 79 insertions(+), 36 deletions(-) create mode 100644 Plugin/usr/local/emhttp/plugins/varaverk/run_job.sh diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/include/config.php b/Plugin/usr/local/emhttp/plugins/varaverk/include/config.php index c10a518..35f4ac8 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/include/config.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/include/config.php @@ -7,6 +7,7 @@ define('PLUGIN_CFG', '/boot/config/plugins/varaverk/varaverk.cfg'); $_vv_cfg = @parse_ini_file(PLUGIN_CFG) ?: []; define('SCRIPTS_DIR', $_vv_cfg['SCRIPTS_DIR'] ?? '/mnt/user/appdata/unraid_scripts'); define('CONF_DIR', SCRIPTS_DIR . '/Configurations'); +define('LOG_DIR', '/var/log/varaverk'); unset($_vv_cfg); function vv_get_hostname(): string { diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/include/monitor.php b/Plugin/usr/local/emhttp/plugins/varaverk/include/monitor.php index 98bdc02..0127c64 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/include/monitor.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/include/monitor.php @@ -648,37 +648,35 @@ function vv_log_tail(string $path, int $lines): string { } function vv_scripts_status(): array { - $tmpBase = '/tmp/user.scripts/tmpScripts'; - $runDir = '/tmp/user.scripts/running'; - - $running = []; - foreach (glob($runDir . '/*') ?: [] as $f) { - $running[basename($f)] = true; - } + $logDir = LOG_DIR; + $statFiles = array_merge( + glob("$logDir/*.json") ?: [], + glob("$logDir/*/*.json") ?: [] + ); $scripts = []; - foreach (glob($tmpBase . '/*/') ?: [] as $dir) { - $name = basename(rtrim($dir, '/')); - $logPath = $dir . 'log.txt'; - $ts = @filemtime($logPath); - if (!$ts) continue; + foreach ($statFiles as $statFile) { + $stat = json_decode(@file_get_contents($statFile) ?: '{}', true) ?: []; + $status = $stat['status'] ?? 'unknown'; - $isRunning = isset($running[$name]); - - if ($isRunning) { - $status = 'running'; - } else { - $tail = vv_log_tail($logPath, 15); - $finished = stripos($tail, 'Script Finished') !== false; - if ($finished) { - $hasWarn = (bool)preg_match('/permission denied|command not found|no such file|: error[\s:]/i', $tail); - $status = $hasWarn ? 'warn' : 'ok'; - } else { - $status = 'unknown'; - } + // Stale running — PID gone (crash or reboot with no cleanup) + if ($status === 'running' && !empty($stat['pid'])) { + if (!file_exists("/proc/{$stat['pid']}")) $status = 'error'; } - $scripts[] = ['name' => $name, 'last_ts' => $ts, 'status' => $status, 'running' => $isRunning]; + $id = $stat['id'] ?? basename($statFile, '.json'); + $name = basename(preg_replace('/\.sh$/', '', $id)); + $ts = (int)($stat['end'] ?? $stat['start'] ?? @filemtime($statFile) ?: 0); + + $scripts[] = [ + 'name' => $name, + 'last_ts' => $ts, + 'status' => $status, + 'running' => $status === 'running', + 'exit' => $stat['exit'] ?? null, + 'duration' => isset($stat['start'], $stat['end']) + ? (int)$stat['end'] - (int)$stat['start'] : null, + ]; } usort($scripts, fn($a, $b) => ($b['last_ts'] ?? 0) <=> ($a['last_ts'] ?? 0)); @@ -689,5 +687,6 @@ function vv_scripts_status(): array { 'running_count' => count(array_filter($scripts, fn($s) => $s['status'] === 'running')), 'ok_count' => count(array_filter($scripts, fn($s) => $s['status'] === 'ok')), 'warn_count' => count(array_filter($scripts, fn($s) => $s['status'] === 'warn')), + 'error_count' => count(array_filter($scripts, fn($s) => $s['status'] === 'error')), ]; } diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php b/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php index d6e81bb..0ef9c76 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php @@ -39,15 +39,18 @@ function vv_job_flags(string $id): string { return !empty($schedule[$id]['log_enabled']) ? '--log' : ''; } -define('LOG_DIR', '/var/log/varaverk'); - function vv_job_log_path(string $id): string { return LOG_DIR . '/' . preg_replace('/\.sh$/', '.log', $id); } +function vv_job_stat_path(string $id): string { + return LOG_DIR . '/' . preg_replace('/\.sh$/', '.json', $id); +} + function vv_cron_rebuild(array $schedule): bool { if (!is_dir(LOG_DIR)) mkdir(LOG_DIR, 0755, true); + $runner = dirname(__DIR__) . '/run_job.sh'; $lines = ["# Varaverk — managed by plugin, do not edit manually"]; $lines[] = "# Regenerated: " . date('Y-m-d H:i:s'); $lines[] = ""; @@ -56,11 +59,9 @@ function vv_cron_rebuild(array $schedule): bool { foreach ($schedule as $entry) { if (empty($entry['enabled']) || empty($entry['cron']) || empty($entry['id'])) continue; $script = "$scriptsDir/{$entry['id']}"; - $logFile = vv_job_log_path($entry['id']); - $logDir = dirname($logFile); - if (!is_dir($logDir)) mkdir($logDir, 0755, true); $flags = !empty($entry['log_enabled']) ? ' --log' : ''; - $lines[] = "{$entry['cron']} bash -c 'echo; echo \"── \$(date \"+%Y-%m-%d %H:%M:%S\") ──────────────────────\"; bash \"$script\"$flags' >> \"$logFile\" 2>&1"; + $id = $entry['id']; + $lines[] = "{$entry['cron']} bash \"$runner\" \"$id\" \"$script\"$flags"; } $lines[] = ""; diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php b/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php index 58e6993..43969a2 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php @@ -341,6 +341,7 @@ function vvRenderScripts() { const running = sc.running_count ?? 0; const ok = sc.ok_count ?? 0; const warn = sc.warn_count ?? 0; + const errors = sc.error_count ?? 0; const now = Math.floor(Date.now() / 1000); function vvScriptPill(type, count, color, bg, border, icon) { @@ -354,7 +355,8 @@ function vvRenderScripts() { if (running > 0) html += vvScriptPill('running', running, '#4fc3f7', '#0a2233', '#1e4060', '●'); if (ok > 0) html += vvScriptPill('ok', ok, '#4caf50', '#0a1f0a', '#1a3a1a', '✓'); if (warn > 0) html += vvScriptPill('warn', warn, '#ff9800', '#1f1200', '#3a2200', '!'); - if (!running && !ok && !warn) html += `No recent runs`; + if (errors > 0) html += vvScriptPill('error', errors, '#f44336', '#2a0a0a', '#5a1a1a', '✗'); + if (!running && !ok && !warn && !errors) html += `No recent runs`; html += ``; const filtered = vvScriptsFilter ? scList.filter(s => s.status === vvScriptsFilter) : scList; @@ -362,13 +364,14 @@ function vvRenderScripts() { filtered.forEach(s => { const diff = now - (s.last_ts ?? now); const ago = diff < 60 ? diff + 's' : diff < 3600 ? Math.floor(diff / 60) + 'm' : diff < 86400 ? Math.floor(diff / 3600) + 'h' : Math.floor(diff / 86400) + 'd'; - const icon = s.status === 'running' ? '●' : s.status === 'ok' ? '✓' : s.status === 'warn' ? '!' : '?'; - const color = s.status === 'running' ? '#4fc3f7' : s.status === 'ok' ? '#4caf50' : s.status === 'warn' ? '#ff9800' : '#555'; + const icon = s.status === 'running' ? '●' : s.status === 'ok' ? '✓' : s.status === 'warn' ? '!' : s.status === 'error' ? '✗' : '?'; + const color = s.status === 'running' ? '#4fc3f7' : s.status === 'ok' ? '#4caf50' : s.status === 'warn' ? '#ff9800' : s.status === 'error' ? '#f44336' : '#555'; const name = s.name.length > 24 ? s.name.slice(0, 23) + '…' : s.name; + const dur = s.duration != null ? ` ${s.duration}s` : ''; listHtml += `
${icon} ${name} - ${ago} + ${ago}${dur}
`; }); diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/run_job.sh b/Plugin/usr/local/emhttp/plugins/varaverk/run_job.sh new file mode 100644 index 0000000..b601cb9 --- /dev/null +++ b/Plugin/usr/local/emhttp/plugins/varaverk/run_job.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Varaverk job runner — wraps script execution with JSON status tracking. +# Called by /etc/cron.d/varaverk for every scheduled job. +# +# Usage: bash run_job.sh [flags...] +# +# Writes: /var/log/varaverk/.json — status, timestamps, exit code, pid +# /var/log/varaverk/.log — latest run output (overwritten each run) +# +# Status values: running → ok (exit 0) | warn (exit 1) | error (exit 2+) + +JOB_ID="$1" +SCRIPT="$2" +shift 2 + +LOG_DIR="/var/log/varaverk" +BASE="${JOB_ID%.sh}" +LOG_FILE="$LOG_DIR/$BASE.log" +STAT_FILE="$LOG_DIR/$BASE.json" + +mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$STAT_FILE")" + +START=$(date +%s) +printf '{"id":"%s","status":"running","start":%s,"pid":%s}\n' \ + "$JOB_ID" "$START" "$$" > "$STAT_FILE" + +bash "$SCRIPT" "$@" > "$LOG_FILE" 2>&1 +EC=$? + +END=$(date +%s) +if [ "$EC" -eq 0 ]; then STATUS="ok" +elif [ "$EC" -eq 1 ]; then STATUS="warn" +else STATUS="error" +fi + +printf '{"id":"%s","status":"%s","start":%s,"end":%s,"exit":%s}\n' \ + "$JOB_ID" "$STATUS" "$START" "$END" "$EC" > "$STAT_FILE" + +exit $EC