Skip to content

Commit

Permalink
WIP: Import GRUB static migration code
Browse files Browse the repository at this point in the history
  • Loading branch information
travier committed Dec 3, 2024
1 parent 822640c commit c940c2e
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 1 deletion.
91 changes: 90 additions & 1 deletion src/bootupd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ use crate::model::{ComponentStatus, ComponentUpdatable, ContentMetadata, SavedSt
use crate::util;
use anyhow::{anyhow, Context, Result};
use clap::crate_version;
use openat_ext::OpenatDirExt;
use rustix::fs::link;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::thread::current;

pub(crate) enum ConfigMode {
None,
Expand Down Expand Up @@ -203,6 +206,10 @@ fn ensure_writable_boot() -> Result<()> {
util::ensure_writable_mount("/boot")
}

fn ensure_writable_sysroot() -> Result<()> {
util::ensure_writable_mount("/sysroot")
}

/// daemon implementation of component update
pub(crate) fn update(name: &str) -> Result<ComponentUpdateResult> {
let mut state = SavedState::load_from_disk("/")?.unwrap_or_default();
Expand Down Expand Up @@ -489,6 +496,88 @@ pub(crate) fn client_run_validate() -> Result<()> {
Ok(())
}

pub(crate) fn client_run_migrate() -> Result<()> {
// Used to condition execution of this unit at the systemd level
let stamp_file="/var/lib/.fedora_atomic_desktops_static_grub";

// Did we already complete the migration?
let ostree_cmd = std::process::Command::new("ostree")
.args(["config", "--repo=/sysroot/ostree/repo", "get", "sysroot.bootloader"]);
let bootloader = util::cmd_output(ostree_cmd).ok_or_else(|| anyhow!("Could not find read 'sysroot.bootloader' config from ostree repo. Not performing migration."));
if bootloader == "none" {
println!("ostree repo 'sysroot.bootloader' config option already set to 'none'.");
println!("Assuming that the migration is already complete.");
File::create(stamp_file)?;
return Ok(());
}

// Remount /boot read write just for this unit (we are called in a slave mount namespace by systemd)
ensure_writable_boot();

let grub_config_dir = PathBuf::from("/boot/grub2");
let dirfd = openat::Dir::open(grub_config_dir).ok_or_else(|| anyhow!("Could not open {}. Maybe /boot is not mounted? Not performing migration.", grub_config_dir));

// Migrate /boot/grub2/grub.cfg to a static GRUB config if it is a symlink
let grub_config_filename ="/boot/grub2/grub.cfg";
match dirfd.read_link("grub.cfg") {
Err(_) => {
println!("/boot/grub2/grub.cfg is not a symlink. Nothing to migrate.");
},
Ok(path) => {
println!("Migrating to a static GRUB config...");

// Backup the current GRUB config which is hopefully working right now
println!("Creating a backup of the current GRUB config in '/boot/grub2/grub.cfg.backup'...");

let current_config_path = grub_config_dir.clone();
current_config_path.push(path);
fs::copy(current_config_path, "/boot/grub2/grub.cfg.backup").ok_or_else(|| anyhow!("Could not copy the current GRUB config. Not performing migration."));

// Copy it again alongside the current symlink
fs::copy(current_config_path, "/boot/grub2/grub.cfg.current").ok_or_else(|| anyhow!("Could not copy the current GRUB config. Not performing migration."));

// Atomically exchange the configs
dirfd.local_exchange("grub.cfg.current", "/grub.cfg")?;

// Remove the now unsused symlink (optional cleanup, ignore any failures)
dirfd.remove_file("grub.cfg.current");

println!("GRUB config symlink successfully replaced with the current config.");
},
};

// # If /etc/default/grub exists then we have to force the regeneration of the
// # GRUB config to remove the ostree entries that duplicates the BLS ones
// if [[ -f "/etc/default/grub" ]]; then
// echo "Marking bootloader as BLS capable..."
// touch "/boot/grub2/.grub2-blscfg-supported"

// echo "Regenerating a pure BLS GRUB config..."
// grub2-mkconfig -o /boot/grub2/grub.cfg
// fi
if !destdir.exists(&first_dir_tmp)? {
copy_dir(destdir, first_dir.as_str(), &first_dir_tmp)?;
updates.insert(first_dir, first_dir_tmp);
}

// # Remount /sysroot read write just for this unit (we are called in a slave mount namespace by systemd)
ensure_writable_sysroot();

println!("Setting up 'sysroot.bootloader' to 'none' in ostree repo config...");
let status = std::process::Command::new("ostree")
.args(["config", "--repo=/sysroot/ostree/repo", "set", "sysroot.bootloader", "none"])
.status()?;
if !status.success() {
anyhow::bail!("Failed to set 'sysroot.bootloader' to 'none' in ostree repo config");
}

// Migration complete, let's write the stamp file
File::create(stamp_file)?;

println!("Static GRUB config migration completed successfully!");
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
9 changes: 9 additions & 0 deletions src/cli/bootupctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum CtlVerb {
AdoptAndUpdate,
#[clap(name = "validate", about = "Validate system state")]
Validate,
#[clap(name = "migrate", about = "Migrate a system to static a GRUB config")]
Migrate,
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -95,6 +97,7 @@ impl CtlCommand {
CtlVerb::Backend(CtlBackend::Install(opts)) => {
super::bootupd::DCommand::run_install(opts)
}
CtlVerb::Migrate => Self::run_migrate(),
}
}

Expand Down Expand Up @@ -132,6 +135,12 @@ impl CtlCommand {
ensure_running_in_systemd()?;
bootupd::client_run_validate()
}

/// Runner for `migrate` verb.
fn run_migrate() -> Result<()> {
ensure_running_in_systemd()?;
bootupd::client_run_migrate()
}
}

/// Checks if the current process is (apparently at least)
Expand Down

0 comments on commit c940c2e

Please sign in to comment.