From acc54d589b4b5dfaff704f51c9026987adb6de8f Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Wed, 8 Nov 2023 23:57:33 +0700 Subject: [PATCH 1/8] Add device_names array to Config struct --- rkvm-server/src/config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rkvm-server/src/config.rs b/rkvm-server/src/config.rs index ffc19cd..b3058a8 100644 --- a/rkvm-server/src/config.rs +++ b/rkvm-server/src/config.rs @@ -12,6 +12,7 @@ pub struct Config { pub key: PathBuf, pub password: String, pub switch_keys: HashSet, + pub device_names: HashSet, } #[derive(Deserialize, Clone, Copy, PartialEq, Eq, Hash)] From 18042f6d6b8314b70dbd91a9208728f6e82957b7 Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Wed, 8 Nov 2023 23:58:57 +0700 Subject: [PATCH 2/8] Modify server run function to accept device_names array config Modify the server's run function to accept the device_names array configuration and use it to decide which input devices to register. --- rkvm-server/src/main.rs | 3 +- rkvm-server/src/server.rs | 132 +++++++++++++++++++++----------------- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/rkvm-server/src/main.rs b/rkvm-server/src/main.rs index 4d3e4fd..686467c 100644 --- a/rkvm-server/src/main.rs +++ b/rkvm-server/src/main.rs @@ -68,9 +68,10 @@ async fn main() -> ExitCode { }; let switch_keys = config.switch_keys.into_iter().map(Into::into).collect(); + let device_names = config.device_names; tokio::select! { - result = server::run(config.listen, acceptor, &config.password, &switch_keys) => { + result = server::run(config.listen, acceptor, &config.password, &switch_keys, &device_names) => { if let Err(err) = result { tracing::error!("Error: {}", err); return ExitCode::FAILURE; diff --git a/rkvm-server/src/server.rs b/rkvm-server/src/server.rs index e7cae21..fd4f799 100644 --- a/rkvm-server/src/server.rs +++ b/rkvm-server/src/server.rs @@ -38,6 +38,7 @@ pub async fn run( acceptor: TlsAcceptor, password: &str, switch_keys: &HashSet, + device_names: &HashSet, ) -> Result<(), Error> { let listener = TcpListener::bind(&listen).await.map_err(Error::Network)?; tracing::info!("Listening on {}", listen); @@ -109,72 +110,87 @@ pub async fn run( let abs = interceptor.abs().collect::>(); let keys = interceptor.key().collect::>(); - for (_, (sender, _)) in &clients { - let update = Update::CreateDevice { - id, - name: name.clone(), - version: version.clone(), - vendor: vendor.clone(), - product: product.clone(), - rel: rel.clone(), - abs: abs.clone(), - keys: keys.clone(), - }; - - let _ = sender.send(update).await; + let mut register_input_device = false; + + if device_names.len() > 0 { + for device_name in device_names { + if name.to_str().unwrap().contains(device_name) { + register_input_device = true; + break; + } + } + } else { + register_input_device = true; } - let (interceptor_sender, mut interceptor_receiver) = mpsc::channel(32); - devices.insert(Device { - name, - version, - vendor, - product, - rel, - abs, - keys, - sender: interceptor_sender, - }); - - let events_sender = events_sender.clone(); - tokio::spawn(async move { - loop { - tokio::select! { - event = interceptor.read() => { - if event.is_err() | events_sender.send((id, event)).await.is_err() { - break; - } - } - event = interceptor_receiver.recv() => { - let event = match event { - Some(event) => event, - None => break, - }; - - match interceptor.write(&event).await { - Ok(()) => {}, - Err(err) => { - let _ = events_sender.send((id, Err(err))).await; + if register_input_device { + for (_, (sender, _)) in &clients { + let update = Update::CreateDevice { + id, + name: name.clone(), + version: version.clone(), + vendor: vendor.clone(), + product: product.clone(), + rel: rel.clone(), + abs: abs.clone(), + keys: keys.clone(), + }; + + let _ = sender.send(update).await; + } + + let (interceptor_sender, mut interceptor_receiver) = mpsc::channel(32); + devices.insert(Device { + name, + version, + vendor, + product, + rel, + abs, + keys, + sender: interceptor_sender, + }); + + let events_sender = events_sender.clone(); + tokio::spawn(async move { + loop { + tokio::select! { + event = interceptor.read() => { + if event.is_err() | events_sender.send((id, event)).await.is_err() { break; } } + event = interceptor_receiver.recv() => { + let event = match event { + Some(event) => event, + None => break, + }; + + match interceptor.write(&event).await { + Ok(()) => {}, + Err(err) => { + let _ = events_sender.send((id, Err(err))).await; + break; + } + } - tracing::trace!(id = %id, "Wrote an event to device"); + tracing::trace!(id = %id, "Wrote an event to device"); + } } } - } - }); - - let device = &devices[id]; - - tracing::info!( - id = %id, - name = ?device.name, - vendor = %device.vendor, - product = %device.product, - version = %device.version, - "Registered new device" - ); + }); + + let device = &devices[id]; + + tracing::info!( + id = %id, + name = ?device.name, + vendor = %device.vendor, + product = %device.product, + version = %device.version, + "Registered new device" + ); + } } (id, result) = event => match result { Ok(event) => { From 30363cfec51ad99bb5a3537f2d43a7e8f25b12b5 Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Thu, 9 Nov 2023 00:01:10 +0700 Subject: [PATCH 3/8] Add device-names array to the example server TOML configuration file --- example/server.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example/server.toml b/example/server.toml index c7f7ba8..01cc3e2 100644 --- a/example/server.toml +++ b/example/server.toml @@ -3,6 +3,9 @@ listen = "0.0.0.0:5258" switch-keys = ["left-alt", "left-ctrl"] certificate = "/etc/rkvm/certificate.pem" key = "/etc/rkvm/key.pem" +# Specify the list of input devices to register in the following array, +# or leave the array emtpy to register all detected input devices. +device-names = [] # This is to prevent malicious clients from connecting to the server. # Make sure this matches your client's config. From 759cc416c5108e04d2687be947ee8e30083f44e3 Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Tue, 14 Nov 2023 00:12:42 +0700 Subject: [PATCH 4/8] Revert the old approach that we used to filter input devices --- rkvm-server/src/server.rs | 132 +++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 74 deletions(-) diff --git a/rkvm-server/src/server.rs b/rkvm-server/src/server.rs index fd4f799..e7cae21 100644 --- a/rkvm-server/src/server.rs +++ b/rkvm-server/src/server.rs @@ -38,7 +38,6 @@ pub async fn run( acceptor: TlsAcceptor, password: &str, switch_keys: &HashSet, - device_names: &HashSet, ) -> Result<(), Error> { let listener = TcpListener::bind(&listen).await.map_err(Error::Network)?; tracing::info!("Listening on {}", listen); @@ -110,87 +109,72 @@ pub async fn run( let abs = interceptor.abs().collect::>(); let keys = interceptor.key().collect::>(); - let mut register_input_device = false; - - if device_names.len() > 0 { - for device_name in device_names { - if name.to_str().unwrap().contains(device_name) { - register_input_device = true; - break; - } - } - } else { - register_input_device = true; + for (_, (sender, _)) in &clients { + let update = Update::CreateDevice { + id, + name: name.clone(), + version: version.clone(), + vendor: vendor.clone(), + product: product.clone(), + rel: rel.clone(), + abs: abs.clone(), + keys: keys.clone(), + }; + + let _ = sender.send(update).await; } - if register_input_device { - for (_, (sender, _)) in &clients { - let update = Update::CreateDevice { - id, - name: name.clone(), - version: version.clone(), - vendor: vendor.clone(), - product: product.clone(), - rel: rel.clone(), - abs: abs.clone(), - keys: keys.clone(), - }; - - let _ = sender.send(update).await; - } - - let (interceptor_sender, mut interceptor_receiver) = mpsc::channel(32); - devices.insert(Device { - name, - version, - vendor, - product, - rel, - abs, - keys, - sender: interceptor_sender, - }); - - let events_sender = events_sender.clone(); - tokio::spawn(async move { - loop { - tokio::select! { - event = interceptor.read() => { - if event.is_err() | events_sender.send((id, event)).await.is_err() { + let (interceptor_sender, mut interceptor_receiver) = mpsc::channel(32); + devices.insert(Device { + name, + version, + vendor, + product, + rel, + abs, + keys, + sender: interceptor_sender, + }); + + let events_sender = events_sender.clone(); + tokio::spawn(async move { + loop { + tokio::select! { + event = interceptor.read() => { + if event.is_err() | events_sender.send((id, event)).await.is_err() { + break; + } + } + event = interceptor_receiver.recv() => { + let event = match event { + Some(event) => event, + None => break, + }; + + match interceptor.write(&event).await { + Ok(()) => {}, + Err(err) => { + let _ = events_sender.send((id, Err(err))).await; break; } } - event = interceptor_receiver.recv() => { - let event = match event { - Some(event) => event, - None => break, - }; - - match interceptor.write(&event).await { - Ok(()) => {}, - Err(err) => { - let _ = events_sender.send((id, Err(err))).await; - break; - } - } - tracing::trace!(id = %id, "Wrote an event to device"); - } + tracing::trace!(id = %id, "Wrote an event to device"); } } - }); - - let device = &devices[id]; - - tracing::info!( - id = %id, - name = ?device.name, - vendor = %device.vendor, - product = %device.product, - version = %device.version, - "Registered new device" - ); - } + } + }); + + let device = &devices[id]; + + tracing::info!( + id = %id, + name = ?device.name, + vendor = %device.vendor, + product = %device.product, + version = %device.version, + "Registered new device" + ); } (id, result) = event => match result { Ok(event) => { From ca04e96ca3a0340752d9bcfa3c5c78d61381d853 Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Tue, 14 Nov 2023 00:24:57 +0700 Subject: [PATCH 5/8] Rename device-names configuration array to input-device-paths --- example/server.toml | 10 +++++++--- rkvm-server/src/config.rs | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/example/server.toml b/example/server.toml index 01cc3e2..fc92309 100644 --- a/example/server.toml +++ b/example/server.toml @@ -3,9 +3,13 @@ listen = "0.0.0.0:5258" switch-keys = ["left-alt", "left-ctrl"] certificate = "/etc/rkvm/certificate.pem" key = "/etc/rkvm/key.pem" -# Specify the list of input devices to register in the following array, -# or leave the array emtpy to register all detected input devices. -device-names = [] +# Specify a list of paths to input device event file to register the +# input devices in the following array, or leave the array emtpy to +# register all detected input devices. +# +# You can find the path to event file of each input device by listing +# the contents of `/dev/input/by-id/` folder. +input-device-paths = [] # This is to prevent malicious clients from connecting to the server. # Make sure this matches your client's config. diff --git a/rkvm-server/src/config.rs b/rkvm-server/src/config.rs index b3058a8..eeeeed6 100644 --- a/rkvm-server/src/config.rs +++ b/rkvm-server/src/config.rs @@ -12,7 +12,7 @@ pub struct Config { pub key: PathBuf, pub password: String, pub switch_keys: HashSet, - pub device_names: HashSet, + pub input_device_paths: HashSet, } #[derive(Deserialize, Clone, Copy, PartialEq, Eq, Hash)] From 8c96b40465ae8cdf7ecd713d4f131ba89e27c9f1 Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Tue, 14 Nov 2023 00:29:30 +0700 Subject: [PATCH 6/8] Pass input-device-paths config array to input monitor and use it to control which input device to register --- rkvm-input/src/monitor.rs | 39 ++++++++++++++++++++++++++++----------- rkvm-server/src/main.rs | 4 ++-- rkvm-server/src/server.rs | 3 ++- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/rkvm-input/src/monitor.rs b/rkvm-input/src/monitor.rs index 1346d28..2747cd3 100644 --- a/rkvm-input/src/monitor.rs +++ b/rkvm-input/src/monitor.rs @@ -5,7 +5,8 @@ use futures::StreamExt; use inotify::{Inotify, WatchMask}; use std::ffi::OsStr; use std::io::{Error, ErrorKind}; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::collections::HashSet; use tokio::fs; use tokio::sync::mpsc::{self, Receiver, Sender}; @@ -16,9 +17,9 @@ pub struct Monitor { } impl Monitor { - pub fn new() -> Self { + pub fn new(input_device_paths: &HashSet) -> Self { let (sender, receiver) = mpsc::channel(1); - tokio::spawn(monitor(sender)); + tokio::spawn(monitor(sender, input_device_paths.clone())); Self { receiver } } @@ -31,7 +32,7 @@ impl Monitor { } } -async fn monitor(sender: Sender>) { +async fn monitor(sender: Sender>, input_device_paths: HashSet) { let run = async { let registry = Registry::new(); @@ -70,14 +71,16 @@ async fn monitor(sender: Sender>) { continue; } - let interceptor = match Interceptor::open(&path, ®istry).await { - Ok(interceptor) => interceptor, - Err(OpenError::Io(err)) => return Err(err), - Err(OpenError::NotAppliable) => continue, - }; + if register_input_device(&input_device_paths, path.clone()) { + let interceptor = match Interceptor::open(&path, ®istry).await { + Ok(interceptor) => interceptor, + Err(OpenError::Io(err)) => return Err(err), + Err(OpenError::NotAppliable) => continue, + }; - if sender.send(Ok(interceptor)).await.is_err() { - return Ok(()); + if sender.send(Ok(interceptor)).await.is_err() { + return Ok(()); + } } } @@ -94,3 +97,17 @@ async fn monitor(sender: Sender>) { _ = sender.closed() => {} } } + +fn register_input_device(input_device_paths: &HashSet, input_device_path: PathBuf) -> bool { + if input_device_paths.len() > 0 { + match input_device_path.into_os_string().into_string() { + Ok(path) => return input_device_paths.contains(&path), + Err(err) => { + tracing::error!("Can't convert a path into string! {:?}", err); + return false; + }, + } + } else { + return true; + } +} diff --git a/rkvm-server/src/main.rs b/rkvm-server/src/main.rs index 686467c..190a932 100644 --- a/rkvm-server/src/main.rs +++ b/rkvm-server/src/main.rs @@ -68,10 +68,10 @@ async fn main() -> ExitCode { }; let switch_keys = config.switch_keys.into_iter().map(Into::into).collect(); - let device_names = config.device_names; + let input_device_paths = config.input_device_paths; tokio::select! { - result = server::run(config.listen, acceptor, &config.password, &switch_keys, &device_names) => { + result = server::run(config.listen, acceptor, &config.password, &switch_keys, &input_device_paths) => { if let Err(err) = result { tracing::error!("Error: {}", err); return ExitCode::FAILURE; diff --git a/rkvm-server/src/server.rs b/rkvm-server/src/server.rs index e7cae21..8f4ba59 100644 --- a/rkvm-server/src/server.rs +++ b/rkvm-server/src/server.rs @@ -38,11 +38,12 @@ pub async fn run( acceptor: TlsAcceptor, password: &str, switch_keys: &HashSet, + input_device_paths: &HashSet, ) -> Result<(), Error> { let listener = TcpListener::bind(&listen).await.map_err(Error::Network)?; tracing::info!("Listening on {}", listen); - let mut monitor = Monitor::new(); + let mut monitor = Monitor::new(input_device_paths); let mut devices = Slab::::new(); let mut clients = Slab::<(Sender<_>, SocketAddr)>::new(); let mut current = 0; From 1f92594bd605d3934f5b78c148c7b1671c870bac Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Tue, 14 Nov 2023 01:21:37 +0700 Subject: [PATCH 7/8] Resolve user-supplied paths, so that paths from /dev/input/by-id/ can be used directly Resolve user-supplied paths in the `input-device-paths` configuration array to resolved, absolute paths, so that users can directly use paths from the `/dev/input/by-id/` in the configuration file. --- rkvm-input/src/monitor.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/rkvm-input/src/monitor.rs b/rkvm-input/src/monitor.rs index 2747cd3..1400365 100644 --- a/rkvm-input/src/monitor.rs +++ b/rkvm-input/src/monitor.rs @@ -7,6 +7,7 @@ use std::ffi::OsStr; use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; use std::collections::HashSet; +use std::fs::canonicalize; use tokio::fs; use tokio::sync::mpsc::{self, Receiver, Sender}; @@ -19,7 +20,8 @@ pub struct Monitor { impl Monitor { pub fn new(input_device_paths: &HashSet) -> Self { let (sender, receiver) = mpsc::channel(1); - tokio::spawn(monitor(sender, input_device_paths.clone())); + let absolute_input_device_paths = canonicalize_input_device_paths(input_device_paths); + tokio::spawn(monitor(sender, absolute_input_device_paths)); Self { receiver } } @@ -98,6 +100,30 @@ async fn monitor(sender: Sender>, input_device_paths: } } +fn canonicalize_input_device_paths(input_device_paths: &HashSet) -> HashSet { + let mut absolute_paths = HashSet::new(); + for path in input_device_paths { + match canonicalize(path) { + Ok(abs_path) => { + match abs_path.into_os_string().into_string() { + Ok(ap) => { + absolute_paths.insert(ap); + }, + Err(err) => { + tracing::error!("Failed to convert absolute path into string {:?}", err); + continue; + }, + } + }, + Err(err) => { + tracing::error!("Failed to canonicalize a path: {}", err); + continue; + }, + } + } + return absolute_paths; +} + fn register_input_device(input_device_paths: &HashSet, input_device_path: PathBuf) -> bool { if input_device_paths.len() > 0 { match input_device_path.into_os_string().into_string() { From d2bf30685f1cd8069ecfbfb021898e46581d0885 Mon Sep 17 00:00:00 2001 From: Smith Dhumbumroong Date: Tue, 14 Nov 2023 01:35:27 +0700 Subject: [PATCH 8/8] Update the documentation of the input-device-paths configuration array --- example/server.toml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/example/server.toml b/example/server.toml index fc92309..5ada42a 100644 --- a/example/server.toml +++ b/example/server.toml @@ -3,12 +3,14 @@ listen = "0.0.0.0:5258" switch-keys = ["left-alt", "left-ctrl"] certificate = "/etc/rkvm/certificate.pem" key = "/etc/rkvm/key.pem" -# Specify a list of paths to input device event file to register the -# input devices in the following array, or leave the array emtpy to -# register all detected input devices. +# Specify a list of absolute paths to input device event file that are +# to be registered by the server in the following array, or leave the +# array emtpy to register all detected input devices. # -# You can find the path to event file of each input device by listing -# the contents of `/dev/input/by-id/` folder. +# You can find symlinks that point to event file of each input device +# in the `/dev/input/by-id/` folder. These can also be used in the +# following array (make sure to input the absolute path of the +# symlinks). input-device-paths = [] # This is to prevent malicious clients from connecting to the server.