Auto merge of #25494 - alexcrichton:stdio-from-raw, r=aturon

This commit implements a number of standard traits for the standard library's
process I/O handles. The `FromRaw{Fd,Handle}` traits are now implemented for the
`Stdio` type and the `AsRaw{Fd,Handle}` traits are now implemented for the
`Child{Stdout,Stdin,Stderr}` types.

The stability markers for these implementations mention that they are stable for
1.1 as I will nominate this commit for cherry-picking to beta.
This commit is contained in:
bors 2015-05-29 19:24:40 +00:00
commit 996fb8d001
8 changed files with 243 additions and 59 deletions

View file

@ -22,11 +22,8 @@
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.
@ -52,10 +49,10 @@
/// ```
#[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")]
@ -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 {
@ -87,6 +88,10 @@ fn flush(&mut self) -> io::Result<()> {
}
}
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 {
@ -100,6 +105,10 @@ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
}
}
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 {
@ -113,6 +122,10 @@ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
}
}
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
@ -131,12 +144,12 @@ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
/// ```
#[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 {
@ -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,
@ -210,25 +223,26 @@ pub fn current_dir<P: AsRef<path::Path>>(&mut self, dir: P) -> &mut 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)
);
@ -239,7 +253,8 @@ fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
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,
@ -256,7 +271,7 @@ fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
/// 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
@ -279,7 +294,7 @@ pub fn spawn(&mut self) -> io::Result<Child> {
/// ```
#[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
@ -318,29 +333,27 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
}
}
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),
})
}
@ -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,
@ -410,8 +427,8 @@ pub fn code(&self) -> Option<i32> {
}
}
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")]

View file

@ -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")]
@ -63,3 +64,49 @@ fn signal(&self) -> Option<i32> {
}
}
}
#[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
/// 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()
}
}

View file

@ -44,7 +44,6 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
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 }
}

View file

@ -123,6 +123,7 @@ pub enum Stdio {
Inherit,
Piped(AnonPipe),
None,
Fd(c_int),
}
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
@ -253,6 +254,7 @@ fn fail(output: &mut AnonPipe) -> ! {
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
@ -416,3 +418,14 @@ pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
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!(),
}
}
}

View file

@ -20,6 +20,7 @@
pub mod fs;
pub mod io;
pub mod raw;
pub mod process;
/// A prelude for conveniently writing platform-specific code.
///

View file

@ -0,0 +1,69 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.
//! Extensions to `std::process` for Windows.
#![stable(feature = "from_raw_os", since = "1.1.0")]
use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle};
use process;
use sys;
use sys_common::{AsInner, FromInner};
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawHandle for process::Stdio {
/// Creates a new instance of `Stdio` from the raw underlying handle.
///
/// When this `Stdio` is used as an I/O handle for a child process the given
/// handle will be duplicated via `DuplicateHandle` to ensure that the
/// handle has the correct permissions to cross the process boundary.
///
/// Note that this function **does not** take ownership of the handle
/// 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 handle, this could cause another I/O
/// primitive's ownership property of its handle to be violated.
///
/// Also note that this handle 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 handle.
unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio {
let handle = sys::handle::RawHandle::new(handle as *mut _);
process::Stdio::from_inner(sys::process::Stdio::Handle(handle))
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawHandle for process::Child {
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawHandle for process::ChildStdin {
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawHandle for process::ChildStdout {
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl AsRawHandle for process::ChildStderr {
fn as_raw_handle(&self) -> RawHandle {
self.as_inner().handle().raw() as *mut _
}
}

View file

@ -15,26 +15,55 @@
use libc::funcs::extra::kernel32::{GetCurrentProcess, DuplicateHandle};
use libc::{self, HANDLE};
use mem;
use ops::Deref;
use ptr;
use sys::cvt;
pub struct Handle(HANDLE);
/// An owned container for `HANDLE` object, closing them on Drop.
///
/// All methods are inherited through a `Deref` impl to `RawHandle`
pub struct Handle(RawHandle);
unsafe impl Send for Handle {}
unsafe impl Sync for Handle {}
/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference
/// as well as Rust-y methods.
///
/// This does **not** drop the handle when it goes out of scope, use `Handle`
/// instead for that.
#[derive(Copy, Clone)]
pub struct RawHandle(HANDLE);
unsafe impl Send for RawHandle {}
unsafe impl Sync for RawHandle {}
impl Handle {
pub fn new(handle: HANDLE) -> Handle {
Handle(handle)
Handle(RawHandle::new(handle))
}
pub fn raw(&self) -> HANDLE { self.0 }
pub fn into_raw(self) -> HANDLE {
let ret = self.0;
let ret = self.raw();
mem::forget(self);
return ret;
}
}
impl Deref for Handle {
type Target = RawHandle;
fn deref(&self) -> &RawHandle { &self.0 }
}
impl Drop for Handle {
fn drop(&mut self) {
unsafe { let _ = libc::CloseHandle(self.raw()); }
}
}
impl RawHandle {
pub fn new(handle: HANDLE) -> RawHandle {
RawHandle(handle)
}
pub fn raw(&self) -> HANDLE { self.0 }
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let mut read = 0;
@ -79,9 +108,3 @@ pub fn duplicate(&self, access: libc::DWORD, inherit: bool,
Ok(Handle::new(ret))
}
}
impl Drop for Handle {
fn drop(&mut self) {
unsafe { let _ = libc::CloseHandle(self.0); }
}
}

View file

@ -27,7 +27,7 @@
use sync::StaticMutex;
use sys::c;
use sys::fs::{OpenOptions, File};
use sys::handle::Handle;
use sys::handle::{Handle, RawHandle};
use sys::pipe::AnonPipe;
use sys::stdio;
use sys::{self, cvt};
@ -109,6 +109,7 @@ pub enum Stdio {
Inherit,
Piped(AnonPipe),
None,
Handle(RawHandle),
}
impl Process {
@ -217,6 +218,8 @@ pub fn wait(&self) -> io::Result<ExitStatus> {
}
}
}
pub fn handle(&self) -> &Handle { &self.handle }
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@ -353,6 +356,15 @@ fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec<u16>) {
}
impl Stdio {
pub fn clone_if_copy(&self) -> Stdio {
match *self {
Stdio::Inherit => Stdio::Inherit,
Stdio::None => Stdio::None,
Stdio::Handle(handle) => Stdio::Handle(handle),
Stdio::Piped(_) => unreachable!(),
}
}
fn to_handle(&self, stdio_id: libc::DWORD) -> io::Result<Handle> {
use libc::DUPLICATE_SAME_ACCESS;
@ -362,6 +374,9 @@ fn to_handle(&self, stdio_id: libc::DWORD) -> io::Result<Handle> {
io.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS)
})
}
Stdio::Handle(ref handle) => {
handle.duplicate(0, true, DUPLICATE_SAME_ACCESS)
}
Stdio::Piped(ref pipe) => {
pipe.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS)
}