Skip to content

Commit 43b6a1a

Browse files
committed
fix(Device Hiding): move hidden device nodes to /dev/inputplumber/sources
1 parent 7a54be8 commit 43b6a1a

File tree

1 file changed

+60
-4
lines changed

1 file changed

+60
-4
lines changed

src/udev/mod.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,33 @@ pub async fn hide_device(path: &str) -> Result<(), Box<dyn Error>> {
3030
return Err("Unable to create match rule for device".into());
3131
};
3232

33+
// Create the directory to move devnodes to
34+
tokio::fs::create_dir_all("/dev/inputplumber/sources").await?;
35+
3336
// Find the chmod command to use for hiding
3437
let chmod_cmd = if Path::new("/bin/chmod").exists() {
35-
"/bin/chmod"
38+
"/bin/chmod".to_string()
39+
} else if Path::new("/usr/bin/chmod").exists() {
40+
"/usr/bin/chmod".to_string()
3641
} else {
37-
"/usr/bin/chmod"
42+
let output = Command::new("which").arg("chmod").output().await?;
43+
if !output.status.success() {
44+
return Err("Unable to determine chmod command location".into());
45+
}
46+
str::from_utf8(output.stdout.as_slice())?.trim().to_string()
47+
};
48+
49+
// Find the mv command to use for hiding
50+
let mv_cmd = if Path::new("/bin/mv").exists() {
51+
"/bin/mv".to_string()
52+
} else if Path::new("/usr/bin/mv").exists() {
53+
"/usr/bin/mv".to_string()
54+
} else {
55+
let output = std::process::Command::new("which").arg("mv").output()?;
56+
if !output.status.success() {
57+
return Err("Unable to determine mv command location".into());
58+
}
59+
str::from_utf8(output.stdout.as_slice())?.trim().to_string()
3860
};
3961

4062
// Create an early udev rule to hide the device
@@ -44,8 +66,10 @@ pub async fn hide_device(path: &str) -> Result<(), Box<dyn Error>> {
4466
{match_rule}, GOTO="inputplumber_valid"
4567
GOTO="inputplumber_end"
4668
LABEL="inputplumber_valid"
47-
KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 /dev/input/%k", SYMLINK+="inputplumber/by-hidden/%k"
48-
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 /dev/%k", SYMLINK+="inputplumber/by-hidden/%k"
69+
KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE:="0000", GROUP:="root", RUN+="{chmod_cmd} 000 /dev/input/%k", SYMLINK+="inputplumber/by-hidden/%k"
70+
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", MODE:="0000", GROUP:="root", RUN+="{chmod_cmd} 000 /dev/%k", SYMLINK+="inputplumber/by-hidden/%k"
71+
KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", RUN+="{mv_cmd} /dev/input/%k /dev/inputplumber/sources/%k"
72+
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", RUN+="{mv_cmd} /dev/%k /dev/inputplumber/sources/%k"
4973
LABEL="inputplumber_end"
5074
"#
5175
);
@@ -65,6 +89,8 @@ GOTO="inputplumber_end"
6589
LABEL="inputplumber_valid"
6690
KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", TAG-="uaccess", RUN+="{chmod_cmd} 000 /dev/input/%k"
6791
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", TAG-="uaccess", RUN+="{chmod_cmd} 000 /dev/%k"
92+
KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", RUN+="{mv_cmd} /dev/input/%k /dev/inputplumber/sources/%k"
93+
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", RUN+="{mv_cmd} /dev/%k /dev/inputplumber/sources/%k"
6894
LABEL="inputplumber_end"
6995
"#
7096
);
@@ -96,6 +122,17 @@ pub async fn unhide_device(path: String) -> Result<(), Box<dyn Error>> {
96122
);
97123
fs::remove_file(rule_path)?;
98124

125+
// Move the device back
126+
let src_path = format!("/dev/inputplumber/sources/{name}");
127+
let dst_path = if name.as_str().starts_with("event") {
128+
format!("/dev/input/{name}")
129+
} else {
130+
format!("/dev/{name}")
131+
};
132+
if let Err(e) = fs::rename(&src_path, &dst_path) {
133+
log::warn!("Failed to move device node from {src_path} to {dst_path}: {e}");
134+
}
135+
99136
// Reload udev
100137
reload_children(parent).await?;
101138

@@ -104,6 +141,7 @@ pub async fn unhide_device(path: String) -> Result<(), Box<dyn Error>> {
104141

105142
/// Unhide all devices hidden by InputPlumber
106143
pub async fn unhide_all() -> Result<(), Box<dyn Error>> {
144+
// Remove all created udev rules
107145
let entries = fs::read_dir(RULES_PREFIX)?;
108146
for entry in entries {
109147
let Ok(entry) = entry else {
@@ -117,6 +155,24 @@ pub async fn unhide_all() -> Result<(), Box<dyn Error>> {
117155
fs::remove_file(path)?;
118156
}
119157

158+
// Move all devices back
159+
let entries = fs::read_dir("/dev/inputplumber/sources")?;
160+
for entry in entries {
161+
let Ok(entry) = entry else {
162+
continue;
163+
};
164+
let path = entry.path();
165+
let name = entry.file_name().to_string_lossy().to_string();
166+
let dst_path = if name.as_str().starts_with("event") {
167+
format!("/dev/input/{name}")
168+
} else {
169+
format!("/dev/{name}")
170+
};
171+
if let Err(e) = fs::rename(&path, &dst_path) {
172+
log::warn!("Failed to move device node from {path:?} to {dst_path}: {e}");
173+
}
174+
}
175+
120176
// Reload udev rules
121177
reload_all().await?;
122178

0 commit comments

Comments
 (0)