#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WaitOutcome {
Exited(i32),
Signaled(i32),
}
impl WaitOutcome {
pub fn is_clean(self) -> bool {
matches!(self, WaitOutcome::Exited(0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Reaped {
pub pid: i32,
pub outcome: WaitOutcome,
}
#[cfg(unix)]
mod imp {
use super::{Reaped, WaitOutcome};
pub fn classify_status(status: i32) -> WaitOutcome {
if libc::WIFSIGNALED(status) {
WaitOutcome::Signaled(libc::WTERMSIG(status))
} else {
WaitOutcome::Exited(libc::WEXITSTATUS(status))
}
}
pub fn reap_pending() -> Vec<Reaped> {
let mut reaped = Vec::new();
loop {
let mut status: libc::c_int = 0;
let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
if pid > 0 {
reaped.push(Reaped {
pid,
outcome: classify_status(status),
});
} else {
break;
}
}
reaped
}
pub fn set_child_subreaper() -> bool {
#[cfg(target_os = "linux")]
{
unsafe { libc::prctl(libc::PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == 0 }
}
#[cfg(not(target_os = "linux"))]
{
false
}
}
pub fn is_init() -> bool {
unsafe { libc::getpid() == 1 }
}
}
#[cfg(not(unix))]
mod imp {
use super::{Reaped, WaitOutcome};
pub fn classify_status(status: i32) -> WaitOutcome {
WaitOutcome::Exited(status)
}
pub fn reap_pending() -> Vec<Reaped> {
Vec::new()
}
pub fn set_child_subreaper() -> bool {
false
}
pub fn is_init() -> bool {
false
}
}
pub use imp::{classify_status, is_init, set_child_subreaper};
pub const INSTANCE_CHILD_ENV: &str = "AGENTD_INSTANCE_CHILD";
pub fn install_instance_pdeathsig() {
#[cfg(target_os = "linux")]
unsafe {
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM, 0, 0, 0);
if libc::getppid() == 1 {
libc::raise(libc::SIGTERM);
}
}
}
pub(in crate::supervisor) use imp::reap_pending;
#[cfg(all(test, unix))]
mod tests {
use super::*;
fn exited(code: i32) -> i32 {
code << 8
}
fn signaled(sig: i32) -> i32 {
sig
}
#[test]
fn classify_exit_code() {
assert_eq!(classify_status(exited(0)), WaitOutcome::Exited(0));
assert_eq!(classify_status(exited(7)), WaitOutcome::Exited(7));
assert!(classify_status(exited(0)).is_clean());
assert!(!classify_status(exited(5)).is_clean());
}
#[test]
fn classify_signal_death() {
assert_eq!(
classify_status(signaled(libc::SIGKILL)),
WaitOutcome::Signaled(9)
);
assert_eq!(
classify_status(signaled(libc::SIGTERM)),
WaitOutcome::Signaled(15)
);
assert!(!classify_status(signaled(libc::SIGKILL)).is_clean());
}
}