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 $now = time(); $stat['status'] = 'stopped'; $stat['end'] = $now; $stat['exit'] = -1; unset($stat['pid']); file_put_contents($statFile, json_encode($stat)); echo json_encode([ 'ok' => true, 'killed' => $dead, 'locks' => $cleared, ]);