Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,6 @@ pub(crate) const EFI_UUID_FILE: &str = "efiuuid.cfg";
/// The EFI Linux directory
pub(crate) const EFI_LINUX: &str = "EFI/Linux";

/// Timeout for systemd-boot bootloader menu
const SYSTEMD_TIMEOUT: &str = "timeout 5";
const SYSTEMD_LOADER_CONF_PATH: &str = "loader/loader.conf";

pub(crate) const INITRD: &str = "initrd";
pub(crate) const VMLINUZ: &str = "vmlinuz";

Expand Down Expand Up @@ -1759,13 +1755,6 @@ fn write_systemd_uki_config(
)?;
}

// Write the timeout for bootloader menu if not exists
if !esp_dir.exists(SYSTEMD_LOADER_CONF_PATH) {
esp_dir
.atomic_write(SYSTEMD_LOADER_CONF_PATH, SYSTEMD_TIMEOUT)
.with_context(|| format!("Writing to {SYSTEMD_LOADER_CONF_PATH}"))?;
}

let esp_dir = esp_dir
.reopen_as_ownedfd()
.context("Reopening as owned fd")?;
Expand Down
54 changes: 54 additions & 0 deletions crates/lib/src/bootloader.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fs::create_dir_all;
use std::io::Read;
use std::process::Command;
use std::sync::OnceLock;

Expand Down Expand Up @@ -262,6 +263,13 @@ pub(crate) fn install_via_bootupd(
}
}

/// Timeout for systemd-boot bootloader menu
///
/// If you update this, make sure to update the readonly test
/// 030-test-composefs.nu
const SYSTEMD_TIMEOUT: &str = "timeout 5";
const SYSTEMD_LOADER_CONF_PATH: &str = "loader/loader.conf";

/// Install systemd-boot using a pre-prepared boot root.
#[context("Installing bootloader")]
pub(crate) fn install_systemd_boot(
Expand Down Expand Up @@ -298,6 +306,13 @@ pub(crate) fn install_systemd_boot(
"Skipping --random-seed: requires systemd >= {BOOTCTL_RANDOM_SEED_MIN_VERSION}, found {systemd_version}"
);
}
} else {
// Update firmware variables if not a generic image
// This corresponds to --update-firmware CLI option for bootupd installs
// This option was added in systemd v258
if systemd_version()? >= 258 {
bootctl_args.push("--variables=yes");
}
}

Command::new("bootctl")
Expand Down Expand Up @@ -358,6 +373,45 @@ pub(crate) fn install_systemd_boot(
}
}

let esp_dir = prepared_root.open_esp_dir()?;

// systemd-boot creates a loader.conf files with all lines
// commented out
Comment on lines +378 to +379

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is just moving existing code but since we're doing that...this doesn't seem like something bootc should be hardcoding.

It should be easy and ergonomic to define the initial config file for systemd-boot as part of the container image itself.

So we should move this into e.g. fedora-bootc's base config or so. Or the package.

I really don't like having a new parser for the config file. (Which btw, could easily have unit tests)

match esp_dir
.open_optional(SYSTEMD_LOADER_CONF_PATH)
.context("Opening systemd-boot loader config")?
{
// Update timeout in the loader config file
// We can't use bootctl set-timeout as that updates EFI variables
Some(mut f) => {
let mut s = String::new();
f.read_to_string(&mut s)
.context("Reading systemd-boot loader config")?;

let mut new_contents = vec![SYSTEMD_TIMEOUT];

for line in s.lines() {
// We only want to replace the timeout
if line.contains("timeout") {
continue;
}

new_contents.push(line);
}

esp_dir
.atomic_write(SYSTEMD_LOADER_CONF_PATH, new_contents.join("\n"))
.with_context(|| format!("Writing to {SYSTEMD_LOADER_CONF_PATH}"))?;
}

// Write the timeout for bootloader menu as it doesn't exist
None => {
esp_dir
.atomic_write(SYSTEMD_LOADER_CONF_PATH, SYSTEMD_TIMEOUT)
.with_context(|| format!("Writing to {SYSTEMD_LOADER_CONF_PATH}"))?;
}
}

Ok(())
}

Expand Down
7 changes: 7 additions & 0 deletions tmt/tests/booted/readonly/030-test-composefs.nu
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ if $expecting_composefs {

if ($bootctl_output | str contains 'Product: systemd-boot') {
find_root_eq_in_cmdline "systemd-boot"

# Make sure our "timeout 5" is actually present

mkdir /var/tmp/efi
mount /dev/disk/by-partlabel/EFI-SYSTEM /var/tmp/efi

assert (open /var/tmp/efi/loader/loader.conf | lines | any {|s| $s == "timeout 5" })
}
}

Expand Down
2 changes: 1 addition & 1 deletion tmt/tests/booted/test-install-composefs-native.nu
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def install [image: string, ...args: string] {
let dropin = glob $"($MNT)/state/deploy/*/etc/tmpfiles.d/bootc-root-ssh.conf"
| append (glob $"($MNT)/ostree/deploy/*/deploy/*/etc/tmpfiles.d/bootc-root-ssh.conf")
| each {|p| {content: (open --raw $p | decode utf-8), mode: (stat -c %a $p | str trim)}}
umount $MNT
umount -l $MNT
rm -f $DISK
assert equal $dropin [{content: $"f~ /var/roothome/.ssh/authorized_keys 600 root root - ($KEY | encode base64)\n", mode: "644"}] "root ssh drop-in"
match [$composefs $ostree] {
Expand Down
10 changes: 5 additions & 5 deletions tmt/tests/booted/test-multi-device-esp.nu
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const ROOT_TYPE = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709"
# Cleanup function for LVM and loop devices
def cleanup [vg_name: string, loops: list<string>, mountpoint: string] {
# Unmount if mounted
do { umount $mountpoint } | complete | ignore
do { umount -l $mountpoint } | complete | ignore
do { rmdir $mountpoint } | complete | ignore

# Deactivate and remove LVM
Expand Down Expand Up @@ -126,7 +126,7 @@ def setup_disk_with_root [

# Simple cleanup for non-LVM scenarios (single loop device, no VG)
def cleanup_simple [loop: string, mountpoint: string] {
do { umount $mountpoint } | complete | ignore
do { umount -l $mountpoint } | complete | ignore
do { rmdir $mountpoint } | complete | ignore

if ($loop | path exists) {
Expand All @@ -143,14 +143,14 @@ def validate_esp [esp_partition: string] {
# Check for EFI directory with bootloader files
let efi_dir = $"($esp_mount)/EFI"
if not ($efi_dir | path exists) {
umount $esp_mount
umount -l $esp_mount
rmdir $esp_mount
error make {msg: $"ESP validation failed: EFI directory not found on ($esp_partition)"}
}

# Verify there's actual content in EFI (not just empty)
let efi_contents = (ls $efi_dir | length)
umount $esp_mount
umount -l $esp_mount
rmdir $esp_mount

if $efi_contents == 0 {
Expand All @@ -164,7 +164,7 @@ def with_esp [esp_partition: string, f: closure] {
mkdir $esp_mount
mount $esp_partition $esp_mount
let r = (do $f $esp_mount)
umount $esp_mount
umount -l $esp_mount
rmdir $esp_mount
$r
}
Expand Down
Loading