fix(kvm): follow seat0 active session across login/logout (VA-107) #111
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/VA-107-session-follow"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem (VA-107)
The root system service resolved seat0's active graphical session once at startup and exported its addressing into the process-global environment (
export_env). It never re-ran, so the desktop tab (KVM capture + input) was pinned to whatever session existed when the agent started:x11rb::connect(None)and spammedx11 connect: $DISPLAY variable not set.systemctl restart.Confirmed on desktop-02: at the "Switch User" greeter the mouse/keyboard still worked (uinput writes to
/dev/uinput, session-independent) while the display went blank (capture is session-pinned) - the exact split this fix targets.Re-running the attach on a timer was not viable: writing global env from a worker thread races the capture threads' env reads (
set_var/getenvis a data race; std marksset_varunsafe).Fix
Replace the global-env coupling with a lock-free, always-current
SessionAddrpublished throughvervain_agent_kvm::session_ctxand read by every backend at connect time. A logind-driven supervisor (session_watch) subscribes on the system bus to seat0'sActiveSessionPropertiesChangedplus the ManagerSessionNew/SessionRemovedsignals, debounces the greeter-to-user login burst (200 ms), re-resolves via the existingloginctldiscovery, and republishes. The desktop tab now follows the seat's active session across boot, login, user-switch, and logout with no restart. Nostd::env::set_varis used for session addressing anywhere; the per-user install seedsSessionAddronce from its own session env and needs no supervisor.Backends address the session explicitly instead of via the ambient environment:
x11conn), replacingx11rb::connect(None).grim/wlr-randrgetWAYLAND_DISPLAY/XDG_RUNTIME_DIRviaCommand::env./run/user/<uid>/bus, keyed by session identity, torn down and reopened against the new bus when the active session changes (PipeWire already used the portal-returned fd, so no env there).session_env::child_env().Invariant
Every capture/input operation addresses the seat's currently-active session, resolved from live shared state at operation time, never a process-start snapshot; session addressing never flows through mutable process-global env. All governed sites were swept and converted (X11 connects x3,
is_wayland_session, grim/wlr-randr x4, Screenshot fallback, portal session, clipboard, consent, VNC);uinput/touch/drm_kmsare N/A (kernel devices, no session env).Checks
just pre-commit(fmt, clippy-D warnings, build, test, all default-feature) and a separatewayland-portalclippy--locked --deny warningsboth green.Verification status
Compiles and unit-tests pass, but the live boot/login/logout cycle is not yet verified on hardware - the only confirmed repro box (desktop-02) is in daily use and cannot be logout-tested freely. The mechanism is testable non-destructively by watching
journalctl -fu vervain-agent | grep supervisorwhile doing a fast-user-switch, which should log anactive graphical session changedline on each transition.Reference-implementation comparison (MeshAgent, RustDesk)
Both battle-tested agents confirm the "follow the active session live and retarget" model, and both use a disposable per-session worker rather than root reaching across:
fs.watch('/var/run/utmp')+loginctlconfirmation, thenfork()+setuid(uid)a fresh X11 capture child (XShm + XTest). Its "Wayland support" is not a Wayland capture backend - it detects Wayland/Xwayland and works around it (disable Wayland in GDM config, or spin up an Xvfb virtual desktop). No portal/PipeWire/uinput.loginctl(~500 ms) and kill/respawns a per-user in-session--serverviasudo -u <user>, resolving DISPLAY/XAUTHORITY/WAYLAND_DISPLAY from the session leader's/proc/<pid>/environ. Full Wayland via xdg-desktop-portal + PipeWire (capture) and portal RemoteDesktop + uinput (input). The GDM-Wayland greeter / switch-user screen is explicitly unsupported.This PR's detection is more event-driven than either (logind D-Bus vs utmp-watch / polling). The notable architectural divergence: this PR keeps the root process reaching across to the session bus/display, whereas both references run capture/input in a process inside the user session (
fork+setuid/sudo -u). Root-reaches-across matches what already worked on this box (the pre-fix portal path worked once the env was exported), but the in-session-worker pattern is more robust for Wayland compositors that reject cross-user clients and is a candidate follow-up if verification shows gaps.#VA-107
🤖 Generated with Claude Code