33// For the full copyright and license information, please view the LICENSE
44// file that was distributed with this source code.
55
6- // spell-checker:ignore (ToDO) chdir progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction
6+ // spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction
77
88pub mod native_int_str;
99pub mod split_iterator;
@@ -21,14 +21,16 @@ use native_int_str::{
2121use nix:: libc;
2222#[ cfg( unix) ]
2323use nix:: sys:: signal:: { SigHandler :: SigIgn , Signal , signal} ;
24+ #[ cfg( unix) ]
25+ use nix:: unistd:: execvp;
2426use std:: borrow:: Cow ;
2527use std:: env;
28+ #[ cfg( unix) ]
29+ use std:: ffi:: CString ;
2630use std:: ffi:: { OsStr , OsString } ;
2731use std:: io:: { self , Write } ;
2832#[ cfg( unix) ]
2933use std:: os:: unix:: ffi:: OsStrExt ;
30- #[ cfg( unix) ]
31- use std:: os:: unix:: process:: CommandExt ;
3234
3335use uucore:: display:: Quotable ;
3436use uucore:: error:: { ExitCode , UError , UResult , USimpleError , UUsageError } ;
@@ -604,16 +606,34 @@ impl EnvAppData {
604606
605607 #[ cfg( unix) ]
606608 {
607- // Execute the program using exec, which replaces the current process.
608- let err = std:: process:: Command :: new ( & * prog)
609- . arg0 ( & * arg0)
610- . args ( args)
611- . exec ( ) ;
612-
613- // exec() only returns if there was an error
614- match err. kind ( ) {
615- io:: ErrorKind :: NotFound => Err ( self . make_error_no_such_file_or_dir ( & prog) ) ,
616- io:: ErrorKind :: PermissionDenied => {
609+ // Convert program name to CString.
610+ let Ok ( prog_cstring) = CString :: new ( prog. as_bytes ( ) ) else {
611+ return Err ( self . make_error_no_such_file_or_dir ( & prog) ) ;
612+ } ;
613+
614+ // Prepare arguments for execvp.
615+ let mut argv = Vec :: new ( ) ;
616+
617+ // Convert arg0 to CString.
618+ let Ok ( arg0_cstring) = CString :: new ( arg0. as_bytes ( ) ) else {
619+ return Err ( self . make_error_no_such_file_or_dir ( & prog) ) ;
620+ } ;
621+ argv. push ( arg0_cstring) ;
622+
623+ // Convert remaining arguments to CString.
624+ for arg in args {
625+ let Ok ( arg_cstring) = CString :: new ( arg. as_bytes ( ) ) else {
626+ return Err ( self . make_error_no_such_file_or_dir ( & prog) ) ;
627+ } ;
628+ argv. push ( arg_cstring) ;
629+ }
630+
631+ // Execute the program using execvp. this replaces the current
632+ // process. The execvp function takes care of appending a NULL
633+ // argument to the argument list so that we don't have to.
634+ match execvp ( & prog_cstring, & argv) {
635+ Err ( nix:: errno:: Errno :: ENOENT ) => Err ( self . make_error_no_such_file_or_dir ( & prog) ) ,
636+ Err ( nix:: errno:: Errno :: EACCES ) => {
617637 uucore:: show_error!(
618638 "{}" ,
619639 translate!(
@@ -623,16 +643,19 @@ impl EnvAppData {
623643 ) ;
624644 Err ( 126 . into ( ) )
625645 }
626- _ => {
646+ Err ( _ ) => {
627647 uucore:: show_error!(
628648 "{}" ,
629649 translate!(
630650 "env-error-unknown" ,
631- "error" => err
651+ "error" => "execvp failed"
632652 )
633653 ) ;
634654 Err ( 126 . into ( ) )
635655 }
656+ Ok ( _) => {
657+ unreachable ! ( "execvp should never return on success" )
658+ }
636659 }
637660 }
638661
0 commit comments