diff --git a/configs/airootfs/root/.automated_script.sh b/configs/airootfs/root/.automated_script.sh index 8370658..0c69e64 100644 --- a/configs/airootfs/root/.automated_script.sh +++ b/configs/airootfs/root/.automated_script.sh @@ -45,19 +45,26 @@ cd /root # Keep the actual install screen calm: the dashboard owns /dev/tty while the # noisy installer stream is captured to the support log. -/usr/local/bin/omarchy-install-dashboard "$OMARCHY_INSTALL_LOG_FILE" /run/omarchy-install/state.json >/dev/tty 2>&1 & +rm -f /run/omarchy-install/dashboard.stop +setsid /usr/local/bin/omarchy-install-dashboard "$OMARCHY_INSTALL_LOG_FILE" /run/omarchy-install/state.json >/dev/tty 2>&1 & dashboard_pid=$! export OMARCHY_INSTALL_DASHBOARD_PID="$dashboard_pid" stop_install_dashboard() { + touch /run/omarchy-install/dashboard.stop 2>/dev/null || true if [[ -n ${dashboard_pid:-} ]]; then if kill -0 "$dashboard_pid" 2>/dev/null; then + # The dashboard runs under setsid, so $dashboard_pid is also its process + # group. Kill the group first so child sleep/tte processes cannot keep + # the dashboard alive over the final/error UI. + kill -- -"$dashboard_pid" 2>/dev/null || true kill "$dashboard_pid" 2>/dev/null || true for _ in {1..20}; do kill -0 "$dashboard_pid" 2>/dev/null || break sleep 0.05 done if kill -0 "$dashboard_pid" 2>/dev/null; then + kill -9 -- -"$dashboard_pid" 2>/dev/null || true kill -9 "$dashboard_pid" 2>/dev/null || true fi fi diff --git a/configs/airootfs/usr/local/bin/omarchy-install-dashboard b/configs/airootfs/usr/local/bin/omarchy-install-dashboard index f8ce653..57608e2 100755 --- a/configs/airootfs/usr/local/bin/omarchy-install-dashboard +++ b/configs/airootfs/usr/local/bin/omarchy-install-dashboard @@ -6,6 +6,8 @@ set -euo pipefail LOG_FILE="${1:-/var/log/omarchy-install.log}" STATE_FILE="${2:-/run/omarchy-install/state.json}" +STATE_DIR="$(dirname "$STATE_FILE")" +STOP_FILE="$STATE_DIR/dashboard.stop" LOGO_PATH="${OMARCHY_PATH:-/usr/share/omarchy}/logo.txt" CSI=$'\033[' @@ -151,13 +153,13 @@ weights = [ ('Starting installation', 1, 2, 2), ('Preparing live environment', 2, 6, 5), ('Verifying protected mounts', 6, 8, 3), - ('Installing Arch + Omarchy', 8, 86, 80), - ('Installing base system', 8, 86, 80), - ('Configuring protected boot', 86, 90, 8), - ('Finalizing in chroot', 90, 96, 25), - ('Configuring login', 96, 97, 3), - ('Validating protected boot setup', 97, 99, 5), - ('Validating boot setup', 97, 99, 5), + ('Installing Arch + Omarchy', 8, 82, 60), + ('Installing base system', 8, 82, 60), + ('Configuring protected boot', 82, 86, 8), + ('Finalizing in chroot', 86, 98, 90), + ('Configuring login', 98, 99, 3), + ('Validating protected boot setup', 99, 100, 5), + ('Validating boot setup', 99, 100, 5), ('Finishing', 99, 100, 3), ] start, end, expected = 1, 99, 90 @@ -234,8 +236,9 @@ printf '%s' "$HIDE_CURSOR" >/dev/tty render_static >/dev/tty frame=0 while true; do + [[ -e $STOP_FILE ]] && break render_dynamic "$frame" >/dev/tty - if install_finished; then + if install_finished || [[ -e $STOP_FILE ]]; then sleep 1 break fi diff --git a/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py b/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py index 119a138..d607740 100644 --- a/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py +++ b/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py @@ -1102,7 +1102,27 @@ def _dashboard_pids() -> list[int]: return pids +def _signal_dashboard(pid: int, signal_number: int) -> None: + # Dashboard is launched with setsid, making its PID the process group ID. + # Signal the group first so child sleep/tte processes cannot keep drawing. + try: + os.killpg(pid, signal_number) + except OSError: + pass + try: + os.kill(pid, signal_number) + except OSError: + pass + + def _stop_install_dashboard() -> None: + stop_file = Path("/run/omarchy-install/dashboard.stop") + try: + stop_file.parent.mkdir(parents=True, exist_ok=True) + stop_file.touch() + except OSError: + pass + pids = _dashboard_pids() for signal_number in (15, 9): for pid in pids: @@ -1110,10 +1130,7 @@ def _stop_install_dashboard() -> None: continue if not _pid_is_dashboard(pid): continue - try: - os.kill(pid, signal_number) - except OSError: - pass + _signal_dashboard(pid, signal_number) deadline = time.time() + (1.0 if signal_number == 15 else 0.5) while time.time() < deadline: if all(_dashboard_stopped(pid) for pid in pids):