Skip to content

Commit af69f4e

Browse files
committed
std: Implement lowering and raising for process IO
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.
1 parent 5e535ea commit af69f4e

File tree

8 files changed

+231
-59
lines changed

8 files changed

+231
-59
lines changed

src/libstd/process.rs

+54-41
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,8 @@ use io::{self, Error, ErrorKind};
2222
use path;
2323
use sync::mpsc::{channel, Receiver};
2424
use sys::pipe::{self, AnonPipe};
25-
use sys::process::Command as CommandImp;
26-
use sys::process::Process as ProcessImp;
27-
use sys::process::ExitStatus as ExitStatusImp;
28-
use sys::process::Stdio as StdioImp2;
29-
use sys_common::{AsInner, AsInnerMut};
25+
use sys::process as imp;
26+
use sys_common::{AsInner, AsInnerMut, FromInner};
3027
use thread;
3128

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

5754
/// None until wait() or wait_with_output() is called.
58-
status: Option<ExitStatusImp>,
55+
status: Option<imp::ExitStatus>,
5956

6057
/// The handle for writing to the child's stdin, if it has been captured
6158
#[stable(feature = "process", since = "1.0.0")]
@@ -87,6 +84,10 @@ impl Write for ChildStdin {
8784
}
8885
}
8986

87+
impl AsInner<AnonPipe> for ChildStdin {
88+
fn as_inner(&self) -> &AnonPipe { &self.inner }
89+
}
90+
9091
/// A handle to a child procesess's stdout
9192
#[stable(feature = "process", since = "1.0.0")]
9293
pub struct ChildStdout {
@@ -100,6 +101,10 @@ impl Read for ChildStdout {
100101
}
101102
}
102103

104+
impl AsInner<AnonPipe> for ChildStdout {
105+
fn as_inner(&self) -> &AnonPipe { &self.inner }
106+
}
107+
103108
/// A handle to a child procesess's stderr
104109
#[stable(feature = "process", since = "1.0.0")]
105110
pub struct ChildStderr {
@@ -113,6 +118,10 @@ impl Read for ChildStderr {
113118
}
114119
}
115120

121+
impl AsInner<AnonPipe> for ChildStderr {
122+
fn as_inner(&self) -> &AnonPipe { &self.inner }
123+
}
124+
116125
/// The `Command` type acts as a process builder, providing fine-grained control
117126
/// over how a new process should be spawned. A default configuration can be
118127
/// generated using `Command::new(program)`, where `program` gives a path to the
@@ -131,12 +140,12 @@ impl Read for ChildStderr {
131140
/// ```
132141
#[stable(feature = "process", since = "1.0.0")]
133142
pub struct Command {
134-
inner: CommandImp,
143+
inner: imp::Command,
135144

136145
// Details explained in the builder methods
137-
stdin: Option<StdioImp>,
138-
stdout: Option<StdioImp>,
139-
stderr: Option<StdioImp>,
146+
stdin: Option<Stdio>,
147+
stdout: Option<Stdio>,
148+
stderr: Option<Stdio>,
140149
}
141150

142151
impl Command {
@@ -153,7 +162,7 @@ impl Command {
153162
#[stable(feature = "process", since = "1.0.0")]
154163
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
155164
Command {
156-
inner: CommandImp::new(program.as_ref()),
165+
inner: imp::Command::new(program.as_ref()),
157166
stdin: None,
158167
stdout: None,
159168
stderr: None,
@@ -210,25 +219,26 @@ impl Command {
210219
/// Configuration for the child process's stdin handle (file descriptor 0).
211220
#[stable(feature = "process", since = "1.0.0")]
212221
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
213-
self.stdin = Some(cfg.0);
222+
self.stdin = Some(cfg);
214223
self
215224
}
216225

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

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

231240
fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
241+
let default_io = Stdio(default_io);
232242
let (their_stdin, our_stdin) = try!(
233243
setup_io(self.stdin.as_ref().unwrap_or(&default_io), true)
234244
);
@@ -239,7 +249,8 @@ impl Command {
239249
setup_io(self.stderr.as_ref().unwrap_or(&default_io), false)
240250
);
241251

242-
match ProcessImp::spawn(&self.inner, their_stdin, their_stdout, their_stderr) {
252+
match imp::Process::spawn(&self.inner, their_stdin, their_stdout,
253+
their_stderr) {
243254
Err(e) => Err(e),
244255
Ok(handle) => Ok(Child {
245256
handle: handle,
@@ -256,7 +267,7 @@ impl Command {
256267
/// By default, stdin, stdout and stderr are inherited by the parent.
257268
#[stable(feature = "process", since = "1.0.0")]
258269
pub fn spawn(&mut self) -> io::Result<Child> {
259-
self.spawn_inner(StdioImp::Inherit)
270+
self.spawn_inner(StdioImp::Raw(imp::Stdio::Inherit))
260271
}
261272

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

285296
/// Executes a command as a child process, waiting for it to finish and
@@ -318,29 +329,27 @@ impl fmt::Debug for Command {
318329
}
319330
}
320331

321-
impl AsInner<CommandImp> for Command {
322-
fn as_inner(&self) -> &CommandImp { &self.inner }
332+
impl AsInner<imp::Command> for Command {
333+
fn as_inner(&self) -> &imp::Command { &self.inner }
323334
}
324335

325-
impl AsInnerMut<CommandImp> for Command {
326-
fn as_inner_mut(&mut self) -> &mut CommandImp { &mut self.inner }
336+
impl AsInnerMut<imp::Command> for Command {
337+
fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner }
327338
}
328339

329-
fn setup_io(io: &StdioImp, readable: bool)
330-
-> io::Result<(StdioImp2, Option<AnonPipe>)>
340+
fn setup_io(io: &Stdio, readable: bool)
341+
-> io::Result<(imp::Stdio, Option<AnonPipe>)>
331342
{
332-
use self::StdioImp::*;
333-
Ok(match *io {
334-
Null => (StdioImp2::None, None),
335-
Inherit => (StdioImp2::Inherit, None),
336-
Piped => {
343+
Ok(match io.0 {
344+
StdioImp::MakePipe => {
337345
let (reader, writer) = try!(pipe::anon_pipe());
338346
if readable {
339-
(StdioImp2::Piped(reader), Some(writer))
347+
(imp::Stdio::Piped(reader), Some(writer))
340348
} else {
341-
(StdioImp2::Piped(writer), Some(reader))
349+
(imp::Stdio::Piped(writer), Some(reader))
342350
}
343351
}
352+
StdioImp::Raw(ref raw) => (raw.clone_if_copy(), None),
344353
})
345354
}
346355

@@ -364,32 +373,36 @@ pub struct Output {
364373
pub struct Stdio(StdioImp);
365374

366375
// The internal enum for stdio setup; see below for descriptions.
367-
#[derive(Clone)]
368376
enum StdioImp {
369-
Piped,
370-
Inherit,
371-
Null,
377+
MakePipe,
378+
Raw(imp::Stdio),
372379
}
373380

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

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

383390
/// This stream will be ignored. This is the equivalent of attaching the
384391
/// stream to `/dev/null`
385392
#[stable(feature = "process", since = "1.0.0")]
386-
pub fn null() -> Stdio { Stdio(StdioImp::Null) }
393+
pub fn null() -> Stdio { Stdio(StdioImp::Raw(imp::Stdio::None)) }
394+
}
395+
396+
impl FromInner<imp::Stdio> for Stdio {
397+
fn from_inner(inner: imp::Stdio) -> Stdio {
398+
Stdio(StdioImp::Raw(inner))
399+
}
387400
}
388401

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

394407
impl ExitStatus {
395408
/// Was termination successful? Signal termination not considered a success,
@@ -410,8 +423,8 @@ impl ExitStatus {
410423
}
411424
}
412425

413-
impl AsInner<ExitStatusImp> for ExitStatus {
414-
fn as_inner(&self) -> &ExitStatusImp { &self.0 }
426+
impl AsInner<imp::ExitStatus> for ExitStatus {
427+
fn as_inner(&self) -> &imp::ExitStatus { &self.0 }
415428
}
416429

417430
#[stable(feature = "process", since = "1.0.0")]

src/libstd/sys/unix/ext/process.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
#![stable(feature = "rust1", since = "1.0.0")]
1414

1515
use os::unix::raw::{uid_t, gid_t};
16+
use os::unix::io::{FromRawFd, RawFd, AsRawFd};
1617
use prelude::v1::*;
1718
use process;
1819
use sys;
19-
use sys_common::{AsInnerMut, AsInner};
20+
use sys_common::{AsInnerMut, AsInner, FromInner};
2021

2122
/// Unix-specific extensions to the `std::process::Command` builder
2223
#[stable(feature = "rust1", since = "1.0.0")]
@@ -63,3 +64,49 @@ impl ExitStatusExt for process::ExitStatus {
6364
}
6465
}
6566
}
67+
68+
#[stable(feature = "from_raw_os", since = "1.1.0")]
69+
impl FromRawFd for process::Stdio {
70+
/// Creates a new instance of `Stdio` from the raw underlying file
71+
/// descriptor.
72+
///
73+
/// When this `Stdio` is used as an I/O handle for a child process the given
74+
/// file descriptor will be `dup`d into the destination file descriptor in
75+
/// the child process.
76+
///
77+
/// Note that this function **does not** take ownership of the file
78+
/// descriptor provided and it will **not** be closed when `Stdio` goes out
79+
/// of scope. As a result this method is unsafe because due to the lack of
80+
/// knowledge about the lifetime of the provided file descriptor, this could
81+
/// cause another I/O primitive's ownership property of its file descriptor
82+
/// to be violated.
83+
///
84+
/// Also note that this file descriptor may be used multiple times to spawn
85+
/// processes. For example the `Command::spawn` function could be called
86+
/// more than once to spawn more than one process sharing this file
87+
/// descriptor.
88+
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
89+
process::Stdio::from_inner(sys::process::Stdio::Fd(fd))
90+
}
91+
}
92+
93+
#[stable(feature = "from_raw_os", since = "1.1.0")]
94+
impl AsRawFd for process::ChildStdin {
95+
fn as_raw_fd(&self) -> RawFd {
96+
self.as_inner().fd().raw()
97+
}
98+
}
99+
100+
#[stable(feature = "from_raw_os", since = "1.1.0")]
101+
impl AsRawFd for process::ChildStdout {
102+
fn as_raw_fd(&self) -> RawFd {
103+
self.as_inner().fd().raw()
104+
}
105+
}
106+
107+
#[stable(feature = "from_raw_os", since = "1.1.0")]
108+
impl AsRawFd for process::ChildStderr {
109+
fn as_raw_fd(&self) -> RawFd {
110+
self.as_inner().fd().raw()
111+
}
112+
}

src/libstd/sys/unix/pipe.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ impl AnonPipe {
4444
self.0.write(buf)
4545
}
4646

47-
pub fn into_fd(self) -> FileDesc {
48-
self.0
49-
}
47+
pub fn fd(&self) -> &FileDesc { &self.0 }
48+
pub fn into_fd(self) -> FileDesc { self.0 }
5049
}

src/libstd/sys/unix/process.rs

+13
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ pub enum Stdio {
123123
Inherit,
124124
Piped(AnonPipe),
125125
None,
126+
Fd(c_int),
126127
}
127128

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

258260
// If a stdio file descriptor is set to be ignored, we open up
@@ -412,3 +414,14 @@ fn translate_status(status: c_int) -> ExitStatus {
412414
ExitStatus::Signal(imp::WTERMSIG(status))
413415
}
414416
}
417+
418+
impl Stdio {
419+
pub fn clone_if_copy(&self) -> Stdio {
420+
match *self {
421+
Stdio::Inherit => Stdio::Inherit,
422+
Stdio::None => Stdio::None,
423+
Stdio::Fd(fd) => Stdio::Fd(fd),
424+
Stdio::Piped(_) => unreachable!(),
425+
}
426+
}
427+
}

src/libstd/sys/windows/ext/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod ffi;
2020
pub mod fs;
2121
pub mod io;
2222
pub mod raw;
23+
pub mod process;
2324

2425
/// A prelude for conveniently writing platform-specific code.
2526
///

0 commit comments

Comments
 (0)