mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 13:31:03 +03:00
fix(linux): a Wayland session without XAUTHORITY is not incomplete (#15978)
Fixes #15952. Hyprland runs Xwayland without exporting `XAUTHORITY`, and `get_display_xauth_xwayland` only returns once it has both `DISPLAY` and `XAUTHORITY`. On such a session that condition is never met, so every refresh runs the retry loop to the end: 10 rounds x 6 process patterns x 4 variables = 240 `get_env` calls, each a `sh -c` pipeline of ~12 processes starting with a full `ps -u <uid> -f`. That is ~2900 fork/exec per refresh, and the service loop repeats every 500 ms. The reporter measured a full core on a low-end laptop and ~60% of a core on a 13600KF. The Wayland side answers for such a session, so accept `DISPLAY` together with either `XAUTHORITY` or `WAYLAND_DISPLAY` + `DBUS_SESSION_BUS_ADDRESS`. The portal answers on the first pattern, which ends the walk there, as it already did on desktops that do export an xauth. The loop also assigned all four variables unconditionally per pattern, so the patterns that do not run on a given desktop blanked out what an earlier one had answered with -- the portal's valid `DISPLAY=:1` included. That is why the `--server` was then started with no `WAYLAND_DISPLAY` and no `DBUS_SESSION_BUS_ADDRESS`. Candidates are now taken from one pattern as a whole and ranked, so a later pattern replaces an earlier answer only by being better, and a session that can only offer a compositor and a bus still keeps them. A compositor that starts Xwayland on demand shows the same shape from the other side: the portal came up before Xwayland did, so its environment carries a valid `WAYLAND_DISPLAY` and `DBUS_SESSION_BUS_ADDRESS` but no `DISPLAY`, and no pattern here may ever produce one. That pair alone is a session the child server can be started against -- it is exactly what `get_display_xauth_wayland` returns on -- so it outranks a bare `DISPLAY` and ends the retrying, while the rest of the round still looks for something that completes the session. Not specific to the drm build: the function is not feature-gated, and the commit the report points at does not touch it. Claude-Session: https://claude.ai/code/session_01Q5egQpH4q4GoXJiuMoTJ5t Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1957,6 +1957,14 @@ mod desktop {
|
||||
const ENV_KEY_WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY";
|
||||
const ENV_KEY_DBUS_SESSION_BUS_ADDRESS: &str = "DBUS_SESSION_BUS_ADDRESS";
|
||||
|
||||
/// A compositor that runs Xwayland without exporting `XAUTHORITY` (wlroots, e.g. Hyprland)
|
||||
/// still hands out a usable session through the Wayland side. Requiring xauth there never
|
||||
/// succeeded, so every refresh ran the retry loop to the end, 240 shell pipelines at a time.
|
||||
/// https://github.com/rustdesk/rustdesk/issues/15952
|
||||
fn is_session_env_complete(display: &str, xauth: &str, wl_display: &str, dbus: &str) -> bool {
|
||||
!display.is_empty() && (!xauth.is_empty() || (!wl_display.is_empty() && !dbus.is_empty()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Desktop {
|
||||
pub sid: String,
|
||||
@@ -2023,15 +2031,51 @@ mod desktop {
|
||||
PLASMA_KDED,
|
||||
tray.as_str(),
|
||||
];
|
||||
self.display.clear();
|
||||
self.xauth.clear();
|
||||
self.wl_display.clear();
|
||||
self.dbus.clear();
|
||||
let mut kept = 0u8;
|
||||
for proc in display_proc {
|
||||
self.display = get_env(ENV_KEY_DISPLAY, &self.uid, proc);
|
||||
self.xauth = get_env(ENV_KEY_XAUTHORITY, &self.uid, proc);
|
||||
self.wl_display = get_env(ENV_KEY_WAYLAND_DISPLAY, &self.uid, proc);
|
||||
self.dbus = get_env(ENV_KEY_DBUS_SESSION_BUS_ADDRESS, &self.uid, proc);
|
||||
if !self.display.is_empty() && !self.xauth.is_empty() {
|
||||
let display = get_env(ENV_KEY_DISPLAY, &self.uid, proc);
|
||||
let xauth = get_env(ENV_KEY_XAUTHORITY, &self.uid, proc);
|
||||
let wl_display = get_env(ENV_KEY_WAYLAND_DISPLAY, &self.uid, proc);
|
||||
let dbus = get_env(ENV_KEY_DBUS_SESSION_BUS_ADDRESS, &self.uid, proc);
|
||||
// Take a candidate whole and keep the best seen. Assigning each variable
|
||||
// unconditionally let a pattern that does not run on this desktop blank out
|
||||
// the values an earlier one had answered with, which is how a session with a
|
||||
// working portal ended up starting its `--server` with no compositor and no
|
||||
// bus at all. The Wayland-only rank is what a session whose Xwayland exports
|
||||
// no `XAUTHORITY` can still offer.
|
||||
let complete = is_session_env_complete(&display, &xauth, &wl_display, &dbus);
|
||||
let rank = if complete {
|
||||
3
|
||||
} else if !wl_display.is_empty() && !dbus.is_empty() {
|
||||
2
|
||||
} else if !display.is_empty() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if rank > kept {
|
||||
kept = rank;
|
||||
self.display = display;
|
||||
self.xauth = xauth;
|
||||
self.wl_display = wl_display;
|
||||
self.dbus = dbus;
|
||||
}
|
||||
if complete {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The Wayland pair on its own is a session the child server can be started
|
||||
// against -- it is what `get_display_xauth_wayland` returns on. Retrying is for a
|
||||
// session that has not finished coming up, and a compositor whose Xwayland starts
|
||||
// on demand may never export a `DISPLAY` for this walk to find, so waiting ten
|
||||
// more rounds for one costs the whole probe again on every refresh.
|
||||
if kept >= 2 {
|
||||
break;
|
||||
}
|
||||
sleep_millis(300);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user