use std::os::unix::io::RawFd;
use crate::CoreError;
use crate::error::syscall_ret;
use super::fork::{child_entry, collect_required_pipe_fds, prepare_child_context, reap_and_drain};
use super::{CLONE_PIDFD, Process, SYS_CLONE3, SpawnDrain, SpawnOptions, make_cloexec_pipe};
#[repr(C)]
struct CloneArgs {
flags: u64,
pidfd: u64,
child_tid: u64,
parent_tid: u64,
exit_signal: u64,
stack: u64,
stack_size: u64,
tls: u64,
set_tid: u64,
set_tid_size: u64,
cgroup: u64,
}
impl CloneArgs {
fn new(pidfd: bool) -> Self {
Self {
flags: if pidfd { CLONE_PIDFD } else { 0 },
pidfd: 0,
child_tid: 0,
parent_tid: 0,
exit_signal: libc::SIGCHLD as u64,
stack: 0,
stack_size: 0,
tls: 0,
set_tid: 0,
set_tid_size: 0,
cgroup: 0,
}
}
}
pub(super) fn spawn_clone3_internal(
opts: SpawnOptions,
pidfd: bool,
) -> Result<(Process, SpawnDrain), CoreError> {
let ctx = prepare_child_context(&opts);
let mut pipes = super::Pipes::new(
opts.stdin.as_deref(),
opts.capture_stdout,
opts.capture_stderr,
)?;
let (child_error_r, child_error_w) = make_cloexec_pipe()?;
let mut required_fds = collect_required_pipe_fds(&pipes);
required_fds.push(child_error_w);
let mut args = CloneArgs::new(pidfd);
let mut pidfd_out: u64 = u64::MAX;
args.pidfd = if pidfd {
(&mut pidfd_out as *mut u64) as u64
} else {
0
};
let pid = unsafe {
libc::syscall(
SYS_CLONE3,
&args as *const CloneArgs,
std::mem::size_of::<CloneArgs>(),
)
};
if pid < 0 {
unsafe {
libc::close(child_error_r);
libc::close(child_error_w);
}
pipes.close_all();
syscall_ret(-1, "clone3")?;
}
if pid == 0 {
unsafe {
child_entry(
&pipes,
&opts,
&ctx,
&required_fds,
child_error_r,
child_error_w,
);
}
}
let process = if pidfd && pid >= 0 && pidfd_out != u64::MAX {
Process::with_pidfd(pid as libc::pid_t, pidfd_out as RawFd)
} else if pidfd {
let cpid = pid as libc::pid_t;
unsafe {
libc::close(child_error_r);
libc::close(child_error_w);
}
pipes.close_all();
super::orphan_child(cpid);
return Err(CoreError::sys(
libc::EIO,
"clone3: CLONE_PIDFD requested but no pidfd delivered",
));
} else {
Process::new(pid as libc::pid_t)
};
let drain = reap_and_drain(
pid as libc::pid_t,
pipes,
child_error_r,
child_error_w,
opts.stdin,
opts.max_output,
opts.early_exit,
)?;
Ok((process, drain))
}