capture_stdio/pipe/
mod.rs

1//! Intercept stdio via `pipe`-then-`dup` method
2//!
3use os_pipe::{PipeReader, PipeWriter};
4use std::{
5    io::Error,
6    os::{unix::io::RawFd, unix::prelude::AsRawFd},
7};
8
9pub mod stderr;
10pub mod stdin;
11pub mod stdout;
12
13pub use stderr::PipedStderr;
14pub use stdin::PipedStdin;
15pub use stdout::PipedStdout;
16
17struct PipedFd {
18    reader: PipeReader,
19    writer: PipeWriter,
20    original: RawFd,
21    target: RawFd,
22    restored: bool,
23}
24
25impl PipedFd {
26    fn capture(target: RawFd, is_stdin: bool) -> Result<Self, Error> {
27        let (reader, writer) = os_pipe::pipe()?;
28        let original = if is_stdin {
29            swap_fd(reader.as_raw_fd(), target)
30        } else {
31            swap_fd(writer.as_raw_fd(), target)
32        };
33
34        Ok(Self {
35            reader,
36            writer,
37            original,
38            target,
39            restored: false,
40        })
41    }
42
43    fn restore(&mut self) {
44        assert!(!self.restored, "You can't restore it twice");
45
46        let fd = swap_fd(self.original, self.target);
47        unsafe {
48            libc::close(fd);
49        }
50
51        self.restored = true;
52    }
53}
54
55impl Drop for PipedFd {
56    fn drop(&mut self) {
57        if !self.restored {
58            self.restore();
59        }
60    }
61}
62
63fn swap_fd(fd: RawFd, target: RawFd) -> RawFd {
64    unsafe {
65        let orig_stdin = libc::dup(target as i32);
66        libc::close(target as i32);
67        libc::dup2(fd as i32, target as i32);
68        orig_stdin as RawFd
69    }
70}