@@ -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,
@@ -120,6 +122,13 @@ impl LocalBoundaryExec {
120122 } )
121123 }
122124
125+ /// Configure whether workload paths should include the standard sbin directories.
126+ #[ must_use]
127+ pub fn with_standard_sbin_path_repair ( mut self , enabled : bool ) -> Self {
128+ self . repair_standard_sbin = enabled;
129+ self
130+ }
131+
123132 fn command ( & self , spec : & ExecSpec ) -> Result < Command , BackendError > {
124133 let ( program, args) = if let Some ( shell_spec) = & spec. shell {
125134 let shell = openshell_core:: shell:: find_login_shell ( ) . ok_or_else ( || {
@@ -150,12 +159,18 @@ impl LocalBoundaryExec {
150159 if program. is_empty ( ) {
151160 return Err ( BackendError :: Process ( "exec program is empty" . to_string ( ) ) ) ;
152161 }
162+ let repair_standard_sbin = self . repair_standard_sbin ;
163+ let args = crate :: process:: process_args_with_standard_sbin_paths (
164+ & program,
165+ & args,
166+ repair_standard_sbin,
167+ ) ;
153168 let mut command = Command :: new ( & program) ;
154- command. args ( & args) ;
169+ command. args ( args) ;
155170 let effective_workdir = spec. workdir . as_deref ( ) . or ( self . base_workdir . as_deref ( ) ) ;
156171 let ( session_user, session_home) =
157172 crate :: process:: session_user_and_home ( & self . policy , effective_workdir) ;
158- let path = std :: env :: var ( "PATH" ) . unwrap_or_else ( |_| "/usr/local/bin:/usr/bin:/bin" . into ( ) ) ;
173+ let path = crate :: child_env :: child_path_from_env ( repair_standard_sbin ) ;
159174 command
160175 . env_clear ( )
161176 . env ( openshell_core:: sandbox_env:: SANDBOX , "1" )
@@ -168,7 +183,17 @@ impl LocalBoundaryExec {
168183 }
169184 for ( key, value) in & self . user_environment {
170185 if !key. starts_with ( "OPENSHELL_" ) {
171- command. env ( key, value) ;
186+ if key == "PATH" {
187+ command. env (
188+ key,
189+ crate :: child_env:: maybe_path_with_standard_sbin_paths (
190+ value,
191+ repair_standard_sbin,
192+ ) ,
193+ ) ;
194+ } else {
195+ command. env ( key, value) ;
196+ }
172197 }
173198 }
174199 if let Some ( ( ca_cert_path, combined_bundle_path) ) = self . ca_file_paths . as_deref ( ) {
@@ -178,13 +203,33 @@ impl LocalBoundaryExec {
178203 }
179204 for ( key, value) in self . provider_credentials . child_env_with_gcp_resolved ( ) {
180205 if !crate :: process:: is_supervisor_only_env_var ( & key) {
181- command. env ( key, value) ;
206+ if key == "PATH" {
207+ command. env (
208+ key,
209+ crate :: child_env:: maybe_path_with_standard_sbin_paths (
210+ & value,
211+ repair_standard_sbin,
212+ ) ,
213+ ) ;
214+ } else {
215+ command. env ( key, value) ;
216+ }
182217 }
183218 }
184219 crate :: process:: strip_proxy_env_std ( & mut command) ;
185220 for ( key, value) in & spec. env {
186221 if !key. starts_with ( "OPENSHELL_" ) {
187- command. env ( key, value) ;
222+ if key == "PATH" {
223+ command. env (
224+ key,
225+ crate :: child_env:: maybe_path_with_standard_sbin_paths (
226+ value,
227+ repair_standard_sbin,
228+ ) ,
229+ ) ;
230+ } else {
231+ command. env ( key, value) ;
232+ }
188233 }
189234 }
190235 if let Some ( workdir) = spec. workdir . as_deref ( ) . or ( self . base_workdir . as_deref ( ) ) {
0 commit comments