2 // ignore-emscripten no processes
3 // ignore-sgx no processes
4 // ignore-fuchsia Child I/O swaps not privileged
6 // Previously libstd would set stdio descriptors of a child process
7 // by `dup`ing the requested descriptors to inherit directly into the
8 // stdio descriptors. This, however, would incorrectly handle cases
9 // where the descriptors to inherit were already stdio descriptors.
10 // This test checks to avoid that regression.
12 #![cfg_attr(unix, feature(rustc_private))]
13 #![cfg_attr(not(unix), allow(unused_imports))]
19 use std::io::{Read, Write};
20 use std::io::{stdout, stderr};
21 use std::process::{Command, Stdio};
24 use std::os::unix::io::FromRawFd;
28 // Bug not present in Windows
33 let mut args = std::env::args();
34 let name = args.next().unwrap();
35 let args: Vec<String> = args.collect();
36 if let Some("--child") = args.get(0).map(|s| &**s) {
38 } else if !args.is_empty() {
39 panic!("unknown options");
42 let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) };
43 let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) };
44 assert!(stdout_backup > -1);
45 assert!(stderr_backup > -1);
47 let (stdout_reader, stdout_writer) = pipe();
48 let (stderr_reader, stderr_writer) = pipe();
49 assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1);
50 assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1);
52 // Make sure we close any duplicates of the writer end of the pipe,
53 // otherwise we can get stuck reading from the pipe which has open
54 // writers but no one supplying any input
55 assert_eq!(unsafe { libc::close(stdout_writer) }, 0);
56 assert_eq!(unsafe { libc::close(stderr_writer) }, 0);
58 stdout().write_all("parent stdout\n".as_bytes()).expect("failed to write to stdout");
59 stderr().write_all("parent stderr\n".as_bytes()).expect("failed to write to stderr");
64 .stdin(Stdio::inherit())
65 .stdout(unsafe { Stdio::from_raw_fd(libc::STDERR_FILENO) })
66 .stderr(unsafe { Stdio::from_raw_fd(libc::STDOUT_FILENO) })
70 // The Stdio passed into the Command took over (and closed) std{out, err}
71 // so we should restore them as they were.
72 assert!(unsafe { libc::dup2(stdout_backup, libc::STDOUT_FILENO) } > -1);
73 assert!(unsafe { libc::dup2(stderr_backup, libc::STDERR_FILENO) } > -1);
75 // Using File as a shim around the descriptor
76 let mut read = String::new();
77 let mut f: File = unsafe { FromRawFd::from_raw_fd(stdout_reader) };
78 f.read_to_string(&mut read).expect("failed to read from stdout file");
79 assert_eq!(read, "parent stdout\nchild stderr\n");
81 // Using File as a shim around the descriptor
83 let mut f: File = unsafe { FromRawFd::from_raw_fd(stderr_reader) };
84 f.read_to_string(&mut read).expect("failed to read from stderr file");
85 assert_eq!(read, "parent stderr\nchild stdout\n");
87 assert!(child.expect("failed to execute child process").wait().unwrap().success());
92 stdout().write_all("child stdout\n".as_bytes()).expect("child failed to write to stdout");
93 stderr().write_all("child stderr\n".as_bytes()).expect("child failed to write to stderr");
97 /// Returns a pipe (reader, writer combo)
98 fn pipe() -> (i32, i32) {
100 assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);