@@ -449,17 +449,70 @@ impl Command {
449449use crate :: mem:: MaybeUninit ;
450450use crate :: sys:: weak:: weak;
451451use crate :: sys:: { self , cvt_nz, on_broken_pipe_flag_used} ;
452+ #[ cfg( target_os ="linux" ) ]
453+ use core:: sync:: atomic:: { AtomicU8 , Ordering } ;
452454
453455if 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{
460461return 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
544597let 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-
549599struct PosixSpawnFileActions < ' a > ( & ' a mut MaybeUninit < libc:: posix_spawn_file_actions_t > ) ;
550600
551601impl Drop for PosixSpawnFileActions < ' _ > {
@@ -640,6 +690,47 @@ impl Command {
640690#[ cfg( target_os ="nto" ) ]
641691let 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+ envpas * 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+
643734let spawn_res =spawn_fn (
644735& mut p. pid ,
645736self . get_program_cstr ( ) . as_ptr ( ) ,