pub const SUBPROCESS_MARKER_ENV: &str = "SYSTEMPROMPT_SUBPROCESS";
pub const AGENT_NAME_ENV: &str = "AGENT_NAME";
pub const MCP_SERVICE_ID_ENV: &str = "MCP_SERVICE_ID";
#[must_use]
pub fn signalable_pid(pid: u32) -> Option<i32> {
if pid == 0 {
return None;
}
i32::try_from(pid).ok()
}
#[must_use]
pub fn environ_identifies_child(environ: &[u8], name_key: &str, service_name: &str) -> bool {
let marker = format!("{SUBPROCESS_MARKER_ENV}=1");
let expected_name = format!("{name_key}={service_name}");
let mut has_marker = false;
let mut has_name = false;
for entry in environ.split(|&b| b == 0) {
if entry == marker.as_bytes() {
has_marker = true;
} else if entry == expected_name.as_bytes() {
has_name = true;
}
}
has_marker && has_name
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn live_pid_is_subprocess(pid: u32, name_key: &str, service_name: &str) -> bool {
match std::fs::read(format!("/proc/{pid}/environ")) {
Ok(environ) => environ_identifies_child(&environ, name_key, service_name),
Err(e) => {
tracing::warn!(pid, error = %e, "Could not read process environ to verify child identity");
false
},
}
}
#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn live_pid_is_subprocess(_pid: u32, _name_key: &str, _service_name: &str) -> bool {
false
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn is_zombie(pid: u32) -> bool {
let Ok(stat) = std::fs::read_to_string(format!("/proc/{pid}/stat")) else {
return false;
};
let Some((_, after_comm)) = stat.rsplit_once(')') else {
return false;
};
after_comm.split_whitespace().next() == Some("Z")
}
#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn is_zombie(_pid: u32) -> bool {
false
}