Skip to content

Commit 6a2ea9c

Browse files
jieyouxucoolreader18Oneirical
committed
run-make-support: add unsafe set_aux_fd helper
Co-authored-by: Noa <[email protected]> Co-authored-by: Oneirical <[email protected]>
1 parent 5cd16b7 commit 6a2ea9c

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

src/tools/run-make-support/src/command.rs

+47
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,53 @@ impl Command {
151151
self
152152
}
153153

154+
/// Set an auxiliary stream passed to the process, besides the stdio streams.
155+
///
156+
/// # Safety
157+
///
158+
/// Use with caution! Ideally, only set one aux fd; if there are multiple, their old `fd` may
159+
/// overlap with another's `new_fd`, and may break. The caller must make sure this is not the
160+
/// case.
161+
#[cfg(unix)]
162+
pub unsafe fn set_aux_fd<F: Into<std::os::fd::OwnedFd>>(
163+
&mut self,
164+
new_fd: std::os::fd::RawFd,
165+
fd: F,
166+
) -> &mut Self {
167+
// NOTE: If more than 1 auxiliary file descriptor is needed, this function should be
168+
// rewritten.
169+
170+
use std::os::fd::AsRawFd;
171+
use std::os::unix::process::CommandExt;
172+
173+
let cvt = |x| if x == -1 { Err(std::io::Error::last_os_error()) } else { Ok(()) };
174+
175+
let fd = fd.into();
176+
if fd.as_raw_fd() == new_fd {
177+
// If the new file descriptor is already the same as fd, just turn off `FD_CLOEXEC`.
178+
179+
// SAFETY(io-safety): `fd` is already owned.
180+
cvt(unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, 0) })
181+
.expect("disabling CLOEXEC failed");
182+
// The `pre_exec` function should be unconditionally set, since it captures `fd`, and
183+
// this ensures that it stays open until the fork.
184+
}
185+
let pre_exec = move || {
186+
if fd.as_raw_fd() != new_fd {
187+
// SAFETY(io-safety): `new_fd` is not necessarily an unused fd. However, we're
188+
// ensuring that `new_fd` will now refer to the same file descriptor as `fd`, which
189+
// is safe as long as we manage the lifecycle of both descriptors correctly. This
190+
// operation will replace the file descriptor referred to by `new_fd` with the one
191+
// from `fd`, allowing for shared access to the same underlying file or resource.
192+
cvt(unsafe { libc::dup2(fd.as_raw_fd(), new_fd) })?;
193+
}
194+
Ok(())
195+
};
196+
// SAFETY(pre-exec-safe): `dup2` is pre-exec-safe.
197+
unsafe { self.cmd.pre_exec(pre_exec) };
198+
self
199+
}
200+
154201
/// Run the constructed command and assert that it is successfully run.
155202
///
156203
/// By default, std{in,out,err} are [`Stdio::piped()`].

src/tools/run-make-support/src/macros.rs

+17
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,23 @@ macro_rules! impl_common_helpers {
104104
self
105105
}
106106

107+
/// Set an auxiliary stream passed to the process, besides the stdio streams.
108+
///
109+
/// # Safety
110+
///
111+
/// Use with caution! Ideally, only set one aux fd; if there are multiple, their old
112+
/// `fd` may overlap with another's `new_fd`, and may break. The caller must make sure
113+
/// this is not the case.
114+
#[cfg(unix)]
115+
pub unsafe fn set_aux_fd<F: Into<std::os::fd::OwnedFd>>(
116+
&mut self,
117+
new_fd: std::os::fd::RawFd,
118+
fd: F,
119+
) -> &mut Self {
120+
self.cmd.set_aux_fd(new_fd, fd);
121+
self
122+
}
123+
107124
/// Run the constructed command and assert that it is successfully run.
108125
#[track_caller]
109126
pub fn run(&mut self) -> crate::command::CompletedProcess {

0 commit comments

Comments
 (0)