Skip to content

Commit f9931d1

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 387e9d3 commit f9931d1

File tree

4 files changed

+94
-1
lines changed

4 files changed

+94
-1
lines changed

crates/lib/src/bootc_composefs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub(crate) mod gc;
66
pub(crate) mod repo;
77
pub(crate) mod rollback;
88
pub(crate) mod service;
9+
pub(crate) mod soft_reboot;
910
pub(crate) mod state;
1011
pub(crate) mod status;
1112
pub(crate) mod switch;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 err = Command::new("systemctl").arg("soft-reboot").exec();
64+
return Err(anyhow::Error::from(err).context("Failed to exec 'systemctl soft-reboot'"));
65+
}
66+
67+
Ok(())
68+
}

crates/lib/src/cli.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use std::process::Command;
1010

1111
use anyhow::{anyhow, ensure, Context, Result};
1212
use camino::{Utf8Path, Utf8PathBuf};
13-
use cap_std_ext::cap_std::fs::Dir;
1413
use cap_std_ext::cap_std;
14+
use cap_std_ext::cap_std::fs::Dir;
1515
use clap::Parser;
1616
use clap::ValueEnum;
1717
use composefs::dumpfile;
@@ -33,6 +33,7 @@ use schemars::schema_for;
3333
use serde::{Deserialize, Serialize};
3434

3535
use crate::bootc_composefs::delete::delete_composefs_deployment;
36+
use crate::bootc_composefs::soft_reboot::soft_reboot_to_deployment;
3637
use crate::bootc_composefs::{
3738
digest::{compute_composefs_digest, new_temp_composefs_repo},
3839
finalize::{composefs_backend_finalize, get_etc_diff},
@@ -603,6 +604,11 @@ pub(crate) enum InternalsOpts {
603604
#[cfg(feature = "docgen")]
604605
/// Dump CLI structure as JSON for documentation generation
605606
DumpCliJson,
607+
PrepSoftReboot {
608+
deployment: String,
609+
#[clap(long)]
610+
reboot: bool,
611+
},
606612
}
607613

608614
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
@@ -1769,6 +1775,19 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
17691775

17701776
Ok(())
17711777
}
1778+
InternalsOpts::PrepSoftReboot { deployment, reboot } => {
1779+
let storage = &get_storage().await?;
1780+
1781+
match storage.kind()? {
1782+
BootedStorageKind::Ostree(..) => {
1783+
// TODO: Call ostree implementation?
1784+
anyhow::bail!("soft-reboot only implemented for composefs")
1785+
}
1786+
BootedStorageKind::Composefs(booted_cfs) => {
1787+
soft_reboot_to_deployment(&storage, &booted_cfs, &deployment, reboot).await
1788+
}
1789+
}
1790+
}
17721791
},
17731792
Opt::State(opts) => match opts {
17741793
StateOpts::WipeOstree => {

crates/lib/src/spec.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ pub(crate) struct DeploymentEntry<'a> {
295295
pub(crate) ty: Option<Slot>,
296296
pub(crate) deployment: &'a BootEntryComposefs,
297297
pub(crate) pinned: bool,
298+
pub(crate) soft_reboot_capable: bool,
298299
}
299300

300301
/// The result of a `bootc container inspect` command.
@@ -362,13 +363,15 @@ impl Host {
362363
ty: Some(Slot::Booted),
363364
deployment: booted,
364365
pinned: false,
366+
soft_reboot_capable: false,
365367
});
366368

367369
if let Some(staged) = &self.status.staged {
368370
all_deps.push(DeploymentEntry {
369371
ty: Some(Slot::Staged),
370372
deployment: staged.require_composefs()?,
371373
pinned: false,
374+
soft_reboot_capable: staged.soft_reboot_capable,
372375
});
373376
}
374377

@@ -377,6 +380,7 @@ impl Host {
377380
ty: Some(Slot::Rollback),
378381
deployment: rollback.require_composefs()?,
379382
pinned: false,
383+
soft_reboot_capable: rollback.soft_reboot_capable,
380384
});
381385
}
382386

@@ -385,6 +389,7 @@ impl Host {
385389
ty: None,
386390
deployment: pinned.require_composefs()?,
387391
pinned: true,
392+
soft_reboot_capable: pinned.soft_reboot_capable,
388393
});
389394
}
390395

0 commit comments

Comments
 (0)