Skip to content

Commit 996fb8d

Browse files
committed
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.
2 parents 8425494 + 3dd3450 commit 996fb8d

File tree

8 files changed

+243
-59
lines changed

8 files changed

+243
-59
lines changed

src/libstd/process.rs

+58-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")]
@@ -70,6 +67,10 @@ pub struct Child {
7067
pub stderr: Option<ChildStderr>,
7168
}
7269

70+
impl AsInner<imp::Process> for Child {
71+
fn as_inner(&self) -> &imp::Process { &self.handle }
72+
}
73+
7374
/// A handle to a child procesess's stdin
7475
#[stable(feature = "process", since = "1.0.0")]
7576
pub struct ChildStdin {
@@ -87,6 +88,10 @@ impl Write for ChildStdin {
8788
}
8889
}
8990

91+
impl AsInner<AnonPipe> for ChildStdin {
92+
fn as_inner(&self) -> &AnonPipe { &self.inner }
93+
}
94+
9095
/// A handle to a child procesess's stdout
9196
#[stable(feature = "process", since = "1.0.0")]
9297
pub struct ChildStdout {
@@ -100,6 +105,10 @@ impl Read for ChildStdout {
100105
}
101106
}
102107

108+
impl AsInner<AnonPipe> for ChildStdout {
109+
fn as_inner(&self) -> &AnonPipe { &self.inner }
110+
}
111+
103112
/// A handle to a child procesess's stderr
104113
#[stable(feature = "process", since = "1.0.0")]
105114
pub struct ChildStderr {
@@ -113,6 +122,10 @@ impl Read for ChildStderr {
113122
}
114123
}
115124

125+
impl AsInner<AnonPipe> for ChildStderr {
126+
fn as_inner(&self) -> &AnonPipe { &self.inner }
127+
}
128+
116129
/// The `Command` type acts as a process builder, providing fine-grained control
117130
/// over how a new process should be spawned. A default configuration can be
118131
/// generated using `Command::new(program)`, where `program` gives a path to the
@@ -131,12 +144,12 @@ impl Read for ChildStderr {
131144
/// ```
132145
#[stable(feature = "process", since = "1.0.0")]
133146
pub struct Command {
134-
inner: CommandImp,
147+
inner: imp::Command,
135148

136149
// Details explained in the builder methods
137-
stdin: Option<StdioImp>,
138-
stdout: Option<StdioImp>,
139-
stderr: Option<StdioImp>,
150+
stdin: Option<Stdio>,
151+
stdout: Option<Stdio>,
152+
stderr: Option<Stdio>,
140153
}
141154

142155
impl Command {
@@ -153,7 +166,7 @@ impl Command {
153166
#[stable(feature = "process", since = "1.0.0")]
154167
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
155168
Command {
156-
inner: CommandImp::new(program.as_ref()),
169+
inner: imp::Command::new(program.as_ref()),
157170
stdin: None,
158171
stdout: None,
159172
stderr: None,
@@ -210,25 +223,26 @@ impl Command {
210223
/// Configuration for the child process's stdin handle (file descriptor 0).
211224
#[stable(feature = "process", since = "1.0.0")]
212225
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
213-
self.stdin = Some(cfg.0);
226+
self.stdin = Some(cfg);
214227
self
215228
}
216229

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

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

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

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

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

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

321-
impl AsInner<CommandImp> for Command {
322-
fn as_inner(&self) -> &CommandImp { &self.inner }
336+
impl AsInner<imp::Command> for Command {
337+
fn as_inner(&self) -> &imp::Command { &self.inner }
323338
}
324339

325-
impl AsInnerMut<CommandImp> for Command {
326-
fn as_inner_mut(&mut self) -> &mut CommandImp { &mut self.inner }
340+
impl AsInnerMut<imp::Command> for Command {
341+
fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner }
327342
}
328343

329-
fn setup_io(io: &StdioImp, readable: bool)
330-
-> io::Result<(StdioImp2, Option<AnonPipe>)>
344+
fn setup_io(io: &Stdio, readable: bool)
345+
-> io::Result<(imp::Stdio, Option<AnonPipe>)>
331346
{
332-
use self::StdioImp::*;
333-
Ok(match *io {
334-
Null => (StdioImp2::None, None),
335-
Inherit => (StdioImp2::Inherit, None),
336-
Piped => {
347+
Ok(match io.0 {
348+
StdioImp::MakePipe => {
337349
let (reader, writer) = try!(pipe::anon_pipe());
338350
if readable {
339-
(StdioImp2::Piped(reader), Some(writer))
351+
(imp::Stdio::Piped(reader), Some(writer))
340352
} else {
341-
(StdioImp2::Piped(writer), Some(reader))
353+
(imp::Stdio::Piped(writer), Some(reader))
342354
}
343355
}
356+
StdioImp::Raw(ref raw) => (raw.clone_if_copy(), None),
344357
})
345358
}
346359

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

366379
// The internal enum for stdio setup; see below for descriptions.
367-
#[derive(Clone)]
368380
enum StdioImp {
369-
Piped,
370-
Inherit,
371-
Null,
381+
MakePipe,
382+
Raw(imp::Stdio),
372383
}
373384

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

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

383394
/// This stream will be ignored. This is the equivalent of attaching the
384395
/// stream to `/dev/null`
385396
#[stable(feature = "process", since = "1.0.0")]
386-
pub fn null() -> Stdio { Stdio(StdioImp::Null) }
397+
pub fn null() -> Stdio { Stdio(StdioImp::Raw(imp::Stdio::None)) }
398+
}
399+
400+
impl FromInner<imp::Stdio> for Stdio {
401+
fn from_inner(inner: imp::Stdio) -> Stdio {
402+
Stdio(StdioImp::Raw(inner))
403+
}
387404
}
388405

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

394411
impl ExitStatus {
395412
/// Was termination successful? Signal termination not considered a success,
@@ -410,8 +427,8 @@ impl ExitStatus {
410427
}
411428
}
412429

413-
impl AsInner<ExitStatusImp> for ExitStatus {
414-
fn as_inner(&self) -> &ExitStatusImp { &self.0 }
430+
impl AsInner<imp::ExitStatus> for ExitStatus {
431+
fn as_inner(&self) -> &imp::ExitStatus { &self.0 }
415432
}
416433

417434
#[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
@@ -416,3 +418,14 @@ fn translate_status(status: c_int) -> ExitStatus {
416418
ExitStatus::Signal(imp::WTERMSIG(status))
417419
}
418420
}
421+
422+
impl Stdio {
423+
pub fn clone_if_copy(&self) -> Stdio {
424+
match *self {
425+
Stdio::Inherit => Stdio::Inherit,
426+
Stdio::None => Stdio::None,
427+
Stdio::Fd(fd) => Stdio::Fd(fd),
428+
Stdio::Piped(_) => unreachable!(),
429+
}
430+
}
431+
}

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)