1use cap_std::fs::Dir;
9use cap_std::io_lifetimes;
10use cap_tempfile::cap_std;
11use io_lifetimes::OwnedFd;
12use rustix::fd::{AsFd, FromRawFd, IntoRawFd};
13use rustix::io::FdFlags;
14use std::os::fd::AsRawFd;
15use std::os::unix::process::CommandExt;
16use std::sync::Arc;
17
18pub trait CapStdExtCommandExt {
22 fn take_fd_n(&mut self, fd: Arc<OwnedFd>, target: i32) -> &mut Self;
24
25 fn cwd_dir(&mut self, dir: Dir) -> &mut Self;
27
28 #[cfg(any(target_os = "linux", target_os = "android"))]
39 fn lifecycle_bind_to_parent_thread(&mut self) -> &mut Self;
40}
41
42#[allow(unsafe_code)]
43impl CapStdExtCommandExt for std::process::Command {
44 fn take_fd_n(&mut self, fd: Arc<OwnedFd>, target: i32) -> &mut Self {
45 unsafe {
46 self.pre_exec(move || {
47 let mut target = OwnedFd::from_raw_fd(target);
48 if target.as_raw_fd() == fd.as_raw_fd() {
51 let fl = rustix::io::fcntl_getfd(&target)?;
52 rustix::io::fcntl_setfd(&mut target, fl.difference(FdFlags::CLOEXEC))?;
53 } else {
54 rustix::io::dup2(&*fd, &mut target)?;
56 }
57 let _ = target.into_raw_fd();
59 Ok(())
60 });
61 }
62 self
63 }
64
65 fn cwd_dir(&mut self, dir: Dir) -> &mut Self {
66 unsafe {
67 self.pre_exec(move || {
68 rustix::process::fchdir(dir.as_fd())?;
69 Ok(())
70 });
71 }
72 self
73 }
74
75 #[cfg(any(target_os = "linux", target_os = "android"))]
76 fn lifecycle_bind_to_parent_thread(&mut self) -> &mut Self {
77 unsafe {
79 self.pre_exec(|| {
80 rustix::process::set_parent_process_death_signal(Some(
81 rustix::process::Signal::TERM,
82 ))
83 .map_err(Into::into)
84 });
85 }
86 self
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use std::sync::Arc;
94
95 #[test]
96 fn test_take_fdn() -> anyhow::Result<()> {
97 for i in 0..=1 {
99 let tempd = cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
100 let tempd_fd = Arc::new(tempd.as_fd().try_clone_to_owned()?);
101 let n = tempd_fd.as_raw_fd() + i;
102 let st = std::process::Command::new("ls")
103 .arg(format!("/proc/self/fd/{n}"))
104 .take_fd_n(tempd_fd, n)
105 .status()?;
106 assert!(st.success());
107 }
108 Ok(())
109 }
110}