@@ -104,6 +104,46 @@ impl Command {
104
104
self
105
105
}
106
106
107
+ /// Set an auxiliary stream passed to the process, besides the stdio streams.
108
+ ///
109
+ /// Use with caution - ideally, only set one aux fd; if there are multiple,
110
+ /// their old `fd` may overlap with another's `newfd`, and thus will break.
111
+ /// If you need more than 1 auxiliary file descriptor, rewrite this function
112
+ /// to be able to support that.
113
+ #[ cfg( unix) ]
114
+ pub fn set_aux_fd < F : Into < std:: os:: fd:: OwnedFd > > (
115
+ & mut self ,
116
+ newfd : std:: os:: fd:: RawFd ,
117
+ fd : F ,
118
+ ) -> & mut Self {
119
+ use std:: os:: fd:: AsRawFd ;
120
+ use std:: os:: unix:: process:: CommandExt ;
121
+
122
+ let cvt = |x| if x == -1 { Err ( std:: io:: Error :: last_os_error ( ) ) } else { Ok ( ( ) ) } ;
123
+
124
+ let fd = fd. into ( ) ;
125
+ if fd. as_raw_fd ( ) == newfd {
126
+ // if fd is already where we want it, just turn off FD_CLOEXEC
127
+ // SAFETY: io-safe: we have ownership over fd
128
+ cvt ( unsafe { libc:: fcntl ( fd. as_raw_fd ( ) , libc:: F_SETFD , 0 ) } )
129
+ . expect ( "disabling CLOEXEC failed" ) ;
130
+ // we still unconditionally set the pre_exec function, since it captures
131
+ // `fd`, and we want to ensure that it stays open until the fork
132
+ }
133
+ let pre_exec = move || {
134
+ if fd. as_raw_fd ( ) != newfd {
135
+ // set newfd to point to the same file as fd
136
+ // SAFETY: io-"safe": newfd is. not necessarily an unused fd.
137
+ // however, we're
138
+ cvt ( unsafe { libc:: dup2 ( fd. as_raw_fd ( ) , newfd) } ) ?;
139
+ }
140
+ Ok ( ( ) )
141
+ } ;
142
+ // SAFETY: dup2 is pre-exec-safe
143
+ unsafe { self . cmd . pre_exec ( pre_exec) } ;
144
+ self
145
+ }
146
+
107
147
/// Run the constructed command and assert that it is successfully run.
108
148
#[ track_caller]
109
149
pub fn run ( & mut self ) -> CompletedProcess {
0 commit comments