diff options
Diffstat (limited to 'crates/rebel-runner/src/util/clone.rs')
-rw-r--r-- | crates/rebel-runner/src/util/clone.rs | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/crates/rebel-runner/src/util/clone.rs b/crates/rebel-runner/src/util/clone.rs new file mode 100644 index 0000000..51a31c3 --- /dev/null +++ b/crates/rebel-runner/src/util/clone.rs @@ -0,0 +1,59 @@ +use std::{mem, process}; + +use nix::{ + errno, sched, + unistd::{self, Pid}, +}; + +#[repr(C)] +#[derive(Debug, Default)] +struct CloneArgs { + flags: u64, + pidfd: u64, + child_tid: u64, + parent_tid: u64, + exit_signal: u64, + stack: u64, + stack_size: u64, + tls: u64, +} + +pub unsafe fn clone(flags: sched::CloneFlags) -> nix::Result<unistd::ForkResult> { + let mut args = CloneArgs { + flags: flags.bits() as u64, + exit_signal: libc::SIGCHLD as u64, + ..CloneArgs::default() + }; + let size = mem::size_of_val(&args) as libc::size_t; + + let pid = libc::syscall(libc::SYS_clone3, &mut args, size); + + #[allow(clippy::comparison_chain)] + if pid < 0 { + Err(errno::Errno::last()) + } else if pid == 0 { + Ok(unistd::ForkResult::Child) + } else { + Ok(unistd::ForkResult::Parent { + child: Pid::from_raw(pid as libc::pid_t), + }) + } +} + +pub unsafe fn spawn<F>(flags: Option<sched::CloneFlags>, f: F) -> nix::Result<Pid> +where + F: FnOnce(), +{ + let res = if let Some(flags) = flags { + clone(flags) + } else { + unistd::fork() + }; + match res? { + unistd::ForkResult::Parent { child } => Ok(child), + unistd::ForkResult::Child => { + f(); + process::exit(0) + } + } +} |