From 3783b923d19011a74d877a0b37e73ef49387177c Mon Sep 17 00:00:00 2001 From: Mackenzie Clark Date: Thu, 20 Dec 2018 21:50:24 -0800 Subject: [PATCH] implement a cross-platform stdout pipe --- src/common/file_descriptor.rs | 28 ++++++++++++++++++++++++++ src/common/mod.rs | 1 + src/common/stdio.rs | 38 +++++++++++++++++------------------ 3 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 src/common/file_descriptor.rs diff --git a/src/common/file_descriptor.rs b/src/common/file_descriptor.rs new file mode 100644 index 000000000..33611499d --- /dev/null +++ b/src/common/file_descriptor.rs @@ -0,0 +1,28 @@ +use std::io::Read; +use std::io; +use std::io::Error; +use std::io::ErrorKind; +use std::ffi::CString; + +pub struct FileDescriptor(libc::c_int); + +impl FileDescriptor { + pub fn new(file_descriptor_number: libc::c_int) -> FileDescriptor { + FileDescriptor(file_descriptor_number) + } +} + +impl Read for FileDescriptor { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let file_descriptor: libc::c_int = self.0; + let count = unsafe { + libc::read(file_descriptor, buf.as_mut_ptr() as *mut libc::c_void, 1) + }; + if count < 0 { + Err(Error::new(ErrorKind::Other, "read error")) + } + else { + Ok(count as usize) + } + } +} \ No newline at end of file diff --git a/src/common/mod.rs b/src/common/mod.rs index 8694cd5f7..7109e6287 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -3,3 +3,4 @@ pub mod slice; #[cfg(test)] pub mod stdio; +mod file_descriptor; \ No newline at end of file diff --git a/src/common/stdio.rs b/src/common/stdio.rs index 26d72c431..01f9e80b8 100644 --- a/src/common/stdio.rs +++ b/src/common/stdio.rs @@ -1,7 +1,8 @@ use libc; -use std::fs::File; +use crate::file_descriptor::FileDescriptor; +use std::io::BufReader; use std::io::Read; -use std::os::unix::io::FromRawFd; +use crate::common::file_descriptor::FileDescriptor; // A struct to hold the references to the base stdout and the captured one pub struct StdioCapturer { @@ -17,7 +18,12 @@ pub struct StdioCapturer { impl StdioCapturer { fn pipe() -> (libc::c_int, libc::c_int) { let mut fds = [0; 2]; + + #[cfg(not(target_os = "windows"))] assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0); + #[cfg(target_os = "windows")] + assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr(), 1000, libc::O_TEXT) }, 0); + (fds[0], fds[1]) } @@ -28,9 +34,6 @@ impl StdioCapturer { let (stdout_reader, stdout_writer) = Self::pipe(); let (stderr_reader, stderr_writer) = Self::pipe(); - // std::io::stdout().flush().unwrap(); - // std::io::stderr().flush().unwrap(); - assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1); assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1); @@ -48,28 +51,23 @@ impl StdioCapturer { } } - pub fn end(self) -> (String, String) { + pub fn end(self) -> Result<(String, String), std::io::Error> { // The Stdio passed into the Command took over (and closed) std{out, err} // so we should restore them as they were. assert!(unsafe { libc::dup2(self.stdout_backup, libc::STDOUT_FILENO) } > -1); assert!(unsafe { libc::dup2(self.stderr_backup, libc::STDERR_FILENO) } > -1); - // assert_eq!(unsafe { libc::close(self.stdout_backup) }, 0); - // assert_eq!(unsafe { libc::close(self.stderr_backup) }, 0); + let fd = FileDescriptor::new(self.stdout_reader); + let mut reader = BufReader::new(fd); + let mut stdout_read = "".to_string(); + let _ = reader.read_to_string(&mut stdout_read)?; - let mut stdout_read = String::new(); - let mut stdout_file: File = unsafe { FromRawFd::from_raw_fd(self.stdout_reader) }; - stdout_file - .read_to_string(&mut stdout_read) - .expect("failed to read from stdout file"); + let fd = FileDescriptor::new(self.stderr_reader); + let mut reader = BufReader::new(fd); + let mut stderr_read = "".to_string(); + let _ = reader.read_to_string(&mut stderr_read)?; - let mut stderr_read = String::new(); - let mut stderr_file: File = unsafe { FromRawFd::from_raw_fd(self.stderr_reader) }; - stderr_file - .read_to_string(&mut stderr_read) - .expect("failed to read from stdout file"); - - (stdout_read, stderr_read) + Ok((stdout_read, stderr_read)) } }