1use std::fs;
6use std::io;
7use std::path::Path;
8use std::process;
9use std::result;
10
11use errno_result;
12
13use libc::{c_long, pid_t, syscall, CLONE_NEWPID, CLONE_NEWUSER, SIGCHLD};
14
15use syscall_defines::linux::LinuxSyscall::SYS_clone;
16
17#[repr(u32)]
19pub enum CloneNamespace {
20 Inherit = 0,
22 NewUserPid = CLONE_NEWUSER as u32 | CLONE_NEWPID as u32,
24}
25
26#[derive(Debug)]
27pub enum CloneError {
28 IterateTasks(io::Error),
30 Multithreaded(usize),
32 Sys(::Error),
34}
35
36unsafe fn do_clone(flags: i32) -> ::Result<pid_t> {
37 let pid = syscall(SYS_clone as c_long, flags | SIGCHLD as i32, 0);
40 if pid < 0 {
41 errno_result()
42 } else {
43 Ok(pid as pid_t)
44 }
45}
46
47fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> {
48 Ok(fs::read_dir(path)?.count())
49}
50
51pub fn clone_process<F>(ns: CloneNamespace, post_clone_cb: F) -> result::Result<pid_t, CloneError>
61where
62 F: FnOnce(),
63{
64 match count_dir_entries("/proc/self/task") {
65 Ok(1) => {}
66 Ok(thread_count) => {
67 let _ = thread_count;
70 #[cfg(not(test))]
71 return Err(CloneError::Multithreaded(thread_count));
72 }
73 Err(e) => return Err(CloneError::IterateTasks(e)),
74 }
75 let ret = unsafe { do_clone(ns as i32) }.map_err(CloneError::Sys)?;
79 if ret == 0 {
80 struct ExitGuard;
81 impl Drop for ExitGuard {
82 fn drop(&mut self) {
83 process::exit(101);
84 }
85 }
86 #[allow(unused_variables)]
88 let exit_guard = ExitGuard {};
89 post_clone_cb();
90 process::exit(0);
92 }
93
94 Ok(ret)
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use libc;
101 use {getpid, EventFd};
102
103 fn wait_process(pid: libc::pid_t) -> ::Result<libc::c_int> {
104 let mut status: libc::c_int = 0;
105 unsafe {
106 if libc::waitpid(pid, &mut status as *mut libc::c_int, 0) < 0 {
107 errno_result()
108 } else {
109 Ok(libc::WEXITSTATUS(status))
110 }
111 }
112 }
113
114 #[test]
115 fn pid_diff() {
116 let evt_fd = EventFd::new().expect("failed to create EventFd");
117 let evt_fd_fork = evt_fd.try_clone().expect("failed to clone EventFd");
118 let pid = getpid();
119 clone_process(CloneNamespace::Inherit, || {
120 if pid != getpid() {
122 evt_fd_fork.write(1).unwrap()
123 } else {
124 evt_fd_fork.write(2).unwrap()
125 }
126 })
127 .expect("failed to clone");
128 assert_eq!(evt_fd.read(), Ok(1));
129 }
130
131 #[test]
132 fn panic_safe() {
133 let pid = getpid();
134 assert_ne!(pid, 0);
135
136 let clone_pid = clone_process(CloneNamespace::Inherit, || {
137 assert!(false);
138 })
139 .expect("failed to clone");
140
141 if pid != getpid() {
143 process::exit(2);
144 }
145
146 let status = wait_process(clone_pid).expect("wait_process failed");
147 assert!(status == 101 || status == 0);
148 }
149}