@@ -339,6 +339,38 @@ fn run(cli: Cli) -> Result<()> {
339339 Ok ( ( ) )
340340}
341341
342+ /// How the supervisor daemon should terminate so a reaping containerd shim sees
343+ /// a wait-status that mirrors the workload.
344+ #[ derive( Debug , PartialEq , Eq ) ]
345+ enum ExitAction {
346+ /// Exit with this code (workload exited normally).
347+ Code ( i32 ) ,
348+ /// Re-raise this signal on self (workload was killed by it) so the shim sees
349+ /// WIFSIGNALED and reports 128+signal, matching runc.
350+ Raise ( i32 ) ,
351+ }
352+
353+ fn supervisor_exit_action ( info : Option < state:: ExitInfo > ) -> ExitAction {
354+ match info {
355+ Some ( state:: ExitInfo { code : Some ( c) , .. } ) => ExitAction :: Code ( c) ,
356+ Some ( state:: ExitInfo { signal : Some ( s) , .. } ) => ExitAction :: Raise ( s) ,
357+ _ => ExitAction :: Code ( 0 ) ,
358+ }
359+ }
360+
361+ /// Terminate the supervisor daemon with the workload's status. Diverges.
362+ fn supervisor_exit ( info : Option < state:: ExitInfo > ) -> ! {
363+ match supervisor_exit_action ( info) {
364+ ExitAction :: Code ( c) => unsafe { libc:: _exit ( c) } ,
365+ ExitAction :: Raise ( s) => unsafe {
366+ libc:: signal ( s as libc:: c_int , libc:: SIG_DFL ) ;
367+ libc:: raise ( s as libc:: c_int ) ;
368+ // raise should not return for a default-action signal; fall back.
369+ libc:: _exit ( 128 + s)
370+ } ,
371+ }
372+ }
373+
342374/// `sandlock-oci create <id> -b <bundle>`
343375///
344376/// 1. Parse OCI config.json from the bundle.
@@ -455,11 +487,13 @@ fn cmd_create(id: &str, bundle: &PathBuf, pid_file: Option<&std::path::Path>) ->
455487 }
456488 }
457489
458- let _ = supervisor:: run_supervisor ( id, & cmd_args, policy, write_fd) ;
459- unsafe {
460- libc:: close ( write_fd) ;
461- libc:: _exit ( 0 ) ;
462- }
490+ let exit_info = supervisor:: run_supervisor ( id, & cmd_args, policy, write_fd)
491+ . ok ( )
492+ . flatten ( ) ;
493+ unsafe { libc:: close ( write_fd) } ;
494+ // Exit with the workload's status so a reaping containerd shim reports
495+ // the right container exit code/signal.
496+ supervisor_exit ( exit_info) ;
463497 }
464498
465499 // ===== ORIGINAL PROCESS (caller) =====
@@ -477,7 +511,7 @@ fn cmd_create(id: &str, bundle: &PathBuf, pid_file: Option<&std::path::Path>) ->
477511 // `OK <pid>\n` — sandbox created, <pid> is the sandbox's init PID
478512 // `ERR <msg>\n` — setup failed; the sandbox was never created
479513 // (EOF) — supervisor crashed before writing; treated as error
480- let child_pid = {
514+ let ( supervisor_pid , init_pid ) = {
481515 let mut buf = [ 0u8 ; 512 ] ;
482516 let n = unsafe {
483517 libc:: read ( read_fd, buf. as_mut_ptr ( ) as * mut libc:: c_void , buf. len ( ) )
@@ -489,25 +523,33 @@ fn cmd_create(id: &str, bundle: &PathBuf, pid_file: Option<&std::path::Path>) ->
489523 let response = String :: from_utf8_lossy ( & buf[ ..n as usize ] ) ;
490524 let response = response. trim ( ) ;
491525 if let Some ( rest) = response. strip_prefix ( "OK " ) {
492- rest. parse :: < i32 > ( )
493- . with_context ( || format ! ( "invalid PID in supervisor response: {:?}" , response) ) ?
526+ let mut parts = rest. split_whitespace ( ) ;
527+ let sup = parts. next ( ) . and_then ( |s| s. parse :: < i32 > ( ) . ok ( ) ) ;
528+ let init = parts. next ( ) . and_then ( |s| s. parse :: < i32 > ( ) . ok ( ) ) ;
529+ match ( sup, init) {
530+ ( Some ( sup) , Some ( init) ) => ( sup, init) ,
531+ _ => bail ! ( "invalid PIDs in supervisor response: {:?}" , response) ,
532+ }
494533 } else if let Some ( msg) = response. strip_prefix ( "ERR " ) {
495534 bail ! ( "sandbox create failed: {}" , msg) ;
496535 } else {
497536 bail ! ( "unexpected supervisor response: {:?}" , response) ;
498537 }
499538 } ;
500539
501- // Update the state file with the actual PID.
540+ // state.pid is the OCI container init (sandlock-init); `start` later updates
541+ // it to the workload pid.
502542 {
503543 let mut state = SandboxState :: load ( id) ?;
504- state. set_created ( child_pid ) ;
544+ state. set_created ( init_pid ) ;
505545 state. save ( ) ?;
506546 }
507547
508- // Write pid-file if requested (CRI-O / containerd expect this).
548+ // The pid-file gets the SUPERVISOR pid: that is the process the containerd
549+ // shim reaps to detect container exit (the supervisor is the shim's child
550+ // and exits with the workload's status).
509551 if let Some ( pf) = pid_file {
510- std:: fs:: write ( pf, child_pid . to_string ( ) )
552+ std:: fs:: write ( pf, supervisor_pid . to_string ( ) )
511553 . with_context ( || format ! ( "write pid file {:?}" , pf) ) ?;
512554 }
513555
@@ -984,6 +1026,19 @@ mod tests {
9841026 assert_eq ! ( explicit. rootless, Some ( false ) ) ;
9851027 }
9861028
1029+ #[ test]
1030+ fn supervisor_exit_action_maps_status ( ) {
1031+ assert_eq ! (
1032+ supervisor_exit_action( Some ( state:: ExitInfo { code: Some ( 7 ) , signal: None } ) ) ,
1033+ ExitAction :: Code ( 7 )
1034+ ) ;
1035+ assert_eq ! (
1036+ supervisor_exit_action( Some ( state:: ExitInfo { code: None , signal: Some ( libc:: SIGSEGV ) } ) ) ,
1037+ ExitAction :: Raise ( libc:: SIGSEGV )
1038+ ) ;
1039+ assert_eq ! ( supervisor_exit_action( None ) , ExitAction :: Code ( 0 ) ) ;
1040+ }
1041+
9871042 #[ test]
9881043 fn error_log_json_is_parseable_with_msg ( ) {
9891044 let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
0 commit comments