From 59aa03d83517e9f1a3d4d5bc69afa167664c6ca8 Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Sun, 22 Dec 2024 13:00:40 +0000 Subject: [PATCH] blsforme: Switch to `sync()` rather than individual install calls This allows us to group the contextual logic and gather necessary details for future cleanups, as well as ensuring each cmdline representation is specific to the sysroot it came from. Signed-off-by: Ikey Doherty --- blsforme/src/bootloader/mod.rs | 11 ++++++ blsforme/src/bootloader/systemd_boot/mod.rs | 39 +++++++++++++++---- blsforme/src/entry.rs | 42 ++++++++++++++++----- blsforme/src/manager.rs | 34 ++++------------- 4 files changed, 83 insertions(+), 43 deletions(-) diff --git a/blsforme/src/bootloader/mod.rs b/blsforme/src/bootloader/mod.rs index 91695ee..26e56c4 100644 --- a/blsforme/src/bootloader/mod.rs +++ b/blsforme/src/bootloader/mod.rs @@ -60,6 +60,17 @@ impl<'a, 'b> Bootloader<'a, 'b> { } } + pub fn sync_entries( + &self, + cmdline: impl Iterator, + entries: &[Entry], + excluded_snippets: impl Iterator, + ) -> Result<(), Error> { + match &self { + Bootloader::Systemd(s) => s.sync_entries(cmdline, entries, excluded_snippets), + } + } + /// Install a single kernel, create records for it. pub fn install(&self, cmdline: &str, entry: &Entry) -> Result<(), Error> { match &self { diff --git a/blsforme/src/bootloader/systemd_boot/mod.rs b/blsforme/src/bootloader/systemd_boot/mod.rs index 446fac9..3500170 100644 --- a/blsforme/src/bootloader/systemd_boot/mod.rs +++ b/blsforme/src/bootloader/systemd_boot/mod.rs @@ -95,6 +95,36 @@ impl<'a, 'b> Loader<'a, 'b> { Ok(()) } + pub(super) fn sync_entries( + &self, + cmdline: impl Iterator, + entries: &[Entry], + excluded_snippets: impl Iterator, + ) -> Result<(), super::Error> { + let base_cmdline = cmdline.map(str::to_string).collect::>(); + let exclusions = excluded_snippets.map(str::to_string).collect::>(); + for entry in entries { + let entry_cmdline = entry + .cmdline + .iter() + .filter(|c| !exclusions.contains(&c.name)) + .map(|c| c.snippet.clone()) + .collect::>(); + let mut full_cmdline = base_cmdline + .iter() + .chain(entry_cmdline.iter()) + .cloned() + .collect::>(); + + // kernel specific cmdline + if let Some(k_cmdline) = entry.kernel.cmdline.as_ref() { + full_cmdline.push(k_cmdline.clone()); + } + self.install(&full_cmdline.join(" "), entry)?; + } + Ok(()) + } + /// Install a kernel to the ESP or XBOOTLDR, write a config for it pub(super) fn install(&self, cmdline: &str, entry: &Entry) -> Result<(), super::Error> { let loader_id = self @@ -185,17 +215,12 @@ impl<'a, 'b> Loader<'a, 'b> { format!("{} ({})", self.schema.os_release().name, entry.kernel.version) }; let vmlinuz = entry.installed_kernel_name(self.schema).expect("linux go boom"); - let options = if let Some(k_cmdline) = entry.kernel.cmdline.as_ref() { - format!("{cmdline} {k_cmdline}") - } else { - cmdline.to_string() - }; format!( r###"title {title} linux /{asset_dir}/{}{} -options {} +options {cmdline} "###, - vmlinuz, initrd, options + vmlinuz, initrd ) } diff --git a/blsforme/src/entry.rs b/blsforme/src/entry.rs index df17ecc..7e2fa25 100644 --- a/blsforme/src/entry.rs +++ b/blsforme/src/entry.rs @@ -4,7 +4,17 @@ use std::path::PathBuf; -use crate::{AuxiliaryFile, Kernel, Schema}; +use crate::{file_utils::cmdline_snippet, AuxiliaryFile, Configuration, Kernel, Schema}; + +/// A cmdline entry is found in the `$sysroot/usr/lib/kernel/cmdline.d` directory +#[derive(Debug)] +pub struct CmdlineEntry { + /// Name of the entry, i.e. `00-quiet.cmdline` + pub name: String, + + /// Text contents of this cmdline entry + pub snippet: String, +} /// An entry corresponds to a single kernel, and may have a supplemental /// cmdline @@ -14,9 +24,7 @@ pub struct Entry<'a> { pub(crate) sysroot: Option, - // Additional cmdline - #[allow(dead_code)] - cmdline: Option, + pub(crate) cmdline: Vec, } impl<'a> Entry<'a> { @@ -24,20 +32,34 @@ impl<'a> Entry<'a> { pub fn new(kernel: &'a Kernel) -> Self { Self { kernel, - cmdline: None, + cmdline: vec![], sysroot: None, } } - /// With the following cmdline - pub fn with_cmdline(self, cmdline: impl AsRef) -> Self { - Self { - cmdline: Some(cmdline.as_ref().to_string()), - ..self + /// Load cmdline snippets from the system root for this entry's sysroot + pub fn load_cmdline_snippets(&mut self, config: &Configuration) -> Result<(), super::Error> { + let sysroot = self.sysroot.clone().unwrap_or(config.root.path().into()); + let cmdline_d = sysroot.join("usr").join("lib").join("kernel").join("cmdline.d"); + + if !cmdline_d.exists() { + return Ok(()); } + + let entries = std::fs::read_dir(&cmdline_d)?; + + for entry in entries { + let entry = entry?; + let name = entry.file_name().to_string_lossy().to_string(); + let snippet = cmdline_snippet(entry.path())?; + self.cmdline.push(CmdlineEntry { name, snippet }); + } + + Ok(()) } /// With the given system root + /// This will cause any local snippets to be discovered pub fn with_sysroot(self, sysroot: impl Into) -> Self { Self { sysroot: Some(sysroot.into()), diff --git a/blsforme/src/manager.rs b/blsforme/src/manager.rs index 714fa12..7e3c730 100644 --- a/blsforme/src/manager.rs +++ b/blsforme/src/manager.rs @@ -211,32 +211,14 @@ impl<'a> Manager<'a> { } // Firstly, get the bootloader updated. let bootloader = self.bootloader(schema)?; - - // Install every kernel that was passed to us - for entry in self.entries.iter() { - let root_dir = entry - .sysroot - .clone() - .unwrap_or_else(|| self.config.root.path().to_path_buf()); - let mut cmdline = self.cmdline.clone(); - - // Merge the system-wide cmdline with the rootfs cmdline - if let Ok(it) = fs::read_dir(root_dir.join("usr").join("lib").join("kernel").join("cmdline.d")) { - log::trace!("reading system cmdline.d entries"); - let entries = it - .filter_map(|p| p.ok()) - .filter(|d| { - if let Some(name) = d.file_name().to_str() { - !self.system_excluded_snippets.contains(&name.to_string()) - } else { - true - } - }) - .filter_map(|p| cmdline_snippet(p.path()).ok()); - cmdline.extend(entries); - } - bootloader.install(&cmdline.join(" "), entry)?; - } + bootloader.sync()?; + + // Sync the entries + bootloader.sync_entries( + self.cmdline.iter().map(String::as_str), + &self.entries, + self.system_excluded_snippets.iter().map(String::as_str), + )?; Ok(()) }