@@ -89,6 +89,7 @@ impl Command {
89
89
// The child calls `mem::forget` to leak the lock, which is crucial because
90
90
// releasing a lock is not async-signal-safe.
91
91
let env_lock = sys:: env:: env_read_lock ( ) ;
92
+
92
93
let pid = unsafe { self . do_fork ( ) ? } ;
93
94
94
95
if pid == 0 {
@@ -276,7 +277,7 @@ impl Command {
276
277
unsafe fn do_exec (
277
278
& mut self ,
278
279
stdio : ChildPipes ,
279
- maybe_envp : Option < & CStringArray > ,
280
+ mut maybe_envp : Option < & CStringArray > ,
280
281
) -> Result < !, io:: Error > {
281
282
use crate :: sys:: { self , cvt_r} ;
282
283
@@ -378,13 +379,17 @@ impl Command {
378
379
callback ( ) ?;
379
380
}
380
381
381
- // Although we're performing an exec here we may also return with an
382
- // error from this function (without actually exec'ing) in which case we
383
- // want to be sure to restore the global environment back to what it
384
- // once was, ensuring that our temporary override, when free'd, doesn't
385
- // corrupt our process's environment.
386
382
let mut _reset = None ;
387
- if let Some ( envp) = maybe_envp {
383
+ if let Some ( envp) = maybe_envp
384
+ && ( !self . get_resolve_in_parent_path ( )
385
+ || !self . env_saw_path ( )
386
+ || self . get_program_kind ( ) != ProgramKind :: PathLookup )
387
+ {
388
+ // Although we're performing an exec here we may also return with an
389
+ // error from this function (without actually exec'ing) in which case we
390
+ // want to be sure to restore the global environment back to what it
391
+ // once was, ensuring that our temporary override, when free'd, doesn't
392
+ // corrupt our process's environment.
388
393
struct Reset ( * const * const libc:: c_char ) ;
389
394
390
395
impl Drop for Reset {
@@ -397,9 +402,69 @@ impl Command {
397
402
398
403
_reset = Some ( Reset ( * sys:: env:: environ ( ) ) ) ;
399
404
* sys:: env:: environ ( ) = envp. as_ptr ( ) ;
405
+ // We've set the environment, no need to set it again.
406
+ maybe_envp = None ;
400
407
}
401
408
402
- libc:: execvp ( self . get_program_cstr ( ) . as_ptr ( ) , self . get_argv ( ) . as_ptr ( ) ) ;
409
+ use crate :: ffi:: CStr ;
410
+ // execvpe is a gnu extension...
411
+ #[ cfg( all( target_os = "linux" , target_env = "gnu" ) ) ]
412
+ unsafe fn exec_with_env (
413
+ program : & CStr ,
414
+ args : & CStringArray ,
415
+ envp : & CStringArray ,
416
+ ) -> libc:: c_int {
417
+ unsafe { libc:: execvpe ( program. as_ptr ( ) , args. as_ptr ( ) , envp. as_ptr ( ) ) }
418
+ }
419
+
420
+ // ...so if we're not gnu then use our own implementation.
421
+ #[ cfg( not( all( target_os = "linux" , target_env = "gnu" ) ) ) ]
422
+ unsafe fn exec_with_env (
423
+ program : & CStr ,
424
+ args : & CStringArray ,
425
+ envp : & CStringArray ,
426
+ ) -> libc:: c_int {
427
+ unsafe {
428
+ let name = program. to_bytes ( ) ;
429
+ let mut buffer =
430
+ [ const { mem:: MaybeUninit :: < u8 > :: uninit ( ) } ; libc:: PATH_MAX as usize ] ;
431
+ let mut environ = * sys:: env:: environ ( ) ;
432
+ // Search the environment for PATH and, if found,
433
+ // search the paths for the executable by trying to execve each candidate.
434
+ while !( * environ) . is_null ( ) {
435
+ let kv = CStr :: from_ptr ( * environ) ;
436
+ if let Some ( value) = kv. to_bytes ( ) . strip_prefix ( b"PATH=" ) {
437
+ for path in value. split ( |& b| b == b':' ) {
438
+ if buffer. len ( ) - 2 >= path. len ( ) . saturating_add ( name. len ( ) ) {
439
+ let buf_ptr = buffer. as_mut_ptr ( ) . cast :: < u8 > ( ) ;
440
+ let mut offset = 0 ;
441
+ if !path. is_empty ( ) {
442
+ buf_ptr. copy_from ( path. as_ptr ( ) , path. len ( ) ) ;
443
+ offset += path. len ( ) ;
444
+ if path. last ( ) != Some ( & b'/' ) {
445
+ * buf_ptr. add ( path. len ( ) ) = b'/' ;
446
+ offset += 1 ;
447
+ }
448
+ }
449
+ buf_ptr. add ( offset) . copy_from ( name. as_ptr ( ) , name. len ( ) ) ;
450
+ offset += name. len ( ) ;
451
+ * buf_ptr. add ( offset) = 0 ;
452
+ libc:: execve ( buf_ptr. cast ( ) , args. as_ptr ( ) , envp. as_ptr ( ) ) ;
453
+ }
454
+ }
455
+ break ;
456
+ }
457
+ environ = environ. add ( 1 ) ;
458
+ }
459
+ // If execve is successful then it'll never return, thus we'll never reach this point.
460
+ -1
461
+ }
462
+ }
463
+ if let Some ( envp) = maybe_envp {
464
+ exec_with_env ( self . get_program_cstr ( ) , self . get_argv ( ) , envp) ;
465
+ } else {
466
+ libc:: execvp ( self . get_program_cstr ( ) . as_ptr ( ) , self . get_argv ( ) . as_ptr ( ) ) ;
467
+ }
403
468
Err ( io:: Error :: last_os_error ( ) )
404
469
}
405
470
@@ -453,7 +518,9 @@ impl Command {
453
518
454
519
if self . get_gid ( ) . is_some ( )
455
520
|| self . get_uid ( ) . is_some ( )
456
- || ( self . env_saw_path ( ) && !self . program_is_path ( ) )
521
+ || ( !self . get_resolve_in_parent_path ( )
522
+ && self . env_saw_path ( )
523
+ && !self . program_is_path ( ) )
457
524
|| !self . get_closures ( ) . is_empty ( )
458
525
|| self . get_groups ( ) . is_some ( )
459
526
|| self . get_chroot ( ) . is_some ( )
@@ -793,6 +860,7 @@ impl Command {
793
860
// Safety: -1 indicates we don't have a pidfd.
794
861
let mut p = Process :: new ( 0 , -1 ) ;
795
862
863
+ // FIXME: if a list of paths to search is passed then retry for every path.
796
864
let spawn_res = spawn_fn (
797
865
& mut p. pid ,
798
866
self . get_program_cstr ( ) . as_ptr ( ) ,
0 commit comments