Skip to content

Commit 0b513f8

Browse files
composefs: Implement soft reboot
Add an internal command for soft rebooting the system. Similar to how it's done for ostree, we only allow soft reboot if the other deployment has the same kernel state, i.e. the SHASum of kernel + initrd is the same as that of the current deployment. soft reboot is not possible in case of UKI deployment Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent f5fdd93 commit 0b513f8

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

crates/lib/src/bootc_composefs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub(crate) mod gc;
55
pub(crate) mod repo;
66
pub(crate) mod rollback;
77
pub(crate) mod service;
8+
pub(crate) mod soft_reboot;
89
pub(crate) mod state;
910
pub(crate) mod status;
1011
pub(crate) mod switch;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crate::{
2+
bootc_composefs::{
3+
service::start_finalize_stated_svc, status::composefs_deployment_status_from,
4+
},
5+
composefs_consts::COMPOSEFS_CMDLINE,
6+
store::{BootedComposefs, Storage},
7+
};
8+
use anyhow::{Context, Result};
9+
use bootc_initramfs_setup::setup_root;
10+
use bootc_kernel_cmdline::utf8::Cmdline;
11+
use bootc_mount::{bind_mount_from_pidns, PID1};
12+
use camino::Utf8Path;
13+
use std::{fs::create_dir_all, os::unix::process::CommandExt, path::PathBuf, process::Command};
14+
15+
const NEXTROOT: &str = "/run/nextroot";
16+
17+
pub(crate) async fn soft_reboot_to_deployment(
18+
storage: &Storage,
19+
booted_cfs: &BootedComposefs,
20+
deployment_id: &String,
21+
reboot: bool,
22+
) -> Result<()> {
23+
if *deployment_id == *booted_cfs.cmdline.digest {
24+
anyhow::bail!("Cannot soft-reboot to currently booted deployment");
25+
}
26+
27+
let host = composefs_deployment_status_from(storage, booted_cfs.cmdline).await?;
28+
29+
let all_deployments = host.all_composefs_deployments()?;
30+
31+
let requred_deployment = all_deployments
32+
.iter()
33+
.find(|entry| entry.deployment.verity == *deployment_id)
34+
.ok_or_else(|| anyhow::anyhow!("Deployment '{deployment_id}' not found"))?;
35+
36+
if !requred_deployment.soft_reboot_capable {
37+
anyhow::bail!("Cannot soft-reboot to deployment with a different kernel state");
38+
}
39+
40+
start_finalize_stated_svc()?;
41+
42+
// escape to global mnt namespace
43+
let run = Utf8Path::new("/run");
44+
bind_mount_from_pidns(PID1, &run, &run, false).context("Bind mounting /run")?;
45+
46+
create_dir_all(NEXTROOT).context("Creating nextroot")?;
47+
48+
let cmdline = Cmdline::from(format!("{COMPOSEFS_CMDLINE}={deployment_id}"));
49+
50+
let args = bootc_initramfs_setup::Args {
51+
cmd: vec![],
52+
sysroot: PathBuf::from("/sysroot"),
53+
config: Default::default(),
54+
root_fs: None,
55+
cmdline: Some(cmdline),
56+
target: Some(NEXTROOT.into()),
57+
};
58+
59+
setup_root(args)?;
60+
61+
if reboot {
62+
// Replacing the current process should be fine as we restart userspace anyway
63+
let res = Command::new("systemctl").arg("soft-reboot").exec();
64+
println!("Soft reboot error: {res:#?}");
65+
return Ok(());
66+
}
67+
68+
Ok(())
69+
}

crates/lib/src/cli.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use std::sync::Arc;
1111

1212
use anyhow::{anyhow, ensure, Context, Result};
1313
use camino::{Utf8Path, Utf8PathBuf};
14-
use cap_std_ext::cap_std::fs::Dir;
1514
use cap_std_ext::cap_std;
15+
use cap_std_ext::cap_std::fs::Dir;
1616
use clap::Parser;
1717
use clap::ValueEnum;
1818
use composefs::dumpfile;
@@ -35,6 +35,7 @@ use serde::{Deserialize, Serialize};
3535
use tempfile::tempdir_in;
3636

3737
use crate::bootc_composefs::delete::delete_composefs_deployment;
38+
use crate::bootc_composefs::soft_reboot::soft_reboot_to_deployment;
3839
use crate::bootc_composefs::{
3940
finalize::{composefs_backend_finalize, get_etc_diff},
4041
rollback::composefs_rollback,
@@ -558,6 +559,11 @@ pub(crate) enum InternalsOpts {
558559
#[cfg(feature = "docgen")]
559560
/// Dump CLI structure as JSON for documentation generation
560561
DumpCliJson,
562+
PrepSoftReboot {
563+
deployment: String,
564+
#[clap(long)]
565+
reboot: bool,
566+
},
561567
}
562568

563569
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
@@ -1636,6 +1642,19 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
16361642

16371643
Ok(())
16381644
}
1645+
InternalsOpts::PrepSoftReboot { deployment, reboot } => {
1646+
let storage = &get_storage().await?;
1647+
1648+
match storage.kind()? {
1649+
BootedStorageKind::Ostree(..) => {
1650+
// TODO: Call ostree implementation?
1651+
anyhow::bail!("soft-reboot only implemented for composefs")
1652+
}
1653+
BootedStorageKind::Composefs(booted_cfs) => {
1654+
soft_reboot_to_deployment(&storage, &booted_cfs, &deployment, reboot).await
1655+
}
1656+
}
1657+
}
16391658
},
16401659
Opt::State(opts) => match opts {
16411660
StateOpts::WipeOstree => {

crates/lib/src/spec.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ pub(crate) struct DeploymentEntry<'a> {
273273
pub(crate) ty: Option<Slot>,
274274
pub(crate) deployment: &'a BootEntryComposefs,
275275
pub(crate) pinned: bool,
276+
pub(crate) soft_reboot_capable: bool,
276277
}
277278

278279
impl Host {
@@ -332,13 +333,15 @@ impl Host {
332333
ty: Some(Slot::Booted),
333334
deployment: booted,
334335
pinned: false,
336+
soft_reboot_capable: false,
335337
});
336338

337339
if let Some(staged) = &self.status.staged {
338340
all_deps.push(DeploymentEntry {
339341
ty: Some(Slot::Staged),
340342
deployment: staged.require_composefs()?,
341343
pinned: false,
344+
soft_reboot_capable: staged.soft_reboot_capable,
342345
});
343346
}
344347

@@ -347,6 +350,7 @@ impl Host {
347350
ty: Some(Slot::Rollback),
348351
deployment: rollback.require_composefs()?,
349352
pinned: false,
353+
soft_reboot_capable: rollback.soft_reboot_capable,
350354
});
351355
}
352356

@@ -355,6 +359,7 @@ impl Host {
355359
ty: None,
356360
deployment: pinned.require_composefs()?,
357361
pinned: true,
362+
soft_reboot_capable: pinned.soft_reboot_capable,
358363
});
359364
}
360365

0 commit comments

Comments
 (0)