|
2 | 2 | // |
3 | 3 | // Author: Johannes Leupolz <dev@leupolz.eu> |
4 | 4 |
|
| 5 | +pub mod state; |
5 | 6 | pub mod vuinput_ioctl; |
6 | 7 | pub mod vuinput_write; |
7 | 8 | pub mod vuinput_release; |
8 | 9 | pub mod vuinput_open; |
9 | 10 |
|
10 | | -use std::collections::HashMap; |
11 | | -use std::fs::{self, File}; |
12 | | -use std::io; |
| 11 | +use std::{fs, io}; |
13 | 12 | use std::os::unix::fs::{FileTypeExt, MetadataExt}; |
14 | | -use std::sync::{Arc, Mutex, OnceLock, RwLock}; |
15 | 13 | use std::io::{ErrorKind}; |
16 | 14 |
|
17 | 15 | use ::cuse_lowlevel::*; |
| 16 | +use state::*; |
18 | 17 |
|
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 | 18 | pub const BUS_USB: u16 = 0x03; |
72 | 19 |
|
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 | 20 |
|
153 | 21 | // Instance of cuse_lowlevel_ops with all stubs assigned. |
154 | 22 | // Setting to None leads to e.g. "write error: Function not implemented". |
|
0 commit comments