about summary refs log tree commit diff
path: root/src/libnative
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnative')
-rw-r--r--src/libnative/io/process.rs130
1 files changed, 78 insertions, 52 deletions
diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs
index d1711c1b890..73371373eb7 100644
--- a/src/libnative/io/process.rs
+++ b/src/libnative/io/process.rs
@@ -244,7 +244,8 @@ struct SpawnProcessResult {
 }
 
 #[cfg(windows)]
-fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_int)
+fn spawn_process_os(cfg: ProcessConfig,
+                    in_fd: c_int, out_fd: c_int, err_fd: c_int)
                  -> IoResult<SpawnProcessResult> {
     use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
     use libc::consts::os::extra::{
@@ -278,38 +279,51 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
 
         let cur_proc = GetCurrentProcess();
 
-        if in_fd != -1 {
-            let orig_std_in = get_osfhandle(in_fd) as HANDLE;
-            if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
-                fail!("failure in get_osfhandle: {}", os::last_os_error());
-            }
-            if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
-                               0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
-                fail!("failure in DuplicateHandle: {}", os::last_os_error());
-            }
-        }
-
-        if out_fd != -1 {
-            let orig_std_out = get_osfhandle(out_fd) as HANDLE;
-            if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
-                fail!("failure in get_osfhandle: {}", os::last_os_error());
-            }
-            if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
-                               0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
-                fail!("failure in DuplicateHandle: {}", os::last_os_error());
+        // Similarly to unix, we don't actually leave holes for the stdio file
+        // descriptors, but rather open up /dev/null equivalents. These
+        // equivalents are drawn from libuv's windows process spawning.
+        let set_fd = |fd: c_int, slot: &mut HANDLE, is_stdin: bool| {
+            if fd == -1 {
+                let access = if is_stdin {
+                    libc::FILE_GENERIC_READ
+                } else {
+                    libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
+                };
+                let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
+                let mut sa = libc::SECURITY_ATTRIBUTES {
+                    nLength: size as libc::DWORD,
+                    lpSecurityDescriptor: ptr::mut_null(),
+                    bInheritHandle: 1,
+                };
+                *slot = os::win32::as_utf16_p("NUL", |filename| {
+                    libc::CreateFileW(filename,
+                                      access,
+                                      libc::FILE_SHARE_READ |
+                                          libc::FILE_SHARE_WRITE,
+                                      &mut sa,
+                                      libc::OPEN_EXISTING,
+                                      0,
+                                      ptr::mut_null())
+                });
+                if *slot == INVALID_HANDLE_VALUE as libc::HANDLE {
+                    return Err(super::last_error())
+                }
+            } else {
+                let orig = get_osfhandle(fd) as HANDLE;
+                if orig == INVALID_HANDLE_VALUE as HANDLE {
+                    return Err(super::last_error())
+                }
+                if DuplicateHandle(cur_proc, orig, cur_proc, slot,
+                                   0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+                    return Err(super::last_error())
+                }
             }
-        }
+            Ok(())
+        };
 
-        if err_fd != -1 {
-            let orig_std_err = get_osfhandle(err_fd) as HANDLE;
-            if orig_std_err == INVALID_HANDLE_VALUE as HANDLE {
-                fail!("failure in get_osfhandle: {}", os::last_os_error());
-            }
-            if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
-                               0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
-                fail!("failure in DuplicateHandle: {}", os::last_os_error());
-            }
-        }
+        try!(set_fd(in_fd, &mut si.hStdInput, true));
+        try!(set_fd(out_fd, &mut si.hStdOutput, false));
+        try!(set_fd(err_fd, &mut si.hStdError, false));
 
         let cmd_str = make_command_line(cfg.program, cfg.args);
         let mut pi = zeroed_process_information();
@@ -338,9 +352,9 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
             })
         });
 
-        if in_fd != -1 { assert!(CloseHandle(si.hStdInput) != 0); }
-        if out_fd != -1 { assert!(CloseHandle(si.hStdOutput) != 0); }
-        if err_fd != -1 { assert!(CloseHandle(si.hStdError) != 0); }
+        assert!(CloseHandle(si.hStdInput) != 0);
+        assert!(CloseHandle(si.hStdOutput) != 0);
+        assert!(CloseHandle(si.hStdError) != 0);
 
         match create_err {
             Some(err) => return Err(err),
@@ -379,9 +393,9 @@ fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
         wShowWindow: 0,
         cbReserved2: 0,
         lpReserved2: ptr::mut_null(),
-        hStdInput: ptr::mut_null(),
-        hStdOutput: ptr::mut_null(),
-        hStdError: ptr::mut_null()
+        hStdInput: libc::INVALID_HANDLE_VALUE as libc::HANDLE,
+        hStdOutput: libc::INVALID_HANDLE_VALUE as libc::HANDLE,
+        hStdError: libc::INVALID_HANDLE_VALUE as libc::HANDLE,
     }
 }
 
@@ -489,6 +503,10 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
             let mut input = file::FileDesc::new(pipe.input, true);
             let mut output = file::FileDesc::new(pipe.out, true);
 
+            // We may use this in the child, so perform allocations before the
+            // fork
+            let devnull = "/dev/null".to_c_str();
+
             set_cloexec(output.fd());
 
             let pid = fork();
@@ -563,21 +581,29 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
 
             rustrt::rust_unset_sigprocmask();
 
-            if in_fd == -1 {
-                let _ = libc::close(libc::STDIN_FILENO);
-            } else if retry(|| dup2(in_fd, 0)) == -1 {
-                fail(&mut output);
-            }
-            if out_fd == -1 {
-                let _ = libc::close(libc::STDOUT_FILENO);
-            } else if retry(|| dup2(out_fd, 1)) == -1 {
-                fail(&mut output);
-            }
-            if err_fd == -1 {
-                let _ = libc::close(libc::STDERR_FILENO);
-            } else if retry(|| dup2(err_fd, 2)) == -1 {
-                fail(&mut output);
-            }
+            // If a stdio file descriptor is set to be ignored (via a -1 file
+            // descriptor), then we don't actually close it, but rather open
+            // up /dev/null into that file descriptor. Otherwise, the first file
+            // descriptor opened up in the child would be numbered as one of the
+            // stdio file descriptors, which is likely to wreak havoc.
+            let setup = |src: c_int, dst: c_int| {
+                let src = if src == -1 {
+                    let flags = if dst == libc::STDIN_FILENO {
+                        libc::O_RDONLY
+                    } else {
+                        libc::O_RDWR
+                    };
+                    devnull.with_ref(|p| libc::open(p, flags, 0))
+                } else {
+                    src
+                };
+                src != -1 && retry(|| dup2(src, dst)) != -1
+            };
+
+            if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
+            if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
+            if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
+
             // close all other fds
             for fd in range(3, getdtablesize()).rev() {
                 if fd != output.fd() {