diff --git a/src/ipc.rs b/src/ipc.rs index 9f6787d..23457a7 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -53,6 +53,25 @@ impl From for IpcError { } } +/// Check whether a process with the given PID is still alive. +#[cfg(unix)] +fn is_process_alive(pid: u32) -> bool { + unsafe { + if libc::kill(pid as i32, 0) == 0 { + return true; + } + let errno = *libc::__errno_location(); + // EPERM means the process exists but we can't signal it. + errno == libc::EPERM + } +} + +#[cfg(not(unix))] +fn is_process_alive(_pid: u32) -> bool { + // Cannot verify on non-Unix; assume alive to stay safe. + true +} + /// Represents an ongoing operation tracked in IPC #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OngoingOperation { @@ -279,7 +298,7 @@ impl IpcCoordinator { let mut operations = self.load_operations()?; log::debug!("Loaded {} existing operations", operations.len()); - // Clean up stale operations (older than 10 minutes) + // Clean up stale operations (older than 10 minutes or with dead PIDs) let now = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap_or(Duration::ZERO) @@ -289,18 +308,21 @@ impl IpcCoordinator { .iter() .filter(|op| { op.status == OperationStatus::Running - && now.saturating_sub(op.started_at) > 600 + && (now.saturating_sub(op.started_at) > 600 + || !is_process_alive(op.pid)) }) .count(); operations.retain(|op| { - if op.status == OperationStatus::Running - && now.saturating_sub(op.started_at) > 600 - { - false // Remove stale operations - } else { - true + if op.status == OperationStatus::Running { + if !is_process_alive(op.pid) { + return false; // Remove dead operations + } + if now.saturating_sub(op.started_at) > 600 { + return false; // Remove old operations + } } + true }); if stale_count > 0 { @@ -311,7 +333,9 @@ impl IpcCoordinator { let conflicting: Vec<_> = operations .iter() .filter(|op| { - op.r#type == operation_type && op.status == OperationStatus::Running + op.r#type == operation_type + && op.status == OperationStatus::Running + && is_process_alive(op.pid) }) .collect(); @@ -403,7 +427,9 @@ impl IpcCoordinator { let conflicts: Vec<_> = operations .iter() .filter(|op| { - op.r#type == operation_type && op.status == OperationStatus::Running + op.r#type == operation_type + && op.status == OperationStatus::Running + && is_process_alive(op.pid) }) .collect(); @@ -426,7 +452,9 @@ impl IpcCoordinator { pub fn has_running_operation(&self, operation_type: OperationType) -> bool { let operations = self.load_operations().unwrap_or_default(); operations.iter().any(|op| { - op.r#type == operation_type && op.status == OperationStatus::Running + op.r#type == operation_type + && op.status == OperationStatus::Running + && is_process_alive(op.pid) }) } @@ -439,7 +467,9 @@ impl IpcCoordinator { operations .into_iter() .filter(|op| { - op.r#type == operation_type && op.status == OperationStatus::Running + op.r#type == operation_type + && op.status == OperationStatus::Running + && is_process_alive(op.pid) }) .collect() }