deb: retry the interrupt chroot removal while children die off

dpkg defers SIGINT until it reaches a safe state, so it can still be
writing into the chroot when the watchdog's rm -rf starts racing
through it, failing with "directory not empty" and leaving the tree
half-removed. Retry the removal for a few seconds while the
interrupted children finish dying off; a genuinely stuck tree still
ends in the pkh prune message.
This commit is contained in:
2026-09-22 19:57:01 +02:00
parent 7a6337e1cb
commit 9a66f8f7df
+26 -9
View File
@@ -60,31 +60,48 @@ fn sigint_cleanup_chroot(chroot_path: &Path) {
}
}
// Remove the chroot tree itself (tolerates a missing directory)
let status = privileged_command("rm", is_root)
.arg("-rf")
.arg(chroot_path)
.status();
match status {
Ok(status) if status.success() => {
// Remove the chroot tree itself (tolerates a missing directory). A
// child the Ctrl+C interrupted may still be finishing its writeout —
// dpkg defers SIGINT until it reaches a safe state — so retry while rm
// reports the tree non-empty instead of leaving it half-removed.
const RETRIES: usize = 10;
const RETRY_DELAY: std::time::Duration = std::time::Duration::from_millis(300);
let mut last = None;
for attempt in 0..=RETRIES {
if attempt > 0 {
std::thread::sleep(RETRY_DELAY);
}
last = Some(
privileged_command("rm", is_root)
.arg("-rf")
.arg(chroot_path)
.status(),
);
if matches!(&last, Some(Ok(status)) if status.success()) {
break;
}
}
match last {
Some(Ok(status)) if status.success() => {
log::debug!(
"Removed chroot {} during interrupt cleanup",
chroot_path.display()
);
}
Ok(status) => {
Some(Ok(status)) => {
log::error!(
"Failed to remove chroot {} during interrupt cleanup \
(rm exited with {status}); run `pkh prune`",
chroot_path.display()
);
}
Err(e) => {
Some(Err(e)) => {
log::error!(
"Failed to run rm for chroot {} during interrupt cleanup: {e}; run `pkh prune`",
chroot_path.display()
);
}
None => unreachable!("at least one rm attempt ran"),
}
}