Skip to content

Commit 80b9332

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 <pragyanpoudyal41999@gmail.com>
1 parent e301380 commit 80b9332

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
@@ -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: 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
@@ -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,
@@ -593,6 +594,11 @@ pub(crate) enum InternalsOpts {
593594
#[cfg(feature = "docgen")]
594595
/// Dump CLI structure as JSON for documentation generation
595596
DumpCliJson,
597+
PrepSoftReboot {
598+
deployment: String,
599+
#[clap(long)]
600+
reboot: bool,
601+
},
596602
}
597603

598604
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
@@ -1764,6 +1770,19 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
17641770

17651771
Ok(())
17661772
}
1773+
InternalsOpts::PrepSoftReboot { deployment, reboot } => {
1774+
let storage = &get_storage().await?;
1775+
1776+
match storage.kind()? {
1777+
BootedStorageKind::Ostree(..) => {
1778+
// TODO: Call ostree implementation?
1779+
anyhow::bail!("soft-reboot only implemented for composefs")
1780+
}
1781+
BootedStorageKind::Composefs(booted_cfs) => {
1782+
soft_reboot_to_deployment(&storage, &booted_cfs, &deployment, reboot).await
1783+
}
1784+
}
1785+
}
17671786
},
17681787
Opt::State(opts) => match opts {
17691788
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)