From 7630a385a57263b7ac5ea0d130542c7400788f8a Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 28 Jun 2026 01:43:24 -0400 Subject: feat(hyprland): guard against live GPU/compositor library upgrades A pacman -Syu that swaps mesa/hyprland/wayland runtime libs out from under a running Hyprland session crashes the compositor: the next GPU-lib call hits a now-"(deleted)" library and SIGABRTs, taking the Wayland clients with it (hit ratio 2026-06-07, mesa + hyprland upgraded live). It's a likely driver of ratio's high unsafe-shutdown ratio. I added a pacman PreTransaction hook (hypr-live-update-guard) on the GPU/compositor runtime set. When such an upgrade is pending and Hyprland is running, it aborts before any package is swapped and tells the user to re-run from a TTY with the session stopped. Aborting at PreTransaction is safe: nothing is replaced yet, so the live session is untouched. With no Hyprland running (the from-a-TTY path) the guard stays quiet and the upgrade proceeds. Override with HYPR_ALLOW_LIVE_UPDATE=1 or by touching the sentinel file named in the abort message. archsetup installs the guard and hook in the hyprland path. The decision logic is covered by tests/hypr-live-update-guard (running/not, override, multi-package, empty-target). The hook firing against a real pacman transaction needs a live Hyprland session, filed as a manual test. --- archsetup | 42 ++++++++++ scripts/hypr-live-update-guard | 70 ++++++++++++++++ .../test_hypr_live_update_guard.py | 95 ++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100755 scripts/hypr-live-update-guard create mode 100644 tests/hypr-live-update-guard/test_hypr_live_update_guard.py diff --git a/archsetup b/archsetup index 264ea0f..7531821 100755 --- a/archsetup +++ b/archsetup @@ -1903,6 +1903,48 @@ UDEVEOF sed -i "s/ARCHSETUP_USERNAME/${username}/" /etc/udev/rules.d/99-logitech-brio.rules chmod 644 /etc/udev/rules.d/99-logitech-brio.rules fi + + # Live-update guard: a pacman PreTransaction hook that aborts an upgrade of + # GPU/compositor runtime libraries while a Hyprland session is running, so + # the live compositor doesn't SIGABRT when a library is swapped underneath + # it (hit ratio 2026-06-07: live mesa + hyprland upgrade crashed Hyprland and + # its clients). Re-run the upgrade from a TTY with Hyprland stopped and the + # guard stays quiet. + action="Live-Update Guard" && display "subtitle" "$action" + run_task "installing the live GPU/compositor update guard" \ + cp "$user_archsetup_dir/scripts/hypr-live-update-guard" /usr/local/bin/hypr-live-update-guard + chmod 755 /usr/local/bin/hypr-live-update-guard + + action="installing the live-update guard pacman hook" && display "task" "$action" + mkdir -p /etc/pacman.d/hooks + cat > /etc/pacman.d/hooks/hypr-live-update-guard.hook << 'HOOKEOF' +[Trigger] +Operation = Upgrade +Type = Package +Target = mesa +Target = mesa-* +Target = wayland +Target = libdrm +Target = libglvnd +Target = hyprland +Target = aquamarine +Target = hyprutils +Target = hyprgraphics +Target = vulkan-radeon +Target = vulkan-intel +Target = vulkan-mesa-layers +Target = nvidia-utils +Target = lib32-nvidia-utils +Target = xorg-xwayland + +[Action] +Description = Checking for a live Hyprland session before swapping GPU/compositor libs... +When = PreTransaction +Exec = /usr/local/bin/hypr-live-update-guard +AbortOnFail +NeedsTargets +HOOKEOF + chmod 644 /etc/pacman.d/hooks/hypr-live-update-guard.hook } ### Display Server (conditional) diff --git a/scripts/hypr-live-update-guard b/scripts/hypr-live-update-guard new file mode 100755 index 0000000..4f561ae --- /dev/null +++ b/scripts/hypr-live-update-guard @@ -0,0 +1,70 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-3.0-or-later +# hypr-live-update-guard - abort a live GPU/compositor library upgrade. +# +# Installed as a pacman PreTransaction hook. When an upgrade transaction +# includes GPU/compositor runtime libraries (mesa, hyprland, wayland, GPU +# drivers, ...) AND a Hyprland session is running, this aborts the +# transaction BEFORE any package is swapped. Replacing those libraries out +# from under a live compositor makes the next GPU-lib call hit a now +# "(deleted)" file and SIGABRT, taking the Wayland clients down with it +# (hit on ratio 2026-06-07: mesa + hyprland upgraded live, Hyprland crashed +# and took awww/insync/emacs with it). Aborting at PreTransaction is the +# safe point: nothing has been replaced yet, so the running session is +# untouched and the user can re-run the upgrade from a TTY. +# +# Pacman feeds the matched package names on stdin (NeedsTargets). +# +# Test seams / overrides (env): +# HYPR_GUARD_RUNNING 1/0 forces the running check (default: pgrep Hyprland) +# HYPR_ALLOW_LIVE_UPDATE 1 proceeds anyway (skip the guard) +# HYPR_GUARD_SENTINEL path whose existence also proceeds anyway +# (default /run/archsetup-allow-live-gpu-update, +# cleared on reboot since /run is tmpfs) + +set -u + +sentinel="${HYPR_GUARD_SENTINEL:-/run/archsetup-allow-live-gpu-update}" + +# Explicit override: the user knows what they're doing. +if [ "${HYPR_ALLOW_LIVE_UPDATE:-0}" = "1" ] || [ -e "$sentinel" ]; then + exit 0 +fi + +hyprland_running() { + if [ -n "${HYPR_GUARD_RUNNING:-}" ]; then + [ "$HYPR_GUARD_RUNNING" = "1" ] + return + fi + pgrep -x Hyprland >/dev/null 2>&1 +} + +# No live session means no live swap to worry about. Let the upgrade run -- +# this is exactly the from-a-TTY-after-logout path the warning points to. +hyprland_running || exit 0 + +# Collect the triggering packages (stdin from NeedsTargets) for the message. +pkgs=$(cat 2>/dev/null | sort -u | tr '\n' ' ') + +cat >&2 <