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
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::PipeReader;
use crate::PipeWriter;
use libc::c_int;
use std::fs::File;
use std::io;
use std::os::unix::prelude::*;

// We need to atomically create pipes and set the CLOEXEC flag on them. This is
// done with the pipe2() API. However, macOS doesn't support pipe2. There, all
// we can do is call pipe() followed by fcntl(), and hope that no other threads
// fork() in between. The following code is copied from the nix crate, where it
// works but is deprecated.
#[cfg(not(any(
    target_os = "aix",
    target_os = "ios",
    target_os = "visionos",
    target_os = "macos",
    target_os = "haiku"
)))]
fn pipe2_cloexec() -> io::Result<(OwnedFd, OwnedFd)> {
    let mut fds: [c_int; 2] = [0; 2];
    let res = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) };
    if res != 0 {
        return Err(io::Error::last_os_error());
    }
    unsafe { Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1]))) }
}

#[cfg(any(
    target_os = "aix",
    target_os = "ios",
    target_os = "visionos",
    target_os = "macos",
    target_os = "haiku"
))]
fn pipe2_cloexec() -> io::Result<(OwnedFd, OwnedFd)> {
    let mut fds: [c_int; 2] = [0; 2];
    let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
    if res != 0 {
        return Err(io::Error::last_os_error());
    }
    // Wrap the fds immediately, so that we'll drop them and close them in the unlikely event that
    // any of the following fcntls fails.
    let owned_fds = unsafe { (OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) };
    let res = unsafe { libc::fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC) };
    if res != 0 {
        return Err(io::Error::last_os_error());
    }
    let res = unsafe { libc::fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC) };
    if res != 0 {
        return Err(io::Error::last_os_error());
    }
    Ok(owned_fds)
}

pub(crate) fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
    let (read_fd, write_fd) = pipe2_cloexec()?;
    Ok((read_fd.into(), write_fd.into()))
}

pub(crate) fn dup(handle: impl AsFd) -> io::Result<OwnedFd> {
    handle.as_fd().try_clone_to_owned()
}

impl IntoRawFd for PipeReader {
    fn into_raw_fd(self) -> RawFd {
        self.0.into_raw_fd()
    }
}

impl AsRawFd for PipeReader {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }
}

impl FromRawFd for PipeReader {
    unsafe fn from_raw_fd(fd: RawFd) -> PipeReader {
        PipeReader(File::from_raw_fd(fd))
    }
}

impl From<PipeReader> for OwnedFd {
    fn from(pr: PipeReader) -> Self {
        pr.0.into()
    }
}

impl AsFd for PipeReader {
    fn as_fd(&self) -> BorrowedFd<'_> {
        self.0.as_fd()
    }
}

impl From<OwnedFd> for PipeReader {
    fn from(fd: OwnedFd) -> Self {
        PipeReader(fd.into())
    }
}

impl IntoRawFd for PipeWriter {
    fn into_raw_fd(self) -> RawFd {
        self.0.into_raw_fd()
    }
}

impl AsRawFd for PipeWriter {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }
}

impl FromRawFd for PipeWriter {
    unsafe fn from_raw_fd(fd: RawFd) -> PipeWriter {
        PipeWriter(File::from_raw_fd(fd))
    }
}

impl From<PipeWriter> for OwnedFd {
    fn from(pw: PipeWriter) -> Self {
        pw.0.into()
    }
}

impl AsFd for PipeWriter {
    fn as_fd(&self) -> BorrowedFd<'_> {
        self.0.as_fd()
    }
}

impl From<OwnedFd> for PipeWriter {
    fn from(fd: OwnedFd) -> Self {
        PipeWriter(fd.into())
    }
}