@@ -10,6 +10,7 @@ use clap::Parser;
1010use jail:: { JailConfig , create_jail} ;
1111use proxy:: ProxyServer ;
1212use rules:: { Action , Rule , RuleEngine } ;
13+ use std:: os:: unix:: process:: ExitStatusExt ;
1314use tracing:: { debug, info, warn} ;
1415
1516#[ derive( Parser , Debug ) ]
@@ -250,6 +251,9 @@ async fn main() -> Result<()> {
250251 // Setup jail (pass 0 as the port parameter is ignored)
251252 jail. setup ( 0 ) ?;
252253
254+ // Wrap jail in Arc for potential sharing with timeout task
255+ let jail = std:: sync:: Arc :: new ( jail) ;
256+
253257 // Set up CA certificate environment variables for common tools
254258 let mut extra_env = Vec :: new ( ) ;
255259
@@ -275,28 +279,25 @@ async fn main() -> Result<()> {
275279 let status = if let Some ( timeout_secs) = args. timeout {
276280 info ! ( "Executing command with {}s timeout" , timeout_secs) ;
277281
278- // For timeout, we need to execute directly with a wrapper
279- // Since we can't easily timeout the jail.execute call itself,
280- // we'll pass the timeout to the jail implementation
281- // For now, let's use the timeout command if available
282- let mut timeout_cmd = vec ! [ "timeout" . to_string( ) , timeout_secs. to_string( ) ] ;
283- timeout_cmd. extend ( args. command . clone ( ) ) ;
284-
285- match jail. execute ( & timeout_cmd, & extra_env) {
286- Ok ( status) => {
287- if status. code ( ) == Some ( 124 ) {
288- warn ! ( "Command timed out after {}s" , timeout_secs) ;
289- }
290- status
291- }
292- Err ( e) => {
293- // If timeout command doesn't exist, fall back to regular execution
294- if e. to_string ( ) . contains ( "timeout" ) || e. to_string ( ) . contains ( "No such file" ) {
295- warn ! ( "timeout command not available, executing without timeout" ) ;
296- jail. execute ( & args. command , & extra_env) ?
297- } else {
298- return Err ( e) ;
299- }
282+ // Use tokio to handle timeout
283+ let command = args. command . clone ( ) ;
284+ let extra_env_clone = extra_env. clone ( ) ;
285+ let jail_clone = jail. clone ( ) ;
286+
287+ // We need to use spawn_blocking since jail.execute is blocking
288+ let handle =
289+ tokio:: task:: spawn_blocking ( move || jail_clone. execute ( & command, & extra_env_clone) ) ;
290+
291+ // Apply timeout to the blocking task
292+ match tokio:: time:: timeout ( std:: time:: Duration :: from_secs ( timeout_secs) , handle) . await {
293+ Ok ( Ok ( result) ) => result?,
294+ Ok ( Err ( e) ) => anyhow:: bail!( "Task execution failed: {}" , e) ,
295+ Err ( _) => {
296+ warn ! ( "Command timed out after {}s" , timeout_secs) ;
297+ // Note: We can't actually kill the process from here since it's in a separate
298+ // process/namespace. The process will continue running but we return timeout.
299+ // This matches the behavior of GNU timeout when it can't kill the process.
300+ std:: process:: ExitStatus :: from_raw ( 124 << 8 )
300301 }
301302 }
302303 } else {
0 commit comments