nix/
spawn.rs

1//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header.
2
3use std::{ffi::CStr, mem, os::fd::RawFd};
4
5#[cfg(any(feature = "fs", feature = "term"))]
6use crate::fcntl::OFlag;
7#[cfg(feature = "signal")]
8use crate::sys::signal::SigSet;
9#[cfg(feature = "fs")]
10use crate::sys::stat::Mode;
11use crate::{errno::Errno, unistd::Pid, NixPath, Result};
12
13/// A spawn attributes object. See [posix_spawnattr_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
14#[repr(transparent)]
15#[derive(Debug)]
16pub struct PosixSpawnAttr {
17    attr: libc::posix_spawnattr_t,
18}
19
20impl PosixSpawnAttr {
21    /// Initialize the spawn attributes object. See
22    /// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
23    #[doc(alias("posix_spawnattr_init"))]
24    pub fn init() -> Result<PosixSpawnAttr> {
25        let mut attr = mem::MaybeUninit::uninit();
26        let res = unsafe { libc::posix_spawnattr_init(attr.as_mut_ptr()) };
27
28        Errno::result(res)?;
29
30        let attr = unsafe { attr.assume_init() };
31        Ok(PosixSpawnAttr { attr })
32    }
33
34    /// Reinitialize the spawn attributes object.
35    /// This is a wrapper around
36    /// [posix_spawnattr_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html)
37    /// followed by
38    /// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
39    #[doc(alias("posix_spawnattr_destroy"))]
40    pub fn reinit(mut self) -> Result<PosixSpawnAttr> {
41        let res = unsafe {
42            libc::posix_spawnattr_destroy(
43                &mut self.attr as *mut libc::posix_spawnattr_t,
44            )
45        };
46        Errno::result(res)?;
47
48        let res = unsafe {
49            libc::posix_spawnattr_init(
50                &mut self.attr as *mut libc::posix_spawnattr_t,
51            )
52        };
53        Errno::result(res)?;
54
55        Ok(self)
56    }
57
58    /// Set spawn flags. See
59    /// [posix_spawnattr_setflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html).
60    #[doc(alias("posix_spawnattr_setflags"))]
61    pub fn set_flags(&mut self, flags: PosixSpawnFlags) -> Result<()> {
62        let res = unsafe {
63            libc::posix_spawnattr_setflags(
64                &mut self.attr as *mut libc::posix_spawnattr_t,
65                flags.bits() as libc::c_short,
66            )
67        };
68        Errno::result(res)?;
69
70        Ok(())
71    }
72
73    /// Get spawn flags. See
74    /// [posix_spawnattr_getflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html).
75    #[doc(alias("posix_spawnattr_getflags"))]
76    pub fn flags(&self) -> Result<PosixSpawnFlags> {
77        let mut flags: libc::c_short = 0;
78        let res = unsafe {
79            libc::posix_spawnattr_getflags(
80                &self.attr as *const libc::posix_spawnattr_t,
81                &mut flags,
82            )
83        };
84        Errno::result(res)?;
85
86        Ok(PosixSpawnFlags::from_bits_truncate(flags.into()))
87    }
88
89    /// Set spawn pgroup. See
90    /// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
91    #[doc(alias("posix_spawnattr_setpgroup"))]
92    pub fn set_pgroup(&mut self, pgroup: Pid) -> Result<()> {
93        let res = unsafe {
94            libc::posix_spawnattr_setpgroup(
95                &mut self.attr as *mut libc::posix_spawnattr_t,
96                pgroup.as_raw(),
97            )
98        };
99        Errno::result(res)?;
100
101        Ok(())
102    }
103
104    /// Get spawn pgroup. See
105    /// [posix_spawnattr_getpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html).
106    #[doc(alias("posix_spawnattr_getpgroup"))]
107    pub fn pgroup(&self) -> Result<Pid> {
108        let mut pid: libc::pid_t = 0;
109
110        let res = unsafe {
111            libc::posix_spawnattr_getpgroup(
112                &self.attr as *const libc::posix_spawnattr_t,
113                &mut pid,
114            )
115        };
116        Errno::result(res)?;
117
118        Ok(Pid::from_raw(pid))
119    }
120
121    feature! {
122    #![feature = "signal"]
123    /// Set spawn sigdefault. See
124    /// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
125    #[doc(alias("posix_spawnattr_setsigdefault"))]
126    pub fn set_sigdefault(&mut self, sigdefault: &SigSet) -> Result<()> {
127        let res = unsafe {
128            libc::posix_spawnattr_setsigdefault(
129                &mut self.attr as *mut libc::posix_spawnattr_t,
130                sigdefault.as_ref(),
131            )
132        };
133        Errno::result(res)?;
134
135        Ok(())
136    }
137
138    /// Get spawn sigdefault. See
139    /// [posix_spawnattr_getsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html).
140    #[doc(alias("posix_spawnattr_getsigdefault"))]
141    pub fn sigdefault(&self) -> Result<SigSet> {
142        let mut sigset = mem::MaybeUninit::uninit();
143
144        let res = unsafe {
145            libc::posix_spawnattr_getsigdefault(
146                &self.attr as *const libc::posix_spawnattr_t,
147                sigset.as_mut_ptr(),
148            )
149        };
150        Errno::result(res)?;
151
152        let sigdefault =
153            unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
154        Ok(sigdefault)
155    }
156
157    /// Set spawn sigmask. See
158    /// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
159    #[doc(alias("posix_spawnattr_setsigmask"))]
160    pub fn set_sigmask(&mut self, sigdefault: &SigSet) -> Result<()> {
161        let res = unsafe {
162            libc::posix_spawnattr_setsigmask(
163                &mut self.attr as *mut libc::posix_spawnattr_t,
164                sigdefault.as_ref(),
165            )
166        };
167        Errno::result(res)?;
168
169        Ok(())
170    }
171
172    /// Get spawn sigmask. See
173    /// [posix_spawnattr_getsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html).
174    #[doc(alias("posix_spawnattr_getsigmask"))]
175    pub fn sigmask(&self) -> Result<SigSet> {
176        let mut sigset = mem::MaybeUninit::uninit();
177
178        let res = unsafe {
179            libc::posix_spawnattr_getsigmask(
180                &self.attr as *const libc::posix_spawnattr_t,
181                sigset.as_mut_ptr(),
182            )
183        };
184        Errno::result(res)?;
185
186        let sigdefault =
187            unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
188        Ok(sigdefault)
189    }
190    }
191}
192
193impl Drop for PosixSpawnAttr {
194    fn drop(&mut self) {
195        unsafe {
196            libc::posix_spawnattr_destroy(
197                &mut self.attr as *mut libc::posix_spawnattr_t,
198            );
199        }
200    }
201}
202
203libc_bitflags!(
204    /// Process attributes to be changed in the new process image when invoking [`posix_spawn`]
205    /// or [`posix_spawnp`]. See
206    /// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
207    pub struct PosixSpawnFlags: libc::c_int {
208        /// Reset effective user ID of the child process to parent's real user ID.
209        POSIX_SPAWN_RESETIDS;
210        /// Put the child in a process group specified by the spawn-pgroup attribute. See
211        /// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
212        POSIX_SPAWN_SETPGROUP;
213        /// Force set signals to default signal handling in child process. See
214        /// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
215        #[cfg(feature = "signal")]
216        POSIX_SPAWN_SETSIGDEF;
217        /// Set signal mask of child process. See
218        /// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
219        #[cfg(feature = "signal")]
220        POSIX_SPAWN_SETSIGMASK;
221        // TODO: Add support for the following two flags whenever support for
222        // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html
223        // is added to nix.
224        // POSIX_SPAWN_SETSCHEDPARAM;
225        // POSIX_SPAWN_SETSCHEDULER;
226    }
227);
228
229/// A spawn file actions object. See [posix_spawn_file_actions_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
230#[repr(transparent)]
231#[derive(Debug)]
232pub struct PosixSpawnFileActions {
233    fa: libc::posix_spawn_file_actions_t,
234}
235
236impl PosixSpawnFileActions {
237    /// Initialize the spawn file actions object. See
238    /// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
239    #[doc(alias("posix_spawn_file_actions_init"))]
240    pub fn init() -> Result<PosixSpawnFileActions> {
241        let mut actions = mem::MaybeUninit::uninit();
242        let res = unsafe {
243            libc::posix_spawn_file_actions_init(actions.as_mut_ptr())
244        };
245        Errno::result(res)?;
246        Ok(unsafe {
247            PosixSpawnFileActions {
248                fa: actions.assume_init(),
249            }
250        })
251    }
252
253    /// Reinitialize the spawn file actions object.
254    /// This is a wrapper around
255    /// [posix_spawn_file_actions_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html).
256    /// followed by
257    /// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
258    #[doc(alias("posix_spawn_file_actions_destroy"))]
259    pub fn reinit(mut self) -> Result<PosixSpawnFileActions> {
260        let res = unsafe {
261            libc::posix_spawn_file_actions_destroy(
262                &mut self.fa as *mut libc::posix_spawn_file_actions_t,
263            )
264        };
265        Errno::result(res)?;
266
267        let res = unsafe {
268            libc::posix_spawn_file_actions_init(
269                &mut self.fa as *mut libc::posix_spawn_file_actions_t,
270            )
271        };
272        Errno::result(res)?;
273
274        Ok(self)
275    }
276
277    /// Add a [dup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html) action. See
278    /// [posix_spawn_file_actions_adddup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html).
279    #[doc(alias("posix_spawn_file_actions_adddup2"))]
280    pub fn add_dup2(&mut self, fd: RawFd, newfd: RawFd) -> Result<()> {
281        let res = unsafe {
282            libc::posix_spawn_file_actions_adddup2(
283                &mut self.fa as *mut libc::posix_spawn_file_actions_t,
284                fd,
285                newfd,
286            )
287        };
288        Errno::result(res)?;
289
290        Ok(())
291    }
292
293    feature! {
294    #![all(feature = "fs", feature = "term")]
295    /// Add an open action. See
296    /// [posix_spawn_file_actions_addopen](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html).
297    #[doc(alias("posix_spawn_file_actions_addopen"))]
298    pub fn add_open<P: ?Sized + NixPath>(
299        &mut self,
300        fd: RawFd,
301        path: &P,
302        oflag: OFlag,
303        mode: Mode,
304    ) -> Result<()> {
305        let res = path.with_nix_path(|cstr| unsafe {
306            libc::posix_spawn_file_actions_addopen(
307                &mut self.fa as *mut libc::posix_spawn_file_actions_t,
308                fd,
309                cstr.as_ptr(),
310                oflag.bits(),
311                mode.bits(),
312            )
313        })?;
314        Errno::result(res)?;
315
316        Ok(())
317    }
318    }
319
320    /// Add a close action. See
321    /// [posix_spawn_file_actions_addclose](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
322    #[doc(alias("posix_spawn_file_actions_addclose"))]
323    pub fn add_close(&mut self, fd: RawFd) -> Result<()> {
324        let res = unsafe {
325            libc::posix_spawn_file_actions_addclose(
326                &mut self.fa as *mut libc::posix_spawn_file_actions_t,
327                fd,
328            )
329        };
330        Errno::result(res)?;
331
332        Ok(())
333    }
334}
335
336impl Drop for PosixSpawnFileActions {
337    fn drop(&mut self) {
338        unsafe {
339            libc::posix_spawn_file_actions_destroy(
340                &mut self.fa as *mut libc::posix_spawn_file_actions_t,
341            );
342        }
343    }
344}
345
346// The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`,
347// but implementations won't modify them, making the `mut` type redundant. Considering this,
348// Nix does not expose this mutability, but we have to change the interface when calling the
349// underlying libc interfaces , this helper function does the conversion job.
350//
351// SAFETY:
352// It is safe to add the mutability in types as implementations won't mutable them.
353unsafe fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*mut libc::c_char> {
354    let mut v: Vec<*mut libc::c_char> = args
355        .iter()
356        .map(|s| s.as_ref().as_ptr().cast_mut())
357        .collect();
358    v.push(std::ptr::null_mut());
359    v
360}
361
362/// Create a new child process from the specified process image. See
363/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
364pub fn posix_spawn<P, SA, SE>(
365    path: &P,
366    file_actions: &PosixSpawnFileActions,
367    attr: &PosixSpawnAttr,
368    args: &[SA],
369    envp: &[SE],
370) -> Result<Pid>
371where
372    P: NixPath + ?Sized,
373    SA: AsRef<CStr>,
374    SE: AsRef<CStr>,
375{
376    let mut pid = 0;
377
378    let ret = unsafe {
379        let args_p = to_exec_array(args);
380        let env_p = to_exec_array(envp);
381
382        path.with_nix_path(|c_str| {
383            libc::posix_spawn(
384                &mut pid as *mut libc::pid_t,
385                c_str.as_ptr(),
386                &file_actions.fa as *const libc::posix_spawn_file_actions_t,
387                &attr.attr as *const libc::posix_spawnattr_t,
388                args_p.as_ptr(),
389                env_p.as_ptr(),
390            )
391        })?
392    };
393
394    if ret != 0 {
395        return Err(Errno::from_raw(ret));
396    }
397
398    Ok(Pid::from_raw(pid))
399}
400
401/// Create a new child process from the specified process image. See
402/// [posix_spawnp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html).
403pub fn posix_spawnp<SA: AsRef<CStr>, SE: AsRef<CStr>>(
404    path: &CStr,
405    file_actions: &PosixSpawnFileActions,
406    attr: &PosixSpawnAttr,
407    args: &[SA],
408    envp: &[SE],
409) -> Result<Pid> {
410    let mut pid = 0;
411
412    let ret = unsafe {
413        let args_p = to_exec_array(args);
414        let env_p = to_exec_array(envp);
415
416        libc::posix_spawnp(
417            &mut pid as *mut libc::pid_t,
418            path.as_ptr(),
419            &file_actions.fa as *const libc::posix_spawn_file_actions_t,
420            &attr.attr as *const libc::posix_spawnattr_t,
421            args_p.as_ptr(),
422            env_p.as_ptr(),
423        )
424    };
425
426    if ret != 0 {
427        return Err(Errno::from_raw(ret));
428    }
429
430    Ok(Pid::from_raw(pid))
431}