@@ -213,6 +213,98 @@ pub trait CommandExt: Sealed {
213213
214214 #[ unstable( feature = "process_setsid" , issue = "105376" ) ]
215215 fn setsid ( & mut self , setsid : bool ) -> & mut process:: Command ;
216+
217+ /// Pass a file descriptor to a child process.
218+ ///
219+ /// `old_fd` is an open file descriptor in the parent process. This fd will be duplicated in the
220+ /// child process and associated with the fd number `new_fd`.
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 `old_fd` is an open file descriptor in the child process (e.g. if multiple parent fds are being
227+ /// mapped to the same child one) and closing it would produce one or more errors,
228+ /// those errors will be lost when this function is called. See
229+ /// [`man 2 dup`](https://www.man7.org/linux/man-pages/man2/dup.2.html#NOTES) for more information.
230+ ///
231+ /// ```
232+ /// #![feature(command_pass_fds)]
233+ ///
234+ /// use std::process::{Command, Stdio};
235+ /// use std::os::unix::process::CommandExt;
236+ /// use std::io::{self, Write};
237+ ///
238+ /// # fn main() -> io::Result<()> {
239+ /// let (pipe_reader, mut pipe_writer) = io::pipe()?;
240+ ///
241+ /// let fd_num = 123;
242+ ///
243+ /// let mut cmd = Command::new("cat");
244+ /// cmd.arg(format!("/dev/fd/{fd_num}")).stdout(Stdio::piped()).fd(fd_num, pipe_reader);
245+ ///
246+ /// let mut child = cmd.spawn()?;
247+ /// let mut stdout = child.stdout.take().unwrap();
248+ ///
249+ /// pipe_writer.write_all(b"Hello, world!")?;
250+ /// drop(pipe_writer);
251+ ///
252+ /// child.wait()?;
253+ /// assert_eq!(io::read_to_string(&mut stdout)?, "Hello, world!");
254+ ///
255+ /// # Ok(())
256+ /// # }
257+ /// ```
258+ ///
259+ /// If this method is called multiple times with the same `new_fd`, all but one file descriptor
260+ /// will be lost.
261+ ///
262+ /// ```
263+ /// #![feature(command_pass_fds)]
264+ ///
265+ /// use std::process::{Command, Stdio};
266+ /// use std::os::unix::process::CommandExt;
267+ /// use std::io::{self, Write};
268+ ///
269+ /// # fn main() -> io::Result<()> {
270+ /// let (pipe_reader1, mut pipe_writer1) = io::pipe()?;
271+ /// let (pipe_reader2, mut pipe_writer2) = io::pipe()?;
272+ ///
273+ /// let fd_num = 123;
274+ ///
275+ /// let mut cmd = Command::new("cat");
276+ /// cmd.arg(format!("/dev/fd/{fd_num}"))
277+ /// .stdout(Stdio::piped())
278+ /// .fd(fd_num, pipe_reader1)
279+ /// .fd(fd_num, pipe_reader2);
280+ ///
281+ /// pipe_writer1.write_all(b"Hello from pipe 1!")?;
282+ /// drop(pipe_writer1);
283+ ///
284+ /// pipe_writer2.write_all(b"Hello from pipe 2!")?;
285+ /// drop(pipe_writer2);
286+ ///
287+ /// let mut child = cmd.spawn()?;
288+ /// let mut stdout = child.stdout.take().unwrap();
289+ ///
290+ /// child.wait()?;
291+ /// assert_eq!(io::read_to_string(&mut stdout)?, "Hello from pipe 2!");
292+ ///
293+ /// # Ok(())
294+ /// # }
295+ /// ```
296+ #[ unstable( feature = "command_pass_fds" , issue = "144989" ) ]
297+ fn fd ( & mut self , new_fd : RawFd , old_fd : impl Into < OwnedFd > ) -> & mut Self ;
298+
299+ /// Check if the last `spawn` of this command used `posix_spawn`.
300+ ///
301+ /// Returns `None` if the `Command` hasn't been spawned yet.
302+ /// Returns `Some(true)` if the last spawn used [`posix_spawn`].
303+ /// Returns `Some(false)` otherwise.
304+ ///
305+ /// [`posix_spawn`]: https://www.man7.org/linux/man-pages/man3/posix_spawn.3.html
306+ #[ unstable( feature = "command_pass_fds" , issue = "144989" ) ]
307+ fn last_spawn_was_posix_spawn ( & self ) -> Option < bool > ;
216308}
217309
218310#[ stable( feature = "rust1" , since = "1.0.0" ) ]
@@ -268,6 +360,15 @@ impl CommandExt for process::Command {
268360 self . as_inner_mut ( ) . setsid ( setsid) ;
269361 self
270362 }
363+
364+ fn fd ( & mut self , new_fd : RawFd , old_fd : impl Into < OwnedFd > ) -> & mut Self {
365+ self . as_inner_mut ( ) . fd ( old_fd. into ( ) , new_fd) ;
366+ self
367+ }
368+
369+ fn last_spawn_was_posix_spawn ( & self ) -> Option < bool > {
370+ self . as_inner ( ) . get_last_spawn_was_posix_spawn ( )
371+ }
271372}
272373
273374/// Unix-specific extensions to [`process::ExitStatus`] and
0 commit comments