This commit is contained in:
@@ -48,7 +48,110 @@ cleanup() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
trap 'gum style --foreground 9 --bold "Failed at line $LINENO. See $LOG"' ERR
|
|
||||||
|
# ── error surfacing ───────────────────────────────────────────────────────────
|
||||||
|
# Step output normally ends up in $LOG only. These helpers make sure a failure
|
||||||
|
# is ALSO shown on screen, paged through `gum pager` so long outputs stay
|
||||||
|
# inspectable instead of scrolling away or hiding in a file nobody opens.
|
||||||
|
|
||||||
|
# Page stdin to the user with a context line. $1 = context. gum is a hard
|
||||||
|
# prerequisite of this installer (check_prereqs), so its pager is used
|
||||||
|
# instead of depending on less being present.
|
||||||
|
page_output() {
|
||||||
|
local ctx="$1"
|
||||||
|
if [[ -c /dev/tty && -t 1 ]]; then
|
||||||
|
{ gum style --foreground 11 --bold "$ctx"; echo ""; cat; } | gum pager || true
|
||||||
|
else
|
||||||
|
gum style --foreground 9 --bold "$ctx" || true
|
||||||
|
cat >&2 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Page the last $2 lines (default 200) of $LOG - for output that was already
|
||||||
|
# redirected there. $1 = context.
|
||||||
|
page_log_tail() {
|
||||||
|
[[ -f "$LOG" ]] || return 0
|
||||||
|
tail -n "${2:-200}" "$LOG" | page_output "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Page the $LOG lines appended after $1 (a line count taken with
|
||||||
|
# `wc -l < "$LOG"` before the step ran). $2 = context. A mark at or past the
|
||||||
|
# end of the log means nothing new was written - skip instead of opening a
|
||||||
|
# pager on an empty screen.
|
||||||
|
page_log_since() {
|
||||||
|
local mark="$1" total=0
|
||||||
|
[[ -f "$LOG" ]] || return 0
|
||||||
|
total=$(wc -l <"$LOG") || total=0
|
||||||
|
(( mark < total )) || return 0
|
||||||
|
sed -n "$((mark + 1)),\$p" "$LOG" | page_output "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run a command, tee its combined output into $LOG (as the old
|
||||||
|
# `>>"$LOG" 2>&1` redirects did - live, so an interrupted command keeps its
|
||||||
|
# partial output) and - on failure - page the lines it added so the error
|
||||||
|
# reaches the user, not just the log. Returns the command's exit status.
|
||||||
|
# Usage: run_logged [-s SPINNER [-t TITLE]] "what failed" cmd [args...]
|
||||||
|
# -s show a gum spinner while cmd runs: dot for linear commands, jump for
|
||||||
|
# ones that compile. Frames render on stderr (the terminal), never $LOG.
|
||||||
|
# -t spinner title; omit when an info line already says what is running.
|
||||||
|
run_logged() {
|
||||||
|
local ctx="" spinner="" title="" rc=0 mark=0 total=0
|
||||||
|
while [[ "${1:-}" == -[st] ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-s) spinner="$2"; shift 2 ;;
|
||||||
|
-t) title="$2"; shift 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
ctx="$1"
|
||||||
|
shift
|
||||||
|
# Line count before the command, so only ITS output gets paged on failure.
|
||||||
|
# A missing $LOG (mark 0) is fine: tee creates it, so lines 1..n are
|
||||||
|
# exactly this command's output.
|
||||||
|
if [[ -f "$LOG" ]]; then
|
||||||
|
mark=$(wc -l <"$LOG") || mark=0
|
||||||
|
fi
|
||||||
|
# tee appends to $LOG while /dev/null keeps the terminal clean. With
|
||||||
|
# pipefail the pipeline status is the command's own; the only false
|
||||||
|
# failure possible is tee itself failing to write (e.g. disk full).
|
||||||
|
if [[ -n "$spinner" ]]; then
|
||||||
|
# gum spin propagates the command's exit status; --show-output keeps
|
||||||
|
# its stdout (the command's output) free of spinner frames for tee,
|
||||||
|
# while the spinner itself renders on stderr = the terminal. The sh
|
||||||
|
# wrapper merges the command's stderr into that stdout first, so no
|
||||||
|
# output bypasses $LOG.
|
||||||
|
local -a spin=(gum spin --show-output --spinner "$spinner")
|
||||||
|
if [[ -n "$title" ]]; then
|
||||||
|
spin+=(--title "$title")
|
||||||
|
fi
|
||||||
|
"${spin[@]}" -- sh -c '"$@" 2>&1' sh "$@" \
|
||||||
|
| tee -a "$LOG" >/dev/null || rc=$?
|
||||||
|
else
|
||||||
|
"$@" 2>&1 | tee -a "$LOG" >/dev/null || rc=$?
|
||||||
|
fi
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
if [[ -f "$LOG" ]]; then
|
||||||
|
total=$(wc -l <"$LOG") || total=0
|
||||||
|
fi
|
||||||
|
if (( mark < total )); then
|
||||||
|
page_log_since "$mark" "${ctx} (exit ${rc}) - output below, full log: $LOG"
|
||||||
|
else
|
||||||
|
# Silent failure: nothing to page, but still surface where and why.
|
||||||
|
gum style --foreground 11 --bold \
|
||||||
|
"${ctx} (exit ${rc}) - no output captured, full log: $LOG" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ERR trap: a fatal failure aborts the install (errexit) - say where, then
|
||||||
|
# page the recent log so the actual error is on screen, not log-only.
|
||||||
|
on_err() {
|
||||||
|
local rc=$?
|
||||||
|
trap - ERR
|
||||||
|
gum style --foreground 9 --bold "Installation failed (exit ${rc}) at line $1. Full log: $LOG" || true
|
||||||
|
page_log_tail "Last output before the failure - q to quit, scroll for more" 300 || true
|
||||||
|
}
|
||||||
|
trap 'on_err "$LINENO"' ERR
|
||||||
|
|
||||||
(( EUID == 0 )) || { echo "Run as root" >&2; exit 1; }
|
(( EUID == 0 )) || { echo "Run as root" >&2; exit 1; }
|
||||||
|
|
||||||
@@ -122,13 +225,14 @@ gvariant_unescape() {
|
|||||||
sync_target_dbs() {
|
sync_target_dbs() {
|
||||||
[[ "${TARGET_DB_SYNCED}" == "true" ]] && return 0
|
[[ "${TARGET_DB_SYNCED}" == "true" ]] && return 0
|
||||||
info "Syncing target package databases..."
|
info "Syncing target package databases..."
|
||||||
arch-chroot "$TARGET" pacman-key --populate archlinux cachyos chaotic \
|
run_logged -s dot "pacman-key --populate failed" \
|
||||||
>>"$LOG" 2>&1 || true
|
arch-chroot "$TARGET" pacman-key --populate archlinux cachyos chaotic || true
|
||||||
if arch-chroot "$TARGET" pacman -Syy --noconfirm >>"$LOG" 2>&1; then
|
if run_logged -s dot "Target pacman -Syy failed" \
|
||||||
|
arch-chroot "$TARGET" pacman -Syy --noconfirm; then
|
||||||
TARGET_DB_SYNCED="true"
|
TARGET_DB_SYNCED="true"
|
||||||
ok "Target databases synced."
|
ok "Target databases synced."
|
||||||
else
|
else
|
||||||
warn "Target pacman -Syy failed (see $LOG) - continuing with existing databases."
|
warn "Target pacman -Syy failed (output above) - continuing with existing databases."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +656,8 @@ install_pacstrap() {
|
|||||||
# Parse only the first field, then drop names not resolvable in the live
|
# Parse only the first field, then drop names not resolvable in the live
|
||||||
# session's repos (e.g. local-aur packages are not configured there).
|
# session's repos (e.g. local-aur packages are not configured there).
|
||||||
info "Syncing package databases for resolvability check..."
|
info "Syncing package databases for resolvability check..."
|
||||||
if pacman -Syy --noconfirm >>"$LOG" 2>&1; then
|
if run_logged -s dot "pacman -Syy failed (resolvability check)" \
|
||||||
|
pacman -Syy --noconfirm; then
|
||||||
declare -A available=()
|
declare -A available=()
|
||||||
local db_line
|
local db_line
|
||||||
while IFS= read -r db_line; do
|
while IFS= read -r db_line; do
|
||||||
@@ -560,7 +665,7 @@ install_pacstrap() {
|
|||||||
done < <(pacman -Slq 2>>"$LOG" || true)
|
done < <(pacman -Slq 2>>"$LOG" || true)
|
||||||
(( ${#available[@]} > 0 )) && filter_ok=true
|
(( ${#available[@]} > 0 )) && filter_ok=true
|
||||||
else
|
else
|
||||||
warn "pacman -Syy failed (see $LOG) — installing without resolvability filter."
|
warn "pacman -Syy failed (output above) — installing without resolvability filter."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local line pkg
|
local line pkg
|
||||||
@@ -608,9 +713,11 @@ install_pacstrap() {
|
|||||||
log "Pacstrap: openssh guaranteed in package list (sshd requested)"
|
log "Pacstrap: openssh guaranteed in package list (sshd requested)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Installing ${#pkgs[@]} packages (this may take a while)..."
|
|
||||||
log "Pacstrap: ${#pkgs[@]} packages, ${#dropped[@]} skipped"
|
log "Pacstrap: ${#pkgs[@]} packages, ${#dropped[@]} skipped"
|
||||||
pacstrap "$TARGET" "${pkgs[@]}" --noconfirm 2>>"$LOG"
|
run_logged -s dot -t "Installing ${#pkgs[@]} packages (this may take a while)" \
|
||||||
|
"pacstrap failed" \
|
||||||
|
pacstrap "$TARGET" "${pkgs[@]}" --noconfirm \
|
||||||
|
|| die "pacstrap failed - the target system is incomplete (output above, full log: $LOG)."
|
||||||
|
|
||||||
ok "Packages installed."
|
ok "Packages installed."
|
||||||
}
|
}
|
||||||
@@ -696,12 +803,13 @@ install_sshd() {
|
|||||||
log "SSH server: enable skipped (sshd binary missing in target)"
|
log "SSH server: enable skipped (sshd binary missing in target)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if arch-chroot "$TARGET" systemctl enable sshd.service >>"$LOG" 2>&1; then
|
if run_logged "Could not enable sshd.service" \
|
||||||
|
arch-chroot "$TARGET" systemctl enable sshd.service; then
|
||||||
SSHD_APPLIED="true"
|
SSHD_APPLIED="true"
|
||||||
log "SSH server: enabled"
|
log "SSH server: enabled"
|
||||||
ok "sshd enabled (starts at boot)."
|
ok "sshd enabled (starts at boot)."
|
||||||
else
|
else
|
||||||
warn "Could not enable sshd.service (see $LOG)."
|
warn "Could not enable sshd.service (output above)."
|
||||||
log "SSH server: enable failed"
|
log "SSH server: enable failed"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -732,12 +840,14 @@ install_git_ssh() {
|
|||||||
if [[ -n "$GIT_EMAIL" ]]; then
|
if [[ -n "$GIT_EMAIL" ]]; then
|
||||||
info "Configuring git..."
|
info "Configuring git..."
|
||||||
local git_name="${GIT_NAME:-$USERNAME}"
|
local git_name="${GIT_NAME:-$USERNAME}"
|
||||||
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
run_logged "git config user.name failed" \
|
||||||
git config --global user.name "$git_name" >>"$LOG" 2>&1 \
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|| warn "git config user.name failed (see $LOG)."
|
git config --global user.name "$git_name" \
|
||||||
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
|| warn "git config user.name failed (output above)."
|
||||||
git config --global user.email "$GIT_EMAIL" >>"$LOG" 2>&1 \
|
run_logged "git config user.email failed" \
|
||||||
|| warn "git config user.email failed (see $LOG)."
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|
git config --global user.email "$GIT_EMAIL" \
|
||||||
|
|| warn "git config user.email failed (output above)."
|
||||||
ok "Git configured for ${USERNAME} (${git_name} <${GIT_EMAIL}>)."
|
ok "Git configured for ${USERNAME} (${git_name} <${GIT_EMAIL}>)."
|
||||||
log "Git: ${git_name} <${GIT_EMAIL}>"
|
log "Git: ${git_name} <${GIT_EMAIL}>"
|
||||||
else
|
else
|
||||||
@@ -775,27 +885,33 @@ install_git_ssh() {
|
|||||||
else
|
else
|
||||||
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
||||||
chmod 440 "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
chmod 440 "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
||||||
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
run_logged "yay -Ss ssh-import-id found nothing" \
|
||||||
env HOME="$home_in_target" yay -Ss ssh-import-id >>"$LOG" 2>&1 \
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|| warn "yay -Ss ssh-import-id found nothing (see $LOG)."
|
env HOME="$home_in_target" yay -Ss ssh-import-id \
|
||||||
if arch-chroot "$TARGET" pacman -S --noconfirm python-distro >>"$LOG" 2>&1 \
|
|| warn "yay -Ss ssh-import-id found nothing (output above)."
|
||||||
&& arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
if run_logged -s dot -t "Installing ssh-import-id" \
|
||||||
env HOME="$home_in_target" yay -S --noconfirm ssh-import-id >>"$LOG" 2>&1; then
|
"pacman -S python-distro failed" \
|
||||||
|
arch-chroot "$TARGET" pacman -S --noconfirm python-distro \
|
||||||
|
&& run_logged -s jump -t "Installing ssh-import-id" \
|
||||||
|
"yay failed to install ssh-import-id" \
|
||||||
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|
env HOME="$home_in_target" yay -S --noconfirm ssh-import-id; then
|
||||||
sid_ok="true"
|
sid_ok="true"
|
||||||
log "ssh-import-id: installed via yay"
|
log "ssh-import-id: installed via yay"
|
||||||
else
|
else
|
||||||
warn "yay could not install ssh-import-id (see $LOG) - falling back to github.com keys."
|
warn "yay could not install ssh-import-id (output above) - falling back to github.com keys."
|
||||||
fi
|
fi
|
||||||
rm -f "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
rm -f "${TARGET}/etc/sudoers.d/99-ssh-import-id"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$sid_ok" == "true" ]]; then
|
if [[ "$sid_ok" == "true" ]]; then
|
||||||
if arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
if run_logged "ssh-import-id failed" \
|
||||||
env HOME="$home_in_target" ssh-import-id "gh:${GH_USERNAME}" >>"$LOG" 2>&1; then
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|
env HOME="$home_in_target" ssh-import-id "gh:${GH_USERNAME}"; then
|
||||||
ok "SSH keys imported via ssh-import-id."
|
ok "SSH keys imported via ssh-import-id."
|
||||||
log "SSH keys: ssh-import-id gh:${GH_USERNAME}"
|
log "SSH keys: ssh-import-id gh:${GH_USERNAME}"
|
||||||
else
|
else
|
||||||
warn "ssh-import-id failed (see $LOG) - falling back to github.com keys."
|
warn "ssh-import-id failed (output above) - falling back to github.com keys."
|
||||||
sid_ok="false"
|
sid_ok="false"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -808,6 +924,8 @@ install_git_ssh() {
|
|||||||
keys=$(curl -fsSL --max-time 30 "$keys_url" 2>>"$LOG") || curl_rc=$?
|
keys=$(curl -fsSL --max-time 30 "$keys_url" 2>>"$LOG") || curl_rc=$?
|
||||||
if (( curl_rc != 0 )); then
|
if (( curl_rc != 0 )); then
|
||||||
warn "Could not fetch ${keys_url} (curl exit ${curl_rc}) - no SSH keys imported."
|
warn "Could not fetch ${keys_url} (curl exit ${curl_rc}) - no SSH keys imported."
|
||||||
|
# curl's stderr went to $LOG only - put it in front of the user.
|
||||||
|
page_log_tail "curl error output (q to quit)" 60 || true
|
||||||
log "SSH keys: curl failed (exit ${curl_rc}) for ${keys_url}"
|
log "SSH keys: curl failed (exit ${curl_rc}) for ${keys_url}"
|
||||||
elif [[ -z "$keys" ]]; then
|
elif [[ -z "$keys" ]]; then
|
||||||
# HTTP 200 with an empty body: the account exists but simply has
|
# HTTP 200 with an empty body: the account exists but simply has
|
||||||
@@ -882,16 +1000,27 @@ install_end4_dots() {
|
|||||||
# --skip-alldeps: every dependency was already installed at ISO build time
|
# --skip-alldeps: every dependency was already installed at ISO build time
|
||||||
# (customize_airootfs.sh runs ./setup install-deps), so this only copies
|
# (customize_airootfs.sh runs ./setup install-deps), so this only copies
|
||||||
# configs, sets up services and builds the python venv from the warm cache.
|
# configs, sets up services and builds the python venv from the warm cache.
|
||||||
if ! arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
# Both attempts run under a jump spinner (this step compiles the venv and
|
||||||
env HOME="$home_in_target" bash -c \
|
# pip bits): --show-output + the sh stderr-merge keep every line flowing
|
||||||
"cd ${repo_in_target} && ./setup install -f -s --skip-alldeps" \
|
# into $LOG, the frames render on the terminal, and stdin still feeds the
|
||||||
< <(yes i) >>"$LOG" 2>&1; then
|
# `yes i` process substitution through to ./setup.
|
||||||
warn "end-4 setup failed, retrying files only (see $LOG)."
|
if ! gum spin --show-output --spinner jump -- \
|
||||||
setups_done=false
|
sh -c '"$@" 2>&1' sh \
|
||||||
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
env HOME="$home_in_target" bash -c \
|
env HOME="$home_in_target" bash -c \
|
||||||
"cd ${repo_in_target} && ./setup install -f -s --skip-alldeps --skip-allsetups" \
|
"cd ${repo_in_target} && ./setup install -f -s --skip-alldeps" \
|
||||||
< <(yes i) >>"$LOG" 2>&1 || dots_ok=false
|
< <(yes i) >>"$LOG"; then
|
||||||
|
warn "end-4 setup failed, retrying files only (see $LOG)."
|
||||||
|
setups_done=false
|
||||||
|
gum spin --show-output --spinner jump -- \
|
||||||
|
sh -c '"$@" 2>&1' sh \
|
||||||
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|
env HOME="$home_in_target" bash -c \
|
||||||
|
"cd ${repo_in_target} && ./setup install -f -s --skip-alldeps --skip-allsetups" \
|
||||||
|
< <(yes i) >>"$LOG" || dots_ok=false
|
||||||
|
# Both attempts' output exists only in $LOG - page it so the user can
|
||||||
|
# inspect what end-4 actually choked on.
|
||||||
|
page_log_since "$log_mark" "end-4 setup output (both attempts) - q to quit, scroll for more" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "${TARGET}/etc/sudoers.d/99-end4-install"
|
rm -f "${TARGET}/etc/sudoers.d/99-end4-install"
|
||||||
@@ -912,8 +1041,9 @@ install_end4_dots() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "${TARGET}/usr/lib/systemd/system/bluetooth.service" ]]; then
|
if [[ -f "${TARGET}/usr/lib/systemd/system/bluetooth.service" ]]; then
|
||||||
arch-chroot "$TARGET" systemctl enable bluetooth.service >>"$LOG" 2>&1 \
|
run_logged "Could not enable bluetooth.service" \
|
||||||
|| warn "Could not enable bluetooth.service (see $LOG)."
|
arch-chroot "$TARGET" systemctl enable bluetooth.service \
|
||||||
|
|| warn "Could not enable bluetooth.service (output above)."
|
||||||
fi
|
fi
|
||||||
# `systemctl --user` cannot work in this chroot either: it needs
|
# `systemctl --user` cannot work in this chroot either: it needs
|
||||||
# /run/user/UID/bus, which does not exist until the target boots (it only
|
# /run/user/UID/bus, which does not exist until the target boots (it only
|
||||||
@@ -921,13 +1051,21 @@ install_end4_dots() {
|
|||||||
# `enable` would write — the same absolute link systemctl produces.
|
# `enable` would write — the same absolute link systemctl produces.
|
||||||
if [[ -f "${TARGET}/usr/lib/systemd/user/ydotool.service" ]]; then
|
if [[ -f "${TARGET}/usr/lib/systemd/user/ydotool.service" ]]; then
|
||||||
local yd_wants="${TARGET}${home_in_target}/.config/systemd/user/default.target.wants"
|
local yd_wants="${TARGET}${home_in_target}/.config/systemd/user/default.target.wants"
|
||||||
# stderr goes to $LOG so the failure warn below points at something
|
# Capture instead of redirecting: on failure the output is both
|
||||||
# that actually records it (as the bluetooth block does).
|
# appended to $LOG and paged to the user (as the bluetooth block's
|
||||||
if { mkdir -p "$yd_wants" \
|
# run_logged does), never kept in the log alone.
|
||||||
&& ln -sfn /usr/lib/systemd/user/ydotool.service "$yd_wants/ydotool.service"; } 2>>"$LOG"; then
|
local yd_rc=0 yd_err=""
|
||||||
|
yd_err=$( { mkdir -p "$yd_wants" \
|
||||||
|
&& ln -sfn /usr/lib/systemd/user/ydotool.service "$yd_wants/ydotool.service"; } 2>&1 ) \
|
||||||
|
|| yd_rc=$?
|
||||||
|
if (( yd_rc == 0 )); then
|
||||||
log "ydotool.service enabled for ${USERNAME} (symlinked in user unit dir)."
|
log "ydotool.service enabled for ${USERNAME} (symlinked in user unit dir)."
|
||||||
else
|
else
|
||||||
warn "Could not enable ydotool.service for ${USERNAME} (see $LOG)."
|
if [[ -n "$yd_err" ]]; then
|
||||||
|
printf '%s\n' "$yd_err" >>"$LOG"
|
||||||
|
printf '%s\n' "$yd_err" | page_output "ydotool.service symlink failed (exit ${yd_rc}) - full log: $LOG"
|
||||||
|
fi
|
||||||
|
warn "Could not enable ydotool.service for ${USERNAME} (output above)."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -941,7 +1079,7 @@ install_end4_dots() {
|
|||||||
# matching value counts as persisted.
|
# matching value counts as persisted.
|
||||||
if ((log_mark > 0)) \
|
if ((log_mark > 0)) \
|
||||||
&& [[ -x "${TARGET}/usr/bin/dbus-run-session" && -x "${TARGET}/usr/bin/gsettings" ]]; then
|
&& [[ -x "${TARGET}/usr/bin/dbus-run-session" && -x "${TARGET}/usr/bin/gsettings" ]]; then
|
||||||
local gs_line gs_schema gs_key gs_val gs_got gs_norm gs_raw
|
local gs_line gs_schema gs_key gs_val gs_got gs_norm gs_raw gs_err gs_rc
|
||||||
# Snapshot and parse first (the loop below appends to $LOG), keeping
|
# Snapshot and parse first (the loop below appends to $LOG), keeping
|
||||||
# log order: a key set twice replays chronologically so the last
|
# log order: a key set twice replays chronologically so the last
|
||||||
# write wins, exactly as end-4 ran it. Dedupe identical repeats only.
|
# write wins, exactly as end-4 ran it. Dedupe identical repeats only.
|
||||||
@@ -971,12 +1109,21 @@ install_end4_dots() {
|
|||||||
gs_val=${BASH_REMATCH[3]}
|
gs_val=${BASH_REMATCH[3]}
|
||||||
# env -i: root's env is exactly what broke dconf; rebuild it for
|
# env -i: root's env is exactly what broke dconf; rebuild it for
|
||||||
# the target user with only what gsettings/dbus need.
|
# the target user with only what gsettings/dbus need.
|
||||||
if arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
# gsettings exits 0 even when the commit fails, so both the exit
|
||||||
|
# status AND the read-back below decide success; its output is
|
||||||
|
# kept in $LOG and paged on failure.
|
||||||
|
gs_err=""
|
||||||
|
gs_rc=0
|
||||||
|
gs_err=$(arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
env -i HOME="$home_in_target" USER="$USERNAME" LOGNAME="$USERNAME" \
|
env -i HOME="$home_in_target" USER="$USERNAME" LOGNAME="$USERNAME" \
|
||||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
|
||||||
XDG_CONFIG_HOME="${home_in_target}/.config" \
|
XDG_CONFIG_HOME="${home_in_target}/.config" \
|
||||||
dbus-run-session -- \
|
dbus-run-session -- \
|
||||||
gsettings set "$gs_schema" "$gs_key" "$gs_val" >>"$LOG" 2>&1; then
|
gsettings set "$gs_schema" "$gs_key" "$gs_val" 2>&1) || gs_rc=$?
|
||||||
|
if [[ -n "$gs_err" ]]; then
|
||||||
|
printf '%s\n' "$gs_err" >>"$LOG"
|
||||||
|
fi
|
||||||
|
if (( gs_rc == 0 )); then
|
||||||
gs_got=$(arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
gs_got=$(arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
env -i HOME="$home_in_target" USER="$USERNAME" LOGNAME="$USERNAME" \
|
env -i HOME="$home_in_target" USER="$USERNAME" LOGNAME="$USERNAME" \
|
||||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \
|
||||||
@@ -990,9 +1137,15 @@ install_end4_dots() {
|
|||||||
if [[ "$gs_norm" == "$gs_val" ]]; then
|
if [[ "$gs_norm" == "$gs_val" ]]; then
|
||||||
log "gsettings persisted: ${gs_schema} ${gs_key} = ${gs_val}"
|
log "gsettings persisted: ${gs_schema} ${gs_key} = ${gs_val}"
|
||||||
else
|
else
|
||||||
|
if [[ -n "$gs_err" ]]; then
|
||||||
|
printf '%s\n' "$gs_err" | page_output "gsettings ${gs_schema} ${gs_key} command output (q to quit) - full log: $LOG"
|
||||||
|
fi
|
||||||
warn "gsettings ${gs_schema} ${gs_key} did not persist (read back: ${gs_got:-nothing}, see $LOG)."
|
warn "gsettings ${gs_schema} ${gs_key} did not persist (read back: ${gs_got:-nothing}, see $LOG)."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
if [[ -n "$gs_err" ]]; then
|
||||||
|
printf '%s\n' "$gs_err" | page_output "gsettings set failed (exit ${gs_rc}) - output below, full log: $LOG"
|
||||||
|
fi
|
||||||
warn "Could not replay gsettings ${gs_schema} ${gs_key} (see $LOG)."
|
warn "Could not replay gsettings ${gs_schema} ${gs_key} (see $LOG)."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -1207,20 +1360,96 @@ install_bootloader() {
|
|||||||
printf '\nGRUB_DISABLE_BOOTNEXT=true\n' >> "${TARGET}/etc/default/grub"
|
printf '\nGRUB_DISABLE_BOOTNEXT=true\n' >> "${TARGET}/etc/default/grub"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
arch-chroot "$TARGET" grub-install \
|
# Two installs, both verified - this is the step whose failure strands a
|
||||||
--target=x86_64-efi --efi-directory=/boot --bootloader-id=ALVIN
|
# machine with no bootable firmware entry:
|
||||||
|
# 1. --removable → writes the removable fallback \EFI\BOOT\BOOTX64.EFI
|
||||||
|
# to the ESP FIRST, so it exists no matter what the NVRAM step does
|
||||||
|
# 2. normal → registers the ALVIN entry in the firmware NVRAM
|
||||||
|
# Some firmwares drop their NVRAM entries over time (the classic
|
||||||
|
# boot-stranding failure); with the fallback on the ESP the firmware
|
||||||
|
# still finds GRUB and the machine is never bricked.
|
||||||
|
# Caveat: the fallback is an unsigned direct copy of grubx64.efi, so it
|
||||||
|
# only boots with Secure Boot disabled or in setup mode - which booting
|
||||||
|
# the unsigned Arch ISO already implies. If Secure Boot support is ever
|
||||||
|
# added, the fallback MUST be shimx64.efi instead - otherwise the
|
||||||
|
# firmware rejects BOOTX64.EFI.
|
||||||
|
if ! run_logged -s dot "grub-install (removable fallback) failed" \
|
||||||
|
arch-chroot "$TARGET" grub-install \
|
||||||
|
--target=x86_64-efi --efi-directory=/boot --bootloader-id=ALVIN \
|
||||||
|
--removable --recheck; then
|
||||||
|
die "grub-install --removable failed - \\EFI\\BOOT\\BOOTX64.EFI was not written (output above, full log: $LOG)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify the fallback file really landed: $TARGET/boot IS the ESP mount,
|
||||||
|
# so this checks the exact file firmware will look for. This is the "is
|
||||||
|
# there any bootloader at all" gate - it must run before any NVRAM
|
||||||
|
# failure is allowed to abort the install.
|
||||||
|
if [[ ! -f "${TARGET}/boot/EFI/BOOT/BOOTX64.EFI" ]]; then
|
||||||
|
die "Fallback bootloader missing: ${TARGET}/boot/EFI/BOOT/BOOTX64.EFI was not created - this system could be unbootable if the firmware drops its boot entry."
|
||||||
|
fi
|
||||||
|
log "Bootloader: removable fallback present (EFI/BOOT/BOOTX64.EFI)"
|
||||||
|
|
||||||
|
if ! run_logged -s dot "grub-install (NVRAM boot entry) failed" \
|
||||||
|
arch-chroot "$TARGET" grub-install \
|
||||||
|
--target=x86_64-efi --efi-directory=/boot --bootloader-id=ALVIN \
|
||||||
|
--recheck; then
|
||||||
|
# grub copies its EFI binaries before it asks efibootmgr to register
|
||||||
|
# the entry, so "files present + non-zero exit" means only the NVRAM
|
||||||
|
# registration failed - fall through to the host-side creation below.
|
||||||
|
if [[ -f "${TARGET}/boot/EFI/ALVIN/grubx64.efi" ]]; then
|
||||||
|
warn "grub-install could not register the NVRAM entry (output above) - trying from the live medium."
|
||||||
|
log "Bootloader: grub-install NVRAM registration failed; EFI/ALVIN/grubx64.efi present"
|
||||||
|
else
|
||||||
|
# Note the fallback honestly: BOOTX64.EFI exists, but grub.cfg is
|
||||||
|
# only generated at the END of this function - an install aborted
|
||||||
|
# here leaves no config, so the fallback alone does NOT boot yet.
|
||||||
|
die "grub-install failed - EFI/ALVIN/grubx64.efi was not written (output above, full log: $LOG). \\EFI\\BOOT\\BOOTX64.EFI exists, but the install stops before grub-mkconfig runs, so no bootable configuration was written."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify the NVRAM entry persisted. arch-chroot rbinds /sys and mounts
|
||||||
|
# efivarfs, so grub-install inside the chroot wrote the same variables
|
||||||
|
# efibootmgr reads here - there is no visibility difference; we verify
|
||||||
|
# and create on the live medium because that is the direct path to the
|
||||||
|
# real disk. Create the entry if grub-install could not persist it
|
||||||
|
# (write-protected or flaky NVRAM), then re-verify. Verification failure
|
||||||
|
# aborts the install.
|
||||||
|
local esp_partnum boot_entry
|
||||||
|
esp_partnum=$(lsblk -no PARTN "$(part 1)" 2>/dev/null | head -n1) || esp_partnum=""
|
||||||
|
esp_partnum=${esp_partnum:-1}
|
||||||
|
# Match the loader path, not just the label: a stale ALVIN entry left by
|
||||||
|
# a previous install on another disk would satisfy a bare label match.
|
||||||
|
boot_entry=$(efibootmgr -v 2>/dev/null \
|
||||||
|
| grep -iF '\EFI\ALVIN\grubx64.efi' | head -n1) || boot_entry=""
|
||||||
|
if [[ -z "$boot_entry" ]]; then
|
||||||
|
warn "No ALVIN entry in the firmware NVRAM - creating one now."
|
||||||
|
if ! run_logged "efibootmgr could not create the NVRAM boot entry" \
|
||||||
|
efibootmgr -c -d "$DISK" -p "$esp_partnum" -L ALVIN \
|
||||||
|
-l '\EFI\ALVIN\grubx64.efi'; then
|
||||||
|
die "Could not create the firmware NVRAM boot entry (output above). Refusing to finish: the install stops before grub-mkconfig runs, so \\EFI\\BOOT\\BOOTX64.EFI exists but is not bootable yet. Check UEFI setup (NVRAM write access) and re-run."
|
||||||
|
fi
|
||||||
|
boot_entry=$(efibootmgr -v 2>/dev/null \
|
||||||
|
| grep -iF '\EFI\ALVIN\grubx64.efi' | head -n1) || boot_entry=""
|
||||||
|
if [[ -z "$boot_entry" ]]; then
|
||||||
|
die "NVRAM boot entry still missing after efibootmgr -c. Refusing to finish: the install stops before grub-mkconfig runs, so the system is not bootable yet - check UEFI setup and re-run."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
log "Bootloader: NVRAM entry verified: ${boot_entry}"
|
||||||
|
|
||||||
if [[ "$UNL0KR" == "true" ]]; then
|
if [[ "$UNL0KR" == "true" ]]; then
|
||||||
# Mode B: grub.cfg is written in install_unl0kr together with the
|
# Mode B: grub.cfg is written in install_unl0kr together with the
|
||||||
# final initramfs (a config generated now would not match it).
|
# final initramfs (a config generated now would not match it).
|
||||||
log "grub-mkconfig deferred (Mode B, after unl0kr build)"
|
log "grub-mkconfig deferred (Mode B, after unl0kr build)"
|
||||||
ok "Bootloader installed (config finalized after unl0kr)."
|
ok "Bootloader installed (NVRAM entry + fallback verified; config finalized after unl0kr)."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg
|
if ! run_logged -s dot "grub-mkconfig failed" \
|
||||||
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg; then
|
||||||
|
die "grub-mkconfig failed - the installed system would boot into an empty GRUB menu (output above, full log: $LOG)."
|
||||||
|
fi
|
||||||
|
|
||||||
ok "Bootloader installed."
|
ok "Bootloader installed (NVRAM entry + removable fallback verified)."
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── TPM2 auto-unlock ──────────────────────────────────────────────────────────
|
# ── TPM2 auto-unlock ──────────────────────────────────────────────────────────
|
||||||
@@ -1280,8 +1509,16 @@ install_tpm() {
|
|||||||
# $PASSWORD (stdin would instead trigger the ask-password agent and block
|
# $PASSWORD (stdin would instead trigger the ask-password agent and block
|
||||||
# until someone typed it on a console). It only adds a keyslot - no
|
# until someone typed it on a console). It only adds a keyslot - no
|
||||||
# --wipe-slot - so the passphrase slot always stays as a fallback.
|
# --wipe-slot - so the passphrase slot always stays as a fallback.
|
||||||
if PASSWORD="$PASS" systemd-cryptenroll --tpm2-device=auto \
|
# Exported rather than put on the command line (env PASSWORD=...): argv
|
||||||
--tpm2-pcrs=0+7 "$(part 2)" >>"$LOG" 2>&1; then
|
# is world-readable via /proc, the environment is not - and unset right
|
||||||
|
# after, so the secret exists only for the duration of this call.
|
||||||
|
export PASSWORD="$PASS"
|
||||||
|
local cenrc=0
|
||||||
|
run_logged "systemd-cryptenroll failed" \
|
||||||
|
systemd-cryptenroll --tpm2-device=auto \
|
||||||
|
--tpm2-pcrs=0+7 "$(part 2)" || cenrc=$?
|
||||||
|
unset PASSWORD
|
||||||
|
if (( cenrc == 0 )); then
|
||||||
TPM_STATUS="enrolled"
|
TPM_STATUS="enrolled"
|
||||||
# Enrolment alone does nothing at boot: systemd-cryptsetup only uses
|
# Enrolment alone does nothing at boot: systemd-cryptsetup only uses
|
||||||
# the TPM2 keyslot when the cmdline asks for it. install_bootloader
|
# the TPM2 keyslot when the cmdline asks for it. install_bootloader
|
||||||
@@ -1291,12 +1528,13 @@ install_tpm() {
|
|||||||
local luks_uuid
|
local luks_uuid
|
||||||
luks_uuid=$(blkid -s UUID -o value "$(part 2)")
|
luks_uuid=$(blkid -s UUID -o value "$(part 2)")
|
||||||
write_mode_a_cmdline "$luks_uuid" "rd.luks.options=${luks_uuid}=tpm2-device=auto"
|
write_mode_a_cmdline "$luks_uuid" "rd.luks.options=${luks_uuid}=tpm2-device=auto"
|
||||||
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg >>"$LOG" 2>&1 \
|
run_logged -s dot "grub-mkconfig failed (rd.luks.options)" \
|
||||||
|| warn "grub-mkconfig failed after adding rd.luks.options (see $LOG) - TPM2 unlock will not activate at boot."
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg \
|
||||||
|
|| warn "grub-mkconfig failed after adding rd.luks.options (output above) - TPM2 unlock will not activate at boot."
|
||||||
ok "TPM2 auto-unlock enrolled and activated (rd.luks.options, PCR 0+7)."
|
ok "TPM2 auto-unlock enrolled and activated (rd.luks.options, PCR 0+7)."
|
||||||
else
|
else
|
||||||
TPM_STATUS="failed"
|
TPM_STATUS="failed"
|
||||||
warn "TPM enrollment failed (see $LOG) - passphrase unlock still works."
|
warn "TPM enrollment failed (output above) - passphrase unlock still works."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# TPM-only unlock with an unencrypted /boot is weak: anyone with physical
|
# TPM-only unlock with an unencrypted /boot is weak: anyone with physical
|
||||||
@@ -1336,10 +1574,12 @@ unl0kr_fallback_mode_a() {
|
|||||||
TPM_STATUS="skipped-unl0kr-fallback"
|
TPM_STATUS="skipped-unl0kr-fallback"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
arch-chroot "$TARGET" mkinitcpio -P >>"$LOG" 2>&1 \
|
run_logged -s dot "mkinitcpio -P failed (unl0kr fallback)" \
|
||||||
|| warn "mkinitcpio -P failed during unl0kr fallback (see $LOG)."
|
arch-chroot "$TARGET" mkinitcpio -P \
|
||||||
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg >>"$LOG" 2>&1 \
|
|| warn "mkinitcpio -P failed during unl0kr fallback (output above)."
|
||||||
|| warn "grub-mkconfig failed during unl0kr fallback (see $LOG)."
|
run_logged -s dot "grub-mkconfig failed (unl0kr fallback)" \
|
||||||
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg \
|
||||||
|
|| warn "grub-mkconfig failed during unl0kr fallback (output above)."
|
||||||
warn "The system will ask for the LUKS passphrase at boot - test a reboot before relying on it."
|
warn "The system will ask for the LUKS passphrase at boot - test a reboot before relying on it."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1355,8 +1595,10 @@ install_unl0kr() {
|
|||||||
# yay runs as the new user and needs passwordless sudo for this one call.
|
# yay runs as the new user and needs passwordless sudo for this one call.
|
||||||
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
||||||
chmod 440 "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
chmod 440 "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
||||||
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
run_logged -s jump -t "Building unl0kr from the AUR (this may take a while)" \
|
||||||
env HOME="$home_in_target" yay -S --noconfirm unl0kr >>"$LOG" 2>&1 || build_rc=$?
|
"yay failed to build/install unl0kr" \
|
||||||
|
arch-chroot "$TARGET" runuser -u "$USERNAME" -- \
|
||||||
|
env HOME="$home_in_target" yay -S --noconfirm unl0kr || build_rc=$?
|
||||||
rm -f "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
rm -f "${TARGET}/etc/sudoers.d/99-unl0kr-install"
|
||||||
|
|
||||||
if (( build_rc != 0 )); then
|
if (( build_rc != 0 )); then
|
||||||
@@ -1388,7 +1630,10 @@ install_unl0kr() {
|
|||||||
cat "$hook"
|
cat "$hook"
|
||||||
echo "-- install/unl0kr --"
|
echo "-- install/unl0kr --"
|
||||||
cat "$hook_install"
|
cat "$hook_install"
|
||||||
} >>"$LOG" 2>&1 || warn "Could not log the unl0kr hook files (see $LOG)."
|
} >>"$LOG" 2>&1 || {
|
||||||
|
warn "Could not log the unl0kr hook files (see $LOG)."
|
||||||
|
page_log_tail "unl0kr hook file output (q to quit)" 40 || true
|
||||||
|
}
|
||||||
|
|
||||||
# Touchscreen/input stack for the initramfs: mkinitcpio fails the build
|
# Touchscreen/input stack for the initramfs: mkinitcpio fails the build
|
||||||
# on a MODULES entry it cannot resolve, and -P applies the list to every
|
# on a MODULES entry it cannot resolve, and -P applies the list to every
|
||||||
@@ -1423,12 +1668,14 @@ install_unl0kr() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
info "Rebuilding initramfs with unl0kr..."
|
info "Rebuilding initramfs with unl0kr..."
|
||||||
if ! arch-chroot "$TARGET" mkinitcpio -P >>"$LOG" 2>&1; then
|
if ! run_logged -s dot "mkinitcpio -P failed (unl0kr image)" \
|
||||||
|
arch-chroot "$TARGET" mkinitcpio -P; then
|
||||||
unl0kr_fallback_mode_a "mkinitcpio -P failed"
|
unl0kr_fallback_mode_a "mkinitcpio -P failed"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
# grub.cfg is written now, next to the final image (Mode B deferred it).
|
# grub.cfg is written now, next to the final image (Mode B deferred it).
|
||||||
if ! arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg >>"$LOG" 2>&1; then
|
if ! run_logged -s dot "grub-mkconfig failed (unl0kr image)" \
|
||||||
|
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg; then
|
||||||
unl0kr_fallback_mode_a "grub-mkconfig failed"
|
unl0kr_fallback_mode_a "grub-mkconfig failed"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -1437,6 +1684,7 @@ install_unl0kr() {
|
|||||||
# `lsinitcpio -a` prints the hook run order and the included module list.
|
# `lsinitcpio -a` prints the hook run order and the included module list.
|
||||||
local analysis
|
local analysis
|
||||||
if ! analysis=$(lsinitcpio -a "${TARGET}/boot/initramfs-linux.img" 2>>"$LOG"); then
|
if ! analysis=$(lsinitcpio -a "${TARGET}/boot/initramfs-linux.img" 2>>"$LOG"); then
|
||||||
|
page_log_tail "lsinitcpio error output (q to quit)" 40 || true
|
||||||
unl0kr_fallback_mode_a "lsinitcpio could not read the image"
|
unl0kr_fallback_mode_a "lsinitcpio could not read the image"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -1567,8 +1815,9 @@ install_login_manager() {
|
|||||||
local u
|
local u
|
||||||
for u in greetd sddm gdm; do
|
for u in greetd sddm gdm; do
|
||||||
if [[ -f "${TARGET}/usr/lib/systemd/system/${u}.service" ]]; then
|
if [[ -f "${TARGET}/usr/lib/systemd/system/${u}.service" ]]; then
|
||||||
arch-chroot "$TARGET" systemctl disable "$u.service" >>"$LOG" 2>&1 \
|
run_logged "Could not disable ${u}.service" \
|
||||||
|| warn "Could not disable ${u}.service (see $LOG)."
|
arch-chroot "$TARGET" systemctl disable "$u.service" \
|
||||||
|
|| warn "Could not disable ${u}.service (output above)."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
rm -f "${TARGET}/etc/systemd/system/getty@tty1.service.d/autologin.conf"
|
rm -f "${TARGET}/etc/systemd/system/getty@tty1.service.d/autologin.conf"
|
||||||
@@ -1586,9 +1835,10 @@ install_login_manager() {
|
|||||||
install_none_profile
|
install_none_profile
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
if ! arch-chroot "$TARGET" pacman -S --noconfirm greetd greetd-tuigreet \
|
if ! run_logged -s dot -t "Installing greetd + tuigreet" \
|
||||||
>>"$LOG" 2>&1; then
|
"greetd install failed" \
|
||||||
warn "greetd install failed (see $LOG) - falling back to 'none'."
|
arch-chroot "$TARGET" pacman -S --noconfirm greetd greetd-tuigreet; then
|
||||||
|
warn "greetd install failed (output above) - falling back to 'none'."
|
||||||
LOGIN_MANAGER="none"
|
LOGIN_MANAGER="none"
|
||||||
install_none_profile
|
install_none_profile
|
||||||
return
|
return
|
||||||
@@ -1596,8 +1846,9 @@ install_login_manager() {
|
|||||||
# The package normally provides the greeter account; create it if
|
# The package normally provides the greeter account; create it if
|
||||||
# this version does not (ArchWiki: user = "greeter").
|
# this version does not (ArchWiki: user = "greeter").
|
||||||
if ! arch-chroot "$TARGET" id greeter >/dev/null 2>&1; then
|
if ! arch-chroot "$TARGET" id greeter >/dev/null 2>&1; then
|
||||||
arch-chroot "$TARGET" useradd -r -M -s /usr/bin/nologin greeter \
|
run_logged "Could not create the greeter user" \
|
||||||
>>"$LOG" 2>&1 || warn "Could not create the greeter user (see $LOG)."
|
arch-chroot "$TARGET" useradd -r -M -s /usr/bin/nologin greeter \
|
||||||
|
|| warn "Could not create the greeter user (output above)."
|
||||||
fi
|
fi
|
||||||
mkdir -p "${TARGET}/etc/greetd"
|
mkdir -p "${TARGET}/etc/greetd"
|
||||||
cat > "${TARGET}/etc/greetd/config.toml" <<'TOML'
|
cat > "${TARGET}/etc/greetd/config.toml" <<'TOML'
|
||||||
@@ -1608,11 +1859,13 @@ vt = 1
|
|||||||
command = "tuigreet --cmd Hyprland"
|
command = "tuigreet --cmd Hyprland"
|
||||||
user = "greeter"
|
user = "greeter"
|
||||||
TOML
|
TOML
|
||||||
if arch-chroot "$TARGET" systemctl enable greetd.service >>"$LOG" 2>&1; then
|
if run_logged "Could not enable greetd.service" \
|
||||||
arch-chroot "$TARGET" systemctl disable getty@tty1.service >>"$LOG" 2>&1 || true
|
arch-chroot "$TARGET" systemctl enable greetd.service; then
|
||||||
|
run_logged "Could not disable getty@tty1.service" \
|
||||||
|
arch-chroot "$TARGET" systemctl disable getty@tty1.service || true
|
||||||
ok "greetd + tuigreet enabled (Hyprland)."
|
ok "greetd + tuigreet enabled (Hyprland)."
|
||||||
else
|
else
|
||||||
warn "Could not enable greetd.service (see $LOG) - keeping getty@tty1 as the login path."
|
warn "Could not enable greetd.service (output above) - keeping getty@tty1 as the login path."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
sddm)
|
sddm)
|
||||||
@@ -1623,18 +1876,22 @@ TOML
|
|||||||
install_none_profile
|
install_none_profile
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
if ! arch-chroot "$TARGET" pacman -S --noconfirm sddm >>"$LOG" 2>&1; then
|
if ! run_logged -s dot -t "Installing sddm" \
|
||||||
warn "sddm install failed (see $LOG) - falling back to 'none'."
|
"sddm install failed" \
|
||||||
|
arch-chroot "$TARGET" pacman -S --noconfirm sddm; then
|
||||||
|
warn "sddm install failed (output above) - falling back to 'none'."
|
||||||
LOGIN_MANAGER="none"
|
LOGIN_MANAGER="none"
|
||||||
install_none_profile
|
install_none_profile
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
ensure_hyprland_session
|
ensure_hyprland_session
|
||||||
if arch-chroot "$TARGET" systemctl enable sddm.service >>"$LOG" 2>&1; then
|
if run_logged "Could not enable sddm.service" \
|
||||||
arch-chroot "$TARGET" systemctl disable getty@tty1.service >>"$LOG" 2>&1 || true
|
arch-chroot "$TARGET" systemctl enable sddm.service; then
|
||||||
|
run_logged "Could not disable getty@tty1.service" \
|
||||||
|
arch-chroot "$TARGET" systemctl disable getty@tty1.service || true
|
||||||
ok "sddm enabled (Hyprland)."
|
ok "sddm enabled (Hyprland)."
|
||||||
else
|
else
|
||||||
warn "Could not enable sddm.service (see $LOG) - keeping getty@tty1 as the login path."
|
warn "Could not enable sddm.service (output above) - keeping getty@tty1 as the login path."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
gdm)
|
gdm)
|
||||||
@@ -1646,25 +1903,29 @@ TOML
|
|||||||
install_none_profile
|
install_none_profile
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
if ! arch-chroot "$TARGET" pacman -S --noconfirm gdm >>"$LOG" 2>&1; then
|
if ! run_logged -s dot -t "Installing gdm" \
|
||||||
warn "gdm install failed (see $LOG) - falling back to 'none'."
|
"gdm install failed" \
|
||||||
|
arch-chroot "$TARGET" pacman -S --noconfirm gdm; then
|
||||||
|
warn "gdm install failed (output above) - falling back to 'none'."
|
||||||
LOGIN_MANAGER="none"
|
LOGIN_MANAGER="none"
|
||||||
install_none_profile
|
install_none_profile
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
ensure_hyprland_session
|
ensure_hyprland_session
|
||||||
if arch-chroot "$TARGET" systemctl enable gdm.service >>"$LOG" 2>&1; then
|
if run_logged "Could not enable gdm.service" \
|
||||||
arch-chroot "$TARGET" systemctl disable getty@tty1.service >>"$LOG" 2>&1 || true
|
arch-chroot "$TARGET" systemctl enable gdm.service; then
|
||||||
|
run_logged "Could not disable getty@tty1.service" \
|
||||||
|
arch-chroot "$TARGET" systemctl disable getty@tty1.service || true
|
||||||
ok "gdm enabled (Hyprland session)."
|
ok "gdm enabled (Hyprland session)."
|
||||||
else
|
else
|
||||||
warn "Could not enable gdm.service (see $LOG) - keeping getty@tty1 as the login path."
|
warn "Could not enable gdm.service (output above) - keeping getty@tty1 as the login path."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# none: keep getty on tty1; install_none_profile writes the
|
# none: keep getty on tty1; install_none_profile writes the
|
||||||
# login profile that starts the compositor.
|
# login profile that starts the compositor.
|
||||||
arch-chroot "$TARGET" systemctl enable getty@tty1.service >>"$LOG" 2>&1 \
|
run_logged "Could not enable getty@tty1.service" \
|
||||||
|| true
|
arch-chroot "$TARGET" systemctl enable getty@tty1.service || true
|
||||||
install_none_profile
|
install_none_profile
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -1829,6 +2090,16 @@ install_cleanup() {
|
|||||||
gum style --foreground 10 --align center \
|
gum style --foreground 10 --align center \
|
||||||
"Run 'reboot' to boot into your new system."
|
"Run 'reboot' to boot into your new system."
|
||||||
echo ""
|
echo ""
|
||||||
|
# Debug aid: every failure was already paged at the moment it happened;
|
||||||
|
# this offers the complete record (all step output) on demand.
|
||||||
|
if gum confirm --affirmative "View install log" --negative "No thanks" \
|
||||||
|
"Open the full install log (${LOG})?"; then
|
||||||
|
gum pager <"$LOG" || true
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
# Last line on screen after the optional log view - the instruction to
|
||||||
|
# reboot must be the final thing the user reads.
|
||||||
|
echo "Run 'reboot' to boot into your new system."
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user