      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122

    use crate::io::{self, IoSlice, IoSliceMut};
    use crate::mem;
    use crate::sys::fd::FileDesc;
    use crate::sys::{cvt, cvt_r};

    ////////////////////////////////////////////////////////////////////////////////
    // Anonymous pipes
    ////////////////////////////////////////////////////////////////////////////////

    pub struct AnonPipe(FileDesc);

    pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
        let mut fds = [0; 2];

        // The only known way right now to create atomically set the CLOEXEC flag is
        // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9
        // and musl 0.9.3, and some other targets also have it.
        cfg_if::cfg_if! {
            if #[cfg(any(
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "linux",
                target_os = "netbsd",
                target_os = "openbsd",
                target_os = "redox"
            ))] {
                cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
                Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
            } else {
                cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;

                let fd0 = FileDesc::new(fds[0]);
                let fd1 = FileDesc::new(fds[1]);
                fd0.set_cloexec()?;
                fd1.set_cloexec()?;
                Ok((AnonPipe(fd0), AnonPipe(fd1)))
            }
        }
    }

    impl AnonPipe {
        pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
            self.0.read(buf)
        }

        pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
            self.0.read_vectored(bufs)
        }

        #[inline]
        pub fn is_read_vectored(&self) -> bool {
            self.0.is_read_vectored()
        }

        pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
            self.0.write(buf)
        }

        pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
            self.0.write_vectored(bufs)
        }

        #[inline]
        pub fn is_write_vectored(&self) -> bool {
            self.0.is_write_vectored()
        }

        pub fn fd(&self) -> &FileDesc {
            &self.0
        }
        pub fn into_fd(self) -> FileDesc {
            self.0
        }
    }

    pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> {
        // Set both pipes into nonblocking mode as we're gonna be reading from both
        // in the `select` loop below, and we wouldn't want one to block the other!
        let p1 = p1.into_fd();
        let p2 = p2.into_fd();
        p1.set_nonblocking(true)?;
        p2.set_nonblocking(true)?;

        let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
        fds[0].fd = p1.raw();
        fds[0].events = libc::POLLIN;
        fds[1].fd = p2.raw();
        fds[1].events = libc::POLLIN;
        loop {
            // wait for either pipe to become readable using `poll`
            cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;

            if fds[0].revents != 0 && read(&p1, v1)? {
                p2.set_nonblocking(false)?;
                return p2.read_to_end(v2).map(drop);
            }
            if fds[1].revents != 0 && read(&p2, v2)? {
                p1.set_nonblocking(false)?;
                return p1.read_to_end(v1).map(drop);
            }
        }

        // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
        // EAGAIN. If we hit EOF, then this will happen because the underlying
        // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
        // this case we flip the other fd back into blocking mode and read
        // whatever's leftover on that file descriptor.
        fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
            match fd.read_to_end(dst) {
                Ok(_) => Ok(true),
                Err(e) => {
                    if e.raw_os_error() == Some(libc::EWOULDBLOCK)
                        || e.raw_os_error() == Some(libc::EAGAIN)
                    {
                        Ok(false)
                    } else {
                        Err(e)
                    }
                }
            }
        }
    }