fix: use socket connectivity alone instead combining it with PID check for daemon liveness (#879)
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"agent-browser": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix daemon detection for PID namespace isolation (e.g. `unshare`). Use socket connectivity as the sole liveness check instead of `kill(pid, 0)`, which fails when the caller cannot see the daemon's PID.
|
||||||
+4
-38
@@ -150,42 +150,6 @@ fn get_port_for_session(session: &str) -> u16 {
|
|||||||
49152 + ((hash.unsigned_abs() as u32 % 16383) as u16)
|
49152 + ((hash.unsigned_abs() as u32 % 16383) as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
fn is_daemon_running(session: &str) -> bool {
|
|
||||||
let pid_path = get_pid_path(session);
|
|
||||||
if !pid_path.exists() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if let Ok(pid_str) = fs::read_to_string(&pid_path) {
|
|
||||||
if let Ok(pid) = pid_str.trim().parse::<i32>() {
|
|
||||||
unsafe {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn is_daemon_running(session: &str) -> bool {
|
|
||||||
let pid_path = get_pid_path(session);
|
|
||||||
if !pid_path.exists() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let port = get_port_for_session(session);
|
|
||||||
TcpStream::connect_timeout(
|
|
||||||
&format!("127.0.0.1:{}", port).parse().unwrap(),
|
|
||||||
Duration::from_millis(100),
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn daemon_ready(session: &str) -> bool {
|
fn daemon_ready(session: &str) -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
@@ -315,8 +279,10 @@ fn apply_daemon_env(cmd: &mut Command, session: &str, opts: &DaemonOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_daemon(session: &str, opts: &DaemonOptions) -> Result<DaemonResult, String> {
|
pub fn ensure_daemon(session: &str, opts: &DaemonOptions) -> Result<DaemonResult, String> {
|
||||||
// Check if daemon is running AND responsive
|
// Socket connectivity is the sole liveness check — no PID check — so
|
||||||
if is_daemon_running(session) && daemon_ready(session) {
|
// callers in a different PID namespace (e.g. unshare) can still reuse
|
||||||
|
// an existing daemon they can reach over the socket.
|
||||||
|
if daemon_ready(session) {
|
||||||
// Double-check it's actually responsive by waiting and checking again
|
// Double-check it's actually responsive by waiting and checking again
|
||||||
// This handles the race condition where daemon is shutting down
|
// This handles the race condition where daemon is shutting down
|
||||||
// (daemon has a 100ms shutdown delay, so we wait longer)
|
// (daemon has a 100ms shutdown delay, so we wait longer)
|
||||||
|
|||||||
Reference in New Issue
Block a user