Skip to content

Commit c792529

Browse files
committed
Structural refactoring. Now everything related to cuse is in its own
module. After the refacoring, a lot of ugly imports need to be cleaned up.
1 parent 320a77e commit c792529

9 files changed

Lines changed: 878 additions & 738 deletions

File tree

vuinputd/src/compat.rs

Lines changed: 0 additions & 34 deletions
This file was deleted.

vuinputd/src/cuse_device/mod.rs

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// SPDX-License-Identifier: MIT
2+
//
3+
// Author: Johannes Leupolz <dev@leupolz.eu>
4+
5+
pub mod vuinput_ioctl;
6+
pub mod vuinput_write;
7+
pub mod vuinput_release;
8+
pub mod vuinput_open;
9+
10+
use std::collections::HashMap;
11+
use std::fs::{self, File};
12+
use std::io;
13+
use std::os::unix::fs::{FileTypeExt, MetadataExt};
14+
use std::sync::{Arc, Mutex, OnceLock, RwLock};
15+
use std::io::{ErrorKind};
16+
17+
use ::cuse_lowlevel::*;
18+
19+
use crate::process_tools::RequestingProcess;
20+
21+
#[derive(Debug)]
22+
struct VuInputDevice {
23+
cuse_fh : u64,
24+
major : u64,
25+
minor : u64,
26+
syspath: String,
27+
devnode: String,
28+
runtime_data: Option<String>,
29+
netlink_data: Option<String>
30+
}
31+
32+
#[derive(Debug)]
33+
pub struct VuInputState {
34+
file: File,
35+
requesting_process: RequestingProcess,
36+
input_device: Option<VuInputDevice>
37+
}
38+
39+
#[derive(Debug,Eq, Hash, PartialEq, Clone)]
40+
pub enum VuFileHandle {
41+
Fh(u64)
42+
}
43+
44+
impl VuFileHandle {
45+
fn from_fuse_file_info(fi: &fuse_lowlevel::fuse_file_info) -> VuFileHandle {
46+
VuFileHandle::Fh(fi.fh)
47+
}
48+
}
49+
50+
impl std::fmt::Display for VuFileHandle {
51+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52+
match self {
53+
VuFileHandle::Fh(fh) => writeln!(f, "VuFileHandle({:?})",fh)?,
54+
}
55+
Ok(())
56+
}
57+
}
58+
59+
#[derive(Debug)]
60+
pub enum VuError {
61+
WriteError
62+
}
63+
64+
pub static VUINPUT_STATE: OnceLock<RwLock<HashMap<VuFileHandle, Arc<Mutex<VuInputState>>>>> = OnceLock::new();
65+
66+
// For log limiting. Idea: Move to log_limit crate
67+
pub static DEDUP_LAST_ERROR: OnceLock<Mutex<Option<(u64,VuError)>>> = OnceLock::new();
68+
69+
70+
pub const SYS_INPUT_DIR: &str = "/sys/devices/virtual/input/";
71+
pub const BUS_USB: u16 = 0x03;
72+
73+
pub fn get_vuinput_state(
74+
fh:&VuFileHandle,
75+
) -> Result<Arc<Mutex<VuInputState>>, String> {
76+
let map = VUINPUT_STATE
77+
.get()
78+
.ok_or("global not initialized".to_string())?;
79+
let guard = map.read().map_err(|e| e.to_string())?;
80+
guard
81+
.get(&fh)
82+
.cloned()
83+
.ok_or("handle not opened".to_string())
84+
}
85+
86+
87+
pub fn insert_vuinput_state(
88+
fh:&VuFileHandle,
89+
state: VuInputState,
90+
) -> Result<(), String> {
91+
let map = VUINPUT_STATE
92+
.get()
93+
.ok_or("global not initialized".to_string())?;
94+
let mut guard = map.write().map_err(|e| e.to_string())?;
95+
96+
if guard.contains_key(&fh) {
97+
return Err(format!(
98+
"file handle {} already exists. file handles must not be reused!",
99+
&fh
100+
));
101+
}
102+
103+
let _ = guard.insert(fh.clone(), Arc::new(Mutex::new(state)));
104+
Ok(())
105+
}
106+
107+
pub fn remove_vuinput_state(
108+
fh:&VuFileHandle,
109+
) -> Result<Arc<Mutex<VuInputState>>, String> {
110+
let map = VUINPUT_STATE
111+
.get()
112+
.ok_or("global not initialized".to_string())?;
113+
let mut guard = map.write().map_err(|e| e.to_string())?;
114+
let old_value = guard.remove(&fh).ok_or("fh unknown")?;
115+
Ok(old_value)
116+
}
117+
118+
pub fn fetch_device_node(path: &str) -> io::Result<String> {
119+
for entry in fs::read_dir(path)? {
120+
let entry = entry?; // propagate per-entry errors
121+
if let Some(name) = entry.file_name().to_str() {
122+
if name.starts_with("event") {
123+
return Ok(format!("/dev/input/{}", name));
124+
}
125+
}
126+
}
127+
// If no device is found, return an error
128+
Err(io::Error::new(ErrorKind::NotFound, "no device found"))
129+
}
130+
131+
/// Returns (major, minor) numbers of a device node at `path`
132+
pub fn fetch_major_minor(path: &str) -> io::Result<(u64, u64)> {
133+
let metadata = fs::metadata(path)?;
134+
135+
// Ensure it's a character device
136+
if !metadata.file_type().is_char_device() {
137+
return Err(io::Error::new(
138+
io::ErrorKind::InvalidInput,
139+
"Not a character device",
140+
));
141+
}
142+
143+
let rdev = metadata.rdev();
144+
let major = ((rdev >> 8) & 0xfff) as u64;
145+
let minor = ((rdev & 0xff) | ((rdev >> 12) & 0xfff00)) as u64;
146+
147+
Ok((major, minor))
148+
}
149+
150+
151+
152+
153+
// Instance of cuse_lowlevel_ops with all stubs assigned.
154+
// Setting to None leads to e.g. "write error: Function not implemented".
155+
// You can find the implementations of the uinput default (open, release ,read, write, poll,
156+
// and ioctl) in uinput_fops of uinput.c.
157+
// See: https://github.com/torvalds/linux/blob/master/drivers/input/misc/uinput.c,
158+
pub fn vuinput_make_cuse_ops() -> cuse_lowlevel::cuse_lowlevel_ops {
159+
cuse_lowlevel::cuse_lowlevel_ops {
160+
init: None,
161+
init_done: None,
162+
destroy: None,
163+
open: Some(vuinput_open::vuinput_open),
164+
read: None,
165+
write: Some(vuinput_write::vuinput_write),
166+
flush: None,
167+
release: Some(vuinput_release::vuinput_release),
168+
fsync: None,
169+
ioctl: Some(vuinput_ioctl::vuinput_ioctl),
170+
poll: None,
171+
}
172+
}

0 commit comments

Comments
 (0)