Skip to content

Commit

Permalink
fix(SubReaper): add standalone reaper command
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadowApex committed Sep 20, 2024
1 parent 620e78a commit c948f32
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 0 deletions.
2 changes: 2 additions & 0 deletions extensions/reaper/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
*.so
46 changes: 46 additions & 0 deletions extensions/reaper/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions extensions/reaper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "reaper"
version = "0.1.0"
edition = "2021"

[dependencies]
nix = { version = "0.29.0", features = ["process"] }
87 changes: 87 additions & 0 deletions extensions/reaper/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::{env, ffi::CString, process::exit};

use nix::{
errno::Errno,
sys::{prctl, wait::wait},
unistd::{execvp, fork, ForkResult},
};

fn main() {
// Parse the arguments that should be passed to the child process
let args = env::args();
let mut child_args = vec![];
let mut end_of_args = false;
for arg in args {
if !end_of_args && arg.as_str() == "--" {
end_of_args = true;
continue;
}
if !end_of_args {
continue;
}
child_args.push(arg);
}

// If "--" was not included as an argument, return an error.
if !end_of_args {
panic!("reaper: no '--' argument was found");
}
if child_args.is_empty() {
panic!("reaper: no sub-command!");
}

// Set the current process to be a subreaper. A subreaper MUST wait
// for all child processes to exit to clean up.
if let Err(e) = prctl::set_child_subreaper(true) {
panic!("reaper: failed to set child subreaper: {e:?}");
}

// Fork to spawn the requested process and wait for the subprocesses to exit
match unsafe { fork() } {
// Parent process of the fork should wait for all child processes to exit
Ok(ForkResult::Parent { child }) => {
println!("reaper: got child PID: {child}");

// Wait for all child processes to exit
loop {
match wait() {
Ok(_) => (),
Err(e) => {
if e == Errno::ECHILD {
break;
}
println!("reaper: got unexpected error: {e}")
}
};
}

println!("reaper: no more children exist; exiting");
exit(0);
}

// Child process of the fork should execute the requested command
Ok(ForkResult::Child) => {
println!("reaper: executing command: {child_args:?}");

// Convert the command to a CString
let cmd = CString::new(child_args.first().unwrap().as_str()).unwrap();

// Build the arguments list
let mut c_args = vec![];
for arg in child_args {
let c_arg = CString::new(arg.as_str()).unwrap();
c_args.push(c_arg);
}

// Execute the command
if let Err(e) = execvp(cmd.as_c_str(), c_args.as_slice()) {
panic!("reaper: failed executing command: {e:?}");
}
}

// Panic if forking fails
Err(e) => {
panic!("reaper: failed to create fork: {e:?}");
}
}
}

0 comments on commit c948f32

Please sign in to comment.