Skip to main content

nuclei/process/
mod.rs

1//! Child processes: a [`Command`](crate::process::Command) spawns one, and
2//! its [`Child`](crate::process::Child) waits for the exit without holding a
3//! thread and talks to it through async pipes.
4//!
5//! A child's exit is reported by a shard's driver, which reaps the child
6//! (see `exit`); a wait never blocks a thread. A child whose `Child` goes
7//! before it exits is still reaped (on unix), by the shard that watches it,
8//! so it leaves no zombie behind while a runtime runs.
9
10use core::future::poll_fn;
11use core::pin::Pin;
12use core::task::{Context, Poll};
13use std::ffi::OsStr;
14use std::io;
15use std::path::Path;
16use std::sync::Arc;
17
18use futures_io::AsyncRead;
19
20pub use std::process::{ExitStatus, Output, Stdio};
21
22pub(crate) mod exit;
23#[cfg(any(target_os = "linux", target_os = "android"))]
24mod fallback;
25mod stdio;
26#[cfg(test)]
27mod tests;
28
29pub use stdio::{ChildStderr, ChildStdin, ChildStdout};
30
31use exit::Exit;
32
33/// Reaps the children of the `SIGCHLD` reaper that exited: the signal
34/// module's dispatch, on a delivery of `SIGCHLD`.
35#[cfg(any(target_os = "linux", target_os = "android"))]
36pub(crate) fn reap_fallback() {
37    fallback::reap();
38}
39
40/// A process to spawn, as [`std::process::Command`] describes it.
41#[derive(Debug)]
42pub struct Command {
43    std: std::process::Command,
44    /// What `output` gives the child's stdin unless this says otherwise.
45    stdin_set: bool,
46    kill_on_drop: bool,
47}
48
49impl Command {
50    /// A command for `program`, with its environment and working directory
51    /// the process's, and its standard streams inherited.
52    pub fn new(program: impl AsRef<OsStr>) -> Self {
53        Self {
54            std: std::process::Command::new(program),
55            stdin_set: false,
56            kill_on_drop: false,
57        }
58    }
59
60    /// Adds an argument.
61    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
62        self.std.arg(arg);
63        self
64    }
65
66    /// Adds arguments.
67    pub fn args<I, S>(&mut self, args: I) -> &mut Self
68    where
69        I: IntoIterator<Item = S>,
70        S: AsRef<OsStr>,
71    {
72        self.std.args(args);
73        self
74    }
75
76    /// Sets an environment variable of the child.
77    pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Self {
78        self.std.env(key, value);
79        self
80    }
81
82    /// Sets environment variables of the child.
83    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
84    where
85        I: IntoIterator<Item = (K, V)>,
86        K: AsRef<OsStr>,
87        V: AsRef<OsStr>,
88    {
89        self.std.envs(vars);
90        self
91    }
92
93    /// Takes an environment variable out of the child's environment.
94    pub fn env_remove(&mut self, key: impl AsRef<OsStr>) -> &mut Self {
95        self.std.env_remove(key);
96        self
97    }
98
99    /// Starts the child with no environment but what is set after.
100    pub fn env_clear(&mut self) -> &mut Self {
101        self.std.env_clear();
102        self
103    }
104
105    /// Sets the child's working directory.
106    pub fn current_dir(&mut self, dir: impl AsRef<Path>) -> &mut Self {
107        self.std.current_dir(dir);
108        self
109    }
110
111    /// Sets the child's stdin.
112    pub fn stdin(&mut self, cfg: impl Into<Stdio>) -> &mut Self {
113        self.std.stdin(cfg);
114        self.stdin_set = true;
115        self
116    }
117
118    /// Sets the child's stdout.
119    pub fn stdout(&mut self, cfg: impl Into<Stdio>) -> &mut Self {
120        self.std.stdout(cfg);
121        self
122    }
123
124    /// Sets the child's stderr.
125    pub fn stderr(&mut self, cfg: impl Into<Stdio>) -> &mut Self {
126        self.std.stderr(cfg);
127        self
128    }
129
130    /// Whether the child is ended, as [`Child::start_kill`] ends it, when
131    /// its [`Child`] is dropped before it exits (default false: it runs on,
132    /// and is still reaped once it exits).
133    ///
134    /// ```no_run
135    /// # nuclei::block_on(async {
136    /// use nuclei::process::Command;
137    ///
138    /// let child = Command::new("sleep").arg("60").kill_on_drop(true).spawn()?;
139    /// drop(child); // the child is ended here
140    /// # std::io::Result::Ok(())
141    /// # }).unwrap();
142    /// ```
143    pub fn kill_on_drop(&mut self, kill: bool) -> &mut Self {
144        self.kill_on_drop = kill;
145        self
146    }
147
148    /// Whether [`kill_on_drop`](Self::kill_on_drop) is set.
149    pub fn get_kill_on_drop(&self) -> bool {
150        self.kill_on_drop
151    }
152
153    /// The std command underneath, for what this one does not mirror.
154    pub fn as_std(&self) -> &std::process::Command {
155        &self.std
156    }
157
158    /// The std command underneath, to change.
159    pub fn as_std_mut(&mut self) -> &mut std::process::Command {
160        &mut self.std
161    }
162
163    /// The std command underneath, taken.
164    pub fn into_std(self) -> std::process::Command {
165        self.std
166    }
167
168    /// Spawns the child. Its piped streams are async.
169    ///
170    /// # Errors
171    ///
172    /// When the child cannot start, or its pipes cannot be taken over.
173    pub fn spawn(&mut self) -> io::Result<Child> {
174        self.spawn_with(std::process::Command::spawn)
175    }
176
177    /// Spawns the child through `with`, which starts it from the std
178    /// command underneath: for a way to start a child this type does not
179    /// mirror, such as a platform's own spawn call with extra attributes.
180    /// The child `with` returns is then this one's, its piped streams async.
181    ///
182    /// ```no_run
183    /// # nuclei::block_on(async {
184    /// use nuclei::process::Command;
185    ///
186    /// let mut child = Command::new("true").spawn_with(|std| std.spawn())?;
187    /// assert!(child.wait().await?.success());
188    /// # std::io::Result::Ok(())
189    /// # }).unwrap();
190    /// ```
191    ///
192    /// # Errors
193    ///
194    /// What `with` returns, and when the child's pipes cannot be taken
195    /// over.
196    pub fn spawn_with(
197        &mut self,
198        with: impl FnOnce(&mut std::process::Command) -> io::Result<std::process::Child>,
199    ) -> io::Result<Child> {
200        let mut std = with(&mut self.std)?;
201        let exit = match Exit::new(&std) {
202            Ok(exit) => exit,
203            Err(e) => {
204                if self.kill_on_drop {
205                    // The error that matters is the one returned.
206                    let _ = std.kill();
207                }
208                return Err(e);
209            }
210        };
211        let (stdin, stdout, stderr) = (std.stdin.take(), std.stdout.take(), std.stderr.take());
212        // Made before the pipes are taken over: should one fail, its drop
213        // still reaps the child (and ends it, if asked).
214        let mut child = Child {
215            stdin: None,
216            stdout: None,
217            stderr: None,
218            exit,
219            kill_on_drop: self.kill_on_drop,
220            _std: std,
221        };
222        child.stdin = stdin.map(ChildStdin::new).transpose()?;
223        child.stdout = stdout.map(ChildStdout::new).transpose()?;
224        child.stderr = stderr.map(ChildStderr::new).transpose()?;
225        Ok(child)
226    }
227
228    /// Spawns the child and waits for its exit; its streams are inherited
229    /// unless set.
230    ///
231    /// # Errors
232    ///
233    /// As for [`spawn`](Self::spawn) and [`Child::wait`].
234    pub async fn status(&mut self) -> io::Result<ExitStatus> {
235        self.spawn()?.wait().await
236    }
237
238    /// Spawns the child with its stdout and stderr piped (and its stdin
239    /// empty, unless set), and collects both until its exit.
240    ///
241    /// # Errors
242    ///
243    /// As for [`spawn`](Self::spawn) and [`Child::wait_with_output`].
244    pub async fn output(&mut self) -> io::Result<Output> {
245        if !self.stdin_set {
246            self.std.stdin(Stdio::null());
247        }
248        self.std.stdout(Stdio::piped()).stderr(Stdio::piped());
249        self.spawn()?.wait_with_output().await
250    }
251}
252
253impl From<std::process::Command> for Command {
254    fn from(std: std::process::Command) -> Self {
255        Self {
256            std,
257            stdin_set: false,
258            kill_on_drop: false,
259        }
260    }
261}
262
263/// A spawned child process.
264#[derive(Debug)]
265pub struct Child {
266    /// The child's stdin, if piped.
267    pub stdin: Option<ChildStdin>,
268    /// The child's stdout, if piped.
269    pub stdout: Option<ChildStdout>,
270    /// The child's stderr, if piped.
271    pub stderr: Option<ChildStderr>,
272    exit: Arc<Exit>,
273    kill_on_drop: bool,
274    /// The std child, kept for what it owns (its process handle).
275    _std: std::process::Child,
276}
277
278impl core::fmt::Debug for Exit {
279    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280        f.debug_struct("Exit")
281            .field("pid", &self.pid())
282            .finish_non_exhaustive()
283    }
284}
285
286impl Child {
287    /// The child's process id.
288    pub fn id(&self) -> u32 {
289        self.exit.pid()
290    }
291
292    /// Waits for the child's exit. Its stdin, if piped, is closed first, so
293    /// a child that reads it to its end can exit.
294    ///
295    /// # Errors
296    ///
297    /// When the exit cannot be watched, or the child was reaped by
298    /// something else (`ECHILD`: `SIGCHLD` ignored, or a `waitpid` of the
299    /// program's own).
300    pub async fn wait(&mut self) -> io::Result<ExitStatus> {
301        drop(self.stdin.take());
302        poll_fn(|cx| self.exit.poll_wait(cx)).await
303    }
304
305    /// The child's status if it exited, else `None`, without waiting.
306    ///
307    /// # Errors
308    ///
309    /// When the child's state cannot be read.
310    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
311        self.exit.try_wait()
312    }
313
314    /// Starts ending the child, without waiting for it to go: `SIGKILL` on
315    /// unix, `TerminateProcess` on Windows. A child that exited already is
316    /// left alone.
317    ///
318    /// # Errors
319    ///
320    /// When the signal or the call fails.
321    pub fn start_kill(&mut self) -> io::Result<()> {
322        self.exit.kill()
323    }
324
325    /// Ends the child, as [`start_kill`](Self::start_kill) does, and waits
326    /// for its exit.
327    ///
328    /// ```no_run
329    /// # nuclei::block_on(async {
330    /// use nuclei::process::Command;
331    ///
332    /// let mut child = Command::new("sleep").arg("60").spawn()?;
333    /// child.kill().await?;
334    /// assert!(child.try_wait()?.is_some());
335    /// # std::io::Result::Ok(())
336    /// # }).unwrap();
337    /// ```
338    ///
339    /// # Errors
340    ///
341    /// As for [`start_kill`](Self::start_kill) and [`wait`](Self::wait).
342    pub async fn kill(&mut self) -> io::Result<()> {
343        self.start_kill()?;
344        self.wait().await.map(drop)
345    }
346
347    /// Waits for the child's exit, collecting its stdout and stderr (those
348    /// piped) until then. Its stdin, if piped, is closed first.
349    ///
350    /// # Errors
351    ///
352    /// As for [`wait`](Self::wait), and when a pipe cannot be read.
353    pub async fn wait_with_output(mut self) -> io::Result<Output> {
354        drop(self.stdin.take());
355        let (mut stdout, mut stderr) = (self.stdout.take(), self.stderr.take());
356        let (mut out, mut err) = (Vec::new(), Vec::new());
357        poll_fn(|cx| {
358            let out_done = drain(&mut stdout, &mut out, cx)?.is_ready();
359            let err_done = drain(&mut stderr, &mut err, cx)?.is_ready();
360            if out_done && err_done {
361                Poll::Ready(Ok::<(), io::Error>(()))
362            } else {
363                Poll::Pending
364            }
365        })
366        .await?;
367        let status = self.wait().await?;
368        Ok(Output {
369            status,
370            stdout: out,
371            stderr: err,
372        })
373    }
374}
375
376impl Drop for Child {
377    fn drop(&mut self) {
378        if self.kill_on_drop {
379            // A drop cannot report a failure, and the kill of a child of
380            // ours fails only once it is gone.
381            let _ = self.exit.kill();
382        }
383        // A child that runs on is still reaped once it exits.
384        #[cfg(unix)]
385        self.exit.watch_orphan();
386    }
387}
388
389/// Reads `pipe` into `into` until its end (then lets it go) or until it
390/// would wait.
391fn drain<R: AsyncRead + Unpin>(
392    pipe: &mut Option<R>,
393    into: &mut Vec<u8>,
394    cx: &mut Context<'_>,
395) -> Poll<io::Result<()>> {
396    const STEP: usize = 16 * 1024;
397    while let Some(reader) = pipe.as_mut() {
398        let at = into.len();
399        into.resize(at + STEP, 0);
400        let read = Pin::new(reader).poll_read(cx, &mut into[at..]);
401        match read {
402            Poll::Ready(Ok(0)) => {
403                into.truncate(at);
404                *pipe = None;
405            }
406            Poll::Ready(Ok(n)) => into.truncate(at + n),
407            Poll::Ready(Err(err)) => {
408                into.truncate(at);
409                return Poll::Ready(Err(err));
410            }
411            Poll::Pending => {
412                into.truncate(at);
413                return Poll::Pending;
414            }
415        }
416    }
417    Poll::Ready(Ok(()))
418}