diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs index 65ee769fb5c..c502ebbfcb7 100644 --- a/src/lib/win32_os.rs +++ b/src/lib/win32_os.rs @@ -38,6 +38,7 @@ mod libc_constants { fn O_TRUNC() -> int { ret 512; } fn O_TEXT() -> int { ret 16384; } fn O_BINARY() -> int { ret 32768; } + fn O_NOINHERIT() -> int { ret 0x0080; } fn S_IRUSR() -> uint { ret 256u; // really _S_IREAD in win32 @@ -60,9 +61,18 @@ fn target_os() -> str { ret "win32"; } fn dylib_filename(base: str) -> str { ret base + ".dll"; } fn pipe() -> {in: int, out: int} { + // Windows pipes work subtly differently than unix pipes, and their + // inheritance has to be handled in a different way that I don't fully + // understand. Here we explicitly make the pipe non-inheritable, + // which means to pass it to a subprocess they need to be duplicated + // first, as in rust_run_program. let fds = {mutable in: 0, mutable out: 0}; - assert (os::libc::_pipe(ptr::addr_of(fds.in), 1024u, - libc_constants::O_BINARY()) == 0); + let res = os::libc::_pipe(ptr::addr_of(fds.in), 1024u, + libc_constants::O_BINARY() + | libc_constants::O_NOINHERIT()); + assert res == 0; + assert fds.in != -1 && fds.in != 0; + assert fds.out != -1 && fds.in != 0; ret {in: fds.in, out: fds.out}; } diff --git a/src/rt/rust_run_program.cpp b/src/rt/rust_run_program.cpp index c89aaf6bcbd..69f3cf7e146 100644 --- a/src/rt/rust_run_program.cpp +++ b/src/rt/rust_run_program.cpp @@ -12,9 +12,20 @@ rust_run_program(void* task, const char* argv[], ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0); - si.hStdOutput = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1); - si.hStdError = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2); + + HANDLE curproc = GetCurrentProcess(); + HANDLE origStdin = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0); + if (!DuplicateHandle(curproc, origStdin, + curproc, &si.hStdInput, 0, 1, DUPLICATE_SAME_ACCESS)) + return -1; + HANDLE origStdout = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1); + if (!DuplicateHandle(curproc, origStdout, + curproc, &si.hStdOutput, 0, 1, DUPLICATE_SAME_ACCESS)) + return -1; + HANDLE origStderr = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2); + if (!DuplicateHandle(curproc, origStderr, + curproc, &si.hStdError, 0, 1, DUPLICATE_SAME_ACCESS)) + return -1; size_t cmd_len = 0; for (const char** arg = argv; *arg; arg++) { @@ -32,6 +43,10 @@ rust_run_program(void* task, const char* argv[], PROCESS_INFORMATION pi; BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); free(cmd); if (!created) return -1; diff --git a/src/test/stdtest/run.rs b/src/test/stdtest/run.rs index 8ba4c6a80ec..2adfbbe173e 100644 --- a/src/test/stdtest/run.rs +++ b/src/test/stdtest/run.rs @@ -1,5 +1,9 @@ use std; import std::run; +import std::os; +import std::io; +import std::option; +import std::str; // Regression test for memory leaks #[cfg(target_os = "linux")] @@ -15,4 +19,49 @@ fn test_leaks() { #[cfg(target_os = "win32")] #[test] #[ignore] -fn test_leaks() { } \ No newline at end of file +fn test_leaks() { } + +#[test] +fn test_pipes() { + let pipe_in = os::pipe(); + let pipe_out = os::pipe(); + let pipe_err = os::pipe(); + + let pid = run::spawn_process("cat", [], + pipe_in.in, pipe_out.out, pipe_err.out); + os::libc::close(pipe_in.in); + os::libc::close(pipe_out.out); + os::libc::close(pipe_err.out); + + if pid == -1 { fail; } + let expected = "test"; + writeclose(pipe_in.out, expected); + let actual = readclose(pipe_out.in); + readclose(pipe_err.in); + os::waitpid(pid); + + log expected; + log actual; + assert expected == actual; + + fn writeclose(fd: int, s: &str) { + let writer = io::new_writer( + io::fd_buf_writer(fd, option::none)); + writer.write_str(s); + + os::libc::close(fd); + } + + fn readclose(fd: int) -> str { + // Copied from run::program_output + let file = os::fd_FILE(fd); + let reader = io::new_reader(io::FILE_buf_reader(file, option::none)); + let buf = ""; + while !reader.eof() { + let bytes = reader.read_bytes(4096u); + buf += str::unsafe_from_bytes(bytes); + } + os::libc::fclose(file); + ret buf; + } +} \ No newline at end of file