Skip to main content

reverie_process/
stdio.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9use core::pin::Pin;
10use core::task::Context;
11use core::task::Poll;
12use std::io;
13use std::os::fd::OwnedFd;
14use std::os::unix::io::AsRawFd;
15use std::os::unix::io::FromRawFd;
16use std::os::unix::io::IntoRawFd;
17
18use syscalls::Errno;
19use tokio::io::AsyncRead;
20use tokio::io::AsyncWrite;
21use tokio::io::ReadBuf;
22
23use super::fd::AsyncFd;
24use super::fd::Fd;
25use super::fd::pipe;
26
27/// Describes what to do with a standard I/O stream for a child process when
28/// passed to the [`stdin`], [`stdout`], and [`stderr`] methods of [`Command`].
29///
30/// [`stdin`]: super::Command::stdin
31/// [`stdout`]: super::Command::stdout
32/// [`stderr`]: super::Command::stderr
33/// [`Command`]: super::Command
34#[derive(Debug)]
35pub struct Stdio(InnerStdio);
36
37/// A handle to a child process's standard input (stdin).
38///
39/// This struct is used in the [`stdin`] field on [`Child`].
40///
41/// When an instance of `ChildStdin` is [dropped], the `ChildStdin`'s underlying
42/// file handle will be closed. If the child process was blocked on input prior
43/// to being dropped, it will become unblocked after dropping.
44///
45/// [`stdin`]: super::Child::stdin
46/// [`Child`]: super::Child
47/// [dropped]: Drop
48#[derive(Debug)]
49pub struct ChildStdin(AsyncFd);
50
51/// A handle to a child process's standard output (stdout).
52///
53/// This struct is used in the [`stdout`] field on [`Child`].
54///
55/// When an instance of `ChildStdout` is [dropped], the `ChildStdout`'s
56/// underlying file handle will be closed.
57///
58/// [`stdout`]: super::Child::stdout
59/// [`Child`]: super::Child
60/// [dropped]: Drop
61#[derive(Debug)]
62pub struct ChildStdout(AsyncFd);
63
64/// A handle to a child process's stderr.
65///
66/// This struct is used in the [`stderr`] field on [`Child`].
67///
68/// When an instance of `ChildStderr` is [dropped], the `ChildStderr`'s
69/// underlying file handle will be closed.
70///
71/// [`stderr`]: super::Child::stderr
72/// [`Child`]: super::Child
73/// [dropped]: Drop
74#[derive(Debug)]
75pub struct ChildStderr(AsyncFd);
76
77#[derive(Debug)]
78enum InnerStdio {
79    Inherit,
80    Null,
81    Piped,
82    File(Fd),
83}
84
85impl Default for Stdio {
86    fn default() -> Self {
87        Self(InnerStdio::Inherit)
88    }
89}
90
91impl Stdio {
92    /// A new pipe should be arranged to connect the parent and child processes.
93    pub fn piped() -> Self {
94        Self(InnerStdio::Piped)
95    }
96
97    /// The child inherits from the corresponding parent descriptor. This is the default mode.
98    pub fn inherit() -> Self {
99        Self(InnerStdio::Inherit)
100    }
101
102    /// This stream will be ignored. This is the equivalent of attaching the
103    /// stream to `/dev/null`.
104    pub fn null() -> Self {
105        Self(InnerStdio::Null)
106    }
107
108    /// Returns a pair of file descriptors, one for the parent and one for the
109    /// child. If the child's file descriptor is `None`, then it shall be
110    /// inherited from the parent. If the parent's file descriptor is `None`,
111    /// then there is no link to the child and the child owns the other half of
112    /// the file descriptor (if any). Both file descriptors will be `None` if
113    /// stdio is being inherited.
114    pub(super) fn pipes(&self, readable: bool) -> Result<(Option<Fd>, Option<Fd>), Errno> {
115        match &self.0 {
116            InnerStdio::Inherit => Ok((None, None)),
117            InnerStdio::Null => Ok((None, Some(Fd::null(readable)?))),
118            InnerStdio::Piped => {
119                let (reader, writer) = pipe()?;
120                let (parent, child) = if readable {
121                    (writer, reader)
122                } else {
123                    (reader, writer)
124                };
125                Ok((Some(parent), Some(child)))
126            }
127            InnerStdio::File(file) => Ok((None, Some(file.dup()?))),
128        }
129    }
130}
131
132impl<T: IntoRawFd> From<T> for Stdio {
133    fn from(f: T) -> Self {
134        Self(InnerStdio::File(Fd::new(f.into_raw_fd())))
135    }
136}
137
138impl From<Stdio> for std::process::Stdio {
139    fn from(stdio: Stdio) -> Self {
140        match stdio.0 {
141            InnerStdio::Inherit => Self::inherit(),
142            InnerStdio::Null => Self::null(),
143            InnerStdio::Piped => Self::piped(),
144            InnerStdio::File(fd) => Self::from(std::fs::File::from(fd)),
145        }
146    }
147}
148
149impl ChildStdin {
150    pub(super) fn new(fd: Fd) -> Result<Self, Errno> {
151        AsyncFd::writable(fd).map(Self)
152    }
153}
154
155impl ChildStdout {
156    pub(super) fn new(fd: Fd) -> Result<Self, Errno> {
157        AsyncFd::readable(fd).map(Self)
158    }
159}
160
161impl ChildStderr {
162    pub(super) fn new(fd: Fd) -> Result<Self, Errno> {
163        AsyncFd::readable(fd).map(Self)
164    }
165}
166
167impl AsyncWrite for ChildStdin {
168    fn poll_write(
169        mut self: Pin<&mut Self>,
170        cx: &mut Context<'_>,
171        buf: &[u8],
172    ) -> Poll<tokio::io::Result<usize>> {
173        Pin::new(&mut self.0).poll_write(cx, buf)
174    }
175
176    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
177        Pin::new(&mut self.0).poll_flush(cx)
178    }
179
180    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
181        Pin::new(&mut self.0).poll_shutdown(cx)
182    }
183}
184
185impl AsyncRead for ChildStdout {
186    fn poll_read(
187        mut self: Pin<&mut Self>,
188        cx: &mut Context,
189        buf: &mut ReadBuf,
190    ) -> Poll<tokio::io::Result<()>> {
191        Pin::new(&mut self.0).poll_read(cx, buf)
192    }
193}
194
195impl AsyncRead for ChildStderr {
196    fn poll_read(
197        mut self: Pin<&mut Self>,
198        cx: &mut Context,
199        buf: &mut ReadBuf,
200    ) -> Poll<tokio::io::Result<()>> {
201        Pin::new(&mut self.0).poll_read(cx, buf)
202    }
203}
204
205impl FromRawFd for ChildStdin {
206    unsafe fn from_raw_fd(fd: i32) -> Self {
207        Self::new(Fd::new(fd)).unwrap()
208    }
209}
210
211impl From<OwnedFd> for ChildStdin {
212    fn from(fd: OwnedFd) -> Self {
213        Self::new(fd.into()).unwrap()
214    }
215}
216
217impl FromRawFd for ChildStdout {
218    unsafe fn from_raw_fd(fd: i32) -> Self {
219        Self::new(Fd::new(fd)).unwrap()
220    }
221}
222
223impl From<OwnedFd> for ChildStdout {
224    fn from(fd: OwnedFd) -> Self {
225        Self::new(fd.into()).unwrap()
226    }
227}
228
229impl FromRawFd for ChildStderr {
230    unsafe fn from_raw_fd(fd: i32) -> Self {
231        Self::new(Fd::new(fd)).unwrap()
232    }
233}
234
235impl From<OwnedFd> for ChildStderr {
236    fn from(fd: OwnedFd) -> Self {
237        Self::new(fd.into()).unwrap()
238    }
239}
240
241impl From<tokio::process::ChildStdin> for ChildStdin {
242    fn from(io: tokio::process::ChildStdin) -> Self {
243        let fd = io.as_raw_fd();
244        let fd = unsafe { libc::dup(fd) };
245        drop(io);
246        unsafe { Self::from_raw_fd(fd) }
247    }
248}
249
250impl From<tokio::process::ChildStdout> for ChildStdout {
251    fn from(io: tokio::process::ChildStdout) -> Self {
252        let fd = io.as_raw_fd();
253        let fd = unsafe { libc::dup(fd) };
254        drop(io);
255        unsafe { Self::from_raw_fd(fd) }
256    }
257}
258
259impl From<tokio::process::ChildStderr> for ChildStderr {
260    fn from(io: tokio::process::ChildStderr) -> Self {
261        let fd = io.as_raw_fd();
262        let fd = unsafe { libc::dup(fd) };
263        drop(io);
264        unsafe { Self::from_raw_fd(fd) }
265    }
266}