#[path = "unix/mod.rs"]
#[cfg(unix)]
mod imp;
#[path = "windows.rs"]
#[cfg(windows)]
mod imp;
mod kill;
use crate::io::{AsyncRead, AsyncWrite};
use crate::process::kill::Kill;
use std::ffi::OsStr;
use std::future::Future;
use std::io;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
#[cfg(windows)]
use std::os::windows::process::CommandExt;
use std::path::Path;
use std::pin::Pin;
use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
use std::task::Context;
use std::task::Poll;
#[derive(Debug)]
pub struct Command {
std: StdCommand,
kill_on_drop: bool,
}
pub(crate) struct SpawnedChild {
child: imp::Child,
stdin: Option<imp::ChildStdin>,
stdout: Option<imp::ChildStdout>,
stderr: Option<imp::ChildStderr>,
}
impl Command {
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
Self::from(StdCommand::new(program))
}
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
self.std.arg(arg);
self
}
pub fn args<I, S>(&mut self, args: I) -> &mut Command
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
self.std.args(args);
self
}
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.std.env(key, val);
self
}
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.std.envs(vars);
self
}
pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command {
self.std.env_remove(key);
self
}
pub fn env_clear(&mut self) -> &mut Command {
self.std.env_clear();
self
}
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {
self.std.current_dir(dir);
self
}
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
self.std.stdin(cfg);
self
}
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
self.std.stdout(cfg);
self
}
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {
self.std.stderr(cfg);
self
}
pub fn kill_on_drop(&mut self, kill_on_drop: bool) -> &mut Command {
self.kill_on_drop = kill_on_drop;
self
}
#[cfg(windows)]
pub fn creation_flags(&mut self, flags: u32) -> &mut Command {
self.std.creation_flags(flags);
self
}
#[cfg(unix)]
pub fn uid(&mut self, id: u32) -> &mut Command {
self.std.uid(id);
self
}
#[cfg(unix)]
pub fn gid(&mut self, id: u32) -> &mut Command {
self.std.gid(id);
self
}
#[cfg(unix)]
pub unsafe fn pre_exec<F>(&mut self, f: F) -> &mut Command
where
F: FnMut() -> io::Result<()> + Send + Sync + 'static,
{
self.std.pre_exec(f);
self
}
pub fn spawn(&mut self) -> io::Result<Child> {
imp::spawn_child(&mut self.std).map(|spawned_child| Child {
child: ChildDropGuard {
inner: spawned_child.child,
kill_on_drop: self.kill_on_drop,
},
stdin: spawned_child.stdin.map(|inner| ChildStdin { inner }),
stdout: spawned_child.stdout.map(|inner| ChildStdout { inner }),
stderr: spawned_child.stderr.map(|inner| ChildStderr { inner }),
})
}
pub fn status(&mut self) -> impl Future<Output = io::Result<ExitStatus>> {
let child = self.spawn();
async {
let mut child = child?;
child.stdin.take();
child.stdout.take();
child.stderr.take();
child.await
}
}
pub fn output(&mut self) -> impl Future<Output = io::Result<Output>> {
self.std.stdout(Stdio::piped());
self.std.stderr(Stdio::piped());
let child = self.spawn();
async { child?.wait_with_output().await }
}
}
impl From<StdCommand> for Command {
fn from(std: StdCommand) -> Command {
Command {
std,
kill_on_drop: false,
}
}
}
#[derive(Debug)]
struct ChildDropGuard<T: Kill> {
inner: T,
kill_on_drop: bool,
}
impl<T: Kill> Kill for ChildDropGuard<T> {
fn kill(&mut self) -> io::Result<()> {
let ret = self.inner.kill();
if ret.is_ok() {
self.kill_on_drop = false;
}
ret
}
}
impl<T: Kill> Drop for ChildDropGuard<T> {
fn drop(&mut self) {
if self.kill_on_drop {
drop(self.kill());
}
}
}
impl<T, E, F> Future for ChildDropGuard<F>
where
F: Future<Output = Result<T, E>> + Kill + Unpin,
{
type Output = Result<T, E>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let ret = Pin::new(&mut self.inner).poll(cx);
if let Poll::Ready(Ok(_)) = ret {
self.kill_on_drop = false;
}
ret
}
}
#[must_use = "futures do nothing unless polled"]
#[derive(Debug)]
pub struct Child {
child: ChildDropGuard<imp::Child>,
pub stdin: Option<ChildStdin>,
pub stdout: Option<ChildStdout>,
pub stderr: Option<ChildStderr>,
}
impl Child {
pub fn id(&self) -> u32 {
self.child.inner.id()
}
pub fn kill(&mut self) -> io::Result<()> {
self.child.kill()
}
#[doc(hidden)]
#[deprecated(note = "please use `child.stdin` instead")]
pub fn stdin(&mut self) -> &mut Option<ChildStdin> {
&mut self.stdin
}
#[doc(hidden)]
#[deprecated(note = "please use `child.stdout` instead")]
pub fn stdout(&mut self) -> &mut Option<ChildStdout> {
&mut self.stdout
}
#[doc(hidden)]
#[deprecated(note = "please use `child.stderr` instead")]
pub fn stderr(&mut self) -> &mut Option<ChildStderr> {
&mut self.stderr
}
pub async fn wait_with_output(mut self) -> io::Result<Output> {
use crate::future::try_join3;
async fn read_to_end<A: AsyncRead + Unpin>(io: Option<A>) -> io::Result<Vec<u8>> {
let mut vec = Vec::new();
if let Some(mut io) = io {
crate::io::util::read_to_end(&mut io, &mut vec).await?;
}
Ok(vec)
}
drop(self.stdin.take());
let stdout_fut = read_to_end(self.stdout.take());
let stderr_fut = read_to_end(self.stderr.take());
let (status, stdout, stderr) = try_join3(self, stdout_fut, stderr_fut).await?;
Ok(Output {
status,
stdout,
stderr,
})
}
}
impl Future for Child {
type Output = io::Result<ExitStatus>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.child).poll(cx)
}
}
#[derive(Debug)]
pub struct ChildStdin {
inner: imp::ChildStdin,
}
#[derive(Debug)]
pub struct ChildStdout {
inner: imp::ChildStdout,
}
#[derive(Debug)]
pub struct ChildStderr {
inner: imp::ChildStderr,
}
impl AsyncWrite for ChildStdin {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.inner).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_shutdown(cx)
}
}
impl AsyncRead for ChildStdout {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}
impl AsyncRead for ChildStderr {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}
#[cfg(unix)]
mod sys {
use std::os::unix::io::{AsRawFd, RawFd};
use super::{ChildStderr, ChildStdin, ChildStdout};
impl AsRawFd for ChildStdin {
fn as_raw_fd(&self) -> RawFd {
self.inner.get_ref().as_raw_fd()
}
}
impl AsRawFd for ChildStdout {
fn as_raw_fd(&self) -> RawFd {
self.inner.get_ref().as_raw_fd()
}
}
impl AsRawFd for ChildStderr {
fn as_raw_fd(&self) -> RawFd {
self.inner.get_ref().as_raw_fd()
}
}
}
#[cfg(windows)]
mod sys {
use std::os::windows::io::{AsRawHandle, RawHandle};
use super::{ChildStderr, ChildStdin, ChildStdout};
impl AsRawHandle for ChildStdin {
fn as_raw_handle(&self) -> RawHandle {
self.inner.get_ref().as_raw_handle()
}
}
impl AsRawHandle for ChildStdout {
fn as_raw_handle(&self) -> RawHandle {
self.inner.get_ref().as_raw_handle()
}
}
impl AsRawHandle for ChildStderr {
fn as_raw_handle(&self) -> RawHandle {
self.inner.get_ref().as_raw_handle()
}
}
}
#[cfg(all(test, not(loom)))]
mod test {
use super::kill::Kill;
use super::ChildDropGuard;
use futures::future::FutureExt;
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
struct Mock {
num_kills: usize,
num_polls: usize,
poll_result: Poll<Result<(), ()>>,
}
impl Mock {
fn new() -> Self {
Self::with_result(Poll::Pending)
}
fn with_result(result: Poll<Result<(), ()>>) -> Self {
Self {
num_kills: 0,
num_polls: 0,
poll_result: result,
}
}
}
impl Kill for Mock {
fn kill(&mut self) -> io::Result<()> {
self.num_kills += 1;
Ok(())
}
}
impl Future for Mock {
type Output = Result<(), ()>;
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
let inner = Pin::get_mut(self);
inner.num_polls += 1;
inner.poll_result
}
}
#[test]
fn kills_on_drop_if_specified() {
let mut mock = Mock::new();
{
let guard = ChildDropGuard {
inner: &mut mock,
kill_on_drop: true,
};
drop(guard);
}
assert_eq!(1, mock.num_kills);
assert_eq!(0, mock.num_polls);
}
#[test]
fn no_kill_on_drop_by_default() {
let mut mock = Mock::new();
{
let guard = ChildDropGuard {
inner: &mut mock,
kill_on_drop: false,
};
drop(guard);
}
assert_eq!(0, mock.num_kills);
assert_eq!(0, mock.num_polls);
}
#[test]
fn no_kill_if_already_killed() {
let mut mock = Mock::new();
{
let mut guard = ChildDropGuard {
inner: &mut mock,
kill_on_drop: true,
};
let _ = guard.kill();
drop(guard);
}
assert_eq!(1, mock.num_kills);
assert_eq!(0, mock.num_polls);
}
#[test]
fn no_kill_if_reaped() {
let mut mock_pending = Mock::with_result(Poll::Pending);
let mut mock_reaped = Mock::with_result(Poll::Ready(Ok(())));
let mut mock_err = Mock::with_result(Poll::Ready(Err(())));
let waker = futures::task::noop_waker();
let mut context = Context::from_waker(&waker);
{
let mut guard = ChildDropGuard {
inner: &mut mock_pending,
kill_on_drop: true,
};
let _ = guard.poll_unpin(&mut context);
let mut guard = ChildDropGuard {
inner: &mut mock_reaped,
kill_on_drop: true,
};
let _ = guard.poll_unpin(&mut context);
let mut guard = ChildDropGuard {
inner: &mut mock_err,
kill_on_drop: true,
};
let _ = guard.poll_unpin(&mut context);
}
assert_eq!(1, mock_pending.num_kills);
assert_eq!(1, mock_pending.num_polls);
assert_eq!(0, mock_reaped.num_kills);
assert_eq!(1, mock_reaped.num_polls);
assert_eq!(1, mock_err.num_kills);
assert_eq!(1, mock_err.num_polls);
}
}