diff --git a/scripts/blob_boot.sh b/scripts/blob_boot.sh index 2b600b7..bfbe9e1 100755 --- a/scripts/blob_boot.sh +++ b/scripts/blob_boot.sh @@ -1,28 +1,640 @@ #!/bin/bash +# +# blob_boot.sh - boot splash and bootloader management. +# +# blob_boot.sh [image] Apply a Plymouth boot splash image (default action) +# blob_boot.sh grub Migrate from Limine back to GRUB, detecting every OS +# --purge removes Limine in the same run instead of +# keeping it for one fallback boot +# blob_boot.sh detect Rescan for other operating systems, rebuild the menu +# blob_boot.sh cleanup Retire Limine once GRUB has been confirmed working +# blob_boot.sh status Show what this machine currently boots with +# +# This machine has a 256 MB EFI System Partition, which is why Omarchy switched +# it to a single unified kernel image. Going back to GRUB means going back to a +# separate kernel and initramfs, so every step here is careful about space. -# Default to the branding image if no argument is provided -if [ -z "$1" ]; then - IMAGE_PATH="$HOME/Documents/dotfiles/branding/boot_flash.png" -else - IMAGE_PATH=$(realpath "$1") -fi +set -euo pipefail -if [ ! -f "$IMAGE_PATH" ]; then - echo "Error: File '$IMAGE_PATH' does not exist." - exit 1 -fi +DEFAULT_IMAGE="$HOME/Documents/dotfiles/branding/boot_flash.png" +PLYMOUTH_LOGO="/usr/share/plymouth/themes/omarchy/logo.png" -echo "Applying boot splash image: $IMAGE_PATH" -echo "This requires sudo privileges." +log() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; } +info() { printf ' %s\n' "$*"; } +warn() { printf '\033[1;33m %s\033[0m\n' "$*"; } +ok() { printf '\033[1;32m==> %s\033[0m\n' "$*"; } +die() { printf '\n\033[1;31m!!! %s\033[0m\n' "$*" >&2; exit 1; } -# Copy the image to the Plymouth theme directory -sudo cp "$IMAGE_PATH" /usr/share/plymouth/themes/omarchy/logo.png +require_sudo() { + echo "This requires sudo privileges." + sudo -v || die "Could not obtain sudo." +} -# Ensure the correct permissions -sudo chmod 644 /usr/share/plymouth/themes/omarchy/logo.png +# -------------------------------------------------------------------------- +# Boot splash +# -------------------------------------------------------------------------- -echo "Rebuilding initramfs..." -# Exclusively use mkinitcpio for GRUB compatibility -sudo mkinitcpio -P +apply_splash() { + local image_path + if [ -z "${1:-}" ]; then + image_path="$DEFAULT_IMAGE" + else + image_path=$(realpath "$1") + fi -echo "Boot splash successfully updated!" \ No newline at end of file + [ -f "$image_path" ] || die "File '$image_path' does not exist." + [ -d "$(dirname "$PLYMOUTH_LOGO")" ] || die "Plymouth omarchy theme is not installed." + + log "Applying boot splash image: $image_path" + require_sudo + + sudo cp "$image_path" "$PLYMOUTH_LOGO" + sudo chmod 644 "$PLYMOUTH_LOGO" + + log "Rebuilding initramfs" + rebuild_initramfs + + # Under GRUB the menu references the initramfs by path, so it only needs + # regenerating if the file set changed - but it is cheap and keeps the menu + # honest after a kernel or os-prober change. + if using_grub; then + log "Refreshing the GRUB menu" + sudo grub-mkconfig -o /boot/grub/grub.cfg + fi + + ok "Boot splash successfully updated!" +} + +# Call mkinitcpio directly: limine-mkinitcpio-hook installs a wrapper at +# /usr/local/bin/mkinitcpio that stops to ask whether to rebuild Limine +# entries, which is useless in a script. While Limine is still the active +# bootloader its own tool is the one that actually updates what boots. +rebuild_initramfs() { + if command -v limine-mkinitcpio >/dev/null && ! using_grub; then + info "Limine is still the bootloader; rebuilding through limine-mkinitcpio" + sudo limine-mkinitcpio + else + sudo /usr/bin/mkinitcpio -P + fi +} + +using_grub() { + [ -f /boot/grub/grub.cfg ] && [ -f /boot/EFI/GRUB/grubx64.efi ] +} + +# -------------------------------------------------------------------------- +# Limine -> GRUB migration +# -------------------------------------------------------------------------- + +migrate_to_grub() { + local purge=0 + [ "${1:-}" = "--purge" ] && purge=1 + + [ -d /sys/firmware/efi ] || die "Not booted in UEFI mode; this script assumes UEFI." + mountpoint -q /boot || die "/boot is not mounted." + command -v grub-install >/dev/null || die "The 'grub' package is not installed." + command -v os-prober >/dev/null || die "The 'os-prober' package is not installed." + + require_sudo + + local machine_id esp_uuid backup + machine_id=$(/dev/null || true + done + sudo efibootmgr -v | sudo tee "$backup/efibootmgr-before.txt" >/dev/null 2>&1 || true + info "saved." + + reclaim_esp_space "$machine_id" "$backup" + restore_standard_initramfs + configure_grub_scripts + install_grub "$esp_uuid" + fix_failing_boot_units + + if (( purge )); then + purge_limine + set_boot_order + enable_fallback_initramfs + generate_menu + else + # Test GRUB with a one-shot BootNext rather than reordering BootOrder. + # If GRUB fails to boot, a power cycle falls straight back to Limine on + # its own - no boot-menu keypress, no timing, nothing to get right while + # staring at a broken screen. + set_boot_next + fi + + log "Result" + df -h /boot | tail -1 | sed 's/^/ /' + echo + info "GRUB menu entries:" + list_menu_entries + echo + if (( purge )); then + ok "GRUB is the only bootloader. Limine is gone and its space is reclaimed." + else + ok "GRUB is installed and set to boot ONCE on the next restart." + info "If it works: run '$0 cleanup' to delete Limine and reclaim its 51 MB." + info "If it fails: hold the power button, then power on - the firmware falls" + info " back to Limine by itself. Nothing to press, nothing lost." + fi + info "Backups: $backup" +} + +# The 'omarchy' metapackage hard-depends on limine, limine-mkinitcpio-hook and +# limine-snapper-sync, so they can only be forced out with -Rdd - and the next +# omarchy upgrade will resolve those dependencies and pull them straight back in. +# +# NoExtract makes that harmless: pacman may reinstall the packages, but it will +# never write the files that actually do anything. Only the active parts are +# listed - the pacman hooks that rebuild unified kernel images and redeploy +# Limine onto the ESP, plus the /usr/local/bin/mkinitcpio wrapper that shadows +# the real one. Delete these lines from /etc/pacman.conf to undo it. +guard_limine_files() { + local marker="# Limine neutralised by blob_boot.sh" + if grep -q "$marker" /etc/pacman.conf; then + info "pacman.conf guard already present" + return 0 + fi + + sudo cp /etc/pacman.conf /etc/pacman.conf.bak + + # These are [options] directives, so they have to go inside that section. + # Appending to the end of the file would land them in the last repo block, + # where pacman would ignore them. + sudo awk -v marker="$marker" ' + /^\[options\]/ && !done { + print + print marker + print "NoExtract = etc/pacman.d/hooks/90-mkinitcpio-install.hook" + print "NoExtract = usr/local/bin/mkinitcpio" + print "NoExtract = usr/share/libalpm/hooks/60-limine-mkinitcpio-remove-pre.hook" + print "NoExtract = usr/share/libalpm/hooks/80-limine-efi-deploy.hook" + print "NoExtract = usr/share/libalpm/hooks/90-limine-mkinitcpio-remove-post.hook" + done = 1 + next + } + { print } + ' /etc/pacman.conf.bak | sudo tee /etc/pacman.conf >/dev/null + + grep -q "$marker" /etc/pacman.conf \ + || die "Failed to write the NoExtract guard. Your original is at /etc/pacman.conf.bak - restore it before doing anything else." + info "added NoExtract guard to /etc/pacman.conf (backup: /etc/pacman.conf.bak)" +} + +# pacman refuses a plain -R because omarchy depends on these. Force it, but only +# once the guard is in place, so a later reinstall cannot resurrect the hooks. +drop_limine_pkgs() { + local present=() pkg + for pkg in "$@"; do + pacman -Qq "$pkg" &>/dev/null && present+=("$pkg") + done + if (( ${#present[@]} == 0 )); then + info "nothing to remove" + return 0 + fi + + guard_limine_files + + if sudo pacman -Rn --noconfirm "${present[@]}" 2>/dev/null; then + info "removed: ${present[*]}" + else + warn "the 'omarchy' metapackage depends on these; forcing removal with -Rdd" + sudo pacman -Rddn --noconfirm "${present[@]}" + info "removed: ${present[*]}" + warn "'omarchy' will now report unsatisfied dependencies. That is expected and" + warn "harmless - nothing checks them outside of a pacman transaction. If a" + warn "future omarchy upgrade reinstalls them, the guard keeps them inert." + fi +} + +# The ESP is 95% full. Reclaim only files that are provably unreachable, and +# leave Limine's own UKI alone so the machine keeps a working fallback. +reclaim_esp_space() { + local machine_id=$1 backup=$2 + log "Reclaiming space on the ESP ($(df -h --output=avail /boot | tail -1 | tr -d ' ') free)" + + # Limine's entry tool writes one kernel directory per machine-id. A + # directory keyed by a different machine-id is left over from a previous + # install and nothing in NVRAM or limine.conf can reach it. + local dir name + shopt -s nullglob + for dir in /boot/[0-9a-f]*; do + name=${dir##*/} + [[ ${#name} -eq 32 && $name =~ ^[0-9a-f]+$ ]] || continue + [[ $name == "$machine_id" ]] && { info "keeping $name (this machine)"; continue; } + info "removing stale kernel dir $name ($(sudo du -sh "$dir" | cut -f1))" + sudo rm -rf "$dir" + done + shopt -u nullglob + + # arch-linux.efi is what the mkinitcpio preset wrote; limine.conf boots + # omarchy_linux.efi and never reads it. Only drop it while the UKI that + # Limine actually boots is present, so a fallback always survives. + if [ -f /boot/EFI/Linux/arch-linux.efi ]; then + if [ -f /boot/EFI/Linux/omarchy_linux.efi ] && ! grep -q "arch-linux.efi" /boot/limine.conf 2>/dev/null; then + info "removing unreferenced UKI arch-linux.efi ($(sudo du -sh /boot/EFI/Linux/arch-linux.efi | cut -f1))" + sudo rm -f /boot/EFI/Linux/arch-linux.efi + else + warn "arch-linux.efi is still referenced; keeping it" + fi + fi + + local avail_kb + avail_kb=$(df --output=avail -k /boot | tail -1 | tr -d ' ') + info "ESP now has $((avail_kb / 1024)) MB free" + (( avail_kb > 61440 )) || die "Only $((avail_kb / 1024)) MB free; an initramfs needs ~60 MB. Nothing further has been changed." +} + +restore_standard_initramfs() { + log "Restoring a standard kernel + initramfs layout" + + # btrfs-overlayfs ships with limine-mkinitcpio-hook and is about to vanish. + # omarchy_hooks.conf assigns HOOKS wholesale, so filter the hook out from a + # drop-in that sorts after it instead of editing an Omarchy-managed file. + sudo tee /etc/mkinitcpio.conf.d/zz-no-limine.conf >/dev/null <<'EOF' +# Written when this machine was migrated from Limine back to GRUB. +# btrfs-overlayfs comes from limine-mkinitcpio-hook, which is no longer +# installed, and this root filesystem is ext4 - so drop the hook rather than +# let mkinitcpio fail on a missing one. +_hooks=() +for _hook in "${HOOKS[@]}"; do + [[ $_hook == "btrfs-overlayfs" ]] || _hooks+=("$_hook") +done +HOOKS=("${_hooks[@]}") +unset _hooks _hook +EOF + info "wrote /etc/mkinitcpio.conf.d/zz-no-limine.conf" + + # Only the 'default' preset: Limine's 51 MB UKI is still on the ESP as a + # fallback and a fallback initramfs will not fit beside it. The cleanup + # step turns the fallback on once that space comes back. + sudo tee /etc/mkinitcpio.d/linux.preset >/dev/null <<'EOF' +# mkinitcpio preset file for the 'linux' package + +#ALL_config="/etc/mkinitcpio.conf" +ALL_kver="/boot/vmlinuz-linux" + +PRESETS=('default') + +#default_config="/etc/mkinitcpio.conf" +default_image="/boot/initramfs-linux.img" +#default_uki="/boot/EFI/Linux/arch-linux.efi" +default_options="" + +#fallback_config="/etc/mkinitcpio.conf" +fallback_image="/boot/initramfs-linux-fallback.img" +#fallback_uki="/boot/EFI/Linux/arch-linux-fallback.efi" +fallback_options="-S autodetect" +EOF + info "wrote /etc/mkinitcpio.d/linux.preset (image, not UKI)" + + # The 'limine' package itself stays for now - EFI/limine plus limine.conf + # remain a working fallback. Only the pieces that hijack mkinitcpio go. + log "Removing Limine's mkinitcpio integration" + drop_limine_pkgs limine-mkinitcpio-hook limine-snapper-sync + [ -e /usr/local/bin/mkinitcpio ] && warn "/usr/local/bin/mkinitcpio still shadows /usr/bin/mkinitcpio" + + log "Building the initramfs" + rebuild_initramfs + [ -f /boot/initramfs-linux.img ] || die "mkinitcpio produced no /boot/initramfs-linux.img. Limine is still bootable - do NOT reboot into GRUB." + [ -f /boot/vmlinuz-linux ] || die "/boot/vmlinuz-linux is missing." + info "initramfs: $(du -h /boot/initramfs-linux.img | cut -f1)" +} + +# The previous GRUB setup booted a UKI: 10_linux had been made non-executable +# and a custom 15_uki emitted a bare 'uki' command in its place. That is why the +# old menu listed Windows and Ubuntu but no Arch entry at all. Back on a normal +# kernel + initramfs, that has to be undone or the menu cannot boot this system. +configure_grub_scripts() { + log "Fixing the GRUB menu generators" + + if [ -f /etc/grub.d/10_linux ] && [ ! -x /etc/grub.d/10_linux ]; then + sudo chmod +x /etc/grub.d/10_linux + info "enabled 10_linux (generates the Arch entries)" + fi + + if [ -x /etc/grub.d/15_uki ]; then + sudo chmod -x /etc/grub.d/15_uki + info "disabled 15_uki (no unified kernel image any more)" + fi + + [ -x /etc/grub.d/30_os-prober ] || { sudo chmod +x /etc/grub.d/30_os-prober; info "enabled 30_os-prober"; } +} + +install_grub() { + local esp_uuid=$1 + log "Configuring GRUB" + + set_grub_key() { + local key=$1 val=$2 + if grep -q "^${key}=" /etc/default/grub; then + sudo sed -i "s|^${key}=.*|${key}=${val}|" /etc/default/grub + elif grep -qE "^#\s*${key}=" /etc/default/grub; then + sudo sed -i "0,/^#\s*${key}=.*/s||${key}=${val}|" /etc/default/grub + else + echo "${key}=${val}" | sudo tee -a /etc/default/grub >/dev/null + fi + info "${key}=${val}" + } + + # Carry over the exact kernel command line Limine was booting, so Plymouth + # and the quiet splash behave the way they do today. + set_grub_key GRUB_CMDLINE_LINUX_DEFAULT '"rtc_cmos.use_acpi_alarm=1 initramfs_async=0 quiet splash loglevel=0 systemd.show_status=false rd.udev.log_level=0 vt.global_cursor_default=0"' + set_grub_key GRUB_DISABLE_OS_PROBER 'false' + set_grub_key GRUB_TIMEOUT '5' + set_grub_key GRUB_TIMEOUT_STYLE 'menu' + set_grub_key GRUB_GFXPAYLOAD_LINUX 'keep' + + log "Installing GRUB to the ESP" + sudo grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --recheck + [ -f /boot/EFI/GRUB/grubx64.efi ] || die "grub-install produced no /boot/EFI/GRUB/grubx64.efi." + + generate_menu "$esp_uuid" +} + +# -------------------------------------------------------------------------- +# OS detection +# -------------------------------------------------------------------------- + +generate_menu() { + local esp_uuid=${1:-$(findmnt -no UUID /boot)} + + log "Generating the GRUB menu (os-prober scans for other systems)" + sudo grub-mkconfig -o /boot/grub/grub.cfg + + # os-prober can miss Windows when its EFI System Partition is the very one + # mounted at /boot, as it is here. Chainload it explicitly rather than ship + # a menu with no Windows in it. + if grep -qi "bootmgfw.efi" /boot/grub/grub.cfg; then + info "os-prober found Windows" + elif [ -f /boot/EFI/Microsoft/Boot/bootmgfw.efi ]; then + warn "os-prober missed Windows; adding an explicit chainload entry" + sudo tee /etc/grub.d/40_custom >/dev/null </dev/null \ + && [ "$(findmnt -no FSTYPE /)" != "btrfs" ]; then + sudo systemctl disable --now snapper-cleanup.timer snapper-timeline.timer &>/dev/null || true + sudo systemctl reset-failed snapper-cleanup.service &>/dev/null || true + info "disabled snapper timers (root is $(findmnt -no FSTYPE /), not btrfs)" + fi +} + +# -------------------------------------------------------------------------- +# Boot order +# -------------------------------------------------------------------------- + +# efibootmgr prints "Boot0000* GRUBHD(1,GPT,...)" on this machine - the +# device path is there even without -v - so match the label field exactly +# rather than anchoring on end of line. +efi_entry_num() { + sudo efibootmgr | awk -F'\t' -v want="$1" ' + $1 ~ /^Boot[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]/ { + num = substr($1, 5, 4) + label = $1 + sub(/^Boot[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]\*? +/, "", label) + if (label == want) { print num; exit } + }' +} + +# Boot GRUB exactly once, leaving Limine as the standing default. A failed +# GRUB boot then needs no user intervention at all to recover. +set_boot_next() { + log "Arming GRUB for a one-shot test boot" + local grub_num limine_num + grub_num=$(efi_entry_num GRUB) + [ -n "$grub_num" ] || die "No GRUB entry in NVRAM after grub-install. BootOrder is untouched, so this machine still boots Limine." + + # grub-install prepends its own entry to BootOrder, which would make GRUB the + # standing default and defeat the whole point of a one-shot test. Put Limine + # back in front so a failed GRUB boot recovers on a plain power cycle. + limine_num=$(efi_entry_num Limine) + if [ -n "$limine_num" ]; then + local current_order new_order n + current_order=$(sudo efibootmgr | sed -n 's/^BootOrder: //p') + new_order=$limine_num + for n in ${current_order//,/ }; do + [ "$n" = "$limine_num" ] || new_order+=",$n" + done + sudo efibootmgr -o "$new_order" >/dev/null + info "BootOrder: $new_order (Limine first - the standing default)" + else + warn "no Limine entry in NVRAM; GRUB will be the standing default with no automatic fallback" + fi + + # Set BootNext last: it must survive the BootOrder rewrite above. + sudo efibootmgr -n "$grub_num" >/dev/null + info "BootNext=$grub_num (GRUB) - next restart only" +} + +set_boot_order() { + log "Setting the firmware boot order" + + local grub_num limine_num current_order new_order n + grub_num=$(efi_entry_num GRUB) + limine_num=$(efi_entry_num Limine) + + [ -n "$grub_num" ] || die "No GRUB entry in NVRAM after grub-install. Limine is still first in the boot order, so the machine remains bootable." + + current_order=$(sudo efibootmgr | sed -n 's/^BootOrder: //p') + new_order=$grub_num + [ -n "$limine_num" ] && new_order+=",$limine_num" + for n in ${current_order//,/ }; do + [ "$n" = "$grub_num" ] && continue + [ "$n" = "${limine_num:-}" ] && continue + new_order+=",$n" + done + + sudo efibootmgr -o "$new_order" >/dev/null + info "BootOrder: $new_order (GRUB=$grub_num, Limine=${limine_num:-none})" +} + +# -------------------------------------------------------------------------- +# Retire Limine +# -------------------------------------------------------------------------- + +# Delete every trace of Limine and hand its EFI fallback path to GRUB. +purge_limine() { + log "Removing Limine" + + drop_limine_pkgs limine limine-mkinitcpio-hook limine-snapper-sync + + # limine-snapper-sync leaves units behind that would fail on every boot once + # there is no limine.conf for them to write into. + sudo systemctl disable --now limine-snapper-sync.service limine-snapper-sync.timer &>/dev/null || true + + # /boot/EFI/Linux holds only the unified kernel images Limine booted; with + # GRUB on a normal kernel + initramfs nothing reads them any more. This is + # where the 51 MB comes back. + sudo rm -rf /boot/EFI/limine /boot/EFI/Linux + sudo rm -f /boot/limine.conf /boot/limine.conf.bak /boot/limine.conf.old + sudo rm -rf /etc/limine-entry-tool.d /etc/limine-entry-tool.conf /var/lib/limine + info "removed Limine's files from the ESP" + + local limine_num + limine_num=$(efi_entry_num Limine) + [ -n "$limine_num" ] && { sudo efibootmgr -b "$limine_num" -B >/dev/null; info "removed the Limine NVRAM entry"; } + + # EFI/BOOT/BOOTX64.EFI is still Limine's copy. Overwrite it with GRUB so the + # firmware's default fallback path keeps working if the NVRAM entry is ever + # lost - otherwise deleting Limine leaves a dead pointer there. + log "Claiming the removable EFI fallback path for GRUB" + sudo grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable --recheck +} + +# Only worth attempting once Limine's UKI has freed up room on the ESP. +enable_fallback_initramfs() { + log "Enabling the fallback initramfs" + sudo sed -i "s|^PRESETS=.*|PRESETS=('default' 'fallback')|" /etc/mkinitcpio.d/linux.preset + if ! rebuild_initramfs; then + warn "the fallback initramfs did not build - reverting to the default preset only" + sudo sed -i "s|^PRESETS=.*|PRESETS=('default')|" /etc/mkinitcpio.d/linux.preset + sudo rm -f /boot/initramfs-linux-fallback.img + rebuild_initramfs + fi +} + +cleanup_limine() { + using_grub || die "GRUB is not installed. Run: $0 grub" + require_sudo + + # Only safe once the machine has actually come up through GRUB. + local current + current=$(sudo efibootmgr | sed -n 's/^BootCurrent: //p') + [ -n "$current" ] || die "Cannot determine the current boot entry." + sudo efibootmgr | grep -qE "^Boot${current}\*?[[:space:]]+GRUB" \ + || die "This session did not boot through GRUB (BootCurrent=$current). Reboot first - GRUB is armed for the next restart - then run this again." + + purge_limine + set_boot_order + enable_fallback_initramfs + generate_menu + + echo + df -h /boot | tail -1 | sed 's/^/ /' + echo + info "GRUB menu entries:" + list_menu_entries + ok "Limine is gone. GRUB is now the only bootloader." +} + +# -------------------------------------------------------------------------- +# Status +# -------------------------------------------------------------------------- + +show_status() { + log "Bootloader" + if using_grub; then + info "GRUB installed at /boot/EFI/GRUB/grubx64.efi" + else + warn "GRUB is not installed" + fi + pacman -Qq limine &>/dev/null && warn "limine is still installed" + [ -e /usr/local/bin/mkinitcpio ] && warn "/usr/local/bin/mkinitcpio shadows /usr/bin/mkinitcpio" + + log "Kernel images" + ls -lh /boot/vmlinuz-linux /boot/initramfs-linux*.img /boot/EFI/Linux/*.efi 2>/dev/null \ + | awk '{print " " $5 "\t" $9}' + + log "ESP usage" + df -h /boot | tail -1 | sed 's/^/ /' + + if [ -f /boot/grub/grub.cfg ]; then + log "GRUB menu entries" + list_menu_entries + fi + + log "Firmware boot order" + sudo efibootmgr 2>/dev/null | grep -E "^(BootCurrent|BootOrder|Boot[0-9A-F]{4})" \ + | grep -viE "USB|Setup|Boot Menu|Diagnostics|NVMe:" | sed 's/^/ /' + + log "Failed units" + systemctl --failed --no-pager --no-legend | sed 's/^/ /' || info "none" +} + +# -------------------------------------------------------------------------- + +case "${1:-}" in + grub|migrate) migrate_to_grub "${2:-}" ;; + detect|osprobe) detect_os ;; + cleanup) cleanup_limine ;; + status) show_status ;; + -h|--help|help) + sed -n '3,11p' "$0" | sed 's/^# \?//' + ;; + *) apply_splash "${1:-}" ;; +esac