systemprompt_loader/subprocess/
linux.rs1use std::process::Command;
8
9#[expect(
10 unsafe_code,
11 reason = "std::os::unix::process::CommandExt::pre_exec is an unsafe fn; there is no safe way \
12 to run code in the forked child before exec, and the parent-death signal must be \
13 armed there to cover children that never opt in"
14)]
15pub(super) fn arm_parent_death_signal(cmd: &mut Command) {
16 use std::os::unix::process::CommandExt;
17
18 let supervisor = std::process::id();
19
20 unsafe {
24 cmd.pre_exec(move || {
25 if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) != 0 {
26 return Err(std::io::Error::last_os_error());
27 }
28 if libc::getppid() != supervisor as libc::pid_t {
31 libc::_exit(0);
32 }
33 Ok(())
34 });
35 }
36}
37
38#[must_use]
39pub fn live_pid_is_subprocess(pid: u32, name_key: &str, service_name: &str) -> bool {
40 match std::fs::read(format!("/proc/{pid}/environ")) {
41 Ok(environ) => systemprompt_models::subprocess::environ_identifies_child(
42 &environ,
43 name_key,
44 service_name,
45 ),
46 Err(e) => {
47 tracing::warn!(pid, error = %e, "Could not read process environ to verify child identity");
48 false
49 },
50 }
51}
52
53#[must_use]
54pub fn is_zombie(pid: u32) -> bool {
55 let Ok(stat) = std::fs::read_to_string(format!("/proc/{pid}/stat")) else {
56 return false;
57 };
58 let Some((_, after_comm)) = stat.rsplit_once(')') else {
61 return false;
62 };
63 after_comm.split_whitespace().next() == Some("Z")
64}