summaryrefslogtreecommitdiffstats
path: root/vendor/rusty-fork/src/child_wrapper.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rusty-fork/src/child_wrapper.rs')
-rw-r--r--vendor/rusty-fork/src/child_wrapper.rs248
1 files changed, 248 insertions, 0 deletions
diff --git a/vendor/rusty-fork/src/child_wrapper.rs b/vendor/rusty-fork/src/child_wrapper.rs
new file mode 100644
index 000000000..a16086283
--- /dev/null
+++ b/vendor/rusty-fork/src/child_wrapper.rs
@@ -0,0 +1,248 @@
+//-
+// Copyright 2018 Jason Lingle
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
+use std::io;
+use std::process::{Child, Output};
+#[cfg(feature = "timeout")]
+use std::time::Duration;
+
+#[cfg(feature = "timeout")]
+use wait_timeout::ChildExt;
+
+/// Wraps `std::process::ExitStatus`. Historically, this was due to the
+/// `wait_timeout` crate having its own `ExitStatus` type.
+///
+/// Method documentation is copied from the [Rust std
+/// docs](https://doc.rust-lang.org/stable/std/process/struct.ExitStatus.html)
+/// and the [`wait_timeout`
+/// docs](https://docs.rs/wait-timeout/0.1.5/wait_timeout/struct.ExitStatus.html).
+#[derive(Clone, Copy)]
+pub struct ExitStatusWrapper(ExitStatusEnum);
+
+#[derive(Debug, Clone, Copy)]
+enum ExitStatusEnum {
+ Std(::std::process::ExitStatus),
+}
+
+impl ExitStatusWrapper {
+ fn std(es: ::std::process::ExitStatus) -> Self {
+ ExitStatusWrapper(ExitStatusEnum::Std(es))
+ }
+
+ /// Was termination successful? Signal termination is not considered a
+ /// success, and success is defined as a zero exit status.
+ pub fn success(&self) -> bool {
+ match self.0 {
+ ExitStatusEnum::Std(es) => es.success(),
+ }
+ }
+
+ /// Returns the exit code of the process, if any.
+ ///
+ /// On Unix, this will return `None` if the process was terminated by a
+ /// signal; `std::os::unix` provides an extension trait for extracting the
+ /// signal and other details from the `ExitStatus`.
+ pub fn code(&self) -> Option<i32> {
+ match self.0 {
+ ExitStatusEnum::Std(es) => es.code(),
+ }
+ }
+
+ /// Returns the Unix signal which terminated this process.
+ ///
+ /// Note that on Windows this will always return None and on Unix this will
+ /// return None if the process successfully exited otherwise.
+ ///
+ /// For simplicity and to match `wait_timeout`, this method is always
+ /// present even on systems that do not support it.
+ #[cfg(not(target_os = "windows"))]
+ pub fn unix_signal(&self) -> Option<i32> {
+ use std::os::unix::process::ExitStatusExt;
+
+ match self.0 {
+ ExitStatusEnum::Std(es) => es.signal(),
+ }
+ }
+
+ /// Returns the Unix signal which terminated this process.
+ ///
+ /// Note that on Windows this will always return None and on Unix this will
+ /// return None if the process successfully exited otherwise.
+ ///
+ /// For simplicity and to match `wait_timeout`, this method is always
+ /// present even on systems that do not support it.
+ #[cfg(target_os = "windows")]
+ pub fn unix_signal(&self) -> Option<i32> {
+ None
+ }
+}
+
+impl fmt::Debug for ExitStatusWrapper {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {
+ ExitStatusEnum::Std(ref es) => fmt::Debug::fmt(es, f),
+ }
+ }
+}
+
+impl fmt::Display for ExitStatusWrapper {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {
+ ExitStatusEnum::Std(ref es) => fmt::Display::fmt(es, f),
+ }
+ }
+}
+
+/// Wraps a `std::process::Child` to coordinate state between `std` and
+/// `wait_timeout`.
+///
+/// This is necessary because the completion of a call to
+/// `wait_timeout::ChildExt::wait_timeout` leaves the `Child` in an
+/// inconsistent state, as it does not know the child has exited, and on Unix
+/// may end up referencing another process.
+///
+/// Documentation for this struct's methods is largely copied from the [Rust
+/// std docs](https://doc.rust-lang.org/stable/std/process/struct.Child.html).
+#[derive(Debug)]
+pub struct ChildWrapper {
+ child: Child,
+ exit_status: Option<ExitStatusWrapper>,
+}
+
+impl ChildWrapper {
+ pub(crate) fn new(child: Child) -> Self {
+ ChildWrapper { child, exit_status: None }
+ }
+
+ /// Return a reference to the inner `std::process::Child`.
+ ///
+ /// Use care on the returned object, as it does not necessarily reference
+ /// the correct process unless you know the child process has not exited
+ /// and no wait calls have succeeded.
+ pub fn inner(&self) -> &Child {
+ &self.child
+ }
+
+ /// Return a mutable reference to the inner `std::process::Child`.
+ ///
+ /// Use care on the returned object, as it does not necessarily reference
+ /// the correct process unless you know the child process has not exited
+ /// and no wait calls have succeeded.
+ pub fn inner_mut(&mut self) -> &mut Child {
+ &mut self.child
+ }
+
+ /// Forces the child to exit. This is equivalent to sending a SIGKILL on
+ /// unix platforms.
+ ///
+ /// If the process has already been reaped by this handle, returns a
+ /// `NotFound` error.
+ pub fn kill(&mut self) -> io::Result<()> {
+ if self.exit_status.is_none() {
+ self.child.kill()
+ } else {
+ Err(io::Error::new(io::ErrorKind::NotFound, "Process already reaped"))
+ }
+ }
+
+ /// Returns the OS-assigned processor identifier associated with this child.
+ ///
+ /// This succeeds even if the child has already been reaped. In this case,
+ /// the process id may reference no process at all or even an unrelated
+ /// process.
+ pub fn id(&self) -> u32 {
+ self.child.id()
+ }
+
+ /// Waits for the child to exit completely, returning the status that it
+ /// exited with. This function will continue to have the same return value
+ /// after it has been called at least once.
+ ///
+ /// The stdin handle to the child process, if any, will be closed before
+ /// waiting. This helps avoid deadlock: it ensures that the child does not
+ /// block waiting for input from the parent, while the parent waits for the
+ /// child to exit.
+ ///
+ /// If the child process has already been reaped, returns its exit status
+ /// without blocking.
+ pub fn wait(&mut self) -> io::Result<ExitStatusWrapper> {
+ if let Some(status) = self.exit_status {
+ Ok(status)
+ } else {
+ let status = ExitStatusWrapper::std(self.child.wait()?);
+ self.exit_status = Some(status);
+ Ok(status)
+ }
+ }
+
+ /// Attempts to collect the exit status of the child if it has already exited.
+ ///
+ /// This function will not block the calling thread and will only
+ /// advisorily check to see if the child process has exited or not. If the
+ /// child has exited then on Unix the process id is reaped. This function
+ /// is guaranteed to repeatedly return a successful exit status so long as
+ /// the child has already exited.
+ ///
+ /// If the child has exited, then `Ok(Some(status))` is returned. If the
+ /// exit status is not available at this time then `Ok(None)` is returned.
+ /// If an error occurs, then that error is returned.
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatusWrapper>> {
+ if let Some(status) = self.exit_status {
+ Ok(Some(status))
+ } else {
+ let status = self.child.try_wait()?.map(ExitStatusWrapper::std);
+ self.exit_status = status;
+ Ok(status)
+ }
+ }
+
+ /// Simultaneously waits for the child to exit and collect all remaining
+ /// output on the stdout/stderr handles, returning an `Output` instance.
+ ///
+ /// The stdin handle to the child process, if any, will be closed before
+ /// waiting. This helps avoid deadlock: it ensures that the child does not
+ /// block waiting for input from the parent, while the parent waits for the
+ /// child to exit.
+ ///
+ /// By default, stdin, stdout and stderr are inherited from the parent. (In
+ /// the context of `rusty_fork`, they are by default redirected to a file.)
+ /// In order to capture the output into this `Result<Output>` it is
+ /// necessary to create new pipes between parent and child. Use
+ /// `stdout(Stdio::piped())` or `stderr(Stdio::piped())`, respectively.
+ ///
+ /// If the process has already been reaped, returns a `NotFound` error.
+ pub fn wait_with_output(self) -> io::Result<Output> {
+ if self.exit_status.is_some() {
+ return Err(io::Error::new(
+ io::ErrorKind::NotFound, "Process already reaped"));
+ }
+
+ self.child.wait_with_output()
+ }
+
+ /// Wait for the child to exit, but only up to the given maximum duration.
+ ///
+ /// If the process has already been reaped, returns its exit status
+ /// immediately. Otherwise, if the process terminates within the duration,
+ /// returns `Ok(Sone(..))`, or `Ok(None)` otherwise.
+ ///
+ /// This is only present if the "timeout" feature is enabled.
+ #[cfg(feature = "timeout")]
+ pub fn wait_timeout(&mut self, dur: Duration)
+ -> io::Result<Option<ExitStatusWrapper>> {
+ if let Some(status) = self.exit_status {
+ Ok(Some(status))
+ } else {
+ let status = self.child.wait_timeout(dur)?.map(ExitStatusWrapper::std);
+ self.exit_status = status;
+ Ok(status)
+ }
+ }
+}