@@ -315,24 +315,85 @@ lazy_static::lazy_static! {
315315 . and_then( |x| x. parse:: <bool >( ) . ok( ) )
316316 . unwrap_or( false ) ;
317317
318+ pub static ref UNSHARE_TINI_PATH : String = {
319+ std:: env:: var( "UNSHARE_TINI_PATH" ) . unwrap_or_else( |_| "tini" . to_string( ) )
320+ } ;
321+
322+ // --fork is required for unshare to work with --pid --mount-proc.
323+ // When tini is available, it runs as PID 1 inside the forked namespace for proper signal handling.
318324 pub static ref UNSHARE_ISOLATION_FLAGS : String = {
319325 std:: env:: var( "UNSHARE_ISOLATION_FLAGS" )
320326 . unwrap_or_else( |_| "--user --map-root-user --pid --fork --mount-proc" . to_string( ) )
321327 } ;
322328
329+ // Check if tini is available for proper PID 1 handling in unshare namespaces.
330+ // tini handles OOM signals correctly, returning exit code 137 instead of sigprocmask errors.
331+ pub static ref TINI_AVAILABLE : Option <String > = {
332+ let tini_path = UNSHARE_TINI_PATH . as_str( ) ;
333+ let test_result = std:: process:: Command :: new( tini_path)
334+ . args( [ "-s" , "--" , "true" ] )
335+ . output( ) ;
336+
337+ match test_result {
338+ Ok ( output) if output. status. success( ) => {
339+ tracing:: info!( "tini available at: {}" , tini_path) ;
340+ Some ( tini_path. to_string( ) )
341+ }
342+ Ok ( output) => {
343+ let stderr = String :: from_utf8_lossy( & output. stderr) ;
344+ tracing:: warn!(
345+ "tini test failed: {}. Proceeding without tini (OOM exit codes may be incorrect)." ,
346+ stderr. trim( )
347+ ) ;
348+ None
349+ }
350+ Err ( e) => {
351+ if e. kind( ) == std:: io:: ErrorKind :: NotFound {
352+ tracing:: warn!(
353+ "tini not found at '{}'. Install tini for correct OOM exit codes, or set UNSHARE_TINI_PATH." ,
354+ tini_path
355+ ) ;
356+ } else {
357+ tracing:: warn!(
358+ "Failed to test tini: {}. Proceeding without tini." ,
359+ e
360+ ) ;
361+ }
362+ None
363+ }
364+ }
365+ } ;
366+
323367 pub static ref UNSHARE_PATH : Option <String > = {
324368 let flags = UNSHARE_ISOLATION_FLAGS . as_str( ) ;
325369 let mut test_cmd_args: Vec <& str > = flags. split_whitespace( ) . collect( ) ;
326- test_cmd_args. push( "--" ) ;
327- test_cmd_args. push( "true" ) ;
370+
371+ // Build the test command based on whether tini is available
372+ // Note: --fork should already be in the flags for proper namespace setup
373+ if let Some ( tini_path) = TINI_AVAILABLE . as_ref( ) {
374+ // Test with tini: unshare <flags> -- tini -s -- true
375+ test_cmd_args. push( "--" ) ;
376+ test_cmd_args. push( tini_path. as_str( ) ) ;
377+ test_cmd_args. push( "-s" ) ;
378+ test_cmd_args. push( "--" ) ;
379+ test_cmd_args. push( "true" ) ;
380+ } else {
381+ // Fallback without tini: unshare <flags> -- true
382+ test_cmd_args. push( "--" ) ;
383+ test_cmd_args. push( "true" ) ;
384+ }
328385
329386 let test_result = std:: process:: Command :: new( "unshare" )
330387 . args( & test_cmd_args)
331388 . output( ) ;
332389
333390 match test_result {
334391 Ok ( output) if output. status. success( ) => {
335- tracing:: info!( "PID namespace isolation enabled. Flags: {}" , flags) ;
392+ if TINI_AVAILABLE . is_some( ) {
393+ tracing:: info!( "PID namespace isolation enabled with tini. Flags: {}" , flags) ;
394+ } else {
395+ tracing:: info!( "PID namespace isolation enabled. Flags: {}" , flags) ;
396+ }
336397 Some ( "unshare" . to_string( ) )
337398 } ,
338399 Ok ( output) => {
@@ -1140,12 +1201,6 @@ pub async fn run_worker(
11401201 if * ENABLE_UNSHARE_PID {
11411202 // Access UNSHARE_PATH to trigger lazy_static initialization and test
11421203 let _ = & * UNSHARE_PATH ;
1143-
1144- tracing:: info!(
1145- worker = %worker_name, hostname = %hostname,
1146- "PID namespace isolation enabled via unshare with flags: {}" ,
1147- UNSHARE_ISOLATION_FLAGS . as_str( )
1148- ) ;
11491204 }
11501205
11511206 let start_time = Instant :: now ( ) ;
0 commit comments