//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header. use std::{ffi::CStr, mem, os::fd::RawFd}; #[cfg(any(feature = "fs", feature = "term"))] use crate::fcntl::OFlag; #[cfg(feature = "signal")] use crate::sys::signal::SigSet; #[cfg(feature = "fs")] use crate::sys::stat::Mode; use crate::{errno::Errno, unistd::Pid, NixPath, Result}; /// A spawn attributes object. See [posix_spawnattr_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html). #[repr(transparent)] #[derive(Debug)] pub struct PosixSpawnAttr { attr: libc::posix_spawnattr_t, } impl PosixSpawnAttr { /// Initialize the spawn attributes object. See /// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html). #[doc(alias("posix_spawnattr_init"))] pub fn init() -> Result { let mut attr = mem::MaybeUninit::uninit(); let res = unsafe { libc::posix_spawnattr_init(attr.as_mut_ptr()) }; Errno::result(res)?; let attr = unsafe { attr.assume_init() }; Ok(PosixSpawnAttr { attr }) } /// Reinitialize the spawn attributes object. /// This is a wrapper around /// [posix_spawnattr_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html) /// followed by /// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html). #[doc(alias("posix_spawnattr_destroy"))] pub fn reinit(mut self) -> Result { let res = unsafe { libc::posix_spawnattr_destroy( &mut self.attr as *mut libc::posix_spawnattr_t, ) }; Errno::result(res)?; let res = unsafe { libc::posix_spawnattr_init( &mut self.attr as *mut libc::posix_spawnattr_t, ) }; Errno::result(res)?; Ok(self) } /// Set spawn flags. See /// [posix_spawnattr_setflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html). #[doc(alias("posix_spawnattr_setflags"))] pub fn set_flags(&mut self, flags: PosixSpawnFlags) -> Result<()> { let res = unsafe { libc::posix_spawnattr_setflags( &mut self.attr as *mut libc::posix_spawnattr_t, flags.bits() as libc::c_short, ) }; Errno::result(res)?; Ok(()) } /// Get spawn flags. See /// [posix_spawnattr_getflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html). #[doc(alias("posix_spawnattr_getflags"))] pub fn flags(&self) -> Result { let mut flags: libc::c_short = 0; let res = unsafe { libc::posix_spawnattr_getflags( &self.attr as *const libc::posix_spawnattr_t, &mut flags, ) }; Errno::result(res)?; Ok(PosixSpawnFlags::from_bits_truncate(flags.into())) } /// Set spawn pgroup. See /// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html). #[doc(alias("posix_spawnattr_setpgroup"))] pub fn set_pgroup(&mut self, pgroup: Pid) -> Result<()> { let res = unsafe { libc::posix_spawnattr_setpgroup( &mut self.attr as *mut libc::posix_spawnattr_t, pgroup.as_raw(), ) }; Errno::result(res)?; Ok(()) } /// Get spawn pgroup. See /// [posix_spawnattr_getpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html). #[doc(alias("posix_spawnattr_getpgroup"))] pub fn pgroup(&self) -> Result { let mut pid: libc::pid_t = 0; let res = unsafe { libc::posix_spawnattr_getpgroup( &self.attr as *const libc::posix_spawnattr_t, &mut pid, ) }; Errno::result(res)?; Ok(Pid::from_raw(pid)) } feature! { #![feature = "signal"] /// Set spawn sigdefault. See /// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html). #[doc(alias("posix_spawnattr_setsigdefault"))] pub fn set_sigdefault(&mut self, sigdefault: &SigSet) -> Result<()> { let res = unsafe { libc::posix_spawnattr_setsigdefault( &mut self.attr as *mut libc::posix_spawnattr_t, sigdefault.as_ref(), ) }; Errno::result(res)?; Ok(()) } /// Get spawn sigdefault. See /// [posix_spawnattr_getsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html). #[doc(alias("posix_spawnattr_getsigdefault"))] pub fn sigdefault(&self) -> Result { let mut sigset = mem::MaybeUninit::uninit(); let res = unsafe { libc::posix_spawnattr_getsigdefault( &self.attr as *const libc::posix_spawnattr_t, sigset.as_mut_ptr(), ) }; Errno::result(res)?; let sigdefault = unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) }; Ok(sigdefault) } /// Set spawn sigmask. See /// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html). #[doc(alias("posix_spawnattr_setsigmask"))] pub fn set_sigmask(&mut self, sigdefault: &SigSet) -> Result<()> { let res = unsafe { libc::posix_spawnattr_setsigmask( &mut self.attr as *mut libc::posix_spawnattr_t, sigdefault.as_ref(), ) }; Errno::result(res)?; Ok(()) } /// Get spawn sigmask. See /// [posix_spawnattr_getsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html). #[doc(alias("posix_spawnattr_getsigmask"))] pub fn sigmask(&self) -> Result { let mut sigset = mem::MaybeUninit::uninit(); let res = unsafe { libc::posix_spawnattr_getsigmask( &self.attr as *const libc::posix_spawnattr_t, sigset.as_mut_ptr(), ) }; Errno::result(res)?; let sigdefault = unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) }; Ok(sigdefault) } } } impl Drop for PosixSpawnAttr { fn drop(&mut self) { unsafe { libc::posix_spawnattr_destroy( &mut self.attr as *mut libc::posix_spawnattr_t, ); } } } libc_bitflags!( /// Process attributes to be changed in the new process image when invoking [`posix_spawn`] /// or [`posix_spawnp`]. See /// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html). pub struct PosixSpawnFlags: libc::c_int { /// Reset effective user ID of the child process to parent's real user ID. POSIX_SPAWN_RESETIDS; /// Put the child in a process group specified by the spawn-pgroup attribute. See /// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html). POSIX_SPAWN_SETPGROUP; /// Force set signals to default signal handling in child process. See /// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html). #[cfg(feature = "signal")] POSIX_SPAWN_SETSIGDEF; /// Set signal mask of child process. See /// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html). #[cfg(feature = "signal")] POSIX_SPAWN_SETSIGMASK; // TODO: Add support for the following two flags whenever support for // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html // is added to nix. // POSIX_SPAWN_SETSCHEDPARAM; // POSIX_SPAWN_SETSCHEDULER; } ); /// A spawn file actions object. See [posix_spawn_file_actions_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html). #[repr(transparent)] #[derive(Debug)] pub struct PosixSpawnFileActions { fa: libc::posix_spawn_file_actions_t, } impl PosixSpawnFileActions { /// Initialize the spawn file actions object. See /// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html). #[doc(alias("posix_spawn_file_actions_init"))] pub fn init() -> Result { let mut actions = mem::MaybeUninit::uninit(); let res = unsafe { libc::posix_spawn_file_actions_init(actions.as_mut_ptr()) }; Errno::result(res)?; Ok(unsafe { PosixSpawnFileActions { fa: actions.assume_init(), } }) } /// Reinitialize the spawn file actions object. /// This is a wrapper around /// [posix_spawn_file_actions_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html). /// followed by /// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html). #[doc(alias("posix_spawn_file_actions_destroy"))] pub fn reinit(mut self) -> Result { let res = unsafe { libc::posix_spawn_file_actions_destroy( &mut self.fa as *mut libc::posix_spawn_file_actions_t, ) }; Errno::result(res)?; let res = unsafe { libc::posix_spawn_file_actions_init( &mut self.fa as *mut libc::posix_spawn_file_actions_t, ) }; Errno::result(res)?; Ok(self) } /// Add a [dup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html) action. See /// [posix_spawn_file_actions_adddup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html). #[doc(alias("posix_spawn_file_actions_adddup2"))] pub fn add_dup2(&mut self, fd: RawFd, newfd: RawFd) -> Result<()> { let res = unsafe { libc::posix_spawn_file_actions_adddup2( &mut self.fa as *mut libc::posix_spawn_file_actions_t, fd, newfd, ) }; Errno::result(res)?; Ok(()) } feature! { #![all(feature = "fs", feature = "term")] /// Add an open action. See /// [posix_spawn_file_actions_addopen](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html). #[doc(alias("posix_spawn_file_actions_addopen"))] pub fn add_open( &mut self, fd: RawFd, path: &P, oflag: OFlag, mode: Mode, ) -> Result<()> { let res = path.with_nix_path(|cstr| unsafe { libc::posix_spawn_file_actions_addopen( &mut self.fa as *mut libc::posix_spawn_file_actions_t, fd, cstr.as_ptr(), oflag.bits(), mode.bits(), ) })?; Errno::result(res)?; Ok(()) } } /// Add a close action. See /// [posix_spawn_file_actions_addclose](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html). #[doc(alias("posix_spawn_file_actions_addclose"))] pub fn add_close(&mut self, fd: RawFd) -> Result<()> { let res = unsafe { libc::posix_spawn_file_actions_addclose( &mut self.fa as *mut libc::posix_spawn_file_actions_t, fd, ) }; Errno::result(res)?; Ok(()) } } impl Drop for PosixSpawnFileActions { fn drop(&mut self) { unsafe { libc::posix_spawn_file_actions_destroy( &mut self.fa as *mut libc::posix_spawn_file_actions_t, ); } } } // The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`, // but implementations won't modify them, making the `mut` type redundant. Considering this, // Nix does not expose this mutability, but we have to change the interface when calling the // underlying libc interfaces , this helper function does the conversion job. // // SAFETY: // It is safe to add the mutability in types as implementations won't mutable them. unsafe fn to_exec_array>(args: &[S]) -> Vec<*mut libc::c_char> { let mut v: Vec<*mut libc::c_char> = args .iter() .map(|s| s.as_ref().as_ptr().cast_mut()) .collect(); v.push(std::ptr::null_mut()); v } /// Create a new child process from the specified process image. See /// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html). pub fn posix_spawn( path: &P, file_actions: &PosixSpawnFileActions, attr: &PosixSpawnAttr, args: &[SA], envp: &[SE], ) -> Result where P: NixPath + ?Sized, SA: AsRef, SE: AsRef, { let mut pid = 0; let ret = unsafe { let args_p = to_exec_array(args); let env_p = to_exec_array(envp); path.with_nix_path(|c_str| { libc::posix_spawn( &mut pid as *mut libc::pid_t, c_str.as_ptr(), &file_actions.fa as *const libc::posix_spawn_file_actions_t, &attr.attr as *const libc::posix_spawnattr_t, args_p.as_ptr(), env_p.as_ptr(), ) })? }; if ret != 0 { return Err(Errno::from_raw(ret)); } Ok(Pid::from_raw(pid)) } /// Create a new child process from the specified process image. See /// [posix_spawnp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html). pub fn posix_spawnp, SE: AsRef>( path: &CStr, file_actions: &PosixSpawnFileActions, attr: &PosixSpawnAttr, args: &[SA], envp: &[SE], ) -> Result { let mut pid = 0; let ret = unsafe { let args_p = to_exec_array(args); let env_p = to_exec_array(envp); libc::posix_spawnp( &mut pid as *mut libc::pid_t, path.as_ptr(), &file_actions.fa as *const libc::posix_spawn_file_actions_t, &attr.attr as *const libc::posix_spawnattr_t, args_p.as_ptr(), env_p.as_ptr(), ) }; if ret != 0 { return Err(Errno::from_raw(ret)); } Ok(Pid::from_raw(pid)) }