cc/parallel/
stderr.rs

1#![cfg_attr(target_family = "wasm", allow(unused))]
2/// Helpers functions for [ChildStderr].
3use std::{convert::TryInto, process::ChildStderr};
4
5use crate::{Error, ErrorKind};
6
7#[cfg(all(not(unix), not(windows), not(target_family = "wasm")))]
8compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature.");
9
10#[cfg(unix)]
11fn get_flags(fd: std::os::unix::io::RawFd) -> Result<i32, Error> {
12    let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
13    if flags == -1 {
14        Err(Error::new(
15            ErrorKind::IOError,
16            format!(
17                "Failed to get flags for pipe {}: {}",
18                fd,
19                std::io::Error::last_os_error()
20            ),
21        ))
22    } else {
23        Ok(flags)
24    }
25}
26
27#[cfg(unix)]
28fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> {
29    if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 {
30        Err(Error::new(
31            ErrorKind::IOError,
32            format!(
33                "Failed to set flags for pipe {}: {}",
34                fd,
35                std::io::Error::last_os_error()
36            ),
37        ))
38    } else {
39        Ok(())
40    }
41}
42
43#[cfg(unix)]
44pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> {
45    // On Unix, switch the pipe to non-blocking mode.
46    // On Windows, we have a different way to be non-blocking.
47    let fd = pipe.as_raw_fd();
48
49    let flags = get_flags(fd)?;
50    set_flags(fd, flags | libc::O_NONBLOCK)
51}
52
53pub fn bytes_available(stderr: &mut ChildStderr) -> Result<usize, Error> {
54    let mut bytes_available = 0;
55    #[cfg(windows)]
56    {
57        use crate::windows::windows_sys::PeekNamedPipe;
58        use std::os::windows::io::AsRawHandle;
59        use std::ptr::null_mut;
60        if unsafe {
61            PeekNamedPipe(
62                stderr.as_raw_handle(),
63                null_mut(),
64                0,
65                null_mut(),
66                &mut bytes_available,
67                null_mut(),
68            )
69        } == 0
70        {
71            return Err(Error::new(
72                ErrorKind::IOError,
73                format!(
74                    "PeekNamedPipe failed with {}",
75                    std::io::Error::last_os_error()
76                ),
77            ));
78        }
79    }
80    #[cfg(unix)]
81    {
82        use std::os::unix::io::AsRawFd;
83        if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 {
84            return Err(Error::new(
85                ErrorKind::IOError,
86                format!("ioctl failed with {}", std::io::Error::last_os_error()),
87            ));
88        }
89    }
90    Ok(bytes_available.try_into().unwrap())
91}