@@ -151,6 +151,61 @@ impl Command {
151151 self
152152 }
153153
154+ /// Set an auxiliary stream passed to the process, besides the stdio streams.
155+ ///
156+ /// # Notes
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. This function is only "safe" because the safety requirements are practically not
161+ /// possible to uphold.
162+ #[ cfg( unix) ]
163+ pub fn set_aux_fd < F : Into < std:: os:: fd:: OwnedFd > > (
164+ & mut self ,
165+ new_fd : std:: os:: fd:: RawFd ,
166+ fd : F ,
167+ ) -> & mut Self {
168+ use std:: mem;
169+ // NOTE: If more than 1 auxiliary file descriptor is needed, this function should be
170+ // rewritten.
171+ use std:: os:: fd:: AsRawFd ;
172+ use std:: os:: unix:: process:: CommandExt ;
173+
174+ let cvt = |x| if x == -1 { Err ( std:: io:: Error :: last_os_error ( ) ) } else { Ok ( ( ) ) } ;
175+
176+ // Ensure fd stays open until the fork.
177+ let fd = mem:: ManuallyDrop :: new ( fd. into ( ) ) ;
178+ let fd = fd. as_raw_fd ( ) ;
179+
180+ if fd == new_fd {
181+ // If the new file descriptor is already the same as fd, just turn off `FD_CLOEXEC`.
182+ let fd_flags = {
183+ let ret = unsafe { libc:: fcntl ( fd, libc:: F_GETFD , 0 ) } ;
184+ if ret < 0 {
185+ panic ! ( "failed to read fd flags: {}" , std:: io:: Error :: last_os_error( ) ) ;
186+ }
187+ ret
188+ } ;
189+ // Clear `FD_CLOEXEC`.
190+ let fd_flags = fd_flags & !libc:: FD_CLOEXEC ;
191+
192+ // SAFETY(io-safety): `fd` is already owned.
193+ cvt ( unsafe { libc:: fcntl ( fd, libc:: F_SETFD , fd_flags as libc:: c_int ) } )
194+ . expect ( "disabling CLOEXEC failed" ) ;
195+ }
196+ let pre_exec = move || {
197+ if fd. as_raw_fd ( ) != new_fd {
198+ // SAFETY(io-safety): it's the caller's responsibility that we won't override the
199+ // target fd.
200+ cvt ( unsafe { libc:: dup2 ( fd, new_fd) } ) ?;
201+ }
202+ Ok ( ( ) )
203+ } ;
204+ // SAFETY(pre-exec-safe): `dup2` is pre-exec-safe.
205+ unsafe { self . cmd . pre_exec ( pre_exec) } ;
206+ self
207+ }
208+
154209 /// Run the constructed command and assert that it is successfully run.
155210 ///
156211 /// By default, std{in,out,err} are [`Stdio::piped()`].
0 commit comments