@@ -449,17 +449,70 @@ impl Command {
449449        use  crate :: mem:: MaybeUninit ; 
450450        use  crate :: sys:: weak:: weak; 
451451        use  crate :: sys:: { self ,  cvt_nz,  on_broken_pipe_flag_used} ; 
452+         #[ cfg( target_os = "linux" ) ]  
453+         use  core:: sync:: atomic:: { AtomicU8 ,  Ordering } ; 
452454
453455        if  self . get_gid ( ) . is_some ( ) 
454456            || self . get_uid ( ) . is_some ( ) 
455457            || ( self . env_saw_path ( )  && !self . program_is_path ( ) ) 
456458            || !self . get_closures ( ) . is_empty ( ) 
457459            || self . get_groups ( ) . is_some ( ) 
458-             || self . get_create_pidfd ( ) 
459460        { 
460461            return  Ok ( None ) ; 
461462        } 
462463
464+         cfg_if:: cfg_if! { 
465+             if  #[ cfg( target_os = "linux" ) ]  { 
466+                 weak! { 
467+                     fn  pidfd_spawnp( 
468+                         * mut  libc:: c_int, 
469+                         * const  libc:: c_char, 
470+                         * const  libc:: posix_spawn_file_actions_t, 
471+                         * const  libc:: posix_spawnattr_t, 
472+                         * const  * mut  libc:: c_char, 
473+                         * const  * mut  libc:: c_char
474+                     )  -> libc:: c_int
475+                 } 
476+ 
477+                 weak! {  fn  pidfd_getpid( libc:: c_int)  -> libc:: c_int } 
478+ 
479+                 static  PIDFD_SPAWN_SUPPORTED :  AtomicU8  = AtomicU8 :: new( 0 ) ; 
480+                 const  UNKNOWN :  u8  = 0 ; 
481+                 const  YES :  u8  = 1 ; 
482+                 // NO currently forces a fallback to fork/exec. We could be more nuanced here and keep using spawn 
483+                 // if we know pidfd's aren't supported at all and the fallback would be futile. 
484+                 const  NO :  u8  = 2 ; 
485+ 
486+                 if  self . get_create_pidfd( )  { 
487+                     let  flag = PIDFD_SPAWN_SUPPORTED . load( Ordering :: Relaxed ) ; 
488+                     if  flag == NO  || pidfd_spawnp. get( ) . is_none( )  || pidfd_getpid. get( ) . is_none( )  { 
489+                         return  Ok ( None ) ; 
490+                     } 
491+                     if  flag == UNKNOWN  { 
492+                         let  mut  support = NO ; 
493+                         let  our_pid = crate :: process:: id( ) ; 
494+                         let  pidfd =
495+                             unsafe  {  libc:: syscall( libc:: SYS_pidfd_open ,  our_pid,  0 )  }  as  libc:: c_int; 
496+                         if  pidfd >= 0  { 
497+                             let  pid = unsafe  {  pidfd_getpid. get( ) . unwrap( ) ( pidfd)  }  as  u32 ; 
498+                             unsafe  {  libc:: close( pidfd)  } ; 
499+                             if  pid == our_pid { 
500+                                 support = YES 
501+                             } ; 
502+                         } 
503+                         PIDFD_SPAWN_SUPPORTED . store( support,  Ordering :: Relaxed ) ; 
504+                         if  support != YES  { 
505+                             return  Ok ( None ) ; 
506+                         } 
507+                     } 
508+                 } 
509+             }  else { 
510+                 if  self . get_create_pidfd( )  { 
511+                     unreachable!( "only implemented on linux" ) 
512+                 } 
513+             } 
514+         } 
515+ 
463516        // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. 
464517        #[ cfg( all( target_os = "linux" ,  target_env = "gnu" ) ) ]  
465518        { 
@@ -543,9 +596,6 @@ impl Command {
543596
544597        let  pgroup = self . get_pgroup ( ) ; 
545598
546-         // Safety: -1 indicates we don't have a pidfd. 
547-         let  mut  p = unsafe  {  Process :: new ( 0 ,  -1 )  } ; 
548- 
549599        struct  PosixSpawnFileActions < ' a > ( & ' a  mut  MaybeUninit < libc:: posix_spawn_file_actions_t > ) ; 
550600
551601        impl  Drop  for  PosixSpawnFileActions < ' _ >  { 
@@ -640,6 +690,47 @@ impl Command {
640690            #[ cfg( target_os = "nto" ) ]  
641691            let  spawn_fn = retrying_libc_posix_spawnp; 
642692
693+             #[ cfg( target_os = "linux" ) ]  
694+             if  self . get_create_pidfd ( )  { 
695+                 let  mut  pidfd:  libc:: c_int  = -1 ; 
696+                 let  spawn_res = pidfd_spawnp. get ( ) . unwrap ( ) ( 
697+                     & mut  pidfd, 
698+                     self . get_program_cstr ( ) . as_ptr ( ) , 
699+                     file_actions. 0 . as_ptr ( ) , 
700+                     attrs. 0 . as_ptr ( ) , 
701+                     self . get_argv ( ) . as_ptr ( )  as  * const  _ , 
702+                     envp as  * const  _ , 
703+                 ) ; 
704+ 
705+                 let  spawn_res = cvt_nz ( spawn_res) ; 
706+                 if  let  Err ( ref  e)  = spawn_res
707+                     && e. raw_os_error ( )  == Some ( libc:: ENOSYS ) 
708+                 { 
709+                     PIDFD_SPAWN_SUPPORTED . store ( NO ,  Ordering :: Relaxed ) ; 
710+                     return  Ok ( None ) ; 
711+                 } 
712+                 spawn_res?; 
713+ 
714+                 let  pid = match  cvt ( pidfd_getpid. get ( ) . unwrap ( ) ( pidfd) )  { 
715+                     Ok ( pid)  => pid, 
716+                     Err ( e)  => { 
717+                         // The child has been spawned and we are holding its pidfd. 
718+                         // But we cannot obtain its pid even though pidfd_getpid support was verified earlier. 
719+                         // This might happen if libc can't open procfs because the file descriptor limit has been reached. 
720+                         libc:: close ( pidfd) ; 
721+                         return  Err ( Error :: new ( 
722+                             e. kind ( ) , 
723+                             "pidfd_spawnp succeeded but the child's PID could not be obtained" , 
724+                         ) ) ; 
725+                     } 
726+                 } ; 
727+ 
728+                 return  Ok ( Some ( Process :: new ( pid,  pidfd) ) ) ; 
729+             } 
730+ 
731+             // Safety: -1 indicates we don't have a pidfd. 
732+             let  mut  p = Process :: new ( 0 ,  -1 ) ; 
733+ 
643734            let  spawn_res = spawn_fn ( 
644735                & mut  p. pid , 
645736                self . get_program_cstr ( ) . as_ptr ( ) , 
0 commit comments