summaryrefslogtreecommitdiffstats
path: root/crates/rebel-runner/src/util/clone.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/rebel-runner/src/util/clone.rs')
-rw-r--r--crates/rebel-runner/src/util/clone.rs59
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)
+ }
+ }
+}