@@ -152,6 +152,10 @@ impl Command {
152152 }
153153
154154 /// Set an auxiliary stream passed to the process, besides the stdio streams.
155+ /// Use with caution - ideally, only set one aux fd; if there are multiple,
156+ /// their old `fd` may overlap with another's `newfd`, and may break.
157+ //FIXME: If more than 1 auxiliary file descriptor is needed, this function
158+ // should be rewritten.
155159 #[ cfg( unix) ]
156160 pub fn set_aux_fd < F : Into < std:: os:: fd:: OwnedFd > > (
157161 & mut self ,
@@ -161,18 +165,31 @@ impl Command {
161165 use std:: os:: fd:: AsRawFd ;
162166 use std:: os:: unix:: process:: CommandExt ;
163167
168+ let cvt = |x| if x == -1 { Err ( std:: io:: Error :: last_os_error ( ) ) } else { Ok ( ( ) ) } ;
169+
164170 let fd = fd. into ( ) ;
165- unsafe {
166- self . cmd . pre_exec ( move || {
167- let fd = fd. as_raw_fd ( ) ;
168- let ret = if fd == newfd {
169- libc:: fcntl ( fd, libc:: F_SETFD , 0 )
170- } else {
171- libc:: dup2 ( fd, newfd)
172- } ;
173- if ret == -1 { Err ( std:: io:: Error :: last_os_error ( ) ) } else { Ok ( ( ) ) }
174- } ) ;
171+ if fd. as_raw_fd ( ) == newfd {
172+ // if the new file descriptor is already the same as fd, just turn off FD_CLOEXEC
173+ // SAFETY: io-safe: fd is already owned.
174+ cvt ( unsafe { libc:: fcntl ( fd. as_raw_fd ( ) , libc:: F_SETFD , 0 ) } )
175+ . expect ( "disabling CLOEXEC failed" ) ;
176+ // The pre_exec function should be unconditionally set, since it captures
177+ // `fd`, and this ensures that it stays open until the fork
175178 }
179+ let pre_exec = move || {
180+ if fd. as_raw_fd ( ) != newfd {
181+ // SAFETY: io-"safe": newfd is not necessarily an unused fd.
182+ // However, we're ensuring that newfd will now refer to the same file descriptor
183+ // as fd, which is safe as long as we manage the lifecycle of both descriptors
184+ // correctly. This operation will replace the file descriptor referred to by newfd
185+ // with the one from fd, allowing for shared access to the same underlying file or
186+ // resource.
187+ cvt ( unsafe { libc:: dup2 ( fd. as_raw_fd ( ) , newfd) } ) ?;
188+ }
189+ Ok ( ( ) )
190+ } ;
191+ // SAFETY: dup2 is pre-exec-safe
192+ unsafe { self . cmd . pre_exec ( pre_exec) } ;
176193 self
177194 }
178195
0 commit comments