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(()) }