use std::ffi::CString;
use std::io::{self, Read};
use std::os::unix::io::FromRawFd;
use std::process::Output;
use super::{OwnedFd, environ, pipe_cloexec, resolve_disclaim_fn, wait_for};
pub(crate) fn spawn_capture_disclaimed(program: &str, args: &[String]) -> io::Result<Output> {
let prog_c = CString::new(program)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "program contains NUL"))?;
let mut argv_c: Vec<CString> = Vec::with_capacity(args.len() + 1);
argv_c.push(prog_c.clone());
for a in args {
argv_c.push(
CString::new(a.as_str()).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "argument contains NUL")
})?,
);
}
let mut argv_ptr: Vec<*mut libc::c_char> = argv_c
.iter()
.map(|c| c.as_ptr() as *mut libc::c_char)
.collect();
argv_ptr.push(std::ptr::null_mut());
let (out_rd, out_wr) = pipe_cloexec()?;
let (err_rd, err_wr) = pipe_cloexec()?;
let mut out_rd = OwnedFd(out_rd);
let out_wr = OwnedFd(out_wr);
let mut err_rd = OwnedFd(err_rd);
let err_wr = OwnedFd(err_wr);
let dev_null = c"/dev/null";
let pid = {
unsafe {
let mut file_actions: libc::posix_spawn_file_actions_t = std::mem::zeroed();
let mut attr: libc::posix_spawnattr_t = std::mem::zeroed();
if libc::posix_spawn_file_actions_init(&mut file_actions) != 0 {
return Err(io::Error::last_os_error());
}
if libc::posix_spawnattr_init(&mut attr) != 0 {
libc::posix_spawn_file_actions_destroy(&mut file_actions);
return Err(io::Error::last_os_error());
}
let rc = libc::posix_spawn_file_actions_addopen(
&mut file_actions,
0,
dev_null.as_ptr(),
libc::O_RDONLY,
0,
);
if rc != 0 {
libc::posix_spawnattr_destroy(&mut attr);
libc::posix_spawn_file_actions_destroy(&mut file_actions);
return Err(io::Error::from_raw_os_error(rc));
}
let rc = libc::posix_spawn_file_actions_adddup2(&mut file_actions, out_wr.0, 1);
if rc != 0 {
libc::posix_spawnattr_destroy(&mut attr);
libc::posix_spawn_file_actions_destroy(&mut file_actions);
return Err(io::Error::from_raw_os_error(rc));
}
let rc = libc::posix_spawn_file_actions_adddup2(&mut file_actions, err_wr.0, 2);
if rc != 0 {
libc::posix_spawnattr_destroy(&mut attr);
libc::posix_spawn_file_actions_destroy(&mut file_actions);
return Err(io::Error::from_raw_os_error(rc));
}
if let Some(disclaim) = resolve_disclaim_fn() {
let _ = disclaim(&mut attr, 1);
}
let mut pid: libc::pid_t = 0;
let rc = libc::posix_spawnp(
&mut pid,
prog_c.as_ptr(),
&file_actions,
&attr,
argv_ptr.as_ptr(),
environ,
);
libc::posix_spawnattr_destroy(&mut attr);
libc::posix_spawn_file_actions_destroy(&mut file_actions);
if rc != 0 {
return Err(io::Error::from_raw_os_error(rc));
}
pid
}
};
drop(out_wr);
drop(err_wr);
let out_fd = std::mem::replace(&mut out_rd, OwnedFd(-1)).into_raw();
let reader = std::thread::spawn(move || {
let mut f = unsafe { std::fs::File::from_raw_fd(out_fd) };
let mut buf = Vec::new();
let _ = f.read_to_end(&mut buf);
buf
});
let err_fd = std::mem::replace(&mut err_rd, OwnedFd(-1)).into_raw();
let mut err_file = unsafe { std::fs::File::from_raw_fd(err_fd) };
let mut stderr_buf = Vec::new();
let err_read = err_file.read_to_end(&mut stderr_buf);
let stdout_buf = reader.join().unwrap_or_default();
let status = wait_for(pid);
err_read?;
let status = status?;
Ok(Output {
status,
stdout: stdout_buf,
stderr: stderr_buf,
})
}