mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-11 15:01:02 +03:00
feat(drm): opt-in DRM/KMS screen capture for Linux/Wayland
adds an opt-in `drm` feature for unattended remote access on Wayland: it captures below the compositor via libdrmtap, so there is no xdg-desktop-portal consent dialog and it works at the login screen. off by default. when the feature is off the build is byte-identical. everything is gated behind feature = "drm" or lives only in the separate rustdesk-unattended-wayland deb, whose package name is the informed consent. architecture (agreed with the maintainer): the capture runs inside the root --service, which already holds the privilege it needs, and streams frames to the user --server over a service-scoped _drm ipc channel. libdrmtap is loaded with dlopen at runtime (no link-time dependency, so the base build is unchanged and it still runs on ubuntu 18), and the .so is built in ci from the rustdesk-org/libdrmtap fork and shipped only in the drm deb. no setcap helper. - service: DrmReader reads scanout directly via the dlopen loader; an IpcDrmCapturer serves _drm consumers with a per-connection capture worker; durable availability cache + pre-warm to avoid enumerate/re-probe restarts - capture: multi-display (targets the selected crtc), hardware cursor over _drm, transient-errno retry with a bounded stall, rejects non-32bpp scanouts before the frame copy - robustness: only active, crtc-bound outputs are offered (an unbound crtc_id=0 connector is filtered and a client-selected 0 is refused, both fall back to pipewire); a per-display rapid-rebuild guard demotes a flapping display to pipewire; per-display (not global) zero-frame failure tracking - root-service hardening: bounded frame allocation and a concurrent-connection cap so a malformed scanout or a buggy consumer cannot OOM or thread-exhaust the service; a negative availability verdict expires so displays that appear after startup recover without a --server restart; exactly-one .so selection in the packaging so a stale object is never silently shipped - build: libdrmtap.so cloned at build time from rustdesk-org/libdrmtap main and bundled only for the --drm deb; ci builds a separate rustdesk-unattended-wayland deb (incl. an ubuntu 18.04 container) - DRM_CAPTURE_SECURITY.md: threat model and hardening notes
This commit is contained in:
@@ -361,6 +361,13 @@ pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
||||
}
|
||||
|
||||
pub fn get_cursor() -> ResultType<Option<u64>> {
|
||||
// DRM/KMS capture: the hardware cursor arrives over the `_drm` stream, not from XFixes.
|
||||
#[cfg(feature = "drm")]
|
||||
if !is_x11() {
|
||||
if let Some(id) = crate::server::drm_capturer::drm_cursor_id() {
|
||||
return Ok(Some(id));
|
||||
}
|
||||
}
|
||||
let mut res = None;
|
||||
DISPLAY.with(|conn| {
|
||||
if let Ok(d) = conn.try_borrow_mut() {
|
||||
@@ -379,6 +386,22 @@ pub fn get_cursor() -> ResultType<Option<u64>> {
|
||||
}
|
||||
|
||||
pub fn get_cursor_data(hcursor: u64) -> ResultType<CursorData> {
|
||||
// DRM/KMS capture: return the latest hardware-cursor snapshot from the `_drm` stream. Its id may
|
||||
// have advanced past `hcursor` between get_cursor() and here, so return the latest rather than
|
||||
// bailing (which would trigger a MouseCursorService backoff).
|
||||
#[cfg(feature = "drm")]
|
||||
if !is_x11() {
|
||||
if let Some(c) = crate::server::drm_capturer::drm_cursor() {
|
||||
let mut cd: CursorData = Default::default();
|
||||
cd.id = c.id;
|
||||
cd.width = c.width;
|
||||
cd.height = c.height;
|
||||
cd.hotx = c.hotx;
|
||||
cd.hoty = c.hoty;
|
||||
cd.colors = c.colors.into();
|
||||
return Ok(cd);
|
||||
}
|
||||
}
|
||||
let mut res = None;
|
||||
DISPLAY.with(|conn| {
|
||||
if let Ok(ref mut d) = conn.try_borrow_mut() {
|
||||
@@ -810,6 +833,15 @@ pub fn start_os_service() {
|
||||
allow_err!(crate::ipc::start(crate::POSTFIX_SERVICE));
|
||||
});
|
||||
|
||||
// DRM/KMS capture producer (opt-in `drm` feature): a dedicated thread + runtime that streams
|
||||
// scanout frames to the user `--server` over the `_drm` service-scoped channel. Runs here
|
||||
// because this process is the root service that already holds CAP_SYS_ADMIN for the in-process
|
||||
// (direct-mode) libdrmtap read.
|
||||
#[cfg(feature = "drm")]
|
||||
std::thread::spawn(|| {
|
||||
crate::ipc::start_drm();
|
||||
});
|
||||
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let r = running.clone();
|
||||
let (mut display, mut xauth): (String, String) = ("".to_owned(), "".to_owned());
|
||||
|
||||
Reference in New Issue
Block a user