]> git.lizzy.rs Git - rust.git/blob - src/test/run-pass/issue-30490.rs
Auto merge of #42480 - eddyb:issue-42463, r=nikomatsakis
[rust.git] / src / test / run-pass / issue-30490.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // ignore-emscripten
12
13 // Previously libstd would set stdio descriptors of a child process
14 // by `dup`ing the requested descriptors to inherit directly into the
15 // stdio descriptors. This, however, would incorrectly handle cases
16 // where the descriptors to inherit were already stdio descriptors.
17 // This test checks to avoid that regression.
18
19 #![cfg_attr(unix, feature(libc))]
20 #![cfg_attr(windows, allow(unused_imports))]
21
22 #[cfg(unix)]
23 extern crate libc;
24
25 use std::fs::File;
26 use std::io::{Read, Write};
27 use std::io::{stdout, stderr};
28 use std::process::{Command, Stdio};
29
30 #[cfg(unix)]
31 use std::os::unix::io::FromRawFd;
32
33 #[cfg(not(unix))]
34 fn main() {
35     // Bug not present in Windows
36 }
37
38 #[cfg(unix)]
39 fn main() {
40     let mut args = std::env::args();
41     let name = args.next().unwrap();
42     let args: Vec<String> = args.collect();
43     if let Some("--child") = args.get(0).map(|s| &**s) {
44         return child();
45     } else if !args.is_empty() {
46         panic!("unknown options");
47     }
48
49     let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) };
50     let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) };
51     assert!(stdout_backup > -1);
52     assert!(stderr_backup > -1);
53
54     let (stdout_reader, stdout_writer) = pipe();
55     let (stderr_reader, stderr_writer) = pipe();
56     assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1);
57     assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1);
58
59     // Make sure we close any duplicates of the writer end of the pipe,
60     // otherwise we can get stuck reading from the pipe which has open
61     // writers but no one supplying any input
62     assert_eq!(unsafe { libc::close(stdout_writer) }, 0);
63     assert_eq!(unsafe { libc::close(stderr_writer) }, 0);
64
65     stdout().write_all("parent stdout\n".as_bytes()).expect("failed to write to stdout");
66     stderr().write_all("parent stderr\n".as_bytes()).expect("failed to write to stderr");
67
68     let child = {
69         Command::new(name)
70             .arg("--child")
71             .stdin(Stdio::inherit())
72             .stdout(unsafe { Stdio::from_raw_fd(libc::STDERR_FILENO) })
73             .stderr(unsafe { Stdio::from_raw_fd(libc::STDOUT_FILENO) })
74             .spawn()
75     };
76
77     // The Stdio passed into the Command took over (and closed) std{out, err}
78     // so we should restore them as they were.
79     assert!(unsafe { libc::dup2(stdout_backup, libc::STDOUT_FILENO) } > -1);
80     assert!(unsafe { libc::dup2(stderr_backup, libc::STDERR_FILENO) } > -1);
81
82     // Using File as a shim around the descriptor
83     let mut read = String::new();
84     let mut f: File = unsafe { FromRawFd::from_raw_fd(stdout_reader) };
85     f.read_to_string(&mut read).expect("failed to read from stdout file");
86     assert_eq!(read, "parent stdout\nchild stderr\n");
87
88     // Using File as a shim around the descriptor
89     read.clear();
90     let mut f: File = unsafe { FromRawFd::from_raw_fd(stderr_reader) };
91     f.read_to_string(&mut read).expect("failed to read from stderr file");
92     assert_eq!(read, "parent stderr\nchild stdout\n");
93
94     assert!(child.expect("failed to execute child process").wait().unwrap().success());
95 }
96
97 #[cfg(unix)]
98 fn child() {
99     stdout().write_all("child stdout\n".as_bytes()).expect("child failed to write to stdout");
100     stderr().write_all("child stderr\n".as_bytes()).expect("child failed to write to stderr");
101 }
102
103 #[cfg(unix)]
104 /// Returns a pipe (reader, writer combo)
105 fn pipe() -> (i32, i32) {
106      let mut fds = [0; 2];
107      assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);
108      (fds[0], fds[1])
109 }