Skip to content

Commit 47ad5b9

Browse files
committed
add std::os::fd::CommandExt::fd
1 parent e8a792d commit 47ad5b9

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

library/std/src/os/fd/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ mod raw;
1212
// `OwnedFd`, `AsFd`, etc.
1313
mod owned;
1414

15+
// `CommandExt`, etc.
16+
#[cfg(unix)]
17+
#[unstable(feature = "command_pass_fds", issue = "144989")]
18+
pub mod process;
19+
1520
// Implementations for `AsRawFd` etc. for network types.
1621
#[cfg(not(target_os = "trusty"))]
1722
mod net;

library/std/src/os/fd/process.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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

Comments
 (0)