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()
// --- Session path resolution -------------------------------------------
// The logind session D-Bus object path contains the session ID with dots
// replaced by underscores (session "3" -> /org/freedesktop/login1/session/_3,
// session "c1" -> /org/freedesktop/login1/session/_c1). We resolve it once
// at startup by asking logind for the current session's object path.
// Resolve the graphical session's REAL object path via the user's `Display`
// session. sessiond resolves exactly this way; see the two measured traps
// documented on `resolve_session_path` in src/sessiond/lockhint.rs:
//
// 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 {
id: sessionResolver
running: true
command: ["sh", "-c",
// loginctl list-sessions -p Name gives the first session's user;
// show-session with no argument operates on the caller's session.
// -p ObjectPath gives the D-Bus path directly, no encoding needed.
"loginctl show-session -p ObjectPath 2>/dev/null | head -1"]
"busctl --system get-property org.freedesktop.login1 "
+ "/org/freedesktop/login1/user/_$(id -u) "
+ "org.freedesktop.login1.User Display 2>/dev/null"]
stdout: StdioCollector {
onStreamFinished: {
const raw = text.trim();
// loginctl outputs ObjectPath=/org/freedesktop/login1/session/_3
const match = raw.match(/^ObjectPath=(.+)$/);
if (match && match[1].length > 0) {
root.sessionPath = match[1];
// `(so) "1" "/org/freedesktop/login1/session/_31"` the object
// path is the second quoted field.
const path = raw.split('"')[3];
if (path && path.startsWith("/org/freedesktop/login1/session/")) {
root.sessionPath = path;
console.log("[session-events] session path:", root.sessionPath);
sessionLockMonitor.running = true;
} else if (raw.length > 0) {
// Some logind versions print just the path without the key.
if (raw.startsWith("/org/freedesktop/login1/session/")) {
root.sessionPath = raw;
console.log("[session-events] session path (bare):", root.sessionPath);
sessionLockMonitor.running = true;
console.log("[session-events] could not parse Display session from:", raw);
} else {
console.log("[session-events] could not parse session path from:", raw);
}
} 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");
}
}

View file

@ -28,6 +28,10 @@ Singleton {
// Registered = shell_ready was answered ok on the CURRENT connection.
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
// and a locked_ack.
property bool oweLock: false
@ -112,8 +116,9 @@ Singleton {
onConnectionStateChanged: {
if (sock.connected) {
console.log("[sessiond-bridge] connected");
const wasRegistered = root.registered;
const wasRegistered = root.wasRegistered;
root.registered = false;
root.wasRegistered = false;
root.ackSent = false;
if (wasRegistered) {
root.shellReady(function(mustLock) {
@ -126,6 +131,7 @@ Singleton {
}
} else {
console.log("[sessiond-bridge] disconnected");
root.wasRegistered = root.registered;
root.registered = false;
root.pendingReady = null;
}