diff --git a/Cargo.toml b/Cargo.toml index abf521dc..80ef41e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ log = { version = "0.4.22", features = [ "release_max_level_debug", ] } mio = { version = "0.8.11", features = ["os-poll", "os-ext", "net"] } -nix = { version = "0.29.0", features = ["fs"] } +nix = { version = "0.29.0", features = ["fs", "ioctl"] } packed_struct = "0.10.1" procfs = "0.16.0" rand = "0.8.5" diff --git a/src/udev/mod.rs b/src/udev/mod.rs index fd45f60e..91fcf34f 100644 --- a/src/udev/mod.rs +++ b/src/udev/mod.rs @@ -6,15 +6,31 @@ pub mod device_test; pub mod device; -use std::{error::Error, fs, path::Path}; +use std::{error::Error, fs, os::fd::IntoRawFd, path::Path}; +use nix::ioctl_write_int; use tokio::process::Command; use udev::Enumerator; use self::device::Device; +const RULE_PRIORITY: &str = "73"; const RULES_PREFIX: &str = "/run/udev/rules.d"; +ioctl_write_int!( + /// Revoke access to an evdev device + /// https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/input.h#L187 + /// https://github.com/torvalds/linux/blob/v6.13/drivers/input/evdev.c#L1090-L1094 + eviocrevoke, b'E', 0x91 +); + +ioctl_write_int!( + /// Revoke access to an hidraw device + /// https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/hidraw.h#L49 + /// https://github.com/torvalds/linux/blob/v6.13/drivers/hid/hidraw.c#L447-L454 + hidiocrevoke, b'H', 0x0d +); + /// Hide the given input device from regular users. pub async fn hide_device(path: String) -> Result<(), Box> { // Get the device to hide @@ -42,19 +58,36 @@ pub async fn hide_device(path: String) -> Result<(), Box> { {match_rule}, GOTO="inputplumber_valid" GOTO="inputplumber_end" LABEL="inputplumber_valid" -KERNEL=="hidraw[0-9]*|js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", TAG-="uaccess", RUN+="{chmod_cmd} 000 {path}" +ACTION=="add", KERNEL=="hidraw[0-9]*|js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", SYMLINK+="inputplumber/%k", TAG-="uaccess", RUN:="{chmod_cmd} 000 {path}" LABEL="inputplumber_end" "# ); // Write the udev rule fs::create_dir_all(RULES_PREFIX)?; - let rule_path = format!("{RULES_PREFIX}/96-inputplumber-hide-{name}.rules"); + let rule_path = format!("{RULES_PREFIX}/{RULE_PRIORITY}-inputplumber-hide-{name}.rules"); fs::write(rule_path, rule)?; // Reload udev reload_children(parent).await?; + // Break ioctl to the device + let file = std::fs::File::options() + .read(true) + .write(true) + .open(&path)?; + let device = file.into_raw_fd(); + log::debug!("Revoking ioctl for '{path}'"); + match subsystem.as_str() { + "input" => unsafe { + eviocrevoke(device, 0)?; + }, + "hidraw" => unsafe { + hidiocrevoke(device, 0)?; + }, + _ => (), + } + Ok(()) } @@ -66,7 +99,7 @@ pub async fn unhide_device(path: String) -> Result<(), Box> { let Some(parent) = device.get_parent() else { return Err("Unable to determine parent for device".into()); }; - let rule_path = format!("{RULES_PREFIX}/96-inputplumber-hide-{name}.rules"); + let rule_path = format!("{RULES_PREFIX}/{RULE_PRIORITY}-inputplumber-hide-{name}.rules"); fs::remove_file(rule_path)?; // Reload udev @@ -83,7 +116,7 @@ pub async fn unhide_all() -> Result<(), Box> { continue; }; let filename = entry.file_name().to_string_lossy().to_string(); - if !filename.starts_with("96-inputplumber-hide") { + if !filename.starts_with(format!("{RULE_PRIORITY}-inputplumber-hide").as_str()) { continue; } let path = entry.path().to_string_lossy().to_string();