Skip to content

Commit 8ffaf42

Browse files
committed
sandlock-oci: pid-file names the supervisor and it exits with the workload status
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent 46da7a6 commit 8ffaf42

1 file changed

Lines changed: 67 additions & 12 deletions

File tree

crates/sandlock-oci/src/main.rs

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)