about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2011-08-03 14:50:51 -0700
committerBrian Anderson <banderson@mozilla.com>2011-08-03 15:09:00 -0700
commit4ae6c835cb69332e982babcfca1a4e5657ec8bbd (patch)
tree3c0a0618f8ff22673529bb412bdf72194d1407f0
parente33de59e47c5076a89eadeb38f4934f58a3618a6 (diff)
downloadrust-4ae6c835cb69332e982babcfca1a4e5657ec8bbd.tar.gz
rust-4ae6c835cb69332e982babcfca1a4e5657ec8bbd.zip
Add some hacks to get stdin piping working more correctly in windows
The way pipes work in windows is not the same as unix, though I'm not
entirely clear on the differences. This patch changes the windows pipe
method to return non-inheritable fds, and the windows rust_run_program
method to duplicate them before spawning the new process.

This allows make-check-pretty to work on windows.
-rw-r--r--src/lib/win32_os.rs14
-rw-r--r--src/rt/rust_run_program.cpp21
-rw-r--r--src/test/stdtest/run.rs51
3 files changed, 80 insertions, 6 deletions
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