Skip to content

Commit c396452

Browse files
committed
Use process groups and clean orphaned sandbox data
1 parent d8a1cdf commit c396452

3 files changed

Lines changed: 39 additions & 2 deletions

File tree

crates/sandchest-node/src/firecracker.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ impl FirecrackerVm {
106106
.stdout(Stdio::piped())
107107
.stderr(Stdio::piped())
108108
.kill_on_drop(true)
109+
.process_group(0)
109110
.spawn()
110111
.map_err(|e| FirecrackerError::Spawn(format!("failed to spawn firecracker: {}", e)))?;
111112

@@ -217,7 +218,7 @@ impl FirecrackerVm {
217218
#[cfg(unix)]
218219
if let Some(pid) = self.child.id() {
219220
unsafe {
220-
libc::kill(pid as i32, libc::SIGTERM);
221+
libc::kill(-(pid as i32), libc::SIGTERM);
221222
}
222223

223224
// Wait up to 5 seconds for graceful exit
@@ -226,7 +227,10 @@ impl FirecrackerVm {
226227

227228
if graceful.is_err() {
228229
warn!(sandbox_id = %self.sandbox_id, "Firecracker did not exit gracefully, sending SIGKILL");
229-
let _ = self.child.kill().await;
230+
unsafe {
231+
libc::kill(-(pid as i32), libc::SIGKILL);
232+
}
233+
let _ = self.child.wait().await;
230234
}
231235
}
232236

crates/sandchest-node/src/jailer.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ pub fn build_jailer_command(
258258
.stderr(Stdio::piped())
259259
.kill_on_drop(true);
260260

261+
#[cfg(unix)]
262+
cmd.process_group(0);
263+
261264
cmd
262265
}
263266

crates/sandchest-node/src/sandbox.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ impl SandboxManager {
594594
.stdout(std::process::Stdio::piped())
595595
.stderr(std::process::Stdio::piped())
596596
.kill_on_drop(true)
597+
.process_group(0)
597598
.spawn()
598599
.map_err(|e| {
599600
SandboxError::CreateFailed(format!("failed to spawn firecracker: {}", e))
@@ -1080,6 +1081,7 @@ impl SandboxManager {
10801081
.stdout(std::process::Stdio::piped())
10811082
.stderr(std::process::Stdio::piped())
10821083
.kill_on_drop(true)
1084+
.process_group(0)
10831085
.spawn()
10841086
{
10851087
Ok(c) => c,
@@ -1214,6 +1216,7 @@ impl SandboxManager {
12141216
info!(sandbox_id = %sandbox_id, "destroying sandbox");
12151217

12161218
self.set_status(sandbox_id, SandboxStatus::Stopping).await;
1219+
let paths = self.sandbox_paths(sandbox_id);
12171220

12181221
// Get the network slot before removing sandbox info
12191222
let network_slot = {
@@ -1229,6 +1232,18 @@ impl SandboxManager {
12291232
}
12301233
}
12311234

1235+
// Best-effort filesystem cleanup even if the VM handle was missing.
1236+
if Path::new(&paths.data_dir).exists() {
1237+
if let Err(e) = tokio::fs::remove_dir_all(&paths.data_dir).await {
1238+
error!(
1239+
sandbox_id = %sandbox_id,
1240+
dir = %paths.data_dir,
1241+
error = %e,
1242+
"failed to clean up sandbox data directory"
1243+
);
1244+
}
1245+
}
1246+
12321247
// Tear down networking
12331248
if let Some(slot) = network_slot {
12341249
network::teardown_network(sandbox_id, slot).await;
@@ -1872,6 +1887,21 @@ mod tests {
18721887
assert!(result.is_ok());
18731888
}
18741889

1890+
#[tokio::test]
1891+
async fn destroy_sandbox_cleans_up_orphaned_disk_without_vm_handle() {
1892+
let manager = SandboxManager::new(test_node_config());
1893+
let sandbox_dir = std::path::Path::new("/tmp/sandchest-test")
1894+
.join("sandboxes")
1895+
.join("sb_orphaned");
1896+
let _ = std::fs::remove_dir_all(&sandbox_dir);
1897+
std::fs::create_dir_all(&sandbox_dir).unwrap();
1898+
std::fs::write(sandbox_dir.join("rootfs.ext4"), b"orphaned").unwrap();
1899+
1900+
let result = manager.destroy_sandbox("sb_orphaned").await;
1901+
assert!(result.is_ok());
1902+
assert!(!sandbox_dir.exists());
1903+
}
1904+
18751905
#[tokio::test]
18761906
async fn event_dropped_when_channel_full() {
18771907
let (tx, _rx) = crate::events::channel(1);

0 commit comments

Comments
 (0)