@@ -151,6 +151,53 @@ impl Command {
151
151
self
152
152
}
153
153
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
+
154
201
/// Run the constructed command and assert that it is successfully run.
155
202
///
156
203
/// By default, std{in,out,err} are [`Stdio::piped()`].
0 commit comments