|
| 1 | +use libc::{F_GETFD, F_SETFD, FD_CLOEXEC}; |
| 2 | + |
| 3 | +use crate::os::fd::raw::AsRawFd; |
| 4 | +use crate::os::fd::{OwnedFd, RawFd}; |
| 5 | +use crate::process::Command; |
| 6 | +use crate::sealed::Sealed; |
| 7 | +use crate::sys::{cvt, cvt_r}; |
| 8 | +use crate::sys_common::AsInnerMut; |
| 9 | + |
| 10 | +/// Extensions to the [`crate::process::Command`] builder for Unix and WASI, platforms that support file |
| 11 | +/// descriptors. |
| 12 | +/// |
| 13 | +/// This trait is sealed: it cannot be implemented outside the standard library. |
| 14 | +/// This is so that future additional methods are not breaking changes. |
| 15 | +#[unstable(feature = "command_pass_fds", issue = "144989")] |
| 16 | +pub trait CommandExt: Sealed { |
| 17 | + /// Pass a file descriptor to a child process. |
| 18 | + /// |
| 19 | + /// Getting this right is tricky. It is recommended to provide further information to the child |
| 20 | + /// process by some other mechanism. This could be an argument confirming file descriptors that |
| 21 | + /// the child can use, device/inode numbers to allow for sanity checks, or something similar. |
| 22 | + /// |
| 23 | + /// If `new_fd` is an open file descriptor and closing it would produce one or more errors, |
| 24 | + /// those errors will be lost when this function is called. See |
| 25 | + /// [`man 2 dup`](https://www.man7.org/linux/man-pages/man2/dup.2.html#NOTES) for more information. |
| 26 | + /// |
| 27 | + /// If this method is called multiple times with the same `new_fd`, all but one file descriptor |
| 28 | + /// will be lost. |
| 29 | + /// |
| 30 | + /// ``` |
| 31 | + /// #![feature(command_pass_fds)] |
| 32 | + /// |
| 33 | + /// use std::process::{Command, Stdio}; |
| 34 | + /// use std::os::fd::process::CommandExt; |
| 35 | + /// use std::io::{self, Write}; |
| 36 | + /// |
| 37 | + /// fn main() -> io::Result<()> { |
| 38 | + /// let (pipe_reader, mut pipe_writer) = io::pipe()?; |
| 39 | + /// |
| 40 | + /// let fd_num = 123; |
| 41 | + /// |
| 42 | + /// let mut cmd = Command::new("cat"); |
| 43 | + /// cmd.arg(format!("/dev/fd/{fd_num}")).stdout(Stdio::piped()).fd(fd_num, pipe_reader); |
| 44 | + /// |
| 45 | + /// let mut child = cmd.spawn()?; |
| 46 | + /// let mut stdout = child.stdout.take().unwrap(); |
| 47 | + /// |
| 48 | + /// pipe_writer.write_all(b"Hello, world!")?; |
| 49 | + /// drop(pipe_writer); |
| 50 | + /// |
| 51 | + /// child.wait()?; |
| 52 | + /// assert_eq!(io::read_to_string(&mut stdout)?, "Hello, world!"); |
| 53 | + /// |
| 54 | + /// Ok(()) |
| 55 | + /// } |
| 56 | + /// ``` |
| 57 | + /// |
| 58 | + /// ``` |
| 59 | + /// #![feature(command_pass_fds)] |
| 60 | + /// |
| 61 | + /// use std::process::{Command, Stdio}; |
| 62 | + /// use std::os::fd::process::CommandExt; |
| 63 | + /// use std::io::{self, Write}; |
| 64 | + /// |
| 65 | + /// fn main() -> io::Result<()> { |
| 66 | + /// let (pipe_reader1, mut pipe_writer1) = io::pipe()?; |
| 67 | + /// let (pipe_reader2, mut pipe_writer2) = io::pipe()?; |
| 68 | + /// |
| 69 | + /// let fd_num = 123; |
| 70 | + /// |
| 71 | + /// let mut cmd = Command::new("cat"); |
| 72 | + /// cmd.arg(format!("/dev/fd/{fd_num}")) |
| 73 | + /// .stdout(Stdio::piped()) |
| 74 | + /// .fd(fd_num, pipe_reader1) |
| 75 | + /// .fd(fd_num, pipe_reader2); |
| 76 | + /// |
| 77 | + /// let mut child = cmd.spawn()?; |
| 78 | + /// let mut stdout = child.stdout.take().unwrap(); |
| 79 | + /// |
| 80 | + /// pipe_writer1.write_all(b"Hello from pipe 1!")?; |
| 81 | + /// drop(pipe_writer1); |
| 82 | + /// |
| 83 | + /// pipe_writer2.write_all(b"Hello from pipe 2!")?; |
| 84 | + /// drop(pipe_writer2); |
| 85 | + /// |
| 86 | + /// child.wait()?; |
| 87 | + /// assert_eq!(io::read_to_string(&mut stdout)?, "Hello from pipe 2!"); |
| 88 | + /// |
| 89 | + /// Ok(()) |
| 90 | + /// } |
| 91 | + /// ``` |
| 92 | + fn fd(&mut self, new_fd: RawFd, old_fd: impl Into<OwnedFd>) -> &mut Self; |
| 93 | +} |
| 94 | + |
| 95 | +#[unstable(feature = "command_pass_fds", issue = "144989")] |
| 96 | +impl CommandExt for Command { |
| 97 | + fn fd(&mut self, new_fd: RawFd, old_fd: impl Into<OwnedFd>) -> &mut Self { |
| 98 | + let old = old_fd.into(); |
| 99 | + unsafe { |
| 100 | + self.as_inner_mut().pre_exec(Box::new(move || { |
| 101 | + cvt_r(|| libc::dup2(old.as_raw_fd(), new_fd))?; |
| 102 | + let flags = cvt(libc::fcntl(new_fd, F_GETFD))?; |
| 103 | + cvt(libc::fcntl(new_fd, F_SETFD, flags & !FD_CLOEXEC))?; |
| 104 | + cvt_r(|| libc::close(old.as_raw_fd()))?; |
| 105 | + Ok(()) |
| 106 | + })) |
| 107 | + } |
| 108 | + |
| 109 | + self |
| 110 | + } |
| 111 | +} |
0 commit comments