Skip to content

Commit 4851161

Browse files
committed
wip soft-reboot
1 parent 3f0bf09 commit 4851161

File tree

4 files changed

+758
-12
lines changed

4 files changed

+758
-12
lines changed

lib/src/cli.rs

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use schemars::schema_for;
2626
use serde::{Deserialize, Serialize};
2727

2828
use crate::deploy::RequiredHostSpec;
29-
use crate::lints;
29+
use crate::{lints, reboot};
3030
use crate::progress_jsonl::{ProgressWriter, RawProgressFd};
3131
use crate::spec::Host;
3232
use crate::spec::ImageReference;
@@ -536,7 +536,7 @@ pub(crate) enum Opt {
536536
Note on Rollbacks and the `/etc` Directory:
537537
538538
When you perform a rollback (e.g., with `bootc rollback`), any
539-
changes made to files in the `/etc` directory wont carry over
539+
changes made to files in the `/etc` directory won't carry over
540540
to the rolled-back deployment. The `/etc` files will revert
541541
to their state from that previous deployment instead.
542542
@@ -835,7 +835,35 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
835835
println!("Staged update present, not changed.");
836836

837837
if opts.apply {
838-
crate::reboot::reboot()?;
838+
// Check if we can do a soft-reboot instead of a full reboot
839+
let can_soft_reboot = host.status.staged
840+
.as_ref()
841+
.map(|s| s.soft_reboot_capable)
842+
.unwrap_or(false);
843+
844+
if can_soft_reboot {
845+
println!("Staged deployment is soft-reboot capable, performing soft-reboot...");
846+
847+
// Find the index of the staged deployment
848+
let deployments_list = sysroot.deployments();
849+
let staged_index = deployments_list
850+
.iter()
851+
.position(|d| d.is_staged())
852+
.ok_or_else(|| anyhow::anyhow!("Failed to find staged deployment index"))?;
853+
854+
// Prepare the soft-reboot using ostree admin prepare-soft-reboot
855+
let mut cmd = std::process::Command::new("ostree");
856+
cmd.args(["admin", "prepare-soft-reboot", &staged_index.to_string()]);
857+
let status = cmd.status().context("Failed to run ostree admin prepare-soft-reboot")?;
858+
if !status.success() {
859+
anyhow::bail!("ostree admin prepare-soft-reboot failed");
860+
}
861+
862+
// Perform the soft-reboot
863+
crate::reboot::soft_reboot()?;
864+
} else {
865+
crate::reboot::reboot()?;
866+
}
839867
}
840868
} else if booted_unchanged {
841869
println!("No update available.")
@@ -931,7 +959,36 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
931959
sysroot.update_mtime()?;
932960

933961
if opts.apply {
934-
crate::reboot::reboot()?;
962+
// Get updated status to check for soft-reboot capability
963+
let (_updated_deployments, updated_host) = crate::status::get_status(sysroot, Some(&booted_deployment))?;
964+
let can_soft_reboot = updated_host.status.staged
965+
.as_ref()
966+
.map(|s| s.soft_reboot_capable)
967+
.unwrap_or(false);
968+
969+
if can_soft_reboot {
970+
println!("Staged deployment is soft-reboot capable, performing soft-reboot...");
971+
972+
// Find the index of the staged deployment
973+
let deployments_list = sysroot.deployments();
974+
let staged_index = deployments_list
975+
.iter()
976+
.position(|d| d.is_staged())
977+
.ok_or_else(|| anyhow::anyhow!("Failed to find staged deployment index"))?;
978+
979+
// Prepare the soft-reboot using ostree admin prepare-soft-reboot
980+
let mut cmd = std::process::Command::new("ostree");
981+
cmd.args(["admin", "prepare-soft-reboot", &staged_index.to_string()]);
982+
let status = cmd.status().context("Failed to run ostree admin prepare-soft-reboot")?;
983+
if !status.success() {
984+
anyhow::bail!("ostree admin prepare-soft-reboot failed");
985+
}
986+
987+
// Perform the soft-reboot
988+
crate::reboot::soft_reboot()?;
989+
} else {
990+
crate::reboot::reboot()?;
991+
}
935992
}
936993

937994
Ok(())
@@ -941,10 +998,39 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
941998
#[context("Rollback")]
942999
async fn rollback(opts: RollbackOpts) -> Result<()> {
9431000
let sysroot = &get_storage().await?;
944-
crate::deploy::rollback(sysroot).await?;
945-
1001+
9461002
if opts.apply {
947-
crate::reboot::reboot()?;
1003+
// Get status before rollback to check soft-reboot capability
1004+
let (_booted_deployment, _deployments, host) = crate::status::get_status_require_booted(sysroot)?;
1005+
let can_soft_reboot = host.status.rollback
1006+
.as_ref()
1007+
.map(|r| r.soft_reboot_capable)
1008+
.unwrap_or(false);
1009+
1010+
// Perform the rollback
1011+
crate::deploy::rollback(sysroot).await?;
1012+
1013+
if can_soft_reboot {
1014+
println!("Rollback deployment is soft-reboot capable, performing soft-reboot...");
1015+
1016+
// For rollback, the target deployment is at index 0 after the rollback operation
1017+
let target_index = 0;
1018+
1019+
// Prepare the soft-reboot using ostree admin prepare-soft-reboot
1020+
let mut cmd = std::process::Command::new("ostree");
1021+
cmd.args(["admin", "prepare-soft-reboot", &target_index.to_string()]);
1022+
let status = cmd.status().context("Failed to run ostree admin prepare-soft-reboot")?;
1023+
if !status.success() {
1024+
anyhow::bail!("ostree admin prepare-soft-reboot failed");
1025+
}
1026+
1027+
// Perform the soft-reboot
1028+
crate::reboot::soft_reboot()?;
1029+
} else {
1030+
crate::reboot::reboot()?;
1031+
}
1032+
} else {
1033+
crate::deploy::rollback(sysroot).await?;
9481034
}
9491035

9501036
Ok(())

lib/src/reboot.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,29 @@ use fn_error_context::context;
66

77
use crate::task::Task;
88

9-
/// Initiate a system reboot.
9+
/// Common reboot implementation that handles flushing streams and running the reboot command.
1010
/// This function will only return in case of error.
11-
#[context("Initiating reboot")]
12-
pub(crate) fn reboot() -> anyhow::Result<()> {
11+
fn perform_reboot(task_name: &str, command: &str) -> anyhow::Result<()> {
1312
// Flush output streams
1413
let _ = std::io::stdout().flush();
1514
let _ = std::io::stderr().flush();
16-
Task::new("Rebooting system", "reboot").run()?;
17-
tracing::debug!("Initiated reboot, sleeping forever...");
15+
Task::new(task_name, command).run()?;
16+
tracing::debug!("Initiated {}, sleeping forever...", task_name.to_lowercase());
1817
loop {
1918
std::thread::park();
2019
}
2120
}
21+
22+
/// Initiate a system reboot.
23+
/// This function will only return in case of error.
24+
#[context("Initiating reboot")]
25+
pub(crate) fn reboot() -> anyhow::Result<()> {
26+
perform_reboot("Rebooting system", "reboot")
27+
}
28+
29+
/// Initiate a soft reboot (userspace-only restart).
30+
/// This function will only return in case of error.
31+
#[context("Initiating soft reboot")]
32+
pub(crate) fn soft_reboot() -> anyhow::Result<()> {
33+
perform_reboot("Performing soft-reboot", "systemctl soft-reboot")
34+
}

lib/src/status.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ fn human_render_slot(
422422
writeln!(out, "yes")?;
423423
}
424424

425+
if entry.soft_reboot_capable {
426+
write_row_name(&mut out, "Soft-reboot", prefix_len)?;
427+
writeln!(out, "capable")?;
428+
}
429+
425430
tracing::debug!("pinned={}", entry.pinned);
426431

427432
Ok(())
@@ -451,6 +456,11 @@ fn human_render_slot_ostree(
451456
writeln!(out, "yes")?;
452457
}
453458

459+
if entry.soft_reboot_capable {
460+
write_row_name(&mut out, "Soft-reboot", prefix_len)?;
461+
writeln!(out, "capable")?;
462+
}
463+
454464
tracing::debug!("pinned={}", entry.pinned);
455465
Ok(())
456466
}

0 commit comments

Comments
 (0)