155 lines
7.4 KiB
Bash
Executable File
155 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# The parts of an install that depend on what the machine actually is: the
|
|
# NVIDIA driver, the machine owner key that lets it load under Secure Boot,
|
|
# Fedora's preinstalled extras, and firmware.
|
|
#
|
|
# Runs last. MOK enrollment arms a prompt consumed at the next boot and firmware
|
|
# updates can ask for a reboot, so neither belongs in front of the package work
|
|
# or the dotfiles -- a machine that reboots out of this stage has already been
|
|
# fully configured.
|
|
#
|
|
# Nothing here decides anything. Every branch is an answer the interview
|
|
# collected before the run began, and an absent answer means no, which is what
|
|
# makes this safe to re-run by hand while repairing one piece of a machine.
|
|
#
|
|
# sunhat's version of this opened an editor in the middle of the run so grub
|
|
# could be hand-corrected. That is the exact failure this repository exists to
|
|
# avoid, and it is unnecessary: grubby replaces an argument that already exists
|
|
# rather than appending a second copy, so the duplicates that had to be cleaned
|
|
# up by hand cannot accumulate in the first place.
|
|
|
|
set -uo pipefail
|
|
|
|
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
|
warn() { echo -e "\033[1;33m[WARN]\033[0m $*" >&2; }
|
|
|
|
# Fedora ships these and Panama uses none of them. Named here rather than in the
|
|
# interview so there is one list: the interview asks for it with --debloat-list
|
|
# to name what it is about to remove, and this stage removes it. A package that
|
|
# is not installed is skipped rather than passed to dnf, so the list can outlive
|
|
# a Fedora release -- totem left in Fedora 43 and the list should not start
|
|
# failing because of it.
|
|
DEBLOAT=(gnome-contacts gnome-tour gnome-maps showtime)
|
|
|
|
if [[ "${1:-}" == "--debloat-list" ]]; then
|
|
printf '%s\n' "${DEBLOAT[@]}"
|
|
exit 0
|
|
fi
|
|
|
|
# ── NVIDIA ───────────────────────────────────────────────────────────────────
|
|
|
|
if [[ "${PANAMA_NVIDIA:-no}" == yes ]]; then
|
|
# The interview asks about MOK enrollment only when mokutil was present to
|
|
# see Secure Boot at all. Re-check here rather than trusting that the
|
|
# question was ever asked: installing akmod-nvidia and blacklisting
|
|
# nouveau under Secure Boot with no key to enroll produces a machine that
|
|
# reboots into an unloadable driver with its fallback disabled -- the one
|
|
# failure in this installer that costs a person their display.
|
|
if mokutil --sb-state 2>/dev/null | grep -qi 'secureboot enabled' \
|
|
&& [[ -z "${PANAMA_MOK_HASH:-}" ]]; then
|
|
warn "Secure Boot is on and no MOK enrollment was prepared; refusing to install"
|
|
warn "the NVIDIA driver, which could not load. Re-run ./install and answer the"
|
|
warn "Secure Boot question, or disable Secure Boot first."
|
|
else
|
|
log "Installing the NVIDIA driver"
|
|
if sudo dnf install -y \
|
|
--repo=fedora --repo=updates \
|
|
--repo=rpmfusion-free --repo=rpmfusion-free-updates \
|
|
--repo=rpmfusion-nonfree --repo=rpmfusion-nonfree-updates \
|
|
--from-repo=rpmfusion-nonfree,rpmfusion-nonfree-updates \
|
|
akmod-nvidia xorg-x11-drv-nvidia-cuda; then
|
|
# nouveau has to be out of the way before the kernel would otherwise
|
|
# bind it, which is why these are kernel arguments and not a modprobe
|
|
# drop-in. modeset=1 is what makes the Wayland session work at all.
|
|
if command -v grubby >/dev/null 2>&1; then
|
|
sudo grubby --update-kernel=ALL \
|
|
--args="rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1"
|
|
log "Kernel arguments set for every installed kernel"
|
|
else
|
|
warn "grubby is not installed; nouveau was not blacklisted"
|
|
fi
|
|
|
|
# Suspend and resume are where a proprietary driver most visibly fails.
|
|
# These units save and restore VRAM across it.
|
|
sudo systemctl enable nvidia-hibernate.service nvidia-suspend.service \
|
|
nvidia-resume.service nvidia-powerd.service
|
|
log "NVIDIA power management services enabled"
|
|
else
|
|
warn "The NVIDIA driver did not install; skipping its kernel arguments and services"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ── Secure Boot ──────────────────────────────────────────────────────────────
|
|
#
|
|
# akmods signs the modules it builds with a key it generates on installation.
|
|
# Under Secure Boot that key means nothing until it is enrolled, and enrollment
|
|
# is deliberately a thing only somebody at the physical machine can complete:
|
|
# the request is queued here, and the next boot shows a blue screen asking for
|
|
# the password before it will trust the key.
|
|
#
|
|
# The password itself never reaches this stage. The interview hashed it and
|
|
# recorded the hash, so nothing readable is passed on a command line or left in
|
|
# this process's environment -- which is the whole reason mokutil has
|
|
# --generate-hash and --hash-file.
|
|
|
|
mok_hash="${PANAMA_MOK_HASH:-}"
|
|
if [[ -n "$mok_hash" ]]; then
|
|
# Overridable so the contract can exercise this against a certificate it is
|
|
# allowed to create. Nothing else sets it.
|
|
cert="${PANAMA_MOK_CERT:-/etc/pki/akmods/certs/public_key.der}"
|
|
|
|
if [[ ! -r "$cert" ]]; then
|
|
warn "No akmods certificate at $cert, so there is no key to enroll"
|
|
elif mokutil --test-key "$cert" 2>/dev/null | grep -q 'already enrolled'; then
|
|
log "The akmods key is already enrolled"
|
|
else
|
|
hash_file="$(mktemp -t panama-mok.XXXXXX)"
|
|
chmod 600 "$hash_file"
|
|
printf '%s\n' "$mok_hash" >"$hash_file"
|
|
if sudo mokutil --import "$cert" --hash-file "$hash_file"; then
|
|
log "Key enrollment requested"
|
|
log "At the next boot, choose 'Enroll MOK' and enter the password you gave the installer"
|
|
else
|
|
warn "Key enrollment failed; the NVIDIA module will not load until it is enrolled"
|
|
fi
|
|
rm -f "$hash_file"
|
|
fi
|
|
fi
|
|
|
|
# ── Fedora's preinstalled extras ─────────────────────────────────────────────
|
|
|
|
if [[ "${PANAMA_DEBLOAT:-no}" == yes ]]; then
|
|
present=()
|
|
for package in "${DEBLOAT[@]}"; do
|
|
rpm -q "$package" >/dev/null 2>&1 && present+=("$package")
|
|
done
|
|
|
|
if (( ${#present[@]} > 0 )); then
|
|
log "Removing ${present[*]}"
|
|
sudo dnf remove -y "${present[@]}" >/dev/null \
|
|
|| warn "Some packages could not be removed"
|
|
else
|
|
log "None of Fedora's extras are installed"
|
|
fi
|
|
fi
|
|
|
|
# ── Firmware ─────────────────────────────────────────────────────────────────
|
|
#
|
|
# Panama's Updates page covers this from then on; the installer covers the
|
|
# first run. --no-reboot-check because deciding to reboot is not this stage's
|
|
# call to make in the middle of an unattended install.
|
|
|
|
if [[ "${PANAMA_FIRMWARE:-no}" == yes ]]; then
|
|
if command -v fwupdmgr >/dev/null 2>&1; then
|
|
log "Refreshing firmware metadata"
|
|
sudo fwupdmgr refresh --force >/dev/null 2>&1
|
|
log "Applying firmware updates"
|
|
sudo fwupdmgr update -y --no-reboot-check \
|
|
|| log "No firmware updates were applied"
|
|
else
|
|
warn "fwupdmgr is not installed; skipping firmware"
|
|
fi
|
|
fi
|