Skip to content

Commit 68e3303

Browse files
committed
Add bootupctl remove-component
1 parent b7c1da3 commit 68e3303

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

src/bootupd.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use camino::{Utf8Path, Utf8PathBuf};
2424
use clap::crate_version;
2525
use fn_error_context::context;
2626
use libc::mode_t;
27+
use openat_ext::OpenatDirExt;
2728
use serde::{Deserialize, Serialize};
2829
use std::borrow::Cow;
2930
use std::collections::BTreeMap;
@@ -653,6 +654,36 @@ pub(crate) fn client_run_validate() -> Result<()> {
653654
Ok(())
654655
}
655656

657+
pub(crate) fn client_run_remove_component(component_name: &str) -> Result<()> {
658+
let sysroot = openat::Dir::open("/").context("opening sysroot directory /")?;
659+
660+
let components = get_available_components(&sysroot)?;
661+
662+
// Find the component (ignore ASCII case)
663+
if let Some((name, component)) = components
664+
.iter()
665+
.find(|(k, _)| k.eq_ignore_ascii_case(component_name))
666+
{
667+
let comp_dirname = crate::component::component_updatedirname(component.as_ref());
668+
669+
// Construct the path relative to sysroot: BOOTUPD_UPDATES_DIR/<comp>.json
670+
let path = comp_dirname.with_extension("json");
671+
672+
// Remove the file using the sysroot Dir handle
673+
sysroot.remove_file_optional(&path).map_err(|e| {
674+
if let Some(libc::EACCES) = e.raw_os_error() {
675+
anyhow::anyhow!("Permission denied: Cannot remove component file at /{}. Try running with sudo.", path.display())
676+
} else {
677+
anyhow::anyhow!(e).context(format!("Failed to remove component file /{}", path.display()))
678+
}
679+
})?;
680+
println!("Removed component '{}'", name);
681+
} else {
682+
anyhow::bail!("Could not find component '{}'", component_name);
683+
}
684+
Ok(())
685+
}
686+
656687
#[context("Migrating to a static GRUB config")]
657688
pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
658689
// Did we already complete the migration?

src/cli/bootupctl.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub enum CtlVerb {
6565
about = "Migrate a system to a static GRUB config"
6666
)]
6767
MigrateStaticGrubConfig,
68+
#[clap(name = "remove-component", about = "Remove component")]
69+
RemoveComponent(RemoveComponentOpts),
6870
}
6971

7072
#[derive(Debug, Parser)]
@@ -95,6 +97,13 @@ pub struct AdoptAndUpdateOpts {
9597
with_static_config: bool,
9698
}
9799

100+
#[derive(Debug, Parser)]
101+
pub struct RemoveComponentOpts {
102+
/// Component name to be removed
103+
#[clap(value_parser)]
104+
component: String,
105+
}
106+
98107
impl CtlCommand {
99108
/// Run CLI application.
100109
pub fn run(self) -> Result<()> {
@@ -103,6 +112,7 @@ impl CtlCommand {
103112
CtlVerb::Update => Self::run_update(),
104113
CtlVerb::AdoptAndUpdate(opts) => Self::run_adopt_and_update(opts),
105114
CtlVerb::Validate => Self::run_validate(),
115+
CtlVerb::RemoveComponent(opts) => Self::run_remove_component(opts),
106116
CtlVerb::Backend(CtlBackend::Generate(opts)) => {
107117
super::bootupd::DCommand::run_generate_meta(opts)
108118
}
@@ -151,6 +161,11 @@ impl CtlCommand {
151161
bootupd::client_run_validate()
152162
}
153163

164+
/// Runner for `remove-component` verb.
165+
fn run_remove_component(opts: RemoveComponentOpts) -> Result<()> {
166+
bootupd::client_run_remove_component(&opts.component)
167+
}
168+
154169
/// Runner for `migrate-static-grub-config` verb.
155170
fn run_migrate_static_grub_config() -> Result<()> {
156171
ensure_running_in_systemd()?;

tests/kola/test-bootupd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ if [ -d /sys/firmware/efi ]; then
141141
update_metadata_move EFI-bak EFI
142142

143143
# Should succeed if BIOS metadata is missing
144-
rm -f ${bootupdir}/BIOS.json
144+
bootupctl remove-component bios
145145
bootupctl update | tee out.txt
146146
assert_file_has_content out.txt 'Adopted and updated: EFI:'
147147
assert_not_file_has_content out.txt 'Adopted and updated: BIOS:'
@@ -156,7 +156,7 @@ else
156156
update_metadata_move BIOS-bak BIOS
157157

158158
# Should succeed if EFI metadata is missing
159-
rm -f ${bootupdir}/EFI.json
159+
bootupctl remove-component efi
160160
bootupctl update | tee out.txt
161161
assert_file_has_content out.txt 'Adopted and updated: BIOS:'
162162
assert_not_file_has_content out.txt 'Adopted and updated: EFI:'

tests/tests/bootupctl-status-in-bootc.sh

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ if [ "$container" ] || [ -f /run/.containerenv ] || [ -f /.dockerenv ]; then
2424
if [ "${arch}" == "x86_64" ]; then
2525
[ "${components_text_x86_64}" == "${output_text}" ]
2626
[ "${components_json_x86_64}" == "${output_json}" ]
27-
# test if BIOS.json is missing
28-
mv /usr/lib/bootupd/updates/BIOS.json{,-bak}
27+
# test with no BIOS.json
28+
bootupctl remove-component bios
2929
output_text=$(bootupctl status | tr -d '\r')
3030
output_json=$(bootupctl status --json)
3131
fi
@@ -35,13 +35,18 @@ if [ "$container" ] || [ -f /run/.containerenv ] || [ -f /.dockerenv ]; then
3535
[ "${components_json_aarch64}" == "${output_json}" ]
3636
fi
3737

38-
# test if no components
39-
mv /usr/lib/bootupd/updates/EFI.json{,-bak}
38+
# test with no components
39+
bootupctl remove-component efi
4040
output_text=$(bootupctl status | tr -d '\r')
4141
output_json=$(bootupctl status --json)
4242
[ -z "${output_text}" ]
4343
[ "${none_components_json}" == "${output_json}" ]
4444

45+
# remove none existing component
46+
if bootupctl remove-component test 2>err.txt; then
47+
echo "unexpectedly passed remove none existing component"
48+
exit 1
49+
fi
4550
else
4651
echo "Skip running as not in container"
4752
fi

0 commit comments

Comments
 (0)