process: Add support for stdio streams

This commit is contained in:
Andreas Rottmann
2019-06-24 16:56:47 -07:00
committed by Ivan Petkov
parent b16a8613b1
commit 849a5ad0b2
5 changed files with 197 additions and 4 deletions
+18
View File
@@ -0,0 +1,18 @@
// A cat-like utility that can be used as a subprocess to test I/O
// stream communication.
use std::io;
use std::io::Write;
fn main() {
let stdin = io::stdin();
let mut stdout = io::stdout();
let mut line = String::new();
loop {
line.clear();
stdin.read_line(&mut line).unwrap();
if line.len() == 0 {
break;
}
stdout.write(line.as_bytes()).unwrap();
}
}
+30
View File
@@ -1,6 +1,7 @@
#[macro_use]
extern crate futures;
extern crate tokio_core;
extern crate mio;
use std::ffi::OsStr;
use std::io;
@@ -18,6 +19,9 @@ mod imp;
#[cfg(windows)]
mod imp;
pub use imp::ChildStdin;
pub use imp::ChildStdout;
pub struct Command {
inner: process::Command,
#[allow(dead_code)]
@@ -94,6 +98,20 @@ impl Command {
self
}
pub fn stdin(&mut self, cfg: process::Stdio) -> &mut Self {
self.inner.stdin(cfg);
self
}
pub fn stdout(&mut self, cfg: process::Stdio) -> &mut Self {
self.inner.stdout(cfg);
self
}
pub fn stderr(&mut self, cfg: process::Stdio) -> &mut Self {
self.inner.stderr(cfg);
self
}
pub fn spawn(self) -> Spawn {
Spawn {
inner: Box::new(imp::spawn(self).map(|c| Child { inner: c })),
@@ -118,6 +136,18 @@ impl Child {
pub fn kill(&mut self) -> io::Result<()> {
self.inner.kill()
}
pub fn stdin(&mut self) -> &mut Option<imp::ChildStdin> {
&mut self.inner.stdin
}
pub fn stdout(&mut self) -> &mut Option<imp::ChildStdout> {
&mut self.inner.stdout
}
pub fn stderr(&mut self) -> &mut Option<imp::ChildStderr> {
&mut self.inner.stderr
}
}
impl Future for Child {
+63 -4
View File
@@ -1,3 +1,4 @@
extern crate mio;
extern crate libc;
extern crate tokio_signal;
@@ -7,15 +8,67 @@ use std::process::{self, ExitStatus};
use futures::stream::Stream;
use futures::{Future, Poll, Async};
use tokio_core::reactor::{Handle,PollEvented};
use self::libc::c_int;
use self::tokio_signal::unix::Signal;
use mio::{Evented,PollOpt,Ready,Token};
use mio::unix::EventedFd;
use Command;
pub struct Child {
child: process::Child,
reaped: bool,
sigchld: Signal,
pub stdin: Option<ChildStdin>,
pub stdout: Option<ChildStdout>,
pub stderr: Option<ChildStderr>,
}
struct RawFdWrap<T>(T);
pub struct StdStream<T> {
io: PollEvented<RawFdWrap<T>>,
}
pub type ChildStdin = StdStream<process::ChildStdin>;
pub type ChildStdout = StdStream<process::ChildStdout>;
pub type ChildStderr = StdStream<process::ChildStderr>;
impl<T> Evented for RawFdWrap<T> where T: AsRawFd {
fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.0.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.0.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
EventedFd(&self.0.as_raw_fd()).deregister(poll)
}
}
impl<T> io::Read for StdStream<T> where T: io::Read {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.get_mut().0.read(buf)
}
}
impl<T> io::Write for StdStream<T> where T: io::Write {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.get_mut().0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.io.get_mut().0.flush()
}
}
fn stdio<T>(option: &mut Option<T>, handle: &Handle) -> Result<Option<StdStream<T>>, io::Error>
where T: AsRawFd {
option.take().map_or(Ok(None), |stream| {
PollEvented::new(RawFdWrap(stream), handle).map(|io| Some(StdStream { io: io }))
})
}
/// Spawns a new child process.
@@ -42,12 +95,18 @@ pub struct Child {
/// bad in theory...
pub fn spawn(mut cmd: Command) -> Box<Future<Item=Child, Error=io::Error>> {
Box::new(Signal::new(libc::SIGCHLD, &cmd.handle).and_then(move |sigchld| {
cmd.inner.spawn().map(|c| {
Child {
cmd.inner.spawn().and_then(|mut c| {
let stdin = try!(stdio(&mut c.stdin, &cmd.handle));
let stdout = try!(stdio(&mut c.stdout, &cmd.handle));
let stderr = try!(stdio(&mut c.stderr, &cmd.handle));
Ok(Child {
child: c,
reaped: false,
sigchld: sigchld
}
sigchld: sigchld,
stdin: stdin,
stdout: stdout,
stderr: stderr,
})
})
}))
}