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