Bug fixes: - fallback.sh: escape sed metacharacters (\ & |) in state_set values - common.sh: parse PID from lock file content correctly (handles pid:metadata format) - unraid_api_key_renew.sh: fix path depth (../../../) and sync registry key to conf when stale - stop.php: only clear pid/status if process is actually dead — D-state survives SIGKILL - array_started.sh: check ARRAY_START_SCRIPTS empty before printing launch header Arr cleanup: - radarr_cleanup.sh / sonarr_cleanup.sh: fetch root folders from arr API instead of reverse-looking up the path map — handles multi-root-folder setups correctly UI: - varaverk.js: extract shared formatters (_relTime, _fmtBytes, _sz, _uptime, _gb, _tb, _n) - arrs.php / partnership.php: use shared formatters, remove duplicates - arrs.php / fallback.php: show error message on fetch failure instead of silent empty - docker.php: disable rename input during request, restore original value on failure - setup.php: abort controller timeout on detect fetch - partnership.php: remove Re-run Phase 2 button opacity dimming
93 lines
2.9 KiB
PHP
93 lines
2.9 KiB
PHP
<?php
|
|
header('Content-Type: application/json');
|
|
require_once dirname(__DIR__) . '/include/scheduler.php';
|
|
|
|
$id = trim($_POST['id'] ?? '');
|
|
|
|
if (!$id || !preg_match('/^[a-zA-Z0-9_.\/\-]+\.sh$/', $id) || str_contains($id, '..')) {
|
|
echo json_encode(['ok' => false, 'error' => 'Invalid id']);
|
|
exit;
|
|
}
|
|
|
|
$statFile = vv_job_stat_path($id);
|
|
if (!file_exists($statFile)) {
|
|
echo json_encode(['ok' => false, 'error' => 'No stat file — script may not be running']);
|
|
exit;
|
|
}
|
|
|
|
$stat = json_decode(file_get_contents($statFile) ?: '{}', true) ?: [];
|
|
|
|
if (($stat['status'] ?? '') !== 'running') {
|
|
echo json_encode(['ok' => true, 'msg' => 'Not running']);
|
|
exit;
|
|
}
|
|
|
|
$pid = (int)($stat['pid'] ?? 0);
|
|
if ($pid < 2) {
|
|
echo json_encode(['ok' => false, 'error' => 'No valid PID in stat file']);
|
|
exit;
|
|
}
|
|
|
|
// Kill the whole process group so the script and all its children die together.
|
|
// pgid is usually the same as the session leader PID from run_job.sh.
|
|
$pgid = (int)trim(shell_exec("ps -o pgid= -p $pid 2>/dev/null") ?: '0');
|
|
|
|
if ($pgid > 1) {
|
|
shell_exec("kill -TERM -$pgid 2>/dev/null");
|
|
} else {
|
|
// Fallback: kill the direct PID and its children
|
|
shell_exec("pkill -TERM -P $pid 2>/dev/null");
|
|
shell_exec("kill -TERM $pid 2>/dev/null");
|
|
}
|
|
|
|
// Give it up to 3s to exit gracefully
|
|
$dead = false;
|
|
for ($i = 0; $i < 6; $i++) {
|
|
usleep(500000);
|
|
if (!file_exists("/proc/$pid")) { $dead = true; break; }
|
|
}
|
|
|
|
// Force-kill if still alive
|
|
if (!$dead) {
|
|
if ($pgid > 1) shell_exec("kill -KILL -$pgid 2>/dev/null");
|
|
shell_exec("pkill -KILL -P $pid 2>/dev/null");
|
|
shell_exec("kill -KILL $pid 2>/dev/null");
|
|
usleep(300000);
|
|
$dead = !file_exists("/proc/$pid");
|
|
}
|
|
|
|
// Clear any lock files in /tmp/unraid_locks whose content matches this PID
|
|
$lockDir = '/tmp/unraid_locks';
|
|
$cleared = [];
|
|
foreach (glob("$lockDir/*.lock") ?: [] as $lf) {
|
|
$content = trim(file_get_contents($lf) ?: '');
|
|
$lockPid = (int)explode(':', $content)[0];
|
|
if ($lockPid === $pid || !file_exists("/proc/$lockPid")) {
|
|
@unlink($lf);
|
|
$cleared[] = basename($lf);
|
|
}
|
|
}
|
|
|
|
// Also clear by script name in case PID rotated
|
|
$scriptBase = basename($id, '.sh');
|
|
$namedLock = "$lockDir/{$scriptBase}.lock";
|
|
if (file_exists($namedLock)) {
|
|
@unlink($namedLock);
|
|
if (!in_array(basename($namedLock), $cleared)) $cleared[] = basename($namedLock);
|
|
}
|
|
|
|
// Update stat file — only clear pid if actually dead (D-state processes survive SIGKILL)
|
|
$now = time();
|
|
$stat['status'] = $dead ? 'stopped' : 'running';
|
|
$stat['end'] = $dead ? $now : ($stat['end'] ?? null);
|
|
$stat['exit'] = $dead ? -1 : ($stat['exit'] ?? null);
|
|
if ($dead) unset($stat['pid']);
|
|
file_put_contents($statFile, json_encode($stat));
|
|
|
|
echo json_encode([
|
|
'ok' => $dead,
|
|
'killed' => $dead,
|
|
'locks' => $cleared,
|
|
'error' => $dead ? null : 'Process still alive after SIGKILL (D-state) — lock may persist',
|
|
]);
|