use std::mem::MaybeUninit;
use crate::CoreError;
use crate::error::posix_ret;
use crate::signal::SignalRuntime;
use libc::{c_char, pid_t};
use super::exec::ExecArgv;
use super::{environ, Pipes, SpawnDrain, SpawnOptions};
pub(crate) const POSIX_SPAWN_SETPGROUP: i32 = 2;
pub(crate) const POSIX_SPAWN_SETSIGDEF: i32 = 4;
pub(crate) const POSIX_SPAWN_SETSIGMASK: i32 = 8;
unsafe extern "C" {
pub(crate) fn posix_spawn(
pid: *mut libc::pid_t,
path: *const libc::c_char,
file_actions: *const libc::posix_spawn_file_actions_t,
attrp: *const libc::posix_spawnattr_t,
argv: *const *mut libc::c_char,
envp: *const *mut libc::c_char,
) -> libc::c_int;
pub(crate) fn posix_spawn_file_actions_addclose(
file_actions: *mut libc::posix_spawn_file_actions_t,
fd: libc::c_int,
) -> libc::c_int;
pub(crate) fn posix_spawn_file_actions_adddup2(
file_actions: *mut libc::posix_spawn_file_actions_t,
fd: libc::c_int,
newfd: libc::c_int,
) -> libc::c_int;
pub(crate) fn posix_spawn_file_actions_destroy(
file_actions: *mut libc::posix_spawn_file_actions_t,
) -> libc::c_int;
pub(crate) fn posix_spawn_file_actions_init(
file_actions: *mut libc::posix_spawn_file_actions_t,
) -> libc::c_int;
pub(crate) fn posix_spawnattr_destroy(attr: *mut libc::posix_spawnattr_t) -> libc::c_int;
pub(crate) fn posix_spawnattr_init(attr: *mut libc::posix_spawnattr_t) -> libc::c_int;
pub(crate) fn posix_spawnattr_setflags(
attr: *mut libc::posix_spawnattr_t,
flags: libc::c_short,
) -> libc::c_int;
pub(crate) fn posix_spawnattr_setpgroup(
attr: *mut libc::posix_spawnattr_t,
pgroup: libc::pid_t,
) -> libc::c_int;
pub(crate) fn posix_spawnattr_setsigdefault(
attr: *mut libc::posix_spawnattr_t,
sigdefault: *const libc::sigset_t,
) -> libc::c_int;
pub(crate) fn posix_spawnattr_setsigmask(
attr: *mut libc::posix_spawnattr_t,
sigmask: *const libc::sigset_t,
) -> libc::c_int;
}
pub(super) fn spawn_posix_internal(opts: SpawnOptions) -> Result<(pid_t, SpawnDrain), CoreError> {
let mut pipes = Pipes::new(
opts.stdin.as_deref(),
opts.capture_stdout,
opts.capture_stderr,
)?;
let exe_ptr = match &opts.ctx.argv {
ExecArgv::Dynamic(v) => v[0].as_ptr(),
};
let argv = opts.ctx.get_argv_ptrs();
let envp = opts.ctx.get_envp_ptrs();
let actions = MaybeUninit::zeroed();
let mut actions = unsafe { actions.assume_init() };
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_init(&mut actions) },
"file_actions_init",
) {
pipes.close_all();
return Err(e);
}
struct Actions(*mut libc::posix_spawn_file_actions_t);
impl Drop for Actions {
fn drop(&mut self) {
unsafe {
posix_spawn_file_actions_destroy(self.0);
}
}
}
let _guard = Actions(&mut actions);
if let (Some(r), Some(w)) = (&pipes.stdin_r, &pipes.stdin_w) {
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_adddup2(&mut actions, r.raw(), 0) },
"dup2 stdin",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_addclose(&mut actions, r.raw()) },
"close stdin pipe",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_addclose(&mut actions, w.raw()) },
"close stdin write pipe",
) {
pipes.close_all();
return Err(e);
}
}
if let (Some(r), Some(w)) = (&pipes.stdout_r, &pipes.stdout_w) {
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_adddup2(&mut actions, w.raw(), 1) },
"dup2 stdout",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_addclose(&mut actions, w.raw()) },
"close stdout pipe",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_addclose(&mut actions, r.raw()) },
"close stdout read pipe",
) {
pipes.close_all();
return Err(e);
}
}
if let (Some(r), Some(w)) = (&pipes.stderr_r, &pipes.stderr_w) {
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_adddup2(&mut actions, w.raw(), 2) },
"dup2 stderr",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_addclose(&mut actions, w.raw()) },
"close stderr pipe",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawn_file_actions_addclose(&mut actions, r.raw()) },
"close stderr read pipe",
) {
pipes.close_all();
return Err(e);
}
}
let attr = MaybeUninit::zeroed();
let mut attr = unsafe { attr.assume_init() };
if let Err(e) = posix_ret(unsafe { posix_spawnattr_init(&mut attr) }, "attr_init") {
pipes.close_all();
return Err(e);
}
struct Attr(*mut libc::posix_spawnattr_t);
impl Drop for Attr {
fn drop(&mut self) {
unsafe {
posix_spawnattr_destroy(self.0);
}
}
}
let _attr = Attr(&mut attr);
let mut flags = 0;
if let Some(pg) = opts.pgroup.leader {
flags |= POSIX_SPAWN_SETPGROUP;
if let Err(e) = posix_ret(
unsafe { posix_spawnattr_setpgroup(&mut attr, pg) },
"setpgroup",
) {
pipes.close_all();
return Err(e);
}
}
flags |= POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
if let Err(e) = posix_ret(
unsafe { posix_spawnattr_setflags(&mut attr, flags as _) },
"setflags",
) {
pipes.close_all();
return Err(e);
}
let empty_mask = SignalRuntime::empty_set();
let def = SignalRuntime::set_with(&[libc::SIGPIPE])?;
if let Err(e) = posix_ret(
unsafe { posix_spawnattr_setsigmask(&mut attr, &empty_mask) },
"setsigmask",
) {
pipes.close_all();
return Err(e);
}
if let Err(e) = posix_ret(
unsafe { posix_spawnattr_setsigdefault(&mut attr, &def) },
"setsigdefault",
) {
pipes.close_all();
return Err(e);
}
let mut pid: pid_t = 0;
let envp_ptr = envp.as_ref().map_or_else(
|| unsafe { environ as *const *mut c_char },
|e: &Vec<*mut c_char>| e.as_ptr(),
);
if let Err(e) = posix_ret(
unsafe { posix_spawn(&mut pid, exe_ptr, &actions, &attr, argv.as_ptr(), envp_ptr) },
"posix_spawn",
) {
pipes.close_all();
return Err(e);
}
drop(pipes.stdin_r.take());
drop(pipes.stdout_w.take());
drop(pipes.stderr_w.take());
let drain = crate::io::DrainState::new(
pipes.stdin_w.take().filter(|_| opts.stdin.is_some()),
opts.stdin,
pipes.stdout_r.take(),
pipes.stderr_r.take(),
opts.max_output,
opts.early_exit,
)?;
Ok((pid, drain))
}