diff --git a/gucc/include/gucc/bootloader.hpp b/gucc/include/gucc/bootloader.hpp index 340e27c..49f4dc5 100644 --- a/gucc/include/gucc/bootloader.hpp +++ b/gucc/include/gucc/bootloader.hpp @@ -56,9 +56,24 @@ struct GrubConfig final { std::optional disable_os_prober{}; }; -// Generate grub_config into string +struct GrubInstallConfig final { + bool is_efi{}; + bool do_recheck{}; + bool is_removable{}; + bool is_root_on_zfs{}; + std::optional efi_directory{}; + std::optional bootloader_id{}; +}; + +// Generate grub config into string auto gen_grub_config(const GrubConfig& grub_config) noexcept -> std::string; +// Writes grub config to file on the system +auto write_grub_config(const GrubConfig& grub_config, std::string_view root_mountpoint) noexcept -> bool; + +// Installs and configures grub on system +auto install_grub(const GrubConfig& grub_config, const GrubInstallConfig& grub_install_config, std::string_view root_mountpoint) noexcept -> bool; + // Installs & configures systemd-boot on system auto install_systemd_boot(std::string_view root_mountpoint, std::string_view efi_directory, bool is_volume_removable) noexcept -> bool; diff --git a/gucc/src/bootloader.cpp b/gucc/src/bootloader.cpp index d02b581..c3c783c 100644 --- a/gucc/src/bootloader.cpp +++ b/gucc/src/bootloader.cpp @@ -1,4 +1,5 @@ #include "gucc/bootloader.hpp" +#include "gucc/file_utils.hpp" #include "gucc/initcpio.hpp" #include "gucc/io_utils.hpp" #include "gucc/string_utils.hpp" @@ -219,4 +220,60 @@ auto install_systemd_boot(std::string_view root_mountpoint, std::string_view efi return true; } +auto write_grub_config(const GrubConfig& grub_config, std::string_view root_mountpoint) noexcept -> bool { + const auto& grub_config_content = bootloader::gen_grub_config(grub_config); + const auto& grub_config_path = fmt::format(FMT_COMPILE("{}/etc/default/grub"), root_mountpoint); + if (!file_utils::create_file_for_overwrite(grub_config_path, grub_config_content)) { + spdlog::error("Failed to open grub config for writing {}", grub_config_path); + return false; + } + return true; +} + +auto install_grub(const GrubConfig& grub_config, const GrubInstallConfig& grub_install_config, std::string_view root_mountpoint) noexcept -> bool { + // Write grub configuration on the system + if (!bootloader::write_grub_config(grub_config, root_mountpoint)) { + return false; + } + + // Get grub install cmd + const auto& grub_install_cmd = [](const GrubInstallConfig& grub_install_config) -> std::string { + std::string result{}; + if (grub_install_config.is_root_on_zfs) { + result += "ZPOOL_VDEV_NAME_PATH=YES "sv; + } + result += "grub-install --target="sv; + result += grub_install_config.is_efi ? "x86_64-efi"sv : "i386-pc"sv; + + if (grub_install_config.do_recheck) { + result += " --recheck"; + } + if (grub_install_config.is_removable) { + result += " --removable"; + } + if (grub_install_config.is_efi && grub_install_config.efi_directory) { + result += fmt::format(FMT_COMPILE(" --efi-directory={}"), *grub_install_config.efi_directory); + } + if (grub_install_config.is_efi && grub_install_config.bootloader_id) { + result += fmt::format(FMT_COMPILE(" --bootloader-id={}"), *grub_install_config.bootloader_id); + } + return result; + }(grub_install_config); + + // Install grub on the system + if (!utils::arch_chroot_checked(grub_install_cmd, root_mountpoint)) { + spdlog::error("Failed to install grub on path {} with: {}", root_mountpoint, grub_install_cmd); + return false; + } + + // Generate grub configuration on the boot partition + static constexpr auto grub_config_cmd = "grub-mkconfig -o /boot/grub/grub.cfg"sv; + if (!utils::arch_chroot_checked(grub_config_cmd, root_mountpoint)) { + spdlog::error("Failed to generate grub on path {} with: {}", root_mountpoint, grub_config_cmd); + return false; + } + + return true; +} + } // namespace gucc::bootloader diff --git a/src/utils.cpp b/src/utils.cpp index 26b4169..8a401c9 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -967,40 +967,47 @@ void install_grub_uefi(const std::string_view& bootid, bool as_default) noexcept const auto& luks_dev = std::get(config_data["LUKS_DEV"]); const auto& grub_installer_path = fmt::format(FMT_COMPILE("{}/usr/bin/grub_installer.sh"), mountpoint); + gucc::bootloader::GrubConfig grub_config_struct{}; + gucc::bootloader::GrubInstallConfig grub_install_config_struct{}; + + grub_install_config_struct.is_efi = true; + grub_install_config_struct.do_recheck = true; + grub_install_config_struct.efi_directory = uefi_mount; + grub_install_config_struct.bootloader_id = bootid; + // grub config changes for zfs root if (gucc::fs::utils::get_mountpoint_fs(mountpoint) == "zfs") { // zfs needs ZPOOL_VDEV_NAME_PATH set to properly find the device - gucc::utils::exec(fmt::format(FMT_COMPILE("echo ZPOOL_VDEV_NAME_PATH=YES >> {}/etc/environment"), mountpoint)); - setenv("ZPOOL_VDEV_NAME_PATH", "YES", 1); + gucc::utils::exec(fmt::format(FMT_COMPILE("echo 'ZPOOL_VDEV_NAME_PATH=YES' >> {}/etc/environment"), mountpoint)); - constexpr auto bash_codepart1 = R"(#!/bin/bash + grub_install_config_struct.is_root_on_zfs = true; + + // zfs is considered a sparse filesystem so we can't use SAVEDEFAULT + grub_config_struct.savedefault = std::nullopt; + + // we need to tell grub where the zfs root is + const auto& mountpoint_source = gucc::fs::utils::get_mountpoint_source(mountpoint); + const auto& zroot_var = fmt::format(FMT_COMPILE("zfs={} rw"), mountpoint_source); + grub_config_struct.cmdline_linux_default = fmt::format(FMT_COMPILE("{} {}"), grub_config_struct.cmdline_linux_default, zroot_var); + grub_config_struct.cmdline_linux = fmt::format(FMT_COMPILE("{} {}"), grub_config_struct.cmdline_linux, zroot_var); + + constexpr auto bash_code = R"(#!/bin/bash ln -s /hostlvm /run/lvm -export ZPOOL_VDEV_NAME_PATH=YES pacman -S --noconfirm --needed grub efibootmgr dosfstools -# zfs is considered a sparse filesystem so we can't use SAVEDEFAULT -sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub -# we need to tell grub where the zfs root is)"; - - const auto& mountpoint_source = gucc::fs::utils::get_mountpoint_source(mountpoint); - const auto& zroot_var = fmt::format(FMT_COMPILE("zroot=\"zfs={} rw\""), mountpoint_source); - - constexpr auto bash_codepart2 = R"( -sed -e '/^GRUB_CMDLINE_LINUX_DEFAULT=/s@"$@ '"${zroot}"'"@g' -e '/^GRUB_CMDLINE_LINUX=/s@"$@ '"${zroot}"'"@g' -i /etc/default/grub -sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub)"; - - static constexpr auto mkconfig_codepart = "grub-mkconfig -o /boot/grub/grub.cfg"; - const auto& bash_code = fmt::format(FMT_COMPILE("{}\n{}\n{}\ngrub-install --target=x86_64-efi --efi-directory={} --bootloader-id={} --recheck\n{}\n"), bash_codepart1, zroot_var, bash_codepart2, uefi_mount, bootid, mkconfig_codepart); +)"; std::ofstream grub_installer{grub_installer_path}; grub_installer << bash_code; } else { - constexpr auto bash_codepart = R"(#!/bin/bash + // we need to disable SAVEDEFAULT if either we are on LVM or BTRFS + const auto is_root_lvm = gucc::utils::exec("lsblk -ino TYPE,MOUNTPOINT | grep ' /$' | grep -q lvm", true) == "0"; + if (is_root_lvm || (gucc::fs::utils::get_mountpoint_fs(mountpoint) == "btrfs")) { + grub_config_struct.savedefault = std::nullopt; + } + + constexpr auto bash_code = R"(#!/bin/bash ln -s /hostlvm /run/lvm pacman -S --noconfirm --needed grub efibootmgr dosfstools grub-btrfs grub-hook -findmnt | awk '/^\/ / {print $3}' | grep -q btrfs && sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub -lsblk -ino TYPE,MOUNTPOINT | grep " /$" | grep -q lvm && sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub)"; - - static constexpr auto mkconfig_codepart = "grub-mkconfig -o /boot/grub/grub.cfg"; - const auto& bash_code = fmt::format(FMT_COMPILE("{}\ngrub-install --target=x86_64-efi --efi-directory={} --bootloader-id={} --recheck\n{}\n"), bash_codepart, uefi_mount, bootid, mkconfig_codepart); +)"; std::ofstream grub_installer{grub_installer_path}; grub_installer << bash_code; } @@ -1012,7 +1019,7 @@ lsblk -ino TYPE,MOUNTPOINT | grep " /$" | grep -q lvm && sed -e '/GRUB_SAVEDEFAU // if the device is removable append removable to the grub-install const auto& removable = gucc::utils::exec(fmt::format(FMT_COMPILE("cat /sys/block/{}/removable"), root_device)); if (utils::to_int(removable) == 1) { - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -e '/^grub-install /s/$/ --removable/g' -i {}"), grub_installer_path)); + grub_install_config_struct.is_removable = true; } // If the root is on btrfs-subvolume, amend grub installation @@ -1022,24 +1029,30 @@ lsblk -ino TYPE,MOUNTPOINT | grep " /$" | grep -q lvm && sed -e '/GRUB_SAVEDEFAU } // If encryption used amend grub if (!luks_dev.empty()) { - const auto& luks_dev_formatted = gucc::utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | {}"), luks_dev, "awk '{print $1}'")); - ret_status = gucc::utils::exec(fmt::format(FMT_COMPILE("echo \"sed -i \\\"s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\\\\\\\"\"{}\\\\\\\"~g\\\"\" /etc/default/grub\" >> {}"), luks_dev_formatted, grub_installer_path), true); - if (ret_status == "0") { - spdlog::info("adding kernel parameter {}", luks_dev); - } + const auto& luks_dev_formatted = gucc::utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | {}"), luks_dev, "awk '{print $1}'")); + grub_config_struct.cmdline_linux = fmt::format(FMT_COMPILE("{} {}"), luks_dev_formatted, grub_config_struct.cmdline_linux); + spdlog::info("adding kernel parameter {}", luks_dev); } // If Full disk encryption is used, use a keyfile const auto& fde = std::get(config_data["fde"]); if (fde == 1) { spdlog::info("Full disk encryption enabled"); - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -i '3a\\grep -q \"^GRUB_ENABLE_CRYPTODISK=y\" /etc/default/grub || sed -i \"s/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/\" /etc/default/grub' {}"), grub_installer_path)); + grub_config_struct.enable_cryptodisk = true; } std::error_code err{}; // install grub utils::arch_chroot("grub_installer.sh"); + + if (!gucc::bootloader::install_grub(grub_config_struct, grub_install_config_struct, mountpoint)) { + spdlog::error("Failed to install grub"); + umount("/mnt/hostlvm"); + fs::remove("/mnt/hostlvm", err); + tui::detail::infobox_widget("\nFailed to install grub\n"); + return; + } umount("/mnt/hostlvm"); fs::remove("/mnt/hostlvm", err); @@ -1226,20 +1239,26 @@ void bios_bootloader(const std::string_view& bootloader) noexcept { const auto& mountpoint = std::get(config_data["MOUNTPOINT"]); const auto& device_info = std::get(config_data["DEVICE"]); + gucc::bootloader::GrubConfig grub_config_struct{}; + gucc::bootloader::GrubInstallConfig grub_install_config_struct{}; + + grub_install_config_struct.is_efi = false; + grub_install_config_struct.do_recheck = true; + // if /boot is LVM (whether using a seperate /boot mount or not), amend grub if ((lvm == 1 && lvm_sep_boot == 0) || lvm_sep_boot == 2) { - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/GRUB_PRELOAD_MODULES=\\\"/GRUB_PRELOAD_MODULES=\\\"lvm /g\" {}/etc/default/grub"), mountpoint)); - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i {}/etc/default/grub"), mountpoint)); + grub_config_struct.preload_modules = fmt::format(FMT_COMPILE("lvm {}"), grub_config_struct.preload_modules); + grub_config_struct.savedefault = std::nullopt; } // If root is on btrfs volume, amend grub if (gucc::fs::utils::get_mountpoint_fs(mountpoint) == "btrfs") { - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i {}/etc/default/grub"), mountpoint)); + grub_config_struct.savedefault = std::nullopt; } // Same setting is needed for LVM if (lvm == 1) { - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i {}/etc/default/grub"), mountpoint)); + grub_config_struct.savedefault = std::nullopt; } const auto& grub_installer_path = fmt::format(FMT_COMPILE("{}/usr/bin/grub_installer.sh"), mountpoint); @@ -1247,38 +1266,36 @@ void bios_bootloader(const std::string_view& bootloader) noexcept { // grub config changes for zfs root if (gucc::fs::utils::get_mountpoint_fs(mountpoint) == "zfs") { // zfs needs ZPOOL_VDEV_NAME_PATH set to properly find the device - gucc::utils::exec(fmt::format(FMT_COMPILE("echo ZPOOL_VDEV_NAME_PATH=YES >> {}/etc/environment"), mountpoint)); - setenv("ZPOOL_VDEV_NAME_PATH", "YES", 1); + gucc::utils::exec(fmt::format(FMT_COMPILE("echo 'ZPOOL_VDEV_NAME_PATH=YES' >> {}/etc/environment"), mountpoint)); - constexpr auto bash_codepart1 = R"(#!/bin/bash + // zfs is considered a sparse filesystem so we can't use SAVEDEFAULT + if (gucc::fs::utils::get_mountpoint_fs(mountpoint) == "btrfs") { + grub_config_struct.savedefault = std::nullopt; + } + + // we need to tell grub where the zfs root is + const auto& mountpoint_source = gucc::fs::utils::get_mountpoint_source(mountpoint); + const auto& zroot_var = fmt::format(FMT_COMPILE("zfs={} rw"), mountpoint_source); + grub_config_struct.cmdline_linux_default = fmt::format(FMT_COMPILE("{} {}"), grub_config_struct.cmdline_linux_default, zroot_var); + grub_config_struct.cmdline_linux = fmt::format(FMT_COMPILE("{} {}"), grub_config_struct.cmdline_linux, zroot_var); + + constexpr auto bash_code = R"(#!/bin/bash ln -s /hostlvm /run/lvm -export ZPOOL_VDEV_NAME_PATH=YES pacman -S --noconfirm --needed grub os-prober -# zfs is considered a sparse filesystem so we can't use SAVEDEFAULT -sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub -# we need to tell grub where the zfs root is)"; - - const auto& mountpoint_source = gucc::fs::utils::get_mountpoint_source(mountpoint); - const auto& zroot_var = fmt::format(FMT_COMPILE("zroot=\"zfs={} rw\""), mountpoint_source); - - constexpr auto bash_codepart2 = R"( -sed -e '/^GRUB_CMDLINE_LINUX_DEFAULT=/s@"$@ '"${zroot}"'"@g' -e '/^GRUB_CMDLINE_LINUX=/s@"$@ '"${zroot}"'"@g' -i /etc/default/grub -sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub -grub-install --target=i386-pc --recheck)"; - - static constexpr auto mkconfig_codepart = "grub-mkconfig -o /boot/grub/grub.cfg"; - const auto& bash_code = fmt::format(FMT_COMPILE("{}\n{}\n{} {}\n{}\n"), bash_codepart1, zroot_var, bash_codepart2, device_info, mkconfig_codepart); +)"; std::ofstream grub_installer{grub_installer_path}; grub_installer << bash_code; } else { - constexpr auto bash_codepart = R"(#!/bin/bash + // we need to disable SAVEDEFAULT if either we are on LVM or BTRFS + const auto is_root_lvm = gucc::utils::exec("lsblk -ino TYPE,MOUNTPOINT | grep ' /$' | grep -q lvm", true) == "0"; + if (is_root_lvm || (gucc::fs::utils::get_mountpoint_fs(mountpoint) == "btrfs")) { + grub_config_struct.savedefault = std::nullopt; + } + + constexpr auto bash_code = R"(#!/bin/bash ln -s /hostlvm /run/lvm pacman -S --noconfirm --needed grub os-prober grub-btrfs grub-hook -findmnt | awk '/^\/ / {print $3}' | grep -q btrfs && sed -e '/GRUB_SAVEDEFAULT/ s/^#*/#/' -i /etc/default/grub -grub-install --target=i386-pc --recheck)"; - - static constexpr auto mkconfig_codepart = "grub-mkconfig -o /boot/grub/grub.cfg"; - const auto& bash_code = fmt::format(FMT_COMPILE("{} {}\n{}\n"), bash_codepart, device_info, mkconfig_codepart); +)"; std::ofstream grub_installer{grub_installer_path}; grub_installer << bash_code; } @@ -1291,18 +1308,16 @@ grub-install --target=i386-pc --recheck)"; // If encryption used amend grub if (!luks_dev.empty()) { - const auto& luks_dev_formatted = gucc::utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | {}"), luks_dev, "awk '{print $1}'")); - ret_status = gucc::utils::exec(fmt::format(FMT_COMPILE("echo \"sed -i \\\"s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\\\\\\\"\"{}\\\\\\\"~g\\\"\" /etc/default/grub\" >> {}"), luks_dev_formatted, grub_installer_path), true); - if (ret_status == "0") { - spdlog::info("adding kernel parameter {}", luks_dev); - } + const auto& luks_dev_formatted = gucc::utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | {}"), luks_dev, "awk '{print $1}'")); + grub_config_struct.cmdline_linux = fmt::format(FMT_COMPILE("{} {}"), luks_dev_formatted, grub_config_struct.cmdline_linux); + spdlog::info("adding kernel parameter {}", luks_dev); } // If Full disk encryption is used, use a keyfile const auto& fde = std::get(config_data["fde"]); if (fde == 1) { spdlog::info("Full disk encryption enabled"); - gucc::utils::exec(fmt::format(FMT_COMPILE("sed -i '3a\\grep -q \"^GRUB_ENABLE_CRYPTODISK=y\" /etc/default/grub || sed -i \"s/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/\" /etc/default/grub' {}"), grub_installer_path)); + grub_config_struct.enable_cryptodisk = true; } // Remove os-prober if not selected @@ -1331,6 +1346,12 @@ grub-install --target=i386-pc --recheck)"; umount("/mnt/hostlvm"); fs::remove("/mnt/hostlvm", err); + + if (!gucc::bootloader::install_grub(grub_config_struct, grub_install_config_struct, mountpoint)) { + spdlog::error("Failed to install grub"); + tui::detail::infobox_widget("\nFailed to install grub\n"); + return; + } #endif }