@@ -35,6 +35,7 @@ pub struct LocalBoundaryExec {
3535 ca_file_paths : Option < Arc < ( std:: path:: PathBuf , std:: path:: PathBuf ) > > ,
3636 provider_credentials : ProviderCredentialState ,
3737 user_environment : HashMap < String , String > ,
38+ repair_standard_sbin : bool ,
3839 runtime : Arc < crate :: boundary_io:: BoundaryRuntimeState > ,
3940 #[ cfg( target_os = "linux" ) ]
4041 launcher : openshell_isolation_interface:: linux:: workload_launcher:: WorkloadLauncher ,
@@ -59,6 +60,7 @@ impl LocalBoundaryExec {
5960 ca_file_paths,
6061 provider_credentials,
6162 user_environment,
63+ repair_standard_sbin : false ,
6264 runtime,
6365 #[ cfg( target_os = "linux" ) ]
6466 launcher,
@@ -121,6 +123,13 @@ impl LocalBoundaryExec {
121123 } )
122124 }
123125
126+ /// Configure whether workload paths should include the standard sbin directories.
127+ #[ must_use]
128+ pub fn with_standard_sbin_path_repair ( mut self , enabled : bool ) -> Self {
129+ self . repair_standard_sbin = enabled;
130+ self
131+ }
132+
124133 fn command ( & self , spec : & ExecSpec ) -> Result < Command , BackendError > {
125134 let ( program, args) = if let Some ( shell_spec) = & spec. shell {
126135 let shell = openshell_core:: shell:: find_login_shell ( ) . ok_or_else ( || {
@@ -151,12 +160,18 @@ impl LocalBoundaryExec {
151160 if program. is_empty ( ) {
152161 return Err ( BackendError :: Process ( "exec program is empty" . to_string ( ) ) ) ;
153162 }
163+ let repair_standard_sbin = self . repair_standard_sbin ;
164+ let args = crate :: process:: process_args_with_standard_sbin_paths (
165+ & program,
166+ & args,
167+ repair_standard_sbin,
168+ ) ;
154169 let mut command = Command :: new ( & program) ;
155- command. args ( & args) ;
170+ command. args ( args) ;
156171 let effective_workdir = spec. workdir . as_deref ( ) . or ( self . base_workdir . as_deref ( ) ) ;
157172 let ( session_user, session_home) =
158173 crate :: process:: session_user_and_home ( & self . policy , effective_workdir) ;
159- let path = std :: env :: var ( "PATH" ) . unwrap_or_else ( |_| "/usr/local/bin:/usr/bin:/bin" . into ( ) ) ;
174+ let path = crate :: child_env :: child_path_from_env ( repair_standard_sbin ) ;
160175 command
161176 . env_clear ( )
162177 . env ( openshell_core:: sandbox_env:: SANDBOX , "1" )
@@ -169,7 +184,17 @@ impl LocalBoundaryExec {
169184 }
170185 for ( key, value) in & self . user_environment {
171186 if !key. starts_with ( "OPENSHELL_" ) {
172- command. env ( key, value) ;
187+ if key == "PATH" {
188+ command. env (
189+ key,
190+ crate :: child_env:: maybe_path_with_standard_sbin_paths (
191+ value,
192+ repair_standard_sbin,
193+ ) ,
194+ ) ;
195+ } else {
196+ command. env ( key, value) ;
197+ }
173198 }
174199 }
175200 if let Some ( ( ca_cert_path, combined_bundle_path) ) = self . ca_file_paths . as_deref ( ) {
@@ -179,13 +204,33 @@ impl LocalBoundaryExec {
179204 }
180205 for ( key, value) in self . provider_credentials . child_env_with_gcp_resolved ( ) {
181206 if !crate :: process:: is_supervisor_only_env_var ( & key) {
182- command. env ( key, value) ;
207+ if key == "PATH" {
208+ command. env (
209+ key,
210+ crate :: child_env:: maybe_path_with_standard_sbin_paths (
211+ & value,
212+ repair_standard_sbin,
213+ ) ,
214+ ) ;
215+ } else {
216+ command. env ( key, value) ;
217+ }
183218 }
184219 }
185220 crate :: process:: strip_proxy_env_std ( & mut command) ;
186221 for ( key, value) in & spec. env {
187222 if !key. starts_with ( "OPENSHELL_" ) {
188- command. env ( key, value) ;
223+ if key == "PATH" {
224+ command. env (
225+ key,
226+ crate :: child_env:: maybe_path_with_standard_sbin_paths (
227+ value,
228+ repair_standard_sbin,
229+ ) ,
230+ ) ;
231+ } else {
232+ command. env ( key, value) ;
233+ }
189234 }
190235 }
191236 if let Some ( workdir) = spec. workdir . as_deref ( ) . or ( self . base_workdir . as_deref ( ) ) {
0 commit comments