From b994ba353be171b6f6bd0c986b2ab0a25b95f310 Mon Sep 17 00:00:00 2001 From: cgwalters-bot Date: Wed, 23 Sep 2026 07:20:58 -0400 Subject: [PATCH] reboot: Propagate a refused reboot as an error We run `systemctl reboot` via `systemd-run` so it's outside our mount namespace, but without --wait we only learn whether the transient unit was started, not whether the reboot was accepted. If logind refuses it, bootc then parks forever waiting for a SIGTERM that never comes, and e.g. `bootc upgrade --apply` from bootc-fetch-apply-updates.service would just hang instead of failing. One way to get there is a block mode shutdown inhibitor: since systemd 257, logind rejects reboot requests from root too while one is held, unless the caller explicitly asks to skip inhibitors. On CentOS Stream 10 with such a lock held, `systemd-run -- systemctl reboot` exits 0 while the system stays up, so bootc would wait forever. With --wait and --pipe, the exit status and systemctl's error message come back to us. --collect avoids leaving a failed transient unit behind in that case. There is a small race with --wait: once the reboot is accepted, shutdown can stop the transient unit or kill systemd-run before it reports success, and bootc would then print an error and exit while the system goes down instead of parking until SIGTERM. The window is short since `systemctl reboot` returns as soon as the job is queued, and the reboot happens either way; only the exit status of the bootc process being shut down is affected. Generated-by: AI --- crates/lib/src/reboot.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/lib/src/reboot.rs b/crates/lib/src/reboot.rs index 63d7a5fe5f..ed35eaa129 100644 --- a/crates/lib/src/reboot.rs +++ b/crates/lib/src/reboot.rs @@ -12,9 +12,17 @@ pub(crate) fn reboot() -> anyhow::Result<()> { // Flush output streams let _ = std::io::stdout().flush(); let _ = std::io::stderr().flush(); + // Wait for the transient unit and pass through its stderr, so that if + // the reboot is refused (e.g. due to an inhibitor) we report an error + // instead of sleeping forever below. Note --wait/--pipe talk to systemd + // via the D-Bus system bus rather than /run/systemd/private, so without + // dbus (e.g. rescue.target) this fails loudly; staged changes are kept. Command::new("systemd-run") .args([ "--quiet", + "--wait", + "--pipe", + "--collect", "--", "systemctl", "reboot",