@@ -244,7 +244,8 @@ struct SpawnProcessResult {
244
244
}
245
245
246
246
#[ cfg( windows) ]
247
- fn spawn_process_os ( cfg : ProcessConfig , in_fd : c_int , out_fd : c_int , err_fd : c_int )
247
+ fn spawn_process_os ( cfg : ProcessConfig ,
248
+ in_fd : c_int , out_fd : c_int , err_fd : c_int )
248
249
-> IoResult < SpawnProcessResult > {
249
250
use libc:: types:: os:: arch:: extra:: { DWORD , HANDLE , STARTUPINFO } ;
250
251
use libc:: consts:: os:: extra:: {
@@ -278,38 +279,51 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
278
279
279
280
let cur_proc = GetCurrentProcess ( ) ;
280
281
281
- if in_fd != -1 {
282
- let orig_std_in = get_osfhandle ( in_fd) as HANDLE ;
283
- if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
284
- fail ! ( "failure in get_osfhandle: {}" , os:: last_os_error( ) ) ;
285
- }
286
- if DuplicateHandle ( cur_proc, orig_std_in, cur_proc, & mut si. hStdInput ,
287
- 0 , TRUE , DUPLICATE_SAME_ACCESS ) == FALSE {
288
- fail ! ( "failure in DuplicateHandle: {}" , os:: last_os_error( ) ) ;
289
- }
290
- }
291
-
292
- if out_fd != -1 {
293
- let orig_std_out = get_osfhandle ( out_fd) as HANDLE ;
294
- if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
295
- fail ! ( "failure in get_osfhandle: {}" , os:: last_os_error( ) ) ;
296
- }
297
- if DuplicateHandle ( cur_proc, orig_std_out, cur_proc, & mut si. hStdOutput ,
298
- 0 , TRUE , DUPLICATE_SAME_ACCESS ) == FALSE {
299
- fail ! ( "failure in DuplicateHandle: {}" , os:: last_os_error( ) ) ;
282
+ // Similarly to unix, we don't actually leave holes for the stdio file
283
+ // descriptors, but rather open up /dev/null equivalents. These
284
+ // equivalents are drawn from libuv's windows process spawning.
285
+ let set_fd = |fd : c_int , slot : & mut HANDLE , is_stdin : bool | {
286
+ if fd == -1 {
287
+ let access = if is_stdin {
288
+ libc:: FILE_GENERIC_READ
289
+ } else {
290
+ libc:: FILE_GENERIC_WRITE | libc:: FILE_READ_ATTRIBUTES
291
+ } ;
292
+ let size = mem:: size_of :: < libc:: SECURITY_ATTRIBUTES > ( ) ;
293
+ let mut sa = libc:: SECURITY_ATTRIBUTES {
294
+ nLength : size as libc:: DWORD ,
295
+ lpSecurityDescriptor : ptr:: mut_null ( ) ,
296
+ bInheritHandle : 1 ,
297
+ } ;
298
+ * slot = os:: win32:: as_utf16_p ( "NUL" , |filename| {
299
+ libc:: CreateFileW ( filename,
300
+ access,
301
+ libc:: FILE_SHARE_READ |
302
+ libc:: FILE_SHARE_WRITE ,
303
+ & mut sa,
304
+ libc:: OPEN_EXISTING ,
305
+ 0 ,
306
+ ptr:: mut_null ( ) )
307
+ } ) ;
308
+ if * slot == INVALID_HANDLE_VALUE as libc:: HANDLE {
309
+ return Err ( super :: last_error ( ) )
310
+ }
311
+ } else {
312
+ let orig = get_osfhandle ( fd) as HANDLE ;
313
+ if orig == INVALID_HANDLE_VALUE as HANDLE {
314
+ return Err ( super :: last_error ( ) )
315
+ }
316
+ if DuplicateHandle ( cur_proc, orig, cur_proc, slot,
317
+ 0 , TRUE , DUPLICATE_SAME_ACCESS ) == FALSE {
318
+ return Err ( super :: last_error ( ) )
319
+ }
300
320
}
301
- }
321
+ Ok ( ( ) )
322
+ } ;
302
323
303
- if err_fd != -1 {
304
- let orig_std_err = get_osfhandle ( err_fd) as HANDLE ;
305
- if orig_std_err == INVALID_HANDLE_VALUE as HANDLE {
306
- fail ! ( "failure in get_osfhandle: {}" , os:: last_os_error( ) ) ;
307
- }
308
- if DuplicateHandle ( cur_proc, orig_std_err, cur_proc, & mut si. hStdError ,
309
- 0 , TRUE , DUPLICATE_SAME_ACCESS ) == FALSE {
310
- fail ! ( "failure in DuplicateHandle: {}" , os:: last_os_error( ) ) ;
311
- }
312
- }
324
+ try!( set_fd ( in_fd, & mut si. hStdInput , true ) ) ;
325
+ try!( set_fd ( out_fd, & mut si. hStdOutput , false ) ) ;
326
+ try!( set_fd ( err_fd, & mut si. hStdError , false ) ) ;
313
327
314
328
let cmd_str = make_command_line ( cfg. program , cfg. args ) ;
315
329
let mut pi = zeroed_process_information ( ) ;
@@ -338,9 +352,9 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
338
352
} )
339
353
} ) ;
340
354
341
- if in_fd != - 1 { assert ! ( CloseHandle ( si. hStdInput) != 0 ) ; }
342
- if out_fd != - 1 { assert ! ( CloseHandle ( si. hStdOutput) != 0 ) ; }
343
- if err_fd != - 1 { assert ! ( CloseHandle ( si. hStdError) != 0 ) ; }
355
+ assert ! ( CloseHandle ( si. hStdInput) != 0 ) ;
356
+ assert ! ( CloseHandle ( si. hStdOutput) != 0 ) ;
357
+ assert ! ( CloseHandle ( si. hStdError) != 0 ) ;
344
358
345
359
match create_err {
346
360
Some ( err) => return Err ( err) ,
@@ -379,9 +393,9 @@ fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
379
393
wShowWindow : 0 ,
380
394
cbReserved2 : 0 ,
381
395
lpReserved2 : ptr:: mut_null ( ) ,
382
- hStdInput : ptr :: mut_null ( ) ,
383
- hStdOutput : ptr :: mut_null ( ) ,
384
- hStdError : ptr :: mut_null ( )
396
+ hStdInput : libc :: INVALID_HANDLE_VALUE as libc :: HANDLE ,
397
+ hStdOutput : libc :: INVALID_HANDLE_VALUE as libc :: HANDLE ,
398
+ hStdError : libc :: INVALID_HANDLE_VALUE as libc :: HANDLE ,
385
399
}
386
400
}
387
401
@@ -489,6 +503,10 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
489
503
let mut input = file:: FileDesc :: new ( pipe. input , true ) ;
490
504
let mut output = file:: FileDesc :: new ( pipe. out , true ) ;
491
505
506
+ // We may use this in the child, so perform allocations before the
507
+ // fork
508
+ let devnull = "/dev/null" . to_c_str ( ) ;
509
+
492
510
set_cloexec ( output. fd ( ) ) ;
493
511
494
512
let pid = fork ( ) ;
@@ -563,21 +581,29 @@ fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_i
563
581
564
582
rustrt:: rust_unset_sigprocmask ( ) ;
565
583
566
- if in_fd == -1 {
567
- let _ = libc:: close ( libc:: STDIN_FILENO ) ;
568
- } else if retry ( || dup2 ( in_fd, 0 ) ) == -1 {
569
- fail ( & mut output) ;
570
- }
571
- if out_fd == -1 {
572
- let _ = libc:: close ( libc:: STDOUT_FILENO ) ;
573
- } else if retry ( || dup2 ( out_fd, 1 ) ) == -1 {
574
- fail ( & mut output) ;
575
- }
576
- if err_fd == -1 {
577
- let _ = libc:: close ( libc:: STDERR_FILENO ) ;
578
- } else if retry ( || dup2 ( err_fd, 2 ) ) == -1 {
579
- fail ( & mut output) ;
580
- }
584
+ // If a stdio file descriptor is set to be ignored (via a -1 file
585
+ // descriptor), then we don't actually close it, but rather open
586
+ // up /dev/null into that file descriptor. Otherwise, the first file
587
+ // descriptor opened up in the child would be numbered as one of the
588
+ // stdio file descriptors, which is likely to wreak havoc.
589
+ let setup = |src : c_int , dst : c_int | {
590
+ let src = if src == -1 {
591
+ let flags = if dst == libc:: STDIN_FILENO {
592
+ libc:: O_RDONLY
593
+ } else {
594
+ libc:: O_RDWR
595
+ } ;
596
+ devnull. with_ref ( |p| libc:: open ( p, flags, 0 ) )
597
+ } else {
598
+ src
599
+ } ;
600
+ src != -1 && retry ( || dup2 ( src, dst) ) != -1
601
+ } ;
602
+
603
+ if !setup ( in_fd, libc:: STDIN_FILENO ) { fail ( & mut output) }
604
+ if !setup ( out_fd, libc:: STDOUT_FILENO ) { fail ( & mut output) }
605
+ if !setup ( err_fd, libc:: STDERR_FILENO ) { fail ( & mut output) }
606
+
581
607
// close all other fds
582
608
for fd in range ( 3 , getdtablesize ( ) ) . rev ( ) {
583
609
if fd != output. fd ( ) {
0 commit comments