Intercept BUS_VIRTUAL devices

This commit is contained in:
Jan Trefil 2023-08-29 20:21:12 +02:00
parent a0e98e43fc
commit adcab7b2c6
5 changed files with 95 additions and 13 deletions

View file

@ -6,6 +6,7 @@ use crate::abs::{AbsAxis, AbsEvent, ToolType};
use crate::event::Event;
use crate::glue::{self, libevdev};
use crate::key::{Key, KeyEvent};
use crate::registry::{Entry, Handle, Registry};
use crate::rel::{RelAxis, RelEvent};
use crate::sync::SyncEvent;
use crate::writer::Writer;
@ -16,6 +17,7 @@ use std::fs::{File, OpenOptions};
use std::io::{Error, ErrorKind};
use std::mem::MaybeUninit;
use std::os::fd::AsRawFd;
use std::os::unix::fs::MetadataExt;
use std::os::unix::prelude::OpenOptionsExt;
use std::path::Path;
use std::ptr::NonNull;
@ -31,6 +33,9 @@ pub struct Interceptor {
events: VecDeque<Event>,
writing: Option<(u16, u16, i32)>,
dropped: bool,
_reader_handle: Handle,
_writer_handle: Handle,
}
impl Interceptor {
@ -173,20 +178,30 @@ impl Interceptor {
}
}
pub(crate) async fn open(path: &Path) -> Result<Self, OpenError> {
pub(crate) async fn open(path: &Path, registry: &Registry) -> Result<Self, OpenError> {
let path = path.to_owned();
task::spawn_blocking(move || Self::open_sync(&path))
let registry = registry.clone();
task::spawn_blocking(move || Self::open_sync(&path, &registry))
.await
.map_err(|err| OpenError::Io(err.into()))?
}
fn open_sync(path: &Path) -> Result<Self, OpenError> {
fn open_sync(path: &Path, registry: &Registry) -> Result<Self, OpenError> {
let file = OpenOptions::new()
.read(true)
.custom_flags(libc::O_NONBLOCK)
.open(path)
.and_then(AsyncFd::new)?;
let metadata = file.get_ref().metadata()?;
let reader_handle = registry
.register(Entry {
device: metadata.dev(),
inode: metadata.ino(),
})
.ok_or(OpenError::NotAppliable)?;
let mut evdev = MaybeUninit::uninit();
let ret = unsafe { glue::libevdev_new_from_fd(file.as_raw_fd(), evdev.as_mut_ptr()) };
@ -202,11 +217,7 @@ impl Interceptor {
// state is in sync."
// We have no way of knowing that.
let sw = unsafe { glue::libevdev_has_event_type(evdev.as_ptr(), glue::EV_SW) };
// Check if we're not opening our own virtual device.
let bus_type = unsafe { glue::libevdev_get_id_bustype(evdev.as_ptr()) };
if bus_type == glue::BUS_VIRTUAL as _ || sw == 1 {
if sw == 1 {
unsafe {
glue::libevdev_free(evdev.as_ptr());
}
@ -275,6 +286,20 @@ impl Interceptor {
}
};
// TODO: This is ugly. Need to refactor the evdev wrappers.
let entry = match writer.entry() {
Ok(entry) => entry,
Err(err) => {
unsafe {
glue::libevdev_free(evdev.as_ptr());
}
return Err(err.into());
}
};
let writer_handle = registry.register(entry).unwrap();
Ok(Self {
file,
evdev,
@ -282,6 +307,9 @@ impl Interceptor {
events: VecDeque::new(),
dropped: false,
writing: None,
_reader_handle: reader_handle,
_writer_handle: writer_handle,
})
}
}

View file

@ -8,3 +8,4 @@ pub mod sync;
pub mod writer;
mod glue;
mod registry;

View file

@ -1,4 +1,5 @@
use crate::interceptor::{Interceptor, OpenError};
use crate::registry::Registry;
use futures::StreamExt;
use inotify::{Inotify, WatchMask};
@ -20,6 +21,8 @@ impl Monitor {
tokio::spawn(async move {
let run = async {
let registry = Registry::new();
let mut read_dir = fs::read_dir(EVENT_PATH).await?;
let mut inotify = Inotify::init()?;
@ -54,7 +57,7 @@ impl Monitor {
continue;
}
let interceptor = match Interceptor::open(&path).await {
let interceptor = match Interceptor::open(&path, &registry).await {
Ok(interceptor) => interceptor,
Err(OpenError::Io(err)) => return Err(err),
Err(OpenError::NotAppliable) => continue,

View file

@ -0,0 +1,43 @@
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub struct Entry {
pub device: u64,
pub inode: u64,
}
#[derive(Clone)]
pub struct Registry {
entries: Arc<Mutex<HashSet<Entry>>>,
}
impl Registry {
pub fn new() -> Self {
Self {
entries: Arc::new(Mutex::new(HashSet::new())),
}
}
pub fn register(&self, entry: Entry) -> Option<Handle> {
if !self.entries.lock().unwrap().insert(entry) {
return None;
}
Some(Handle {
entries: self.entries.clone(),
entry,
})
}
}
pub struct Handle {
entries: Arc<Mutex<HashSet<Entry>>>,
entry: Entry,
}
impl Drop for Handle {
fn drop(&mut self) {
assert!(self.entries.lock().unwrap().remove(&self.entry));
}
}

View file

@ -2,6 +2,7 @@ use crate::abs::{AbsAxis, AbsEvent, AbsInfo};
use crate::event::Event;
use crate::glue::{self, input_absinfo, libevdev, libevdev_uinput};
use crate::key::{Key, KeyEvent};
use crate::registry::Entry;
use crate::rel::{RelAxis, RelEvent};
use std::ffi::{CStr, OsStr};
@ -10,10 +11,11 @@ use std::io::{Error, ErrorKind};
use std::mem::MaybeUninit;
use std::os::fd::AsRawFd;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::prelude::OpenOptionsExt;
use std::path::Path;
use std::ptr;
use std::ptr::NonNull;
use std::{fs, ptr};
use tokio::io::unix::AsyncFd;
use tokio::task;
@ -44,17 +46,22 @@ impl Writer {
Ok(())
}
pub fn path(&self) -> Option<&Path> {
pub(crate) fn entry(&self) -> Result<Entry, Error> {
let path = unsafe { glue::libevdev_uinput_get_devnode(self.uinput.as_ptr()) };
if path.is_null() {
return None;
return Err(Error::new(ErrorKind::Other, "No syspath for device"));
}
let path = unsafe { CStr::from_ptr(path) };
let path = OsStr::from_bytes(path.to_bytes());
let path = Path::new(path);
Some(path)
let metadata = fs::metadata(path)?;
Ok(Entry {
device: metadata.dev(),
inode: metadata.ino(),
})
}
pub(crate) unsafe fn from_evdev(evdev: *const libevdev) -> Result<Self, Error> {