|
| 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::fs::OpenOptions; |
| 34 | + /// use std::process::Command; |
| 35 | + /// use std::os::fd::process::CommandExt; |
| 36 | + /// |
| 37 | + /// eprintln!("chom"); |
| 38 | + /// let file = OpenOptions::new().read(true).write(true).create(true).open("/tmp/fd_doctest.rs").unwrap(); |
| 39 | + /// eprintln!("chom"); |
| 40 | + /// |
| 41 | + /// let mut command = Command::new("ls"); |
| 42 | + /// command.arg("/proc/self/fd").fd(5, file); |
| 43 | + /// eprintln!("chom"); |
| 44 | + /// |
| 45 | + /// let output = command.output().inspect(|o| eprintln!("{o:?}")).unwrap(); |
| 46 | + /// assert_eq!(String::from_utf8_lossy(&output.stdout).split_whitespace().map(|s| s.parse::<usize>().unwrap()).collect::<Vec<_>>(), vec![0,1,2,3,5]); |
| 47 | + /// ``` |
| 48 | + fn fd(&mut self, new_fd: RawFd, old_fd: impl Into<OwnedFd>) -> &mut Self; |
| 49 | +} |
| 50 | + |
| 51 | +#[unstable(feature = "command_pass_fds", issue = "144989")] |
| 52 | +impl CommandExt for Command { |
| 53 | + fn fd(&mut self, new_fd: RawFd, old_fd: impl Into<OwnedFd>) -> &mut Self { |
| 54 | + let old = old_fd.into().as_raw_fd(); |
| 55 | + unsafe { |
| 56 | + self.as_inner_mut().pre_exec(Box::new(move || { |
| 57 | + cvt_r(|| libc::dup2(old, new_fd))?; |
| 58 | + let flags = cvt(libc::fcntl(new_fd, F_GETFD))?; |
| 59 | + cvt(libc::fcntl(new_fd, F_SETFD, flags & !FD_CLOEXEC))?; |
| 60 | + cvt_r(|| libc::close(old))?; |
| 61 | + Ok(()) |
| 62 | + })) |
| 63 | + } |
| 64 | + |
| 65 | + self |
| 66 | + } |
| 67 | +} |
0 commit comments