From d445f965ceccb6eec02f32de25f96d0c6dd2e7bd Mon Sep 17 00:00:00 2001 From: The_miro Date: Fri, 26 Jun 2026 20:58:37 +0200 Subject: [PATCH] fix(modules): make service/session ops chroot-safe to prevent install aborts When the TUI modules run inside the archiso installer chroot, the new system's systemd is not the running init and there is no user session bus. Operations like `systemctl start`, `enable --now`, `systemctl --user`, and `gsettings` fail there and, under `set -e`, abort the whole module. - logging.sh: add in_chroot/have_user_bus/enable_service/start_service helpers. enable_service warns instead of aborting; start_service skips in a chroot (unit starts on first boot via its enable symlink). - core.sh, hyprland.sh, hyprlua.sh, niri.sh: route enables through enable_service, starts through start_service, and guard gsettings behind have_user_bus. Fixes the cronie-enable failure and the ly/DM setup abort. - app modules (tlp, timeshift, open-webui, mysql, qemu, ollama, cockpit, docker, ssh-server): convert `enable --now`/plain enables to enable_service + start_service so they no longer abort during chroot install. - tui-install.sh: run modules with output to a file plus a pid-bound tail, waiting on the module PID, so a daemon child inheriting the pipe can no longer hang the installer after a module (e.g. flatpak) finishes. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM --- .../modules/Desktop-Environments/hyprland.sh | 16 ++++---- setup/modules/Desktop-Environments/hyprlua.sh | 26 ++++++++----- setup/modules/Desktop-Environments/niri.sh | 24 +++++++----- setup/modules/core.sh | 10 ++--- setup/modules/lib/logging.sh | 38 +++++++++++++++++++ .../modules/optional-Modules/apps/cockpit.sh | 2 +- setup/modules/optional-Modules/apps/docker.sh | 2 +- setup/modules/optional-Modules/apps/mysql.sh | 2 +- setup/modules/optional-Modules/apps/ollama.sh | 2 +- .../optional-Modules/apps/open-webui.sh | 2 +- setup/modules/optional-Modules/apps/qemu.sh | 2 +- .../optional-Modules/apps/ssh-server.sh | 2 +- .../optional-Modules/apps/timeshift.sh | 2 +- setup/modules/optional-Modules/apps/tlp.sh | 4 +- setup/tui-install.sh | 19 ++++++++-- 15 files changed, 106 insertions(+), 47 deletions(-) diff --git a/setup/modules/Desktop-Environments/hyprland.sh b/setup/modules/Desktop-Environments/hyprland.sh index f690620..2df1d44 100755 --- a/setup/modules/Desktop-Environments/hyprland.sh +++ b/setup/modules/Desktop-Environments/hyprland.sh @@ -150,7 +150,7 @@ sudo pacman -Syu --noconfirm --needed -- "${HYPRLAND_PACKAGES[@]}" log "Enabling essential services..." # NetworkManager must be active on boot for network connectivity. -sudo systemctl enable NetworkManager.service +enable_service NetworkManager.service # getty@tty1 is the default text-mode login prompt; we replace it with ly. # '|| true' prevents abort if the unit is already disabled or doesn't exist. @@ -158,11 +158,11 @@ sudo systemctl disable getty@tty1.service || true # ly is the TUI display manager that runs on tty1 and launches Hyprland after # the user logs in. -sudo systemctl enable ly@tty1.service +enable_service ly@tty1.service # udisks2 provides the D-Bus API for block devices; required by udiskie for # automatic USB/external drive mounting. -sudo systemctl enable udisks2.service +enable_service udisks2.service # --------------------------------------------------------------------------- # 4. Install AUR packages @@ -285,11 +285,11 @@ tar -zxf ~/install-tmp/Nordzy-cursors-lefthand.tar.gz -C ~/.icons/ log "Enabling Bluetooth and wireless services..." # bluez: core Bluetooth protocol stack (powers and manages adapters on boot) -sudo systemctl enable bluez +enable_service bluez # bluetooth.service: high-level service handling pairing and profiles -sudo systemctl enable bluetooth.service +enable_service bluetooth.service # iwd: modern Wi-Fi daemon from Intel; used by iwmenu for the bar Wi-Fi picker -sudo systemctl enable iwd.service +enable_service iwd.service # --------------------------------------------------------------------------- # 9. Hyprland plugins (must be run from inside a live Hyprland session) @@ -378,8 +378,8 @@ fi # Enable the systemd user unit and start it immediately so this session already # benefits from auto-mounting without requiring a reboot. log "Enabling udiskie service..." -sudo systemctl enable udiskie.service -sudo systemctl start udiskie.service +enable_service udiskie.service +start_service udiskie.service # --------------------------------------------------------------------------- # 15. Install config updater and theme script diff --git a/setup/modules/Desktop-Environments/hyprlua.sh b/setup/modules/Desktop-Environments/hyprlua.sh index 69fa998..fe5b771 100755 --- a/setup/modules/Desktop-Environments/hyprlua.sh +++ b/setup/modules/Desktop-Environments/hyprlua.sh @@ -153,17 +153,17 @@ sudo pacman -Syu --noconfirm --needed -- "${HYPRLUA_PACKAGES[@]}" log "Enabling essential services..." # NetworkManager: manages all network connections (wired, Wi-Fi, VPN). -sudo systemctl enable NetworkManager.service +enable_service NetworkManager.service # Disable the default getty login prompt on tty1 so ly can own that TTY. # '|| true' prevents abort if the unit is already disabled. sudo systemctl disable getty@tty1.service || true # ly: TUI display manager that presents the login screen on tty1. -sudo systemctl enable ly@tty1.service +enable_service ly@tty1.service # udisks2: D-Bus block-device service required by udiskie for auto-mounting. -sudo systemctl enable udisks2.service +enable_service udisks2.service # --------------------------------------------------------------------------- # 4. Install AUR packages @@ -227,7 +227,7 @@ case $doit in # evdev-right-click-emulation enables long-press → right-click on touch # screens; its systemd service must be started alongside the session. yay -S evdev-right-click-emulation - sudo systemctl enable --now evdev-rce.service ;; + enable_service evdev-rce.service; start_service evdev-rce.service ;; # Unrecognised key: leave ~/.config/eww absent; user must copy manually. *) warn "No valid choice — skipping EWW copy. Run manually later." ;; esac @@ -278,7 +278,13 @@ sudo ln -sf /usr/bin/ksshaskpass /usr/lib/ssh/ssh-askpass # Instruct GTK apps that prefer the system colour-scheme to use dark mode. # This is particularly important under Hyprland where there is no GNOME # settings daemon propagating the user's preference automatically. -gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' +# gsettings needs a session D-Bus (dconf) — absent in the installer chroot, where +# it would hang/fail — so only apply it when a user bus is reachable. +if have_user_bus; then + gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' || true +else + skip "No session bus — set GTK dark mode after first login (gsettings)." +fi # --------------------------------------------------------------------------- # 7. Cursor setup @@ -299,11 +305,11 @@ tar -zxf ~/install-tmp/Nordzy-cursors-lefthand.tar.gz -C ~/.icons/ log "Enabling Bluetooth and wireless services..." # bluez: core Bluetooth daemon; powers adapters and makes them available on boot -sudo systemctl enable bluez +enable_service bluez # bluetooth.service: handles pairing, profiles, and device reconnection -sudo systemctl enable bluetooth.service +enable_service bluetooth.service # iwd: Intel Wireless Daemon; the Wi-Fi back-end used by iwmenu in the bar -sudo systemctl enable iwd.service +enable_service iwd.service #systemctl --user enable --now hyprmoncfgd # --------------------------------------------------------------------------- @@ -390,8 +396,8 @@ fi # Enable the systemd unit for future boots and start it immediately so USB # devices inserted during this session are handled right away. log "Enabling udiskie service..." -sudo systemctl enable udiskie.service -sudo systemctl start udiskie.service +enable_service udiskie.service +start_service udiskie.service # --------------------------------------------------------------------------- # 15. Install config updater and theme script diff --git a/setup/modules/Desktop-Environments/niri.sh b/setup/modules/Desktop-Environments/niri.sh index b3c2278..38975a5 100755 --- a/setup/modules/Desktop-Environments/niri.sh +++ b/setup/modules/Desktop-Environments/niri.sh @@ -31,10 +31,10 @@ sudo pacman -Syu --noconfirm --needed \ # 3. Enable essential services log "Enabling essential services..." -sudo systemctl enable NetworkManager.service +enable_service NetworkManager.service sudo systemctl disable getty@tty1.service || true -sudo systemctl enable ly@tty1.service -sudo systemctl enable udisks2.service +enable_service ly@tty1.service +enable_service udisks2.service # 4. Install AUR packages log "Installing AUR packages..." @@ -72,7 +72,11 @@ sudo cp -r ~/Dotfiles/gtk-themes/cyberqueer /usr/share/themes sudo cp ~/Dotfiles/desktopenvs/niri/btop/themes/cyberqueer.theme /usr/share/btop/themes sudo cp -f ~/Dotfiles/etc-ly-config.ini /etc/ly/config.ini sudo ln -sf /usr/bin/kitty /usr/bin/xdg-terminal-exec -gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' +if have_user_bus; then + gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' || true +else + skip "No session bus — set GNOME color-scheme to dark after first login." +fi # 7. Cursor setup log "Installing cursor theme..." @@ -83,9 +87,9 @@ tar -zxf ~/install-tmp/Nordzy-cursors-lefthand.tar.gz -C ~/.icons/ # 8. Enable Bluetooth and wireless services log "Enabling Bluetooth and wireless services..." -sudo systemctl enable bluez -sudo systemctl enable bluetooth.service -sudo systemctl enable iwd.service +enable_service bluez +enable_service bluetooth.service +enable_service iwd.service # 9. Copy configs log "Copying configs..." @@ -101,7 +105,7 @@ cp ~/Dotfiles/colors.conf ~/.config/colors.conf log "Deploying greetd config for niri..." sudo mkdir -p /etc/greetd sudo cp -f ~/Dotfiles/desktopenvs/niri/greetd-tuigreet/config.toml /etc/greetd/config.toml -sudo systemctl enable greetd.service +enable_service greetd.service # 11. Wallpaper and resources log "Copying wallpaper and resources..." @@ -128,8 +132,8 @@ fi # 14. Enable udiskie log "Enabling udiskie service..." -sudo systemctl enable udiskie.service -sudo systemctl start udiskie.service +enable_service udiskie.service +start_service udiskie.service # 15. Make scripts executable log "Setting script permissions..." diff --git a/setup/modules/core.sh b/setup/modules/core.sh index abb79e9..690a240 100644 --- a/setup/modules/core.sh +++ b/setup/modules/core.sh @@ -31,13 +31,13 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib/logging.sh" # the most user-friendly option — handles DHCP, WiFi, VPN, and has applets. # NOTE: systemctl enable only marks it to start at boot; it doesn't start it now. log "Enabling NetworkManager..." -sudo systemctl enable NetworkManager.service +enable_service NetworkManager.service # ── cronie ──────────────────────────────────────────────────────────────────── # WHY: Provides the system cron daemon. Some tools (backups, vnstat stats, # fail2ban cleanup) rely on cron jobs. Arch does not enable it by default. log "Enabling cronie..." -sudo systemctl enable cronie.service +enable_service cronie.service # ── greetd / tuigreet ───────────────────────────────────────────────────────── # WHY: greetd is a minimal, standards-compliant display manager (login screen). @@ -50,20 +50,20 @@ sudo systemctl enable cronie.service # -f flag forces overwrite of any existing config. log "Deploying greetd config..." sudo cp -f ~/Dotfiles/desktopenvs/hyprland/greetd-tuigreet/config.toml /etc/greetd/config.toml -sudo systemctl enable greetd.service +enable_service greetd.service # ── fail2ban ────────────────────────────────────────────────────────────────── # WHY: Protects against brute-force attacks by monitoring log files and # temporarily banning IPs that show malicious signs (too many failed logins). # Important on any machine with SSH open to the network. log "Enabling fail2ban..." -sudo systemctl enable fail2ban.service +enable_service fail2ban.service # ── udisks2 ─────────────────────────────────────────────────────────────────── # WHY: udisks2 provides automatic mounting of USB drives and other removable # media via D-Bus. Required by file managers (Thunar, pcmanfm) and desktop # utilities that want to offer "Open when inserted" functionality. log "Enabling udisks2..." -sudo systemctl enable udisks2.service +enable_service udisks2.service log "Core services enabled." diff --git a/setup/modules/lib/logging.sh b/setup/modules/lib/logging.sh index 52891c0..7e6a795 100644 --- a/setup/modules/lib/logging.sh +++ b/setup/modules/lib/logging.sh @@ -48,6 +48,44 @@ warn() { printf "${YELLOW}[!] %s${RESET}\n" "$*" >&2; } # Used for: unrecoverable failures (caller decides whether to exit) err() { printf "${RED}[✖] %s${RESET}\n" "$*" >&2; } +# ── Chroot-/session-aware operation helpers ──────────────────────────────────── +# Modules are frequently run inside the archiso installer's chroot, where the +# new system's systemd is NOT the running init and there is no user session bus. +# In that environment: +# * `systemctl enable` works (it only writes symlinks), and +# * `systemctl start` / `--now` / `systemctl --user` / `gsettings` FAIL, +# which would abort a module running under `set -e`. These helpers make those +# operations safe so an install completes; the runtime bits take effect on first +# boot / first login instead. + +# in_chroot: true when running inside a chroot (no booted system manager here). +in_chroot() { systemd-detect-virt --chroot 2>/dev/null; } + +# have_user_bus: true when a user session D-Bus is reachable (needed by +# `systemctl --user`, gsettings/dconf, flatpak --user overrides, etc.). +have_user_bus() { + [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]] && return 0 + [[ -S "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/bus" ]] +} + +# enable_service: enable units to start at boot. Best-effort — a failure is +# warned, never fatal, so one unenroll-able unit can't abort the whole module. +enable_service() { + sudo systemctl enable "$@" 2>/dev/null \ + || warn "Could not enable: $* — enable it after first boot." +} + +# start_service: start units only when a system manager is actually running. +# Inside the installer chroot there is none, so we skip — the unit starts on +# first boot via its enable symlink. +start_service() { + if in_chroot; then + skip "Not starting '$*' now (install chroot) — it starts on first boot." + return 0 + fi + sudo systemctl start "$@" 2>/dev/null || warn "Could not start: $*" +} + # ── ensure_flatpak ───────────────────────────────────────────────────────────── # WHY: Many optional app modules install via Flatpak. Rather than duplicating # the bootstrap code in every app script, this function handles it once. diff --git a/setup/modules/optional-Modules/apps/cockpit.sh b/setup/modules/optional-Modules/apps/cockpit.sh index 14cdafd..9e32705 100644 --- a/setup/modules/optional-Modules/apps/cockpit.sh +++ b/setup/modules/optional-Modules/apps/cockpit.sh @@ -55,6 +55,6 @@ yay -S --answerdiff None --answerclean All --noconfirm \ # port 9090 and starts cockpit.service only when an incoming connection arrives. # This is more efficient than keeping the full web server running at all times. log "Enabling Cockpit socket..." -sudo systemctl enable cockpit.socket +enable_service cockpit.socket log "Cockpit enabled. Web UI available at https://localhost:9090" diff --git a/setup/modules/optional-Modules/apps/docker.sh b/setup/modules/optional-Modules/apps/docker.sh index 06ae341..4c4a832 100644 --- a/setup/modules/optional-Modules/apps/docker.sh +++ b/setup/modules/optional-Modules/apps/docker.sh @@ -33,7 +33,7 @@ sudo pacman -S --noconfirm --needed docker docker-compose # docker.service starts the Docker daemon at boot. Without this, running # `docker` commands would fail with "Cannot connect to the Docker daemon". log "Enabling Docker service..." -sudo systemctl enable docker.service +enable_service docker.service # ── Add current user to docker group ───────────────────────────────────────── # By default, only root can communicate with the Docker socket at diff --git a/setup/modules/optional-Modules/apps/mysql.sh b/setup/modules/optional-Modules/apps/mysql.sh index 3ae3003..903e26e 100755 --- a/setup/modules/optional-Modules/apps/mysql.sh +++ b/setup/modules/optional-Modules/apps/mysql.sh @@ -21,5 +21,5 @@ fi log "Enabling MariaDB service..." # enable --now: persist across reboots and start the daemon in the current session. -sudo systemctl enable --now mariadb.service +enable_service mariadb.service; start_service mariadb.service log "MariaDB installed and running." diff --git a/setup/modules/optional-Modules/apps/ollama.sh b/setup/modules/optional-Modules/apps/ollama.sh index 6bde67b..8bd5f68 100755 --- a/setup/modules/optional-Modules/apps/ollama.sh +++ b/setup/modules/optional-Modules/apps/ollama.sh @@ -13,7 +13,7 @@ sudo pacman -S --noconfirm --needed ollama log "Enabling Ollama service..." # The ollama systemd service exposes a REST API on port 11434 used by frontends # such as open-webui; enable --now starts it immediately without a reboot. -sudo systemctl enable --now ollama.service +enable_service ollama.service; start_service ollama.service log "Ollama running on http://localhost:11434" log "Pull models with: ollama pull " diff --git a/setup/modules/optional-Modules/apps/open-webui.sh b/setup/modules/optional-Modules/apps/open-webui.sh index e028ab7..550b45a 100755 --- a/setup/modules/optional-Modules/apps/open-webui.sh +++ b/setup/modules/optional-Modules/apps/open-webui.sh @@ -13,6 +13,6 @@ yay -S --answerdiff None --answerclean All --noconfirm open-webui log "Enabling Open WebUI service..." # The systemd service proxies requests to Ollama at localhost:11434 and serves # the web interface on port 8080; enable --now starts it without a reboot. -sudo systemctl enable --now open-webui.service +enable_service open-webui.service; start_service open-webui.service log "Open WebUI running at http://localhost:8080" diff --git a/setup/modules/optional-Modules/apps/qemu.sh b/setup/modules/optional-Modules/apps/qemu.sh index be49e5a..e428de6 100644 --- a/setup/modules/optional-Modules/apps/qemu.sh +++ b/setup/modules/optional-Modules/apps/qemu.sh @@ -24,7 +24,7 @@ sudo pacman -S --noconfirm --needed \ log "Enabling libvirtd service..." # libvirtd must run as root to manage KVM devices and network namespaces. -sudo systemctl enable --now libvirtd.service +enable_service libvirtd.service; start_service libvirtd.service log "Configuring default NAT network for autostart..." # The 'default' NAT network is created by libvirt on first start; net-autostart diff --git a/setup/modules/optional-Modules/apps/ssh-server.sh b/setup/modules/optional-Modules/apps/ssh-server.sh index b744a9c..ea3a3da 100644 --- a/setup/modules/optional-Modules/apps/ssh-server.sh +++ b/setup/modules/optional-Modules/apps/ssh-server.sh @@ -12,6 +12,6 @@ sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' "$SSHD_C sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' "$SSHD_CONF" log "Enabling sshd service..." -sudo systemctl enable sshd.service +enable_service sshd.service log "SSH server installed and enabled (key auth only, root login disabled)." warn "Add your public key to ~/.ssh/authorized_keys before first use." diff --git a/setup/modules/optional-Modules/apps/timeshift.sh b/setup/modules/optional-Modules/apps/timeshift.sh index d877c40..f4b4c17 100755 --- a/setup/modules/optional-Modules/apps/timeshift.sh +++ b/setup/modules/optional-Modules/apps/timeshift.sh @@ -9,5 +9,5 @@ log "Installing timeshift-autosnap (AUR)..." yay -S --answerdiff None --answerclean All --noconfirm timeshift-autosnap log "Enabling cronie service..." -sudo systemctl enable --now cronie.service +enable_service cronie.service; start_service cronie.service log "Timeshift installed with autosnap on pacman transactions." diff --git a/setup/modules/optional-Modules/apps/tlp.sh b/setup/modules/optional-Modules/apps/tlp.sh index 567e9cd..0a35596 100755 --- a/setup/modules/optional-Modules/apps/tlp.sh +++ b/setup/modules/optional-Modules/apps/tlp.sh @@ -6,7 +6,7 @@ log "Installing TLP (laptop power management)..." sudo pacman -S --noconfirm --needed tlp tlp-rdw log "Enabling TLP and masking rfkill to avoid conflicts..." -sudo systemctl enable --now tlp.service -sudo systemctl enable NetworkManager-dispatcher.service +enable_service tlp.service; start_service tlp.service +enable_service NetworkManager-dispatcher.service sudo systemctl mask systemd-rfkill.service systemd-rfkill.socket log "TLP installed." diff --git a/setup/tui-install.sh b/setup/tui-install.sh index 892d014..ce58a09 100755 --- a/setup/tui-install.sh +++ b/setup/tui-install.sh @@ -263,11 +263,22 @@ run_module() { printf "\n\033[1;35m [$STEP/$TOTAL] %s\033[0m\n" "$label" printf "\033[35m ─────────────────────────────────────────────\033[0m\n\n" - # Run the module script, merging stderr into stdout, and tee to the log. - # PIPESTATUS[0] captures the exit code of 'bash "$script"' even though - # 'tee' is the last command in the pipe (and would always succeed). + # Run the module, combining stderr into stdout. We append to the log FILE + # (not through a 'tee' pipe) and tail it for live console output, waiting only + # on the module's own PID. A 'bash "$script" | tee' pipeline would block AFTER + # the module finished whenever a daemon it spawned (flatpak's system helper, + # gpg-agent, dbus, …) inherited the pipe's write end and held it open — that is + # the "module done but it pauses" symptom. Writing to a real file avoids the + # wait: lingering daemons hold a harmless file descriptor, not the pipe. local rc=0 - bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]} + bash "$script" >>"$LOG" 2>&1 & + local _modpid=$! + # Mirror newly appended log lines to the console while the module runs. + # --pid makes tail exit once the module process is gone. + tail -n0 --pid="$_modpid" -f "$LOG" 2>/dev/null & + local _tailpid=$! + wait "$_modpid"; rc=$? + wait "$_tailpid" 2>/dev/null || true if [[ $rc -ne 0 ]]; then if [[ $ANSWERFILE_MODE == true ]]; then