From 2bf0021daa40a79254d3de8676bb795569258231 Mon Sep 17 00:00:00 2001 From: kosayoda Date: Sun, 26 Feb 2023 18:15:02 -0500 Subject: [PATCH 1/3] Add wrappers for posix_spawn related functions. --- src/lib.rs | 5 + src/spawn.rs | 463 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 468 insertions(+) create mode 100644 src/spawn.rs diff --git a/src/lib.rs b/src/lib.rs index dffac29b54..e7e2388211 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,6 +185,11 @@ pub mod unistd; #[cfg(any(feature = "poll", feature = "event"))] mod poll_timeout; +feature! { + #![feature = "process"] + pub mod spawn; +} + use std::ffi::{CStr, CString, OsStr}; use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; diff --git a/src/spawn.rs b/src/spawn.rs new file mode 100644 index 0000000000..5533f61b8e --- /dev/null +++ b/src/spawn.rs @@ -0,0 +1,463 @@ +//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header. + +use std::{ + ffi::CStr, + mem, + os::unix::io::{AsFd, AsRawFd}, +}; + +#[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()) }; + + if res != 0 { + return Err(Errno::from_raw(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, + ) + }; + if res != 0 { + return Err(Errno::from_raw(res)); + } + + let res = unsafe { + libc::posix_spawnattr_init( + &mut self.attr as *mut libc::posix_spawnattr_t, + ) + }; + if res != 0 { + return Err(Errno::from_raw(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, + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(&mut self) -> Result { + let mut flags: libc::c_short = 0; + let res = unsafe { + libc::posix_spawnattr_getflags( + &mut self.attr as *mut libc::posix_spawnattr_t, + &mut flags, + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(&mut self) -> Result { + let mut pid: libc::pid_t = 0; + + let res = unsafe { + libc::posix_spawnattr_getpgroup( + &mut self.attr as *mut libc::posix_spawnattr_t, + &mut pid, + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(&mut self) -> Result { + let mut sigset = mem::MaybeUninit::uninit(); + + let res = unsafe { + libc::posix_spawnattr_getsigdefault( + &mut self.attr as *mut libc::posix_spawnattr_t, + sigset.as_mut_ptr(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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(&mut self) -> Result { + let mut sigset = mem::MaybeUninit::uninit(); + + let res = unsafe { + libc::posix_spawnattr_getsigmask( + &mut self.attr as *mut libc::posix_spawnattr_t, + sigset.as_mut_ptr(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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()) + }; + + if res == 0 { + Ok(unsafe { + PosixSpawnFileActions { + fa: actions.assume_init(), + } + }) + } else { + Err(Errno::from_raw(res)) + } + } + + /// 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, + ) + }; + if res != 0 { + return Err(Errno::from_raw(res)); + } + + let res = unsafe { + libc::posix_spawn_file_actions_init( + &mut self.fa as *mut libc::posix_spawn_file_actions_t, + ) + }; + if res != 0 { + return Err(Errno::from_raw(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: Fd1, + newfd: Fd2, + ) -> Result<()> { + let res = unsafe { + libc::posix_spawn_file_actions_adddup2( + &mut self.fa as *mut libc::posix_spawn_file_actions_t, + fd.as_fd().as_raw_fd(), + newfd.as_fd().as_raw_fd(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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: Fd, + 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.as_fd().as_raw_fd(), + cstr.as_ptr(), + oflag.bits(), + mode.bits(), + ) + })?; + if res != 0 { + return Err(Errno::from_raw(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: Fd) -> Result<()> { + let res = unsafe { + libc::posix_spawn_file_actions_addclose( + &mut self.fa as *mut libc::posix_spawn_file_actions_t, + fd.as_fd().as_raw_fd(), + ) + }; + if res != 0 { + return Err(Errno::from_raw(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, + ); + } + } +} + +// Specifically for use with posix_spawn and posix_spawnp. +// https://github.com/rust-lang/libc/issues/1272 +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() as *mut libc::c_char) + .collect(); + v.push(std::ptr::null::() as *mut libc::c_char); + 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, SE: AsRef>( + path: &CStr, + file_actions: &PosixSpawnFileActions, + attr: &PosixSpawnAttr, + args: &[SA], + envp: &[SE], +) -> Result { + let mut pid = 0; + + let res = unsafe { + let args_p = to_exec_array(args); + let env_p = to_exec_array(envp); + + libc::posix_spawn( + &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 res == 0 { + Ok(Pid::from_raw(pid)) + } else { + Err(Errno::from_raw(res)) + } +} + +/// 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 res = 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 res == 0 { + Ok(Pid::from_raw(pid)) + } else { + Err(Errno::from_raw(res)) + } +} From 4db179fdd42746659bd997cb32c401056af4ffa0 Mon Sep 17 00:00:00 2001 From: kosayoda Date: Sun, 31 Mar 2024 17:10:59 -0400 Subject: [PATCH 2/3] Gate posix_spawn related functions. --- src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e7e2388211..9e6110f389 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,6 +185,14 @@ pub mod unistd; #[cfg(any(feature = "poll", feature = "event"))] mod poll_timeout; +#[cfg(any( + target_os = "freebsd", + target_os = "haiku", + target_os = "linux", + target_os = "netbsd", + target_os = "macos", + target_os = "ios" +))] feature! { #![feature = "process"] pub mod spawn; From ab833286c906ffed4779fa08ce3643fd42f7316c Mon Sep 17 00:00:00 2001 From: kosayoda Date: Wed, 24 Apr 2024 17:19:50 -0400 Subject: [PATCH 3/3] Address review. --- src/lib.rs | 3 +-- src/spawn.rs | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9e6110f389..6811aaa18a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -190,8 +190,7 @@ mod poll_timeout; target_os = "haiku", target_os = "linux", target_os = "netbsd", - target_os = "macos", - target_os = "ios" + apple_targets ))] feature! { #![feature = "process"] diff --git a/src/spawn.rs b/src/spawn.rs index 5533f61b8e..e754b54bd1 100644 --- a/src/spawn.rs +++ b/src/spawn.rs @@ -85,11 +85,11 @@ impl PosixSpawnAttr { /// 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(&mut self) -> Result { + pub fn flags(&self) -> Result { let mut flags: libc::c_short = 0; let res = unsafe { libc::posix_spawnattr_getflags( - &mut self.attr as *mut libc::posix_spawnattr_t, + &self.attr as *const libc::posix_spawnattr_t, &mut flags, ) }; @@ -120,12 +120,12 @@ impl PosixSpawnAttr { /// 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(&mut self) -> Result { + pub fn pgroup(&self) -> Result { let mut pid: libc::pid_t = 0; let res = unsafe { libc::posix_spawnattr_getpgroup( - &mut self.attr as *mut libc::posix_spawnattr_t, + &self.attr as *const libc::posix_spawnattr_t, &mut pid, ) }; @@ -158,12 +158,12 @@ impl PosixSpawnAttr { /// 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(&mut self) -> Result { + pub fn sigdefault(&self) -> Result { let mut sigset = mem::MaybeUninit::uninit(); let res = unsafe { libc::posix_spawnattr_getsigdefault( - &mut self.attr as *mut libc::posix_spawnattr_t, + &self.attr as *const libc::posix_spawnattr_t, sigset.as_mut_ptr(), ) }; @@ -196,12 +196,12 @@ impl PosixSpawnAttr { /// 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(&mut self) -> Result { + pub fn sigmask(&self) -> Result { let mut sigset = mem::MaybeUninit::uninit(); let res = unsafe { libc::posix_spawnattr_getsigmask( - &mut self.attr as *mut libc::posix_spawnattr_t, + &self.attr as *const libc::posix_spawnattr_t, sigset.as_mut_ptr(), ) }; @@ -387,14 +387,19 @@ impl Drop for PosixSpawnFileActions { } } -// Specifically for use with posix_spawn and posix_spawnp. -// https://github.com/rust-lang/libc/issues/1272 +// 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() as *mut libc::c_char) + .map(|s| s.as_ref().as_ptr().cast_mut()) .collect(); - v.push(std::ptr::null::() as *mut libc::c_char); + v.push(std::ptr::null_mut()); v }