about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorIvan Petkov <ivanppetkov@gmail.com>2015-12-19 17:12:19 -0800
committerIvan Petkov <ivanppetkov@gmail.com>2015-12-25 07:09:36 -0800
commit7f7a059c4719bbff6c2859802bc50ab2fcaf249f (patch)
tree51a9f891865e0ca0f2ad78dcf73ba34cd24f0d96 /src/libstd
parent439e1843b9f53a2ad9e83861f47f95227737e998 (diff)
downloadrust-7f7a059c4719bbff6c2859802bc50ab2fcaf249f.tar.gz
rust-7f7a059c4719bbff6c2859802bc50ab2fcaf249f.zip
libstd: unix process spawning: fix bug with setting stdio
* If the requested descriptors to inherit are stdio descriptors there
  are situations where they will not be set correctly
* Example: parent's stdout --> child's stderr
           parent's stderr --> child's stdout
* Solution: if the requested descriptors for the child are stdio
  descriptors, `dup` them before overwriting the child's stdio
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/sys/unix/process.rs32
1 files changed, 32 insertions, 0 deletions
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
index 407fcb0a1b8..671cfbd3613 100644
--- a/src/libstd/sys/unix/process.rs
+++ b/src/libstd/sys/unix/process.rs
@@ -288,6 +288,32 @@ impl Process {
             unsafe { libc::_exit(1) }
         }
 
+        // Make sure that the source descriptors are not an stdio descriptor,
+        // otherwise the order which we set the child's descriptors may blow
+        // away a descriptor which we are hoping to save. For example,
+        // suppose we want the child's stderr to be the parent's stdout, and
+        // the child's stdout to be the parent's stderr. No matter which we
+        // dup first, the second will get overwritten prematurely.
+        let maybe_migrate = |src: Stdio, output: &mut AnonPipe| {
+            match src {
+                Stdio::Raw(fd @ libc::STDIN_FILENO) |
+                Stdio::Raw(fd @ libc::STDOUT_FILENO) |
+                Stdio::Raw(fd @ libc::STDERR_FILENO) => {
+                    let fd = match cvt_r(|| libc::dup(fd)) {
+                        Ok(fd) => fd,
+                        Err(_) => fail(output),
+                    };
+                    let fd = FileDesc::new(fd);
+                    fd.set_cloexec();
+                    Stdio::Raw(fd.into_raw())
+                },
+
+                s @ Stdio::None |
+                s @ Stdio::Inherit |
+                s @ Stdio::Raw(_) => s,
+            }
+        };
+
         let setup = |src: Stdio, dst: c_int| {
             match src {
                 Stdio::Inherit => true,
@@ -313,6 +339,12 @@ impl Process {
             }
         };
 
+        // Make sure we migrate all source descriptors before
+        // we start overwriting them
+        let in_fd = maybe_migrate(in_fd, &mut output);
+        let out_fd = maybe_migrate(out_fd, &mut output);
+        let err_fd = maybe_migrate(err_fd, &mut output);
+
         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) }