|
4 | 4 |
|
5 | 5 | #![stable(feature = "rust1", since = "1.0.0")] |
6 | 6 |
|
| 7 | +use libc::{F_GETFD, F_SETFD, FD_CLOEXEC}; |
| 8 | + |
7 | 9 | use crate::ffi::OsStr; |
8 | 10 | use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
9 | 11 | use crate::path::Path; |
10 | 12 | use crate::sealed::Sealed; |
| 13 | +use crate::sys::{cvt, cvt_r}; |
11 | 14 | use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; |
12 | 15 | use crate::{io, process, sys}; |
13 | 16 |
|
@@ -213,6 +216,84 @@ pub trait CommandExt: Sealed { |
213 | 216 |
|
214 | 217 | #[unstable(feature = "process_setsid", issue = "105376")] |
215 | 218 | fn setsid(&mut self, setsid: bool) -> &mut process::Command; |
| 219 | + |
| 220 | + /// Pass a file descriptor to a child process. |
| 221 | + /// |
| 222 | + /// Getting this right is tricky. It is recommended to provide further information to the child |
| 223 | + /// process by some other mechanism. This could be an argument confirming file descriptors that |
| 224 | + /// the child can use, device/inode numbers to allow for sanity checks, or something similar. |
| 225 | + /// |
| 226 | + /// If `new_fd` is an open file descriptor and closing it would produce one or more errors, |
| 227 | + /// those errors will be lost when this function is called. See |
| 228 | + /// [`man 2 dup`](https://www.man7.org/linux/man-pages/man2/dup.2.html#NOTES) for more information. |
| 229 | + /// |
| 230 | + /// If this method is called multiple times with the same `new_fd`, all but one file descriptor |
| 231 | + /// will be lost. |
| 232 | + /// |
| 233 | + /// ``` |
| 234 | + /// #![feature(command_pass_fds)] |
| 235 | + /// |
| 236 | + /// use std::process::{Command, Stdio}; |
| 237 | + /// use std::os::fd::process::CommandExt; |
| 238 | + /// use std::io::{self, Write}; |
| 239 | + /// |
| 240 | + /// fn main() -> io::Result<()> { |
| 241 | + /// let (pipe_reader, mut pipe_writer) = io::pipe()?; |
| 242 | + /// |
| 243 | + /// let fd_num = 123; |
| 244 | + /// |
| 245 | + /// let mut cmd = Command::new("cat"); |
| 246 | + /// cmd.arg(format!("/dev/fd/{fd_num}")).stdout(Stdio::piped()).fd(fd_num, pipe_reader); |
| 247 | + /// |
| 248 | + /// let mut child = cmd.spawn()?; |
| 249 | + /// let mut stdout = child.stdout.take().unwrap(); |
| 250 | + /// |
| 251 | + /// pipe_writer.write_all(b"Hello, world!")?; |
| 252 | + /// drop(pipe_writer); |
| 253 | + /// |
| 254 | + /// child.wait()?; |
| 255 | + /// assert_eq!(io::read_to_string(&mut stdout)?, "Hello, world!"); |
| 256 | + /// |
| 257 | + /// Ok(()) |
| 258 | + /// } |
| 259 | + /// ``` |
| 260 | + /// |
| 261 | + /// ``` |
| 262 | + /// #![feature(command_pass_fds)] |
| 263 | + /// |
| 264 | + /// use std::process::{Command, Stdio}; |
| 265 | + /// use std::os::fd::process::CommandExt; |
| 266 | + /// use std::io::{self, Write}; |
| 267 | + /// |
| 268 | + /// fn main() -> io::Result<()> { |
| 269 | + /// let (pipe_reader1, mut pipe_writer1) = io::pipe()?; |
| 270 | + /// let (pipe_reader2, mut pipe_writer2) = io::pipe()?; |
| 271 | + /// |
| 272 | + /// let fd_num = 123; |
| 273 | + /// |
| 274 | + /// let mut cmd = Command::new("cat"); |
| 275 | + /// cmd.arg(format!("/dev/fd/{fd_num}")) |
| 276 | + /// .stdout(Stdio::piped()) |
| 277 | + /// .fd(fd_num, pipe_reader1) |
| 278 | + /// .fd(fd_num, pipe_reader2); |
| 279 | + /// |
| 280 | + /// let mut child = cmd.spawn()?; |
| 281 | + /// let mut stdout = child.stdout.take().unwrap(); |
| 282 | + /// |
| 283 | + /// pipe_writer1.write_all(b"Hello from pipe 1!")?; |
| 284 | + /// drop(pipe_writer1); |
| 285 | + /// |
| 286 | + /// pipe_writer2.write_all(b"Hello from pipe 2!")?; |
| 287 | + /// drop(pipe_writer2); |
| 288 | + /// |
| 289 | + /// child.wait()?; |
| 290 | + /// assert_eq!(io::read_to_string(&mut stdout)?, "Hello from pipe 2!"); |
| 291 | + /// |
| 292 | + /// Ok(()) |
| 293 | + /// } |
| 294 | + /// ``` |
| 295 | + #[unstable(feature = "command_pass_fds", issue = "144989")] |
| 296 | + fn fd(&mut self, new_fd: RawFd, old_fd: impl Into<OwnedFd>) -> &mut Self; |
216 | 297 | } |
217 | 298 |
|
218 | 299 | #[stable(feature = "rust1", since = "1.0.0")] |
@@ -268,6 +349,21 @@ impl CommandExt for process::Command { |
268 | 349 | self.as_inner_mut().setsid(setsid); |
269 | 350 | self |
270 | 351 | } |
| 352 | + |
| 353 | + fn fd(&mut self, new_fd: RawFd, old_fd: impl Into<OwnedFd>) -> &mut Self { |
| 354 | + let old = old_fd.into(); |
| 355 | + unsafe { |
| 356 | + self.as_inner_mut().pre_exec(Box::new(move || { |
| 357 | + cvt_r(|| libc::dup2(old.as_raw_fd(), new_fd))?; |
| 358 | + let flags = cvt(libc::fcntl(new_fd, F_GETFD))?; |
| 359 | + cvt(libc::fcntl(new_fd, F_SETFD, flags & !FD_CLOEXEC))?; |
| 360 | + cvt_r(|| libc::close(old.as_raw_fd()))?; |
| 361 | + Ok(()) |
| 362 | + })) |
| 363 | + } |
| 364 | + |
| 365 | + self |
| 366 | + } |
271 | 367 | } |
272 | 368 |
|
273 | 369 | /// Unix-specific extensions to [`process::ExitStatus`] and |
|
0 commit comments