varaverk: replace User Scripts dependency with native job tracking

run_job.sh: new wrapper called by scheduler cron for every job.
  Writes /var/log/varaverk/<id>.json (status/start/end/exit/pid)
  and /var/log/varaverk/<id>.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/<pid> 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.
This commit is contained in:
Gmer4Lfe
2026-05-24 17:51:31 -04:00
parent ea9f39a803
commit 57d55c647c
5 changed files with 79 additions and 36 deletions
@@ -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 {
@@ -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')),
];
}
@@ -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[] = "";