/// Helpers functions for [ChildStderr]. use std::{convert::TryInto, process::ChildStderr}; use crate::{Error, ErrorKind}; #[cfg(all(not(unix), not(windows)))] compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature."); #[cfg(unix)] fn get_flags(fd: std::os::unix::io::RawFd) -> Result { let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) }; if flags == -1 { Err(Error::new( ErrorKind::IOError, format!( "Failed to get flags for pipe {}: {}", fd, std::io::Error::last_os_error() ), )) } else { Ok(flags) } } #[cfg(unix)] fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> { if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 { Err(Error::new( ErrorKind::IOError, format!( "Failed to set flags for pipe {}: {}", fd, std::io::Error::last_os_error() ), )) } else { Ok(()) } } #[cfg(unix)] pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> { // On Unix, switch the pipe to non-blocking mode. // On Windows, we have a different way to be non-blocking. let fd = pipe.as_raw_fd(); let flags = get_flags(fd)?; set_flags(fd, flags | libc::O_NONBLOCK) } pub fn bytes_available(stderr: &mut ChildStderr) -> Result { let mut bytes_available = 0; #[cfg(windows)] { use crate::windows::windows_sys::PeekNamedPipe; use std::os::windows::io::AsRawHandle; use std::ptr::null_mut; if unsafe { PeekNamedPipe( stderr.as_raw_handle(), null_mut(), 0, null_mut(), &mut bytes_available, null_mut(), ) } == 0 { return Err(Error::new( ErrorKind::IOError, format!( "PeekNamedPipe failed with {}", std::io::Error::last_os_error() ), )); } } #[cfg(unix)] { use std::os::unix::io::AsRawFd; if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 { return Err(Error::new( ErrorKind::IOError, format!("ioctl failed with {}", std::io::Error::last_os_error()), )); } } Ok(bytes_available.try_into().unwrap()) }