From df56379556e77b128b040ea9b9e835e806d510e0 Mon Sep 17 00:00:00 2001 From: Pathfinder Date: Tue, 27 Oct 2026 15:32:41 +0100 Subject: [PATCH 1/2] libvirt group gate: virt-manager alone justifies membership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fresh-VM repro: user in yuri+wheel with virt-manager installed. Gate was 'pkg_installed libvirt', but virt-manager does NOT require the 'libvirt' metapkg (verified vs F44 package page: requires libvirt-glib etc., client side only) — so the usermod never fired. Gate now: libvirt OR libvirt-daemon OR libvirt-daemon-driver-qemu OR virt-manager packages, or an existing /usr/sbin/libvirtd binary. --- lib/session.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/session.sh b/lib/session.sh index 421b2d2..32fe710 100644 --- a/lib/session.sh +++ b/lib/session.sh @@ -129,7 +129,13 @@ UNIT # libvirt group: members manage qemu:///system without a polkit prompt. # Without this, virt-manager dies on "access denied by policy" even with # a working agent (VM-proven). Idempotent; applies after re-login. - if pkg_installed libvirt; then + # + # Gate on the DAEMON, not the 'libvirt' metapkg name: virt-manager only + # requires libvirt-glib client-side, so on a fresh VM libvirt can be + # functionally present via libvirt-daemon* without rpm -q libvirt passing + # (VM-proven: user stuck in yuri+wheel with virt-manager installed). + if pkg_installed libvirt libvirt-daemon libvirt-daemon-driver-qemu virt-manager \ + || [[ -x /usr/sbin/libvirtd ]]; then local real_user="${SUDO_USER:-$USER}" if ! id -nG "$real_user" 2>/dev/null | grep -qw libvirt; then info "Adding $real_user to the libvirt group (re-login to apply)..." -- 2.47.3 From f069026a7ccd0dd3a6a4dfa4350c5e45bf6836f9 Mon Sep 17 00:00:00 2001 From: Pathfinder Date: Tue, 27 Oct 2026 15:50:30 +0100 Subject: [PATCH 2/2] libvirt group: check at summary time (final state) + OR-semantics gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fresh-VM timeline explained: on run 1 virt-manager installs AFTER the niri module's prereq check ran (menu dispatch order), so the group add was evaluated against a package state that did not include it yet. A second run 'fixed' it — matching the observed behavior. - New ensure_libvirt_group runs in the Summary section, seeing the FINAL package state; menu/module-time check kept as an early pass. - pkg_installed is AND-semantics (all must be installed); the gate needs OR. New pkg_installed_any helper; both gates switched to it. - Also fixed a set -e trap: '$done_any && ok' returned 1 (killing the script) when no optional tools were picked — replaced with an if. All four gate cases harness-proven: virt-only, menu-pick, no-virt, already-in-group. --- lib/common.sh | 11 +++++++++++ lib/session.sh | 2 +- setup.sh | 24 +++++++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/common.sh b/lib/common.sh index cac492a..5b22c1d 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -92,6 +92,17 @@ pkg_installed() { return 0 } +# True if ANY of the listed packages is installed (pkg_installed is AND). +pkg_installed_any() { + local p + for p in "$@"; do + if rpm -q "$p" &>/dev/null; then + return 0 + fi + done + return 1 +} + remove_packages() { local packages_to_remove=() for pkg in "$@"; do diff --git a/lib/session.sh b/lib/session.sh index 32fe710..3ad6ebe 100644 --- a/lib/session.sh +++ b/lib/session.sh @@ -134,7 +134,7 @@ UNIT # requires libvirt-glib client-side, so on a fresh VM libvirt can be # functionally present via libvirt-daemon* without rpm -q libvirt passing # (VM-proven: user stuck in yuri+wheel with virt-manager installed). - if pkg_installed libvirt libvirt-daemon libvirt-daemon-driver-qemu virt-manager \ + if pkg_installed_any libvirt libvirt-daemon libvirt-daemon-driver-qemu virt-manager \ || [[ -x /usr/sbin/libvirtd ]]; then local real_user="${SUDO_USER:-$USER}" if ! id -nG "$real_user" 2>/dev/null | grep -qw libvirt; then diff --git a/setup.sh b/setup.sh index 8437f72..bb6e736 100755 --- a/setup.sh +++ b/setup.sh @@ -525,7 +525,10 @@ run_advanced_menu() { unset to_install; local -a to_install=() # fresh slate for next pick fi done - $done_any && ok "Optional tools installed." + # not `&& ok` — a false done_any would return 1 and kill the script under set -e + if [[ "$done_any" == "true" ]]; then + ok "Optional tools installed." + fi } # Per-category pre-repo wiring. Add third-party repos here when you add an @@ -584,6 +587,25 @@ pause_if_debug step "8 Summary" rule + +# Post-install wiring that must see the FINAL package state. The libvirt +# group add earlier (menu/module time) can miss packages installed later in +# the same run — this is the authoritative pass (VM-proven: a fresh run left +# the user out of the group when virt-manager landed after the check). +ensure_libvirt_group() { + # shellcheck source=lib/common.sh + source "$SCRIPT_DIR/lib/common.sh" 2>/dev/null || true + if pkg_installed_any libvirt libvirt-daemon libvirt-daemon-driver-qemu virt-manager \ + || [[ -x /usr/sbin/libvirtd ]]; then + local real_user="${SUDO_USER:-$USER}" + if ! id -nG "$real_user" 2>/dev/null | grep -qw libvirt; then + info "Adding $real_user to the libvirt group (re-login to apply)..." + sudo usermod -aG libvirt "$real_user" || true + warn "Group membership applies after re-login — do it before using virt-manager." + fi + fi +} +ensure_libvirt_group ok "DE: $DE" ok "TWM: $TWM" ok "SELinux: $([[ "$SELINUX_DISABLED" == "true" ]] && echo disabled || echo keeping)" -- 2.47.3