#!/bin/bash # omarchy:summary=Finish the hardware setup an image build deferred to this machine # omarchy:requires-sudo=true # omarchy:hidden=true # Started by omarchy-provision-hardware.service on every boot while an image's # deferred steps remain (see install/helpers/image-target.sh), and safe to call # from a platform's own first boot as well: runs are serialised. The first run # retires the image's build manifest, so from then on this machine is itself. # Steps run in order and each one that succeeds leaves the queue, so a failure # stops the run and the next run resumes at that step. Once the queue is empty # the initramfs is rebuilt if any step changed its configuration or asked for # the rebuild, and the service is disabled. With nothing deferred this does # nothing. A failed step, or a failure to make the pacman keyring the build # asked for, exits 75 (it stays queued for the next run); anything else that # stops the run, such as an untrusted manifest or queue, exits 1. set -euo pipefail fail() { echo "Error: $*" >&2 exit 1 } if (( EUID == 0 )); then export OMARCHY_PATH=/usr/share/omarchy export PATH=/usr/share/omarchy/bin:/usr/local/sbin:/usr/local/bin:/usr/bin elif [[ -z ${OMARCHY_IMAGE_ROOT:-} || -z ${OMARCHY_PATH:-} ]]; then fail "omarchy-provision-hardware must run as root" fi export OMARCHY_INSTALL=$OMARCHY_PATH/install export OMARCHY_INSTALL_USER="" source "$OMARCHY_INSTALL/helpers/logging.sh" source "$OMARCHY_INSTALL/helpers/image-target.sh" omarchy_image_init export OMARCHY_INSTALL_LOG_FILE=$omarchy_image_root/var/log/omarchy-install.log queue_present() { [[ -e $omarchy_image_queue || -L $omarchy_image_queue ]] } # Anything left for the first boot: the manifest, a queue, or the keyring an # image build asked for, which is owed even when no step was queued. work_present() { omarchy_image_manifest_present || queue_present || [[ -e $omarchy_image_keyring_request ]] } work_present || exit 0 omarchy_image_lock || exit 1 # Another run may have finished while this one waited for the lock. work_present || exit 0 if omarchy_image_manifest_present; then omarchy_image_read_manifest || exit 1 mv -f "$omarchy_image_manifest" "$omarchy_image_manifest.booted" || fail "could not retire $omarchy_image_manifest" live_platform=$(omarchy-hw-platform) || live_platform=unknown echo "First boot of an image built for $omarchy_image_platform, on $live_platform hardware" fi remaining=() if queue_present; then [[ -f $omarchy_image_queue ]] && omarchy_image_trusted "$omarchy_image_queue" || fail "$omarchy_image_queue is not a root-owned regular file" mapfile -t queued <"$omarchy_image_queue" for step in "${queued[@]}"; do [[ -z $step ]] || remaining+=("$step") done fi # What the boot image is built from: mkinitcpio's configuration, the module # options its modconf hook copies in (leaves such as nvidia.sh write them), and # the kernel command line Limine's entry tool adds (leaves such as intel/fred.sh # add to it). initramfs_inputs() { local file for file in "$omarchy_image_root/etc/mkinitcpio.conf" "$omarchy_image_root/etc/mkinitcpio.conf.d/"* \ "$omarchy_image_root/etc/modprobe.d/"* "$omarchy_image_root/usr/lib/modprobe.d/"* \ "$omarchy_image_root/etc/default/limine" "$omarchy_image_root/etc/limine-entry-tool.d/"*; do [[ -e $file ]] || continue printf '%s\0' "$file" cat -- "$file" done | cksum } # Recorded before the first step and kept until a rebuild lands, so a step that # changed the initramfs and then failed or lost power still gets one. if [[ ! -f $omarchy_image_initramfs_baseline ]]; then baseline=$(mktemp "$omarchy_image_dir/.initramfs-inputs.XXXXXX") && initramfs_inputs >"$baseline" && chmod 0644 "$baseline" && mv -f "$baseline" "$omarchy_image_initramfs_baseline" || fail "could not record the initramfs inputs" fi mkdir -p "$(dirname "$OMARCHY_INSTALL_LOG_FILE")" # A step that would build the boot image itself (a platform's boot setup) asks # for this run's rebuild instead, so the image is built once, after the last # step. The request stays on disk until that rebuild lands. export OMARCHY_IMAGE_BOOT_REBUILD=$omarchy_image_boot_rebuild # Tells a step it runs on the booted machine, after boot already brought up the # targets its units hang off (see install/hardware/bluetooth.sh). export OMARCHY_IMAGE_DEFERRED_HARDWARE=1 # An image build asks for this machine's own pacman keyring (see # install/post-install/pacman.sh), made before any step installs a package. Any # keyring the build or an interrupted run left is replaced; the request stays # until a keyring is complete. if [[ -e $omarchy_image_keyring_request ]]; then echo "Creating this machine's pacman keyring" keyring=$omarchy_image_root/etc/pacman.d/gnupg if ! { rm -rf "$keyring" && pacman-key --gpgdir "$keyring" --init && pacman-key --gpgdir "$keyring" --populate; } >"$OMARCHY_INSTALL_LOG_FILE" 2>&1; then echo "Error: could not create the pacman keyring (see $OMARCHY_INSTALL_LOG_FILE); the next run retries it" >&2 exit 75 fi rm -f "$omarchy_image_keyring_request" fi while (( ${#remaining[@]} )); do step=${remaining[0]} [[ $step == install/hardware/*.sh && $step != *..* ]] || fail "Deferred step is not a hardware setup step: $step" script=$OMARCHY_PATH/$step if [[ -L $script ]]; then fail "Deferred step is a symlink: $step" elif [[ ! -e $script ]]; then echo "Skipping deferred hardware step $step: this Omarchy no longer ships it" else echo "Running deferred hardware step $step" if ! run_logged "$script"; then echo "Error: Deferred hardware step failed: $step (see $OMARCHY_INSTALL_LOG_FILE); the next run resumes here" >&2 exit 75 fi fi remaining=("${remaining[@]:1}") omarchy_image_write_queue "${remaining[@]}" || fail "could not update $omarchy_image_queue" done if [[ -e $omarchy_image_boot_rebuild || $(initramfs_inputs) != "$(<"$omarchy_image_initramfs_baseline")" ]]; then echo "Rebuilding the initramfs for the deferred hardware setup" if omarchy-cmd-present limine-mkinitcpio; then limine-mkinitcpio >"$OMARCHY_INSTALL_LOG_FILE" 2>&1 else mkinitcpio -P >"$OMARCHY_INSTALL_LOG_FILE" 2>&1 fi || fail "Initramfs rebuild failed (see $OMARCHY_INSTALL_LOG_FILE); the next run retries it" # The new image must be on disk before the queue that owes it is cleared. sync || fail "could not flush the rebuilt initramfs to disk" fi rm -f "$omarchy_image_initramfs_baseline" "$omarchy_image_boot_rebuild" # Disabled, not deleted: a platform first boot that runs this while the # service's start job waits would otherwise fail that job at the next # daemon-reload. The emptied queue keeps the unit from running. rm -f "$omarchy_image_systemd_dir/multi-user.target.wants/$omarchy_image_unit" rm -f "$omarchy_image_queue" if (( EUID == 0 )); then systemctl daemon-reload 2>/dev/null || true fi echo "Deferred hardware setup is complete"