From 7dd3683034f627c9abe9b21f0dde76b296f24312 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Mon, 12 Jan 2026 10:51:59 +0530 Subject: [PATCH] composefs: Add option to reset soft reboot state Signed-off-by: Pragyan Poudyal --- crates/lib/src/bootc_composefs/soft_reboot.rs | 47 ++++++++++++++++++- crates/lib/src/bootc_composefs/update.rs | 2 +- crates/lib/src/cli.rs | 24 ++++++++-- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/crates/lib/src/bootc_composefs/soft_reboot.rs b/crates/lib/src/bootc_composefs/soft_reboot.rs index 3e4509da..3e0bcf8c 100644 --- a/crates/lib/src/bootc_composefs/soft_reboot.rs +++ b/crates/lib/src/bootc_composefs/soft_reboot.rs @@ -10,25 +10,70 @@ use bootc_initramfs_setup::setup_root; use bootc_kernel_cmdline::utf8::Cmdline; use bootc_mount::{PID1, bind_mount_from_pidns}; use camino::Utf8Path; +use cap_std_ext::cap_std::ambient_authority; +use cap_std_ext::cap_std::fs::Dir; +use cap_std_ext::dirext::CapStdExtDirExt; use fn_error_context::context; use ostree_ext::systemd_has_soft_reboot; +use rustix::mount::{unmount, UnmountFlags}; use std::{fs::create_dir_all, os::unix::process::CommandExt, path::PathBuf, process::Command}; const NEXTROOT: &str = "/run/nextroot"; +#[context("Resetting soft reboot state")] +fn reset_soft_reboot() -> Result<()> { + let run = Utf8Path::new("/run"); + bind_mount_from_pidns(PID1, &run, &run, true).context("Bind mounting /run")?; + + let run_dir = Dir::open_ambient_dir("/run", ambient_authority()).context("Opening run")?; + + let nextroot = run_dir + .open_dir_optional("nextroot") + .context("Opening nextroot")?; + + let Some(nextroot) = nextroot else { + tracing::debug!("Nextroot is not a directory"); + println!("No deployment staged for soft rebooting"); + return Ok(()); + }; + + let nextroot_mounted = nextroot + .is_mountpoint(".")? + .ok_or_else(|| anyhow::anyhow!("Failed to get mount info"))?; + + if !nextroot_mounted { + tracing::debug!("Nextroot is not a mountpoint"); + println!("No deployment staged for soft rebooting"); + return Ok(()); + } + + unmount(NEXTROOT, UnmountFlags::DETACH).context("Unmounting nextroot")?; + + println!("Soft reboot state cleared successfully"); + + Ok(()) +} + /// Checks if the provided deployment is soft reboot capable, and soft reboots the system if /// argument `reboot` is true #[context("Soft rebooting")] pub(crate) async fn prepare_soft_reboot_composefs( storage: &Storage, booted_cfs: &BootedComposefs, - deployment_id: &String, + deployment_id: Option<&String>, reboot: bool, + reset: bool, ) -> Result<()> { if !systemd_has_soft_reboot() { anyhow::bail!("System does not support soft reboots") } + if reset { + return reset_soft_reboot(); + } + + let deployment_id = deployment_id.ok_or_else(|| anyhow::anyhow!("Expected deployment id"))?; + if *deployment_id == *booted_cfs.cmdline.digest { anyhow::bail!("Cannot soft-reboot to currently booted deployment"); } diff --git a/crates/lib/src/bootc_composefs/update.rs b/crates/lib/src/bootc_composefs/update.rs index f6252a65..206bb733 100644 --- a/crates/lib/src/bootc_composefs/update.rs +++ b/crates/lib/src/bootc_composefs/update.rs @@ -267,7 +267,7 @@ pub(crate) async fn do_upgrade( } if opts.soft_reboot.is_some() { - prepare_soft_reboot_composefs(storage, booted_cfs, &id.to_hex(), true).await?; + prepare_soft_reboot_composefs(storage, booted_cfs, Some(&id.to_hex()), true, false).await?; } Ok(()) diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index 567ba8dd..4ae6e69b 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -618,8 +618,12 @@ pub(crate) enum InternalsOpts { /// Dump CLI structure as JSON for documentation generation DumpCliJson, PrepSoftReboot { - deployment: String, - #[clap(long)] + #[clap(required_unless_present = "reset")] + deployment: Option, + #[clap(long, conflicts_with = "reset")] + reboot: bool, + #[clap(long, conflicts_with = "reboot")] + reset: bool, reboot: bool, }, } @@ -1836,7 +1840,11 @@ async fn run_from_opt(opt: Opt) -> Result<()> { Ok(()) } - InternalsOpts::PrepSoftReboot { deployment, reboot } => { + InternalsOpts::PrepSoftReboot { + deployment, + reboot, + reset, + } => { let storage = &get_storage().await?; match storage.kind()? { @@ -1845,8 +1853,14 @@ async fn run_from_opt(opt: Opt) -> Result<()> { anyhow::bail!("soft-reboot only implemented for composefs") } BootedStorageKind::Composefs(booted_cfs) => { - prepare_soft_reboot_composefs(&storage, &booted_cfs, &deployment, reboot) - .await + prepare_soft_reboot_composefs( + &storage, + &booted_cfs, + deployment.as_ref(), + reboot, + reset, + ) + .await } } }