Watch
1
0
Fork
You've already forked souveraine
0

shell: resolve logind session via User.Display, re-register after sessiond restart

SessionEvents asked loginctl for -p ObjectPath, which is not a property — it
returned empty on every boot, so external lock signals were never monitored and
the shell had no unlock ingress. Resolve the graphical session the way
lockhint.rs does; not GetSession(auto) (this shell is a session-less user unit)
and not the /session/auto alias (PropertiesChanged only fires on the concrete
path).

SessiondBridge read root.registered to decide whether to re-register on
reconnect, but the disconnect branch had already cleared it, so the shell never
re-registered after a sessiond restart. Latch it.
This commit is contained in:
Fimeg 2026-07-27 21:21:26 -04:00
commit 4ed8f3dd74
2 changed files with 33 additions and 22 deletions

View file

@ -76,38 +76,43 @@ Singleton {
signal sleepInhibitorAcquired() signal sleepInhibitorAcquired()
// --- Session path resolution ------------------------------------------- // --- Session path resolution -------------------------------------------
// The logind session D-Bus object path contains the session ID with dots // Resolve the graphical session's REAL object path via the user's `Display`
// replaced by underscores (session "3" -> /org/freedesktop/login1/session/_3, // session. sessiond resolves exactly this way; see the two measured traps
// session "c1" -> /org/freedesktop/login1/session/_c1). We resolve it once // documented on `resolve_session_path` in src/sessiond/lockhint.rs:
// at startup by asking logind for the current session's object path. //
// Not `auto`. `GetSession("auto")` resolves to the CALLER's session, and
// this shell is a systemd user unit under user@1000.service, outside any
// session scope it has no session of its own to name.
//
// Not the `auto` path either. /org/freedesktop/login1/session/auto is an
// alias, not an object: PropertiesChanged only fires on the concrete
// path, so monitoring the alias subscribes to something never delivered.
//
// The user's `Display` session is the graphical one by definition, whoever
// asks. The previous probe here asked for a `-p ObjectPath` property that
// loginctl does not have, so it returned empty on every boot and external
// lock signals were never monitored at all.
Process { Process {
id: sessionResolver id: sessionResolver
running: true running: true
command: ["sh", "-c", command: ["sh", "-c",
// loginctl list-sessions -p Name gives the first session's user; "busctl --system get-property org.freedesktop.login1 "
// show-session with no argument operates on the caller's session. + "/org/freedesktop/login1/user/_$(id -u) "
// -p ObjectPath gives the D-Bus path directly, no encoding needed. + "org.freedesktop.login1.User Display 2>/dev/null"]
"loginctl show-session -p ObjectPath 2>/dev/null | head -1"]
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
const raw = text.trim(); const raw = text.trim();
// loginctl outputs ObjectPath=/org/freedesktop/login1/session/_3 // `(so) "1" "/org/freedesktop/login1/session/_31"` the object
const match = raw.match(/^ObjectPath=(.+)$/); // path is the second quoted field.
if (match && match[1].length > 0) { const path = raw.split('"')[3];
root.sessionPath = match[1]; if (path && path.startsWith("/org/freedesktop/login1/session/")) {
root.sessionPath = path;
console.log("[session-events] session path:", root.sessionPath); console.log("[session-events] session path:", root.sessionPath);
sessionLockMonitor.running = true; sessionLockMonitor.running = true;
} else if (raw.length > 0) { } else if (raw.length > 0) {
// Some logind versions print just the path without the key. console.log("[session-events] could not parse Display session from:", raw);
if (raw.startsWith("/org/freedesktop/login1/session/")) {
root.sessionPath = raw;
console.log("[session-events] session path (bare):", root.sessionPath);
sessionLockMonitor.running = true;
} else {
console.log("[session-events] could not parse session path from:", raw);
}
} else { } else {
console.log("[session-events] no session path returned; " console.log("[session-events] no Display session for this user; "
+ "external lock signals will not be monitored"); + "external lock signals will not be monitored");
} }
} }

View file

@ -28,6 +28,10 @@ Singleton {
// Registered = shell_ready was answered ok on the CURRENT connection. // Registered = shell_ready was answered ok on the CURRENT connection.
property bool registered: false property bool registered: false
// Latched copy of `registered` taken when the connection drops. The
// disconnect branch clears `registered` before the reconnect branch runs,
// so reconnect cannot read it directly to decide whether to re-register.
property bool wasRegistered: false
// sessiond held the session lock when we registered; we owe it a lock // sessiond held the session lock when we registered; we owe it a lock
// and a locked_ack. // and a locked_ack.
property bool oweLock: false property bool oweLock: false
@ -112,8 +116,9 @@ Singleton {
onConnectionStateChanged: { onConnectionStateChanged: {
if (sock.connected) { if (sock.connected) {
console.log("[sessiond-bridge] connected"); console.log("[sessiond-bridge] connected");
const wasRegistered = root.registered; const wasRegistered = root.wasRegistered;
root.registered = false; root.registered = false;
root.wasRegistered = false;
root.ackSent = false; root.ackSent = false;
if (wasRegistered) { if (wasRegistered) {
root.shellReady(function(mustLock) { root.shellReady(function(mustLock) {
@ -126,6 +131,7 @@ Singleton {
} }
} else { } else {
console.log("[sessiond-bridge] disconnected"); console.log("[sessiond-bridge] disconnected");
root.wasRegistered = root.registered;
root.registered = false; root.registered = false;
root.pendingReady = null; root.pendingReady = null;
} }