ipc: check for PID liveness via libc:kill

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I961c138cf8341bad6a27c450706de11d6a6a6964
This commit is contained in:
raf 2026-05-01 23:58:54 +03:00
commit 62f3230456
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF

View file

@ -53,6 +53,25 @@ impl From<serde_json::Error> 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()
}