Skip to content

Commit b755b4d

Browse files
committed
auto merge of #14781 : alexcrichton/rust/issue-14724, r=brson
* os::pipe() now returns `IoResult<os::Pipe>` * os::pipe() is now unsafe because it does not arrange for deallocation of file descriptors * PipeStream::pair() has been added. This is a safe method to get a pair of pipes. * Dealing with pipes in native process bindings have been improved to be more robust in the face of failure and intermittent errors. This converts a few fail!() situations to Err situations. cc #13538 Closes #14724 [breaking-change]
2 parents 0973eb4 + 04eced7 commit b755b4d

File tree

7 files changed

+211
-118
lines changed

7 files changed

+211
-118
lines changed

src/liblibc/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub use funcs::bsd43::{shutdown};
172172
#[cfg(unix)] pub use consts::os::posix88::{ENOTCONN, ECONNABORTED, EADDRNOTAVAIL, EINTR};
173173
#[cfg(unix)] pub use consts::os::posix88::{EADDRINUSE, ENOENT, EISDIR, EAGAIN, EWOULDBLOCK};
174174
#[cfg(unix)] pub use consts::os::posix88::{ECANCELED, SIGINT, EINPROGRESS};
175-
#[cfg(unix)] pub use consts::os::posix88::{ENOSYS, ENOTTY, ETIMEDOUT};
175+
#[cfg(unix)] pub use consts::os::posix88::{ENOSYS, ENOTTY, ETIMEDOUT, EMFILE};
176176
#[cfg(unix)] pub use consts::os::posix88::{SIGTERM, SIGKILL, SIGPIPE, PROT_NONE};
177177
#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN};
178178
#[cfg(unix)] pub use consts::os::bsd44::{AF_UNIX};
@@ -196,7 +196,7 @@ pub use funcs::bsd43::{shutdown};
196196
#[cfg(windows)] pub use consts::os::c95::{WSAECONNREFUSED, WSAECONNRESET, WSAEACCES};
197197
#[cfg(windows)] pub use consts::os::c95::{WSAEWOULDBLOCK, WSAENOTCONN, WSAECONNABORTED};
198198
#[cfg(windows)] pub use consts::os::c95::{WSAEADDRNOTAVAIL, WSAEADDRINUSE, WSAEINTR};
199-
#[cfg(windows)] pub use consts::os::c95::{WSAEINPROGRESS, WSAEINVAL};
199+
#[cfg(windows)] pub use consts::os::c95::{WSAEINPROGRESS, WSAEINVAL, WSAEMFILE};
200200
#[cfg(windows)] pub use consts::os::extra::{ERROR_INSUFFICIENT_BUFFER};
201201
#[cfg(windows)] pub use consts::os::extra::{O_BINARY, O_NOINHERIT, PAGE_NOACCESS};
202202
#[cfg(windows)] pub use consts::os::extra::{PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE};

src/libnative/io/file_unix.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,9 @@ mod tests {
525525
fn test_file_desc() {
526526
// Run this test with some pipes so we don't have to mess around with
527527
// opening or closing files.
528-
let os::Pipe { input, out } = os::pipe();
529-
let mut reader = FileDesc::new(input, true);
530-
let mut writer = FileDesc::new(out, true);
528+
let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
529+
let mut reader = FileDesc::new(reader, true);
530+
let mut writer = FileDesc::new(writer, true);
531531

532532
writer.inner_write(bytes!("test")).ok().unwrap();
533533
let mut buf = [0u8, ..4];

src/libnative/io/helper_thread.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ mod imp {
158158
pub type signal = libc::c_int;
159159

160160
pub fn new() -> (signal, signal) {
161-
let pipe = os::pipe();
162-
(pipe.input, pipe.out)
161+
let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
162+
(reader, writer)
163163
}
164164

165165
pub fn signal(fd: libc::c_int) {

src/libnative/io/process.rs

+123-80
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010

1111
use libc::{pid_t, c_void, c_int};
1212
use libc;
13+
use std::c_str::CString;
14+
use std::io;
1315
use std::mem;
1416
use std::os;
1517
use std::ptr;
16-
use std::rt::rtio;
1718
use std::rt::rtio::{ProcessConfig, IoResult, IoError};
18-
use std::c_str::CString;
19+
use std::rt::rtio;
1920

2021
use super::file;
2122
use super::util;
@@ -73,47 +74,43 @@ impl Process {
7374

7475
fn get_io(io: rtio::StdioContainer,
7576
ret: &mut Vec<Option<file::FileDesc>>)
76-
-> (Option<os::Pipe>, c_int)
77+
-> IoResult<Option<file::FileDesc>>
7778
{
7879
match io {
79-
rtio::Ignored => { ret.push(None); (None, -1) }
80-
rtio::InheritFd(fd) => { ret.push(None); (None, fd) }
80+
rtio::Ignored => { ret.push(None); Ok(None) }
81+
rtio::InheritFd(fd) => {
82+
ret.push(None);
83+
Ok(Some(file::FileDesc::new(fd, true)))
84+
}
8185
rtio::CreatePipe(readable, _writable) => {
82-
let pipe = os::pipe();
86+
let (reader, writer) = try!(pipe());
8387
let (theirs, ours) = if readable {
84-
(pipe.input, pipe.out)
88+
(reader, writer)
8589
} else {
86-
(pipe.out, pipe.input)
90+
(writer, reader)
8791
};
88-
ret.push(Some(file::FileDesc::new(ours, true)));
89-
(Some(pipe), theirs)
92+
ret.push(Some(ours));
93+
Ok(Some(theirs))
9094
}
9195
}
9296
}
9397

9498
let mut ret_io = Vec::new();
95-
let (in_pipe, in_fd) = get_io(cfg.stdin, &mut ret_io);
96-
let (out_pipe, out_fd) = get_io(cfg.stdout, &mut ret_io);
97-
let (err_pipe, err_fd) = get_io(cfg.stderr, &mut ret_io);
98-
99-
let res = spawn_process_os(cfg, in_fd, out_fd, err_fd);
100-
101-
unsafe {
102-
for pipe in in_pipe.iter() { let _ = libc::close(pipe.input); }
103-
for pipe in out_pipe.iter() { let _ = libc::close(pipe.out); }
104-
for pipe in err_pipe.iter() { let _ = libc::close(pipe.out); }
105-
}
99+
let res = spawn_process_os(cfg,
100+
try!(get_io(cfg.stdin, &mut ret_io)),
101+
try!(get_io(cfg.stdout, &mut ret_io)),
102+
try!(get_io(cfg.stderr, &mut ret_io)));
106103

107104
match res {
108105
Ok(res) => {
109-
Ok((Process {
110-
pid: res.pid,
111-
handle: res.handle,
112-
exit_code: None,
113-
exit_signal: None,
114-
deadline: 0,
115-
},
116-
ret_io))
106+
let p = Process {
107+
pid: res.pid,
108+
handle: res.handle,
109+
exit_code: None,
110+
exit_signal: None,
111+
deadline: 0,
112+
};
113+
Ok((p, ret_io))
117114
}
118115
Err(e) => Err(e)
119116
}
@@ -194,6 +191,37 @@ impl Drop for Process {
194191
}
195192
}
196193

194+
fn pipe() -> IoResult<(file::FileDesc, file::FileDesc)> {
195+
#[cfg(unix)] use ERROR = libc::EMFILE;
196+
#[cfg(windows)] use ERROR = libc::WSAEMFILE;
197+
struct Closer { fd: libc::c_int }
198+
199+
let os::Pipe { reader, writer } = match unsafe { os::pipe() } {
200+
Ok(p) => p,
201+
Err(io::IoError { detail, .. }) => return Err(IoError {
202+
code: ERROR as uint,
203+
extra: 0,
204+
detail: detail,
205+
})
206+
};
207+
let mut reader = Closer { fd: reader };
208+
let mut writer = Closer { fd: writer };
209+
210+
let native_reader = file::FileDesc::new(reader.fd, true);
211+
reader.fd = -1;
212+
let native_writer = file::FileDesc::new(writer.fd, true);
213+
writer.fd = -1;
214+
return Ok((native_reader, native_writer));
215+
216+
impl Drop for Closer {
217+
fn drop(&mut self) {
218+
if self.fd != -1 {
219+
let _ = unsafe { libc::close(self.fd) };
220+
}
221+
}
222+
}
223+
}
224+
197225
#[cfg(windows)]
198226
unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
199227
let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
@@ -246,7 +274,9 @@ struct SpawnProcessResult {
246274

247275
#[cfg(windows)]
248276
fn spawn_process_os(cfg: ProcessConfig,
249-
in_fd: c_int, out_fd: c_int, err_fd: c_int)
277+
in_fd: Option<file::FileDesc>,
278+
out_fd: Option<file::FileDesc>,
279+
err_fd: Option<file::FileDesc>)
250280
-> IoResult<SpawnProcessResult> {
251281
use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
252282
use libc::consts::os::extra::{
@@ -283,47 +313,51 @@ fn spawn_process_os(cfg: ProcessConfig,
283313
// Similarly to unix, we don't actually leave holes for the stdio file
284314
// descriptors, but rather open up /dev/null equivalents. These
285315
// equivalents are drawn from libuv's windows process spawning.
286-
let set_fd = |fd: c_int, slot: &mut HANDLE, is_stdin: bool| {
287-
if fd == -1 {
288-
let access = if is_stdin {
289-
libc::FILE_GENERIC_READ
290-
} else {
291-
libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
292-
};
293-
let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
294-
let mut sa = libc::SECURITY_ATTRIBUTES {
295-
nLength: size as libc::DWORD,
296-
lpSecurityDescriptor: ptr::mut_null(),
297-
bInheritHandle: 1,
298-
};
299-
let filename = "NUL".to_utf16().append_one(0);
300-
*slot = libc::CreateFileW(filename.as_ptr(),
301-
access,
302-
libc::FILE_SHARE_READ |
303-
libc::FILE_SHARE_WRITE,
304-
&mut sa,
305-
libc::OPEN_EXISTING,
306-
0,
307-
ptr::mut_null());
308-
if *slot == INVALID_HANDLE_VALUE as libc::HANDLE {
309-
return Err(super::last_error())
310-
}
311-
} else {
312-
let orig = get_osfhandle(fd) as HANDLE;
313-
if orig == INVALID_HANDLE_VALUE as HANDLE {
314-
return Err(super::last_error())
316+
let set_fd = |fd: &Option<file::FileDesc>, slot: &mut HANDLE,
317+
is_stdin: bool| {
318+
match *fd {
319+
None => {
320+
let access = if is_stdin {
321+
libc::FILE_GENERIC_READ
322+
} else {
323+
libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
324+
};
325+
let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
326+
let mut sa = libc::SECURITY_ATTRIBUTES {
327+
nLength: size as libc::DWORD,
328+
lpSecurityDescriptor: ptr::mut_null(),
329+
bInheritHandle: 1,
330+
};
331+
let filename = "NUL".to_utf16().append_one(0);
332+
*slot = libc::CreateFileW(filename.as_ptr(),
333+
access,
334+
libc::FILE_SHARE_READ |
335+
libc::FILE_SHARE_WRITE,
336+
&mut sa,
337+
libc::OPEN_EXISTING,
338+
0,
339+
ptr::mut_null());
340+
if *slot == INVALID_HANDLE_VALUE as libc::HANDLE {
341+
return Err(super::last_error())
342+
}
315343
}
316-
if DuplicateHandle(cur_proc, orig, cur_proc, slot,
317-
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
318-
return Err(super::last_error())
344+
Some(ref fd) => {
345+
let orig = get_osfhandle(fd.fd()) as HANDLE;
346+
if orig == INVALID_HANDLE_VALUE as HANDLE {
347+
return Err(super::last_error())
348+
}
349+
if DuplicateHandle(cur_proc, orig, cur_proc, slot,
350+
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
351+
return Err(super::last_error())
352+
}
319353
}
320354
}
321355
Ok(())
322356
};
323357

324-
try!(set_fd(in_fd, &mut si.hStdInput, true));
325-
try!(set_fd(out_fd, &mut si.hStdOutput, false));
326-
try!(set_fd(err_fd, &mut si.hStdError, false));
358+
try!(set_fd(&in_fd, &mut si.hStdInput, true));
359+
try!(set_fd(&out_fd, &mut si.hStdOutput, false));
360+
try!(set_fd(&err_fd, &mut si.hStdError, false));
327361

328362
let cmd_str = make_command_line(cfg.program, cfg.args);
329363
let mut pi = zeroed_process_information();
@@ -464,7 +498,10 @@ fn make_command_line(prog: &CString, args: &[CString]) -> String {
464498
}
465499

466500
#[cfg(unix)]
467-
fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_int)
501+
fn spawn_process_os(cfg: ProcessConfig,
502+
in_fd: Option<file::FileDesc>,
503+
out_fd: Option<file::FileDesc>,
504+
err_fd: Option<file::FileDesc>)
468505
-> IoResult<SpawnProcessResult>
469506
{
470507
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
@@ -498,9 +535,7 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
498535

499536
with_envp(cfg.env, proc(envp) {
500537
with_argv(cfg.program, cfg.args, proc(argv) unsafe {
501-
let pipe = os::pipe();
502-
let mut input = file::FileDesc::new(pipe.input, true);
503-
let mut output = file::FileDesc::new(pipe.out, true);
538+
let (mut input, mut output) = try!(pipe());
504539

505540
// We may use this in the child, so perform allocations before the
506541
// fork
@@ -510,7 +545,7 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
510545

511546
let pid = fork();
512547
if pid < 0 {
513-
fail!("failure in fork: {}", os::last_os_error());
548+
return Err(super::last_error())
514549
} else if pid > 0 {
515550
drop(output);
516551
let mut bytes = [0, ..4];
@@ -586,16 +621,24 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
586621
// up /dev/null into that file descriptor. Otherwise, the first file
587622
// descriptor opened up in the child would be numbered as one of the
588623
// stdio file descriptors, which is likely to wreak havoc.
589-
let setup = |src: c_int, dst: c_int| {
590-
let src = if src == -1 {
591-
let flags = if dst == libc::STDIN_FILENO {
592-
libc::O_RDONLY
593-
} else {
594-
libc::O_RDWR
595-
};
596-
devnull.with_ref(|p| libc::open(p, flags, 0))
597-
} else {
598-
src
624+
let setup = |src: Option<file::FileDesc>, dst: c_int| {
625+
let src = match src {
626+
None => {
627+
let flags = if dst == libc::STDIN_FILENO {
628+
libc::O_RDONLY
629+
} else {
630+
libc::O_RDWR
631+
};
632+
devnull.with_ref(|p| libc::open(p, flags, 0))
633+
}
634+
Some(obj) => {
635+
let fd = obj.fd();
636+
// Leak the memory and the file descriptor. We're in the
637+
// child now an all our resources are going to be
638+
// cleaned up very soon
639+
mem::forget(obj);
640+
fd
641+
}
599642
};
600643
src != -1 && retry(|| dup2(src, dst)) != -1
601644
};

0 commit comments

Comments
 (0)