fix: treat EPERM from kill(pid, 0) as "process exists" in daemon liveness checks (#564)
Per POSIX, kill(pid, 0) returns EPERM when the process exists but the caller lacks permission to signal it, and ESRCH when it does not exist. The daemon liveness checks in both the Rust CLI and TypeScript daemon treated any kill failure as "not running", which is incorrect when running inside a macOS sandbox that restricts signal delivery to (target self). This caused the CLI to delete the real daemon's socket and PID files, then spawn a duplicate daemon. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
7238b7da4c
commit
e912f541f2
@@ -159,7 +159,13 @@ fn is_daemon_running(session: &str) -> bool {
|
||||
if let Ok(pid_str) = fs::read_to_string(&pid_path) {
|
||||
if let Ok(pid) = pid_str.trim().parse::<i32>() {
|
||||
unsafe {
|
||||
return libc::kill(pid, 0) == 0;
|
||||
if libc::kill(pid, 0) == 0 {
|
||||
return true;
|
||||
}
|
||||
// EPERM means the process exists but we lack permission to
|
||||
// signal it (e.g. inside a macOS sandbox). Only ESRCH means
|
||||
// the process is genuinely gone.
|
||||
return std::io::Error::last_os_error().raw_os_error() != Some(libc::ESRCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -170,7 +170,11 @@ fn run_session(args: &[String], session: &str, json_mode: bool) {
|
||||
if let Ok(pid_str) = fs::read_to_string(&pid_path) {
|
||||
if let Ok(pid) = pid_str.trim().parse::<u32>() {
|
||||
#[cfg(unix)]
|
||||
let running = unsafe { libc::kill(pid as i32, 0) == 0 };
|
||||
let running = unsafe {
|
||||
libc::kill(pid as i32, 0) == 0
|
||||
|| std::io::Error::last_os_error().raw_os_error()
|
||||
!= Some(libc::ESRCH)
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let running = unsafe {
|
||||
let handle =
|
||||
|
||||
+6
-1
@@ -262,7 +262,12 @@ export function isDaemonRunning(session?: string): boolean {
|
||||
// Check if process exists (works on both Unix and Windows)
|
||||
process.kill(pid, 0);
|
||||
return true;
|
||||
} catch {
|
||||
} catch (err: unknown) {
|
||||
// EPERM means the process exists but we lack permission to signal it
|
||||
// (e.g. caller is inside a macOS sandbox). Only ESRCH means it's gone.
|
||||
if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'EPERM') {
|
||||
return true;
|
||||
}
|
||||
// Process doesn't exist, clean up stale files
|
||||
cleanupSocket(session);
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user