use std::collections::HashMap;
use std::ffi::{CString, OsString};
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::process::{Command, ExitStatus};
use super::{posix_spawn_file_actions_addchdir_np, resolve_disclaim_fn, wait_for};
fn spawn_pid_inherit_disclaimed(cmd: &Command) -> io::Result<libc::pid_t> {
let prog_c = CString::new(cmd.get_program().as_bytes())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "program contains NUL"))?;
let mut argv_c: Vec<CString> = Vec::with_capacity(1);
argv_c.push(prog_c.clone());
for a in cmd.get_args() {
argv_c.push(
CString::new(a.as_bytes()).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 mut env_map: HashMap<OsString, OsString> = std::env::vars_os().collect();
for (k, v) in cmd.get_envs() {
match v {
Some(val) => {
env_map.insert(k.to_os_string(), val.to_os_string());
}
None => {
env_map.remove(k);
}
}
}
let mut envp_c: Vec<CString> = Vec::with_capacity(env_map.len());
for (k, v) in &env_map {
let mut buf = Vec::with_capacity(k.as_bytes().len() + v.as_bytes().len() + 1);
buf.extend_from_slice(k.as_bytes());
buf.push(b'=');
buf.extend_from_slice(v.as_bytes());
envp_c.push(CString::new(buf).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"environment variable contains NUL",
)
})?);
}
let mut envp_ptr: Vec<*mut libc::c_char> = envp_c
.iter()
.map(|c| c.as_ptr() as *mut libc::c_char)
.collect();
envp_ptr.push(std::ptr::null_mut());
let cwd_c = cmd
.get_current_dir()
.map(|p| {
CString::new(p.as_os_str().as_bytes())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "cwd contains NUL"))
})
.transpose()?;
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());
}
if let Some(ref cwd) = cwd_c {
let rc = posix_spawn_file_actions_addchdir_np(&mut file_actions, cwd.as_ptr());
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 sigdefault: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&mut sigdefault);
libc::sigaddset(&mut sigdefault, libc::SIGINT);
libc::sigaddset(&mut sigdefault, libc::SIGQUIT);
if libc::posix_spawnattr_setsigdefault(&mut attr, &sigdefault) == 0 {
let mut flags: libc::c_short = 0;
libc::posix_spawnattr_getflags(&attr, &mut flags);
flags |= libc::POSIX_SPAWN_SETSIGDEF as libc::c_short;
let _ = libc::posix_spawnattr_setflags(&mut attr, flags);
}
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(),
envp_ptr.as_ptr(),
);
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
}
};
Ok(pid)
}
pub(crate) fn spawn_status_inherit_disclaimed(cmd: &mut Command) -> io::Result<ExitStatus> {
let pid = spawn_pid_inherit_disclaimed(cmd)?;
wait_for(pid)
}
pub(crate) fn spawn_detached_disclaimed(cmd: &mut Command) -> io::Result<()> {
spawn_pid_inherit_disclaimed(cmd)?;
Ok(())
}