@@ -449,17 +449,61 @@ impl Command {
449
449
use crate :: mem:: MaybeUninit ;
450
450
use crate :: sys:: weak:: weak;
451
451
use crate :: sys:: { self , cvt_nz, on_broken_pipe_flag_used} ;
452
+ use core:: sync:: atomic:: { AtomicU8 , Ordering } ;
452
453
453
454
if self . get_gid ( ) . is_some ( )
454
455
|| self . get_uid ( ) . is_some ( )
455
456
|| ( self . env_saw_path ( ) && !self . program_is_path ( ) )
456
457
|| !self . get_closures ( ) . is_empty ( )
457
458
|| self . get_groups ( ) . is_some ( )
458
- || self . get_create_pidfd ( )
459
459
{
460
460
return Ok ( None ) ;
461
461
}
462
462
463
+ weak ! {
464
+ fn pidfd_spawnp(
465
+ * mut libc:: c_int,
466
+ * const libc:: c_char,
467
+ * const libc:: posix_spawn_file_actions_t,
468
+ * const libc:: posix_spawnattr_t,
469
+ * const * mut libc:: c_char,
470
+ * const * mut libc:: c_char
471
+ ) -> libc:: c_int
472
+ }
473
+
474
+ weak ! { fn pidfd_getpid( libc:: c_int) -> libc:: c_int }
475
+
476
+ static PIDFD_SPAWN_SUPPORTED : AtomicU8 = AtomicU8 :: new ( 0 ) ;
477
+ const UNKNOWN : u8 = 0 ;
478
+ const YES : u8 = 1 ;
479
+ // NO currently forces a fallback to fork/exec. We could be more nuanced here and keep using spawn
480
+ // if we know pidfd's aren't supported at all and the fallback would be futile.
481
+ const NO : u8 = 2 ;
482
+
483
+ if self . get_create_pidfd ( ) {
484
+ let flag = PIDFD_SPAWN_SUPPORTED . load ( Ordering :: Relaxed ) ;
485
+ if flag == NO || pidfd_spawnp. get ( ) . is_none ( ) || pidfd_getpid. get ( ) . is_none ( ) {
486
+ return Ok ( None ) ;
487
+ }
488
+ if flag == UNKNOWN {
489
+ let mut support = NO ;
490
+ let our_pid = crate :: process:: id ( ) ;
491
+ let pidfd =
492
+ unsafe { libc:: syscall ( libc:: SYS_pidfd_open , our_pid, 0 ) } as libc:: c_int ;
493
+ if pidfd >= 0 {
494
+ let pid = unsafe { pidfd_getpid. get ( ) . unwrap ( ) ( pidfd) } as u32 ;
495
+ unsafe { libc:: close ( pidfd) } ;
496
+ if pid == our_pid {
497
+ support = YES
498
+ } ;
499
+ }
500
+ PIDFD_SPAWN_SUPPORTED . store ( support, Ordering :: Relaxed ) ;
501
+ if support != YES {
502
+ return Ok ( None ) ;
503
+ }
504
+ }
505
+ }
506
+
463
507
// Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
464
508
#[ cfg( all( target_os = "linux" , target_env = "gnu" ) ) ]
465
509
{
@@ -543,9 +587,6 @@ impl Command {
543
587
544
588
let pgroup = self . get_pgroup ( ) ;
545
589
546
- // Safety: -1 indicates we don't have a pidfd.
547
- let mut p = unsafe { Process :: new ( 0 , -1 ) } ;
548
-
549
590
struct PosixSpawnFileActions < ' a > ( & ' a mut MaybeUninit < libc:: posix_spawn_file_actions_t > ) ;
550
591
551
592
impl Drop for PosixSpawnFileActions < ' _ > {
@@ -640,6 +681,46 @@ impl Command {
640
681
#[ cfg( target_os = "nto" ) ]
641
682
let spawn_fn = retrying_libc_posix_spawnp;
642
683
684
+ if self . get_create_pidfd ( ) {
685
+ let mut pidfd: libc:: c_int = -1 ;
686
+ let spawn_res = pidfd_spawnp. get ( ) . unwrap ( ) (
687
+ & mut pidfd,
688
+ self . get_program_cstr ( ) . as_ptr ( ) ,
689
+ file_actions. 0 . as_ptr ( ) ,
690
+ attrs. 0 . as_ptr ( ) ,
691
+ self . get_argv ( ) . as_ptr ( ) as * const _ ,
692
+ envp as * const _ ,
693
+ ) ;
694
+
695
+ let spawn_res = cvt_nz ( spawn_res) ;
696
+ if let Err ( ref e) = spawn_res
697
+ && e. raw_os_error ( ) == Some ( libc:: ENOSYS )
698
+ {
699
+ PIDFD_SPAWN_SUPPORTED . store ( NO , Ordering :: Relaxed ) ;
700
+ return Ok ( None ) ;
701
+ }
702
+ spawn_res?;
703
+
704
+ let pid = match cvt ( pidfd_getpid. get ( ) . unwrap ( ) ( pidfd) ) {
705
+ Ok ( pid) => pid,
706
+ Err ( e) => {
707
+ // The child has been spawned and we are holding its pidfd.
708
+ // But we cannot obtain its pid even though pidfd_getpid support was verified earlier.
709
+ // This might happen if libc can't open procfs because the file descriptor limit has been reached.
710
+ libc:: close ( pidfd) ;
711
+ return Err ( Error :: new (
712
+ e. kind ( ) ,
713
+ "pidfd_spawnp succeeded but the child's PID could not be obtained" ,
714
+ ) ) ;
715
+ }
716
+ } ;
717
+
718
+ return Ok ( Some ( Process :: new ( pid, pidfd) ) ) ;
719
+ }
720
+
721
+ // Safety: -1 indicates we don't have a pidfd.
722
+ let mut p = Process :: new ( 0 , -1 ) ;
723
+
643
724
let spawn_res = spawn_fn (
644
725
& mut p. pid ,
645
726
self . get_program_cstr ( ) . as_ptr ( ) ,
0 commit comments