@@ -6,7 +6,11 @@ pub mod device_test;
66
77pub mod device;
88
9- use std:: { error:: Error , fs, path:: Path } ;
9+ use std:: {
10+ error:: Error ,
11+ fs,
12+ path:: { Path , PathBuf } ,
13+ } ;
1014
1115use tokio:: process:: Command ;
1216use udev:: Enumerator ;
@@ -17,8 +21,15 @@ const RULE_HIDE_DEVICE_EARLY_PRIORITY: &str = "50";
1721const RULE_HIDE_DEVICE_LATE_PRIORITY : & str = "96" ;
1822const RULES_PREFIX : & str = "/run/udev/rules.d" ;
1923
24+ /// HideFlags can be used to change the behavior of how devices are hidden.
25+ #[ derive( Debug , PartialEq , Eq ) ]
26+ pub enum HideFlag {
27+ ChangePermissions ,
28+ MoveSourceDevice ,
29+ }
30+
2031/// Hide the given input device from regular users.
21- pub async fn hide_device ( path : & str ) -> Result < ( ) , Box < dyn Error > > {
32+ pub async fn hide_device ( path : & str , flags : & [ HideFlag ] ) -> Result < ( ) , Box < dyn Error > > {
2233 // Get the device to hide
2334 let device = get_device ( path. to_string ( ) ) . await ?;
2435 let name = device. name . clone ( ) ;
@@ -30,34 +41,64 @@ pub async fn hide_device(path: &str) -> Result<(), Box<dyn Error>> {
3041 return Err ( "Unable to create match rule for device" . into ( ) ) ;
3142 } ;
3243
33- // Create the directory to move devnodes to
34- tokio:: fs:: create_dir_all ( "/dev/inputplumber/sources" ) . await ?;
35-
36- // Find the chmod command to use for hiding
37- let chmod_cmd = if Path :: new ( "/bin/chmod" ) . exists ( ) {
38- "/bin/chmod" . to_string ( )
39- } else if Path :: new ( "/usr/bin/chmod" ) . exists ( ) {
40- "/usr/bin/chmod" . to_string ( )
41- } else {
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- } ;
44+ // Create the udev rule content to update permissions on the source node.
45+ let mut chmod_early_rule = String :: new ( ) ;
46+ let mut chmod_late_rule = String :: new ( ) ;
47+ if flags. contains ( & HideFlag :: ChangePermissions ) {
48+ // Find the chmod command to use for hiding
49+ let chmod_cmd = if Path :: new ( "/bin/chmod" ) . exists ( ) {
50+ "/bin/chmod" . to_string ( )
51+ } else if Path :: new ( "/usr/bin/chmod" ) . exists ( ) {
52+ "/usr/bin/chmod" . to_string ( )
53+ } else {
54+ let output = Command :: new ( "which" ) . arg ( "chmod" ) . output ( ) . await ?;
55+ if !output. status . success ( ) {
56+ return Err ( "Unable to determine chmod command location" . into ( ) ) ;
57+ }
58+ str:: from_utf8 ( output. stdout . as_slice ( ) ) ?. trim ( ) . to_string ( )
59+ } ;
4860
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 = Command :: new ( "which" ) . arg ( "mv" ) . output ( ) . await ?;
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 ( )
60- } ;
61+ // Build the rule content
62+ chmod_early_rule = format ! (
63+ r#"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"
64+ KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", MODE:="0000", GROUP:="root", RUN+="{chmod_cmd} 000 /dev/%k", SYMLINK+="inputplumber/by-hidden/%k"
65+ "#
66+ ) ;
67+ chmod_late_rule = format ! (
68+ r#"KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", TAG-="uaccess", RUN+="{chmod_cmd} 000 /dev/input/%k"
69+ KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", TAG-="uaccess", RUN+="{chmod_cmd} 000 /dev/%k"
70+ "#
71+ ) ;
72+ }
73+
74+ // Create the udev rule content to move the device node
75+ let mut mv_early_rule = String :: new ( ) ;
76+ let mut mv_late_rule = String :: new ( ) ;
77+ if flags. contains ( & HideFlag :: MoveSourceDevice ) {
78+ // Create the directory to move devnodes to
79+ tokio:: fs:: create_dir_all ( "/dev/inputplumber/sources" ) . await ?;
80+
81+ // Find the mv command to use for hiding
82+ let mv_cmd = if Path :: new ( "/bin/mv" ) . exists ( ) {
83+ "/bin/mv" . to_string ( )
84+ } else if Path :: new ( "/usr/bin/mv" ) . exists ( ) {
85+ "/usr/bin/mv" . to_string ( )
86+ } else {
87+ let output = Command :: new ( "which" ) . arg ( "mv" ) . output ( ) . await ?;
88+ if !output. status . success ( ) {
89+ return Err ( "Unable to determine mv command location" . into ( ) ) ;
90+ }
91+ str:: from_utf8 ( output. stdout . as_slice ( ) ) ?. trim ( ) . to_string ( )
92+ } ;
93+
94+ // Build the rule content
95+ mv_early_rule = format ! (
96+ r#"KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", RUN+="{mv_cmd} /dev/input/%k /dev/inputplumber/sources/%k"
97+ KERNEL=="hidraw[0-9]*", SUBSYSTEM=="{subsystem}", RUN+="{mv_cmd} /dev/%k /dev/inputplumber/sources/%k"
98+ "#
99+ ) ;
100+ mv_late_rule = mv_early_rule. clone ( ) ;
101+ }
61102
62103 // Create an early udev rule to hide the device
63104 let rule = format ! (
@@ -66,10 +107,8 @@ pub async fn hide_device(path: &str) -> Result<(), Box<dyn Error>> {
66107{match_rule}, GOTO="inputplumber_valid"
67108GOTO="inputplumber_end"
68109LABEL="inputplumber_valid"
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"
110+ {chmod_early_rule}
111+ {mv_early_rule}
73112LABEL="inputplumber_end"
74113"#
75114 ) ;
@@ -87,10 +126,8 @@ LABEL="inputplumber_end"
87126{match_rule}, GOTO="inputplumber_valid"
88127GOTO="inputplumber_end"
89128LABEL="inputplumber_valid"
90- KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", TAG-="uaccess", RUN+="{chmod_cmd} 000 /dev/input/%k"
91- 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"
129+ {chmod_late_rule}
130+ {mv_late_rule}
94131LABEL="inputplumber_end"
95132"#
96133 ) ;
@@ -126,14 +163,16 @@ pub async fn unhide_device(path: String) -> Result<(), Box<dyn Error>> {
126163
127164 // Move the device back
128165 let src_path = format ! ( "/dev/inputplumber/sources/{name}" ) ;
129- let dst_path = if name. starts_with ( "event" ) || name. starts_with ( "js" ) {
130- format ! ( "/dev/input/{name}" )
131- } else {
132- format ! ( "/dev/{name}" )
133- } ;
134- log:: debug!( "Restoring device node path '{src_path}' to '{dst_path}'" ) ;
135- if let Err ( e) = fs:: rename ( & src_path, & dst_path) {
136- log:: warn!( "Failed to move device node from {src_path} to {dst_path}: {e}" ) ;
166+ if PathBuf :: from ( & src_path) . exists ( ) {
167+ let dst_path = if name. starts_with ( "event" ) || name. starts_with ( "js" ) {
168+ format ! ( "/dev/input/{name}" )
169+ } else {
170+ format ! ( "/dev/{name}" )
171+ } ;
172+ log:: debug!( "Restoring device node path '{src_path}' to '{dst_path}'" ) ;
173+ if let Err ( e) = fs:: rename ( & src_path, & dst_path) {
174+ log:: warn!( "Failed to move device node from {src_path} to {dst_path}: {e}" ) ;
175+ }
137176 }
138177
139178 // Reload udev
0 commit comments