mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix: detect stale PID in runner state after abnormal shutdown (#931)
* fix: verify PID belongs to hapi before treating runner as alive After OS upgrade, stale runner.state.json PID can be reused by unrelated processes. The old kill(pid, 0) check passes for any process, causing start-sync to loop with 'Runner already running' indefinitely. Now uses ps/wmic to confirm the process command line contains 'hapi' before considering the runner alive. Falls back to alive-only check if ps/wmic fails. * fix: precise runner process detection and wmic fallback * fix: add fallback for ps failure in non-Windows branch
This commit is contained in:
@@ -10,7 +10,7 @@ import packageJson from '../../package.json';
|
||||
import { existsSync, statSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { isBunCompiled, projectPath } from '@/projectPath';
|
||||
import { isProcessAlive, killProcess } from '@/utils/process';
|
||||
import { isProcessAlive, isHapiRunnerProcess, killProcess } from '@/utils/process';
|
||||
import { configuration } from '@/configuration';
|
||||
import { hashRunnerCliApiToken, isRunnerStateCompatibleWithIdentity } from './runnerIdentity';
|
||||
|
||||
@@ -143,12 +143,12 @@ export async function checkIfRunnerRunningAndCleanupStaleState(): Promise<boolea
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the runner is running
|
||||
if (isProcessAlive(state.pid)) {
|
||||
// Verify PID is alive AND belongs to hapi (not a reused PID from another process)
|
||||
if (isHapiRunnerProcess(state.pid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.debug('[RUNNER RUN] Runner PID not running, cleaning up state');
|
||||
logger.debug('[RUNNER RUN] Runner PID not running or not a hapi process, cleaning up state');
|
||||
await cleanupRunnerState();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,32 @@ export function isProcessAlive(pid: number): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// ponytail: ps -p is cheap and avoids PID-reuse false positives after OS upgrades/reboots
|
||||
function isRunnerCommand(commandLine: string): boolean {
|
||||
return /(?:^|\s)runner(?:\s|$)/.test(commandLine) && /(?:^|\s)start-sync(?:\s|$)/.test(commandLine);
|
||||
}
|
||||
|
||||
export function isHapiRunnerProcess(pid: number): boolean {
|
||||
if (!isProcessAlive(pid)) {
|
||||
return false;
|
||||
}
|
||||
if (isWindows()) {
|
||||
const result = spawn.sync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'CommandLine'], { stdio: 'pipe' });
|
||||
if (result.error) {
|
||||
return true;
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
return isProcessAlive(pid);
|
||||
}
|
||||
return isRunnerCommand(result.stdout?.toString() ?? '');
|
||||
}
|
||||
const result = spawn.sync('ps', ['-p', String(pid), '-o', 'command='], { stdio: 'pipe' });
|
||||
if (result.error || result.status !== 0) {
|
||||
return isProcessAlive(pid);
|
||||
}
|
||||
return isRunnerCommand(result.stdout?.toString() ?? '');
|
||||
}
|
||||
|
||||
function killProcessWindows(pid: number, force: boolean): boolean {
|
||||
if (!isProcessAlive(pid)) {
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user