From 419d75949c6d2720ef7703b863a060a15a171755 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 24 Apr 2025 17:44:52 +0200 Subject: [PATCH] reexec: close evdev fds in a separate process --- src/backend.rs | 4 +++ src/backends/metal.rs | 12 ++++++++ src/ifs/jay_reexec.rs | 65 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index fa3ad5ce..2f555402 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -60,6 +60,10 @@ pub trait Backend: Any { fn supports_presentation_feedback(&self) -> bool { false } + + fn get_input_fds(&self) -> Vec> { + vec![] + } } #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 48d2ddf2..2ab08d57 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -277,6 +277,18 @@ impl Backend for MetalBackend { fn supports_presentation_feedback(&self) -> bool { true } + + fn get_input_fds(&self) -> Vec> { + let mut res = vec![]; + for dev in &*self.device_holder.input_devices.borrow() { + if let Some(dev) = dev { + if let Some(fd) = dev.fd.get() { + res.push(fd); + } + } + } + res + } } fn dup_fd(fd: c::c_int) -> Result, MetalError> { diff --git a/src/ifs/jay_reexec.rs b/src/ifs/jay_reexec.rs index 8d7cc465..1c60aa76 100644 --- a/src/ifs/jay_reexec.rs +++ b/src/ifs/jay_reexec.rs @@ -3,12 +3,16 @@ use { client::{Client, ClientError}, leaks::Tracker, object::{Object, Version}, - utils::oserror::OsError, + utils::{ + clone3::{Forked, fork_with_pidfd}, + errorfmt::ErrorFmt, + oserror::OsError, + }, wire::{JayReexecId, jay_reexec::*}, }, - std::{cell::RefCell, rc::Rc}, + std::{array::from_mut, cell::RefCell, rc::Rc}, thiserror::Error, - uapi::UstrPtr, + uapi::{OwnedFd, UstrPtr, c, close_range, dup2, pipe2}, }; pub struct JayReexec { @@ -26,6 +30,60 @@ impl JayReexec { msg, }); } + + fn delay_close_input_fd(&self) -> Option { + // It's 2025 and closing evdev fds is still abysmally slow. + let mut fds = self.client.state.backend.get().get_input_fds(); + if fds.is_empty() { + return None; + } + macro_rules! pipe { + () => { + match pipe2(c::O_CLOEXEC) { + Ok(p) => p, + Err(e) => { + log::error!("Could not create pipe: {}", ErrorFmt(OsError::from(e))); + return None; + } + } + }; + } + let (p1, c1) = pipe!(); + let (c2, p2) = pipe!(); + if let Ok(f) = fork_with_pidfd(false) { + if let Forked::Child { .. } = f { + drop(p2); + fds.sort_by_key(|fd| fd.raw()); + let c2_dup = fds.last().unwrap().raw() + 1; + let c1_dup = c2_dup + 1; + let _ = dup2(c1.raw(), c1_dup); + let _ = dup2(c2.raw(), c2_dup); + for (idx, fd) in fds.iter().enumerate() { + let _ = dup2(fd.raw(), idx as _); + } + let c2_dup_dup = fds.len() as _; + let _ = dup2(c2_dup, c2_dup_dup); + let _ = close_range(c2_dup_dup as c::c_uint + 1, !0, 0); + let mut pollfd = c::pollfd { + fd: c2_dup_dup, + events: 0, + revents: 0, + }; + let _ = uapi::poll(from_mut(&mut pollfd), -1); + unsafe { + c::_exit(0); + } + } + } + drop(c1); + let mut pollfd = c::pollfd { + fd: p1.raw(), + events: 0, + revents: 0, + }; + let _ = uapi::poll(from_mut(&mut pollfd), -1); + Some(p2) + } } impl JayReexecRequestHandler for JayReexec { @@ -43,6 +101,7 @@ impl JayReexecRequestHandler for JayReexec { for arg in &*args { args2.push(&**arg); } + let _drop_after_exec = self.delay_close_input_fd(); if let Err(e) = uapi::execvp(req.path, &args2) { self.send_failed(&OsError(e.0).to_string()); }