Skip to content

std: Implement lowering and raising for process IO #25494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 29, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 58 additions & 41 deletions src/libstd/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ use io::{self, Error, ErrorKind};
use path;
use sync::mpsc::{channel, Receiver};
use sys::pipe::{self, AnonPipe};
use sys::process::Command as CommandImp;
use sys::process::Process as ProcessImp;
use sys::process::ExitStatus as ExitStatusImp;
use sys::process::Stdio as StdioImp2;
use sys_common::{AsInner, AsInnerMut};
use sys::process as imp;
use sys_common::{AsInner, AsInnerMut, FromInner};
use thread;

/// Representation of a running or exited child process.
Expand All @@ -52,10 +49,10 @@ use thread;
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub struct Child {
handle: ProcessImp,
handle: imp::Process,

/// None until wait() or wait_with_output() is called.
status: Option<ExitStatusImp>,
status: Option<imp::ExitStatus>,

/// The handle for writing to the child's stdin, if it has been captured
#[stable(feature = "process", since = "1.0.0")]
Expand All @@ -70,6 +67,10 @@ pub struct Child {
pub stderr: Option<ChildStderr>,
}

impl AsInner<imp::Process> for Child {
fn as_inner(&self) -> &imp::Process { &self.handle }
}

/// A handle to a child procesess's stdin
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStdin {
Expand All @@ -87,6 +88,10 @@ impl Write for ChildStdin {
}
}

impl AsInner<AnonPipe> for ChildStdin {
fn as_inner(&self) -> &AnonPipe { &self.inner }
}

/// A handle to a child procesess's stdout
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStdout {
Expand All @@ -100,6 +105,10 @@ impl Read for ChildStdout {
}
}

impl AsInner<AnonPipe> for ChildStdout {
fn as_inner(&self) -> &AnonPipe { &self.inner }
}

/// A handle to a child procesess's stderr
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStderr {
Expand All @@ -113,6 +122,10 @@ impl Read for ChildStderr {
}
}

impl AsInner<AnonPipe> for ChildStderr {
fn as_inner(&self) -> &AnonPipe { &self.inner }
}

/// The `Command` type acts as a process builder, providing fine-grained control
/// over how a new process should be spawned. A default configuration can be
/// generated using `Command::new(program)`, where `program` gives a path to the
Expand All @@ -131,12 +144,12 @@ impl Read for ChildStderr {
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub struct Command {
inner: CommandImp,
inner: imp::Command,

// Details explained in the builder methods
stdin: Option<StdioImp>,
stdout: Option<StdioImp>,
stderr: Option<StdioImp>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}

impl Command {
Expand All @@ -153,7 +166,7 @@ impl Command {
#[stable(feature = "process", since = "1.0.0")]
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
Command {
inner: CommandImp::new(program.as_ref()),
inner: imp::Command::new(program.as_ref()),
stdin: None,
stdout: None,
stderr: None,
Expand Down Expand Up @@ -210,25 +223,26 @@ impl Command {
/// Configuration for the child process's stdin handle (file descriptor 0).
#[stable(feature = "process", since = "1.0.0")]
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
self.stdin = Some(cfg.0);
self.stdin = Some(cfg);
self
}

/// Configuration for the child process's stdout handle (file descriptor 1).
#[stable(feature = "process", since = "1.0.0")]
pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
self.stdout = Some(cfg.0);
self.stdout = Some(cfg);
self
}

/// Configuration for the child process's stderr handle (file descriptor 2).
#[stable(feature = "process", since = "1.0.0")]
pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
self.stderr = Some(cfg.0);
self.stderr = Some(cfg);
self
}

fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
let default_io = Stdio(default_io);
let (their_stdin, our_stdin) = try!(
setup_io(self.stdin.as_ref().unwrap_or(&default_io), true)
);
Expand All @@ -239,7 +253,8 @@ impl Command {
setup_io(self.stderr.as_ref().unwrap_or(&default_io), false)
);

match ProcessImp::spawn(&self.inner, their_stdin, their_stdout, their_stderr) {
match imp::Process::spawn(&self.inner, their_stdin, their_stdout,
their_stderr) {
Err(e) => Err(e),
Ok(handle) => Ok(Child {
handle: handle,
Expand All @@ -256,7 +271,7 @@ impl Command {
/// By default, stdin, stdout and stderr are inherited by the parent.
#[stable(feature = "process", since = "1.0.0")]
pub fn spawn(&mut self) -> io::Result<Child> {
self.spawn_inner(StdioImp::Inherit)
self.spawn_inner(StdioImp::Raw(imp::Stdio::Inherit))
}

/// Executes the command as a child process, waiting for it to finish and
Expand All @@ -279,7 +294,7 @@ impl Command {
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn output(&mut self) -> io::Result<Output> {
self.spawn_inner(StdioImp::Piped).and_then(|p| p.wait_with_output())
self.spawn_inner(StdioImp::MakePipe).and_then(|p| p.wait_with_output())
}

/// Executes a command as a child process, waiting for it to finish and
Expand Down Expand Up @@ -318,29 +333,27 @@ impl fmt::Debug for Command {
}
}

impl AsInner<CommandImp> for Command {
fn as_inner(&self) -> &CommandImp { &self.inner }
impl AsInner<imp::Command> for Command {
fn as_inner(&self) -> &imp::Command { &self.inner }
}

impl AsInnerMut<CommandImp> for Command {
fn as_inner_mut(&mut self) -> &mut CommandImp { &mut self.inner }
impl AsInnerMut<imp::Command> for Command {
fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner }
}

fn setup_io(io: &StdioImp, readable: bool)
-> io::Result<(StdioImp2, Option<AnonPipe>)>
fn setup_io(io: &Stdio, readable: bool)
-> io::Result<(imp::Stdio, Option<AnonPipe>)>
{
use self::StdioImp::*;
Ok(match *io {
Null => (StdioImp2::None, None),
Inherit => (StdioImp2::Inherit, None),
Piped => {
Ok(match io.0 {
StdioImp::MakePipe => {
let (reader, writer) = try!(pipe::anon_pipe());
if readable {
(StdioImp2::Piped(reader), Some(writer))
(imp::Stdio::Piped(reader), Some(writer))
} else {
(StdioImp2::Piped(writer), Some(reader))
(imp::Stdio::Piped(writer), Some(reader))
}
}
StdioImp::Raw(ref raw) => (raw.clone_if_copy(), None),
})
}

Expand All @@ -364,32 +377,36 @@ pub struct Output {
pub struct Stdio(StdioImp);

// The internal enum for stdio setup; see below for descriptions.
#[derive(Clone)]
enum StdioImp {
Piped,
Inherit,
Null,
MakePipe,
Raw(imp::Stdio),
}

impl Stdio {
/// A new pipe should be arranged to connect the parent and child processes.
#[stable(feature = "process", since = "1.0.0")]
pub fn piped() -> Stdio { Stdio(StdioImp::Piped) }
pub fn piped() -> Stdio { Stdio(StdioImp::MakePipe) }

/// The child inherits from the corresponding parent descriptor.
#[stable(feature = "process", since = "1.0.0")]
pub fn inherit() -> Stdio { Stdio(StdioImp::Inherit) }
pub fn inherit() -> Stdio { Stdio(StdioImp::Raw(imp::Stdio::Inherit)) }

/// This stream will be ignored. This is the equivalent of attaching the
/// stream to `/dev/null`
#[stable(feature = "process", since = "1.0.0")]
pub fn null() -> Stdio { Stdio(StdioImp::Null) }
pub fn null() -> Stdio { Stdio(StdioImp::Raw(imp::Stdio::None)) }
}

impl FromInner<imp::Stdio> for Stdio {
fn from_inner(inner: imp::Stdio) -> Stdio {
Stdio(StdioImp::Raw(inner))
}
}

/// Describes the result of a process after it has terminated.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[stable(feature = "process", since = "1.0.0")]
pub struct ExitStatus(ExitStatusImp);
pub struct ExitStatus(imp::ExitStatus);

impl ExitStatus {
/// Was termination successful? Signal termination not considered a success,
Expand All @@ -410,8 +427,8 @@ impl ExitStatus {
}
}

impl AsInner<ExitStatusImp> for ExitStatus {
fn as_inner(&self) -> &ExitStatusImp { &self.0 }
impl AsInner<imp::ExitStatus> for ExitStatus {
fn as_inner(&self) -> &imp::ExitStatus { &self.0 }
}

#[stable(feature = "process", since = "1.0.0")]
Expand Down
49 changes: 48 additions & 1 deletion src/libstd/sys/unix/ext/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
#![stable(feature = "rust1", since = "1.0.0")]

use os::unix::raw::{uid_t, gid_t};
use os::unix::io::{FromRawFd, RawFd, AsRawFd};
use prelude::v1::*;
use process;
use sys;
use sys_common::{AsInnerMut, AsInner};
use sys_common::{AsInnerMut, AsInner, FromInner};

/// Unix-specific extensions to the `std::process::Command` builder
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -63,3 +64,49 @@ impl ExitStatusExt for process::ExitStatus {
}
}
}

#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawFd for process::Stdio {
/// Creates a new instance of `Stdio` from the raw underlying file
/// descriptor.
///
/// When this `Stdio` is used as an I/O handle for a child process the given
/// file descriptor will be `dup`d into the destination file descriptor in
/// the child process.
///
/// Note that this function **does not** take ownership of the file
/// descriptor provided and it will **not** be closed when `Stdio` goes out
/// of scope. As a result this method is unsafe because due to the lack of
/// knowledge about the lifetime of the provided file descriptor, this could
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel slightly uneasy about this comment, as it directly contradicts the documentation for the trait.

It would be good to be more crisp about the story here.

/// cause another I/O primitive's ownership property of its file descriptor
/// to be violated.
///
/// Also note that this file descriptor may be used multiple times to spawn
/// processes. For example the `Command::spawn` function could be called
/// more than once to spawn more than one process sharing this file
/// descriptor.
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
process::Stdio::from_inner(sys::process::Stdio::Fd(fd))
}
}

#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawFd for process::ChildStdin {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}

#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawFd for process::ChildStdout {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}

#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawFd for process::ChildStderr {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}
5 changes: 2 additions & 3 deletions src/libstd/sys/unix/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ impl AnonPipe {
self.0.write(buf)
}

pub fn into_fd(self) -> FileDesc {
self.0
}
pub fn fd(&self) -> &FileDesc { &self.0 }
pub fn into_fd(self) -> FileDesc { self.0 }
}
13 changes: 13 additions & 0 deletions src/libstd/sys/unix/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ pub enum Stdio {
Inherit,
Piped(AnonPipe),
None,
Fd(c_int),
}

const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
Expand Down Expand Up @@ -253,6 +254,7 @@ impl Process {
let setup = |src: Stdio, dst: c_int| {
let fd = match src {
Stdio::Inherit => return true,
Stdio::Fd(fd) => return cvt_r(|| libc::dup2(fd, dst)).is_ok(),
Stdio::Piped(pipe) => pipe.into_fd(),

// If a stdio file descriptor is set to be ignored, we open up
Expand Down Expand Up @@ -412,3 +414,14 @@ fn translate_status(status: c_int) -> ExitStatus {
ExitStatus::Signal(imp::WTERMSIG(status))
}
}

impl Stdio {
pub fn clone_if_copy(&self) -> Stdio {
match *self {
Stdio::Inherit => Stdio::Inherit,
Stdio::None => Stdio::None,
Stdio::Fd(fd) => Stdio::Fd(fd),
Stdio::Piped(_) => unreachable!(),
}
}
}
1 change: 1 addition & 0 deletions src/libstd/sys/windows/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod ffi;
pub mod fs;
pub mod io;
pub mod raw;
pub mod process;

/// A prelude for conveniently writing platform-specific code.
///
Expand Down
Loading