conch_runtime_pshaw/sys/unix/
io.rs

1//! Defines interfaces and methods for doing IO operations on UNIX file descriptors.
2
3use crate::io::FileDesc;
4use crate::sys::cvt_r;
5use crate::IntoInner;
6use libc::{self, c_void, size_t};
7use std::fs::File;
8use std::io::{Result, SeekFrom};
9use std::mem;
10use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
11use std::process::Stdio;
12
13/// A wrapper around an owned UNIX file descriptor. The wrapper
14/// allows reading from or write to the descriptor, and will
15/// close it once it goes out of scope.
16#[derive(Debug, PartialEq, Eq)]
17pub struct RawIo {
18    /// The underlying descriptor.
19    fd: RawFd,
20}
21
22impl Into<Stdio> for RawIo {
23    fn into(self) -> Stdio {
24        unsafe { FromRawFd::from_raw_fd(self.into_inner()) }
25    }
26}
27
28impl FromRawFd for FileDesc {
29    unsafe fn from_raw_fd(fd: RawFd) -> Self {
30        Self::new(fd)
31    }
32}
33
34impl AsRawFd for FileDesc {
35    fn as_raw_fd(&self) -> RawFd {
36        self.inner().inner()
37    }
38}
39
40impl IntoRawFd for FileDesc {
41    fn into_raw_fd(self) -> RawFd {
42        unsafe { self.into_inner().into_inner() }
43    }
44}
45
46impl From<File> for FileDesc {
47    fn from(file: File) -> Self {
48        unsafe { FromRawFd::from_raw_fd(file.into_raw_fd()) }
49    }
50}
51
52impl mio::Evented for FileDesc {
53    fn register(
54        &self,
55        poll: &mio::Poll,
56        token: mio::Token,
57        interest: mio::Ready,
58        opts: mio::PollOpt,
59    ) -> Result<()> {
60        mio::unix::EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
61    }
62
63    fn reregister(
64        &self,
65        poll: &mio::Poll,
66        token: mio::Token,
67        interest: mio::Ready,
68        opts: mio::PollOpt,
69    ) -> Result<()> {
70        mio::unix::EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
71    }
72
73    fn deregister(&self, poll: &mio::Poll) -> Result<()> {
74        mio::unix::EventedFd(&self.as_raw_fd()).deregister(poll)
75    }
76}
77
78impl RawIo {
79    /// Takes ownership of and wraps an OS file descriptor.
80    pub unsafe fn new(fd: RawFd) -> Self {
81        RawIo { fd }
82    }
83
84    /// Unwraps the underlying file descriptor and transfers ownership to the caller.
85    pub unsafe fn into_inner(self) -> RawFd {
86        // Make sure our desctructor doesn't actually close
87        // the fd we just transfered to the caller.
88        let fd = self.fd;
89        mem::forget(self);
90        fd
91    }
92
93    /// Returns the underlying file descriptor without transfering ownership.
94    pub fn inner(&self) -> RawFd {
95        self.fd
96    }
97
98    /// Duplicates the underlying file descriptor via `libc::dup`.
99    pub fn duplicate(&self) -> Result<Self> {
100        unsafe { Ok(RawIo::new(cvt_r(|| libc::dup(self.fd))?)) }
101    }
102
103    /// Reads from the underlying file descriptor.
104    // Taken from rust: libstd/sys/unix/fd.rs
105    pub fn read_inner(&self, buf: &mut [u8]) -> Result<usize> {
106        let ret = cvt_r(|| unsafe {
107            libc::read(
108                self.fd,
109                buf.as_mut_ptr() as *mut c_void,
110                buf.len() as size_t,
111            )
112        })?;
113        Ok(ret as usize)
114    }
115
116    /// Writes to the underlying file descriptor.
117    // Taken from rust: libstd/sys/unix/fd.rs
118    pub fn write_inner(&self, buf: &[u8]) -> Result<usize> {
119        let ret = cvt_r(|| unsafe {
120            libc::write(self.fd, buf.as_ptr() as *const c_void, buf.len() as size_t)
121        })?;
122        Ok(ret as usize)
123    }
124
125    pub fn flush_inner(&self) -> Result<()> {
126        Ok(())
127    }
128
129    /// Seeks the underlying file descriptor.
130    // Adapted from rust: libstd/sys/unix/fs.rs
131    pub fn seek(&self, pos: SeekFrom) -> Result<u64> {
132        let (whence, pos) = match pos {
133            SeekFrom::Start(off) => (libc::SEEK_SET, off as libc::off_t),
134            SeekFrom::End(off) => (libc::SEEK_END, off as libc::off_t),
135            SeekFrom::Current(off) => (libc::SEEK_CUR, off as libc::off_t),
136        };
137        let n = cvt_r(|| unsafe { libc::lseek(self.fd, pos, whence) })?;
138        Ok(n as u64)
139    }
140
141    // NB: Linux platforms which support opening a file with O_CLOEXEC won't
142    // use this function, so we can suppress the dead_code lint
143    #[cfg_attr(
144        any(target_os = "linux", target_os = "android", target_os = "emscripten"),
145        allow(dead_code)
146    )]
147    /// Sets the `CLOEXEC` flag on the descriptor to the desired state
148    pub fn set_cloexec(&self, set: bool) -> Result<()> {
149        unsafe {
150            let flags = cvt_r(|| libc::fcntl(self.fd, libc::F_GETFD))?;
151            let new_flags = if set {
152                flags | libc::FD_CLOEXEC
153            } else {
154                flags & !libc::FD_CLOEXEC
155            };
156            cvt_r(|| libc::fcntl(self.fd, libc::F_SETFD, new_flags)).map(|_| ())
157        }
158    }
159
160    /// Sets the `O_NONBLOCK` flag on the descriptor to the desired state.
161    ///
162    /// Requires a mutable handle so that blocking state is not unexpectedly
163    /// changed by someone else while sharing immutably.
164    pub fn set_nonblock(&mut self, set: bool) -> Result<()> {
165        unsafe {
166            let flags = cvt_r(|| libc::fcntl(self.fd, libc::F_GETFL))?;
167            let new_flags = if set {
168                flags | libc::O_NONBLOCK
169            } else {
170                flags & !libc::O_NONBLOCK
171            };
172            cvt_r(|| libc::fcntl(self.fd, libc::F_SETFL, new_flags)).map(|_| ())
173        }
174    }
175}
176
177impl Drop for RawIo {
178    // Adapted from rust: libstd/sys/unix/fd.rs
179    fn drop(&mut self) {
180        // Note that errors are ignored when closing a file descriptor. The
181        // reason for this is that if an error occurs we don't actually know if
182        // the file descriptor was closed or not, and if we retried (for
183        // something like EINTR), we might close another valid file descriptor
184        // (opened after we closed ours).
185        let _ = unsafe { libc::close(self.fd) };
186    }
187}
188
189/// Duplicates a file descriptor and sets its CLOEXEC flag.
190unsafe fn dup_fd_cloexec(fd: RawFd) -> Result<RawIo> {
191    let min_fd = libc::STDERR_FILENO + 1;
192    Ok(RawIo::new(cvt_r(|| {
193        libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, min_fd)
194    })?))
195}
196
197/// Creates and returns a `(reader, writer)` pipe pair.
198///
199/// The CLOEXEC flag will be set on both file descriptors on creation.
200#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
201pub fn pipe() -> Result<(RawIo, RawIo)> {
202    unsafe {
203        let mut fds = [0; 2];
204        cvt_r(|| libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?;
205
206        let reader = RawIo::new(fds[0]);
207        let writer = RawIo::new(fds[1]);
208
209        Ok((reader, writer))
210    }
211}
212
213/// Creates and returns a `(reader, writer)` pipe pair.
214///
215/// The CLOEXEC flag will be set on both file descriptors, however,
216/// on some UNIX systems (like BSD), setting these flags is nonatomic.
217#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))]
218pub fn pipe() -> Result<(RawIo, RawIo)> {
219    unsafe {
220        let mut fds = [0; 2];
221        cvt_r(|| libc::pipe(fds.as_mut_ptr()))?;
222        let reader = RawIo::new(fds[0]);
223        let writer = RawIo::new(fds[1]);
224
225        reader.set_cloexec(true)?;
226        writer.set_cloexec(true)?;
227
228        Ok((reader, writer))
229    }
230}
231
232/// Duplicates file descriptors for (stdin, stdout, stderr) and returns them in that order.
233pub fn dup_stdio() -> Result<(RawIo, RawIo, RawIo)> {
234    unsafe {
235        Ok((
236            dup_fd_cloexec(libc::STDIN_FILENO)?,
237            dup_fd_cloexec(libc::STDOUT_FILENO)?,
238            dup_fd_cloexec(libc::STDERR_FILENO)?,
239        ))
240    }
241}
242
243/// Returns the process ID of the calling process
244pub fn getpid() -> libc::pid_t {
245    unsafe { libc::getpid() }
246}