diff --git a/vuinputd-tests/src/bin/bwrap-ipc.rs b/vuinputd-tests/src/bin/bwrap-ipc.rs index 16d33ca..cd7a509 100644 --- a/vuinputd-tests/src/bin/bwrap-ipc.rs +++ b/vuinputd-tests/src/bin/bwrap-ipc.rs @@ -3,7 +3,7 @@ // Author: Johannes Leupolz use core::panic; -use std::{time::Duration}; +use std::time::Duration; use vuinputd_tests::bwrap::SandboxChildIpc; @@ -21,7 +21,7 @@ fn main() { ipc.send(b"ok").unwrap(); } else { ipc.send(b"nok").unwrap(); - println!("child received {}",incoming_str); + println!("child received {}", incoming_str); panic!("expected ipc message to be 'continue'"); } } diff --git a/vuinputd-tests/src/bin/test-keyboard.rs b/vuinputd-tests/src/bin/test-keyboard.rs index 18eda1f..ed039e2 100644 --- a/vuinputd-tests/src/bin/test-keyboard.rs +++ b/vuinputd-tests/src/bin/test-keyboard.rs @@ -3,9 +3,8 @@ // Author: Johannes Leupolz use clap::Parser; -use libc::{CLOCK_MONOTONIC, input_event, timespec, uinput_setup}; use libc::{c_int, close, open, write, O_NONBLOCK, O_WRONLY}; -use vuinputd_tests::test_log::{LoggedInputEvent, TestLog}; +use libc::{input_event, timespec, uinput_setup, CLOCK_MONOTONIC}; use std::ffi::{CStr, CString}; use std::fs::{self, File, OpenOptions}; use std::io::{self, ErrorKind}; @@ -14,6 +13,7 @@ use std::os::fd::AsRawFd; use std::os::raw::{c_char, c_void}; use std::ptr; pub use uinput_ioctls::*; +use vuinputd_tests::test_log::{LoggedInputEvent, TestLog}; // Constants (same numeric values as in linux headers) const EV_SYN: u16 = 0x00; @@ -276,7 +276,6 @@ unsafe fn set_standard_keyboard_keys(fd: i32) -> Result<(), std::io::Error> { Ok(()) } - #[derive(Debug, Parser)] #[command(author, version, about)] struct Args { @@ -322,17 +321,25 @@ fn emit(fd: c_int, ev_type: u16, code: u16, val: i32) -> io::Result<()> { Ok(()) } - // Note that before we can read, a SYN needs to be sent. Thus combine it. -fn emit_read_and_log(emit_to: c_int, read_from:&File, ev_type: u16, code: u16, val: i32) -> io::Result { - let (time_sent_sec,time_sent_nsec) = monotonic_time(); +fn emit_read_and_log( + emit_to: c_int, + read_from: &File, + ev_type: u16, + code: u16, + val: i32, +) -> io::Result { + let (time_sent_sec, time_sent_nsec) = monotonic_time(); emit(emit_to, ev_type, code, val)?; - emit(emit_to,EV_SYN, SYN_REPORT, 0)?; - let input_event_recv=read_event(&read_from).unwrap(); - let _syn_recv=read_event(&read_from).unwrap(); - let (time_recv_sec,time_recv_nsec) = monotonic_time(); - let duration_nsec =(time_recv_sec-time_sent_sec)*1_000_000+(time_recv_nsec-time_sent_nsec)/1000; - let send_and_receive_match = input_event_recv.type_==ev_type && input_event_recv.code==code && input_event_recv.value==val; + emit(emit_to, EV_SYN, SYN_REPORT, 0)?; + let input_event_recv = read_event(&read_from).unwrap(); + let _syn_recv = read_event(&read_from).unwrap(); + let (time_recv_sec, time_recv_nsec) = monotonic_time(); + let duration_nsec = + (time_recv_sec - time_sent_sec) * 1_000_000 + (time_recv_nsec - time_sent_nsec) / 1000; + let send_and_receive_match = input_event_recv.type_ == ev_type + && input_event_recv.code == code + && input_event_recv.value == val; Ok(LoggedInputEvent { tv_sec: time_sent_sec, @@ -341,13 +348,12 @@ fn emit_read_and_log(emit_to: c_int, read_from:&File, ev_type: u16, code: u16, v type_: ev_type, code: code, value: val, - send_and_receive_match: send_and_receive_match + send_and_receive_match: send_and_receive_match, }) } - pub fn fetch_device_node(path: &str) -> io::Result { - println!("Read dir {}",&path); + println!("Read dir {}", &path); for entry in fs::read_dir(path)? { let entry = entry?; // propagate per-entry errors if let Some(name) = entry.file_name().to_str() { @@ -360,23 +366,22 @@ pub fn fetch_device_node(path: &str) -> io::Result { Err(io::Error::new(ErrorKind::NotFound, "no device found")) } -pub fn read_event(event_dev : &File) -> io::Result { - +pub fn read_event(event_dev: &File) -> io::Result { let mut ev: input_event = unsafe { mem::zeroed() }; let ret = unsafe { - libc::read( - event_dev.as_raw_fd(), - &mut ev as *mut _ as *mut c_void, - mem::size_of::(), - ) - }; + libc::read( + event_dev.as_raw_fd(), + &mut ev as *mut _ as *mut c_void, + mem::size_of::(), + ) + }; if ret as usize != mem::size_of::() { return Err(io::Error::last_os_error()); } Ok(ev) } -fn monotonic_time() -> (i64,i64) { +fn monotonic_time() -> (i64, i64) { let mut ts = timespec { tv_sec: 0, tv_nsec: 0, @@ -385,13 +390,12 @@ fn monotonic_time() -> (i64,i64) { unsafe { libc::clock_gettime(CLOCK_MONOTONIC, &mut ts); } - (ts.tv_sec ,ts.tv_nsec) + (ts.tv_sec, ts.tv_nsec) } - fn main() -> io::Result<()> { // open device - matches: open("/dev/uinput", O_WRONLY | O_NONBLOCK); - let args=Args::parse(); + let args = Args::parse(); let device = match args.dev_path { Some(dev_path) => dev_path, @@ -468,7 +472,8 @@ fn main() -> io::Result<()> { CStr::from_ptr(resultbuf.as_ptr()).to_string_lossy() ); println!("syspath: {}", sysname); - let devnode = fetch_device_node(&sysname).unwrap_or_else(|e| panic!("failed to fetch device node!: {e}")); + let devnode = fetch_device_node(&sysname) + .unwrap_or_else(|e| panic!("failed to fetch device node!: {e}")); println!("devnode: {}", devnode); eprintln!("sysname: {}", sysname); @@ -477,17 +482,21 @@ fn main() -> io::Result<()> { // Comment this out: sleep(Duration::from_secs(12)); let event_device = OpenOptions::new() - .read(true) - .open(&devnode) - .unwrap_or_else(|err| panic!("Could not open event device {}, Error {}",&devnode,err)); + .read(true) + .open(&devnode) + .unwrap_or_else(|err| { + panic!("Could not open event device {}, Error {}", &devnode, err) + }); // Emit (press + syn) + (release + syn) let ev1 = emit_read_and_log(fd, &event_device, EV_KEY, KEY_SPACE, 1)?; - let ev2 = emit_read_and_log(fd, &event_device,EV_KEY, KEY_SPACE, 0)?; + let ev2 = emit_read_and_log(fd, &event_device, EV_KEY, KEY_SPACE, 0)?; - let eventlog = TestLog{events:vec![ev1,ev2]}; + let eventlog = TestLog { + events: vec![ev1, ev2], + }; let serialized = serde_json::to_string(&eventlog).unwrap(); - println!("Event log: {}",serialized); + println!("Event log: {}", serialized); // Destroy device and close fd ui_dev_destroy(fd).unwrap_or_else(|e| { diff --git a/vuinputd-tests/src/bwrap.rs b/vuinputd-tests/src/bwrap.rs index 6801d00..17e7065 100644 --- a/vuinputd-tests/src/bwrap.rs +++ b/vuinputd-tests/src/bwrap.rs @@ -119,7 +119,8 @@ impl BwrapBuilder { } pub fn dev_bind(mut self, src: &str, dst: &str) -> Self { - self.args.extend(["--dev-bind".into(), src.into(), dst.into()]); + self.args + .extend(["--dev-bind".into(), src.into(), dst.into()]); self } @@ -164,7 +165,6 @@ impl BwrapBuilder { let mut cmd = Command::new("bwrap"); - if let Some(fd) = self.ipc_child_fd.take() { // give up ownership of ipc_child_fd in host process. let fd = fd.into_raw_fd(); @@ -204,7 +204,7 @@ mod tests { .ro_bind("/", "/") .tmpfs("/tmp") .die_with_parent() - .command("/usr/bin/echo",&[]) + .command("/usr/bin/echo", &[]) .run() .unwrap_or_else(|e| panic!("failed to run bwrap!: {e}")); diff --git a/vuinputd-tests/src/test_log.rs b/vuinputd-tests/src/test_log.rs index 9da3eae..de02bea 100644 --- a/vuinputd-tests/src/test_log.rs +++ b/vuinputd-tests/src/test_log.rs @@ -2,13 +2,12 @@ // // Author: Johannes Leupolz -use serde::{Serialize, Deserialize}; - +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct LoggedInputEvent { pub tv_sec: i64, - + pub tv_usec: i64, pub duration_nsec: i64, @@ -24,5 +23,5 @@ pub struct LoggedInputEvent { #[derive(Serialize, Deserialize, Debug)] pub struct TestLog { - pub events:Vec -} \ No newline at end of file + pub events: Vec, +} diff --git a/vuinputd-tests/tests/integration_tests.rs b/vuinputd-tests/tests/integration_tests.rs index 34ac79f..400e667 100644 --- a/vuinputd-tests/tests/integration_tests.rs +++ b/vuinputd-tests/tests/integration_tests.rs @@ -14,7 +14,7 @@ fn test_bwrap_simple() { .ro_bind("/", "/") .tmpfs("/tmp") .die_with_parent() - .command("/usr/bin/echo",&["test","test","test"]) + .command("/usr/bin/echo", &["test", "test", "test"]) .run() .unwrap_or_else(|e| panic!("failed to run bwrap!: {e}")); @@ -37,12 +37,12 @@ fn test_bwrap_ipc() { .expect("failed to create IPC"); // Note that builder.run() will block. Thus, the send needs to happen before the child process blocks - // the host process. + // the host process. ipc.send("continue".as_bytes()) .unwrap_or_else(|e| panic!("failed to send data via ipc: {e}")); let out = builder - .command(bwrap_ipc,&[]) + .command(bwrap_ipc, &[]) .run() .unwrap_or_else(|e| panic!("failed to run bwrap!: {e}")); @@ -54,12 +54,10 @@ fn test_bwrap_ipc() { let result = result.expect("error receiving input from ipc as host within 5 seconds"); let result_str = - str::from_utf8(&result).expect("message received from ipc is not encoded as utf8"); - println!("host received {}",result_str); - + str::from_utf8(&result).expect("message received from ipc is not encoded as utf8"); + println!("host received {}", result_str); } - #[cfg(all(feature = "requires-privileges", feature = "requires-bwrap"))] #[test] fn test_list_sys_in_container() { @@ -68,7 +66,10 @@ fn test_list_sys_in_container() { .ro_bind("/", "/") .tmpfs("/tmp") .die_with_parent() - .command("/usr/bin/ls",&["-lh","/sys/devices/virtual/input/input235"]) + .command( + "/usr/bin/ls", + &["-lh", "/sys/devices/virtual/input/input235"], + ) .run() .unwrap_or_else(|e| panic!("failed to run bwrap!: {e}")); @@ -89,12 +90,15 @@ fn test_keyboard_on_host() { assert!(status.success()); } - -#[cfg(all(feature = "requires-privileges", feature = "requires-uinput", feature = "requires-bwrap"))] +#[cfg(all( + feature = "requires-privileges", + feature = "requires-uinput", + feature = "requires-bwrap" +))] #[test] fn test_keyboard_in_container_with_uinput() { let test_keyboard = env!("CARGO_BIN_EXE_test-keyboard"); - + let out = bwrap::BwrapBuilder::new() .unshare_net() .ro_bind("/", "/") @@ -102,7 +106,7 @@ fn test_keyboard_in_container_with_uinput() { .dev_bind("/dev/uinput", "/dev/uinput") .dev_bind("/dev/input", "/dev/input") .die_with_parent() - .command(test_keyboard,&[]) + .command(test_keyboard, &[]) .run() .unwrap_or_else(|e| panic!("failed to run bwrap!: {e}")); @@ -113,14 +117,18 @@ fn test_keyboard_in_container_with_uinput() { assert!(out.status.success()); } -#[cfg(all(feature = "requires-privileges", feature = "requires-uinput", feature = "requires-bwrap"))] -#[ignore] +#[cfg(all( + feature = "requires-privileges", + feature = "requires-uinput", + feature = "requires-bwrap" +))] +//#[ignore] #[test] fn test_keyboard_in_container_with_vuinput() { run_vuinputd::ensure_vuinputd_running(); - + let test_keyboard = env!("CARGO_BIN_EXE_test-keyboard"); - + let out = bwrap::BwrapBuilder::new() .unshare_net() .ro_bind("/", "/") @@ -132,7 +140,7 @@ fn test_keyboard_in_container_with_vuinput() { .tmpfs("/run") .dev_bind("/dev/vuinput-test", "/dev/uinput") .die_with_parent() - .command(test_keyboard,&[]) + .command(test_keyboard, &[]) .run() .unwrap_or_else(|e| panic!("failed to run bwrap!: {e}")); diff --git a/vuinputd/src/actions/action.rs b/vuinputd/src/actions/action.rs index fa57f01..6939e82 100644 --- a/vuinputd/src/actions/action.rs +++ b/vuinputd/src/actions/action.rs @@ -2,9 +2,9 @@ // // Author: Johannes Leupolz -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; -#[derive(Serialize,Deserialize)] +#[derive(Serialize, Deserialize)] #[serde(tag = "action")] pub enum Action { #[serde(rename = "mknod-device")] @@ -16,12 +16,8 @@ pub enum Action { }, #[serde(rename = "announce-via-netlink")] - AnnounceViaNetlink { - message: String, - }, + AnnounceViaNetlink { message: String }, #[serde(rename = "remove-device")] - RemoveDevice { - path: String, - }, + RemoveDevice { path: String }, } diff --git a/vuinputd/src/actions/handle_action.rs b/vuinputd/src/actions/handle_action.rs index aea5ec3..10f12e2 100644 --- a/vuinputd/src/actions/handle_action.rs +++ b/vuinputd/src/actions/handle_action.rs @@ -5,26 +5,17 @@ use super::action::Action; pub fn handle_cli_action(json: String) -> i32 { - let action: Action = serde_json::from_str(&json) - .expect("invalid action JSON"); - handle_action(action).unwrap_or_else(|err| - { - panic!("Error handling action: {}",err); - }); + let action: Action = serde_json::from_str(&json).expect("invalid action JSON"); + handle_action(action).unwrap_or_else(|err| { + panic!("Error handling action: {}", err); + }); 0 } -fn handle_action(action: Action) -> Result<(), String> { - +fn handle_action(action: Action) -> Result<(), String> { match action { - Action::MknodDevice { .. } => { - Ok(()) - } - Action::AnnounceViaNetlink { .. } => { - Ok(()) - } - Action::RemoveDevice { .. } => { - Ok(()) - } + Action::MknodDevice { .. } => Ok(()), + Action::AnnounceViaNetlink { .. } => Ok(()), + Action::RemoveDevice { .. } => Ok(()), } } diff --git a/vuinputd/src/jobs/inject_in_container_job.rs b/vuinputd/src/jobs/inject_in_container_job.rs index e557a8d..ae3067b 100644 --- a/vuinputd/src/jobs/inject_in_container_job.rs +++ b/vuinputd/src/jobs/inject_in_container_job.rs @@ -14,7 +14,14 @@ use async_io::Timer; use log::debug; use crate::{ - actions::{mknod_input_device::ensure_input_device, netlink_message::send_udev_monitor_message_with_properties, runtime_data::{ensure_udev_structure, read_udev_data, write_udev_data}}, job_engine::job::{Job, JobTarget}, jobs::monitor_udev_job::EVENT_STORE, process_tools::{Pid, RequestingProcess, await_process, run_in_net_and_mnt_namespace} + actions::{ + mknod_input_device::ensure_input_device, + netlink_message::send_udev_monitor_message_with_properties, + runtime_data::{ensure_udev_structure, read_udev_data, write_udev_data}, + }, + job_engine::job::{Job, JobTarget}, + jobs::monitor_udev_job::EVENT_STORE, + process_tools::{await_process, run_in_net_and_mnt_namespace, Pid, RequestingProcess}, }; #[derive(Clone, Debug, Copy, PartialOrd, PartialEq)] diff --git a/vuinputd/src/jobs/remove_from_container_job.rs b/vuinputd/src/jobs/remove_from_container_job.rs index c299047..48d23d2 100644 --- a/vuinputd/src/jobs/remove_from_container_job.rs +++ b/vuinputd/src/jobs/remove_from_container_job.rs @@ -11,7 +11,13 @@ use std::{ use log::debug; use crate::{ - actions::{mknod_input_device::remove_input_device, netlink_message::send_udev_monitor_message_with_properties, runtime_data::delete_udev_data}, job_engine::job::{Job, JobTarget}, jobs::monitor_udev_job::EVENT_STORE, process_tools::{Pid, RequestingProcess, await_process, run_in_net_and_mnt_namespace} + actions::{ + mknod_input_device::remove_input_device, + netlink_message::send_udev_monitor_message_with_properties, runtime_data::delete_udev_data, + }, + job_engine::job::{Job, JobTarget}, + jobs::monitor_udev_job::EVENT_STORE, + process_tools::{await_process, run_in_net_and_mnt_namespace, Pid, RequestingProcess}, }; #[derive(Clone, Debug, Copy, PartialOrd, PartialEq)] diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index c73b723..d80330c 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -68,13 +68,11 @@ struct Args { fn validate_args(args: &Args) -> Result<(), String> { // action might only occur alone - match (&args.major,&args.minor,&args.devname,&args.action) { - (None,None,None,Some(_)) => {}, - (_,_,_,None) => {}, + match (&args.major, &args.minor, &args.devname, &args.action) { + (None, None, None, Some(_)) => {} + (_, _, _, None) => {} _ => { - return Err( - "--action must not be used in combination with any other argument".into(), - ); + return Err("--action must not be used in combination with any other argument".into()); } } @@ -82,9 +80,7 @@ fn validate_args(args: &Args) -> Result<(), String> { match (&args.major, &args.minor) { (Some(_), Some(_)) | (None, None) => {} _ => { - return Err( - "--major and --minor must be specified together or not at all".into(), - ); + return Err("--major and --minor must be specified together or not at all".into()); } } @@ -107,7 +103,9 @@ fn main() -> std::io::Result<()> { check_permissions().expect("failed to read the capabilities of the vuinputd process"); let args = Args::parse(); - let argv0 = std::env::args_os().next().expect("Couldn't retrieve program name"); + let argv0 = std::env::args_os() + .next() + .expect("Couldn't retrieve program name"); if let Err(e) = validate_args(&args) { eprintln!("Error: {e}"); @@ -115,11 +113,10 @@ fn main() -> std::io::Result<()> { } if args.action.is_some() { - let error_code=actions::handle_action::handle_cli_action(args.action.unwrap()); + let error_code = actions::handle_action::handle_cli_action(args.action.unwrap()); std::process::exit(error_code); } - initialize_vuinput_state(); VUINPUT_COUNTER.set(AtomicU64::new(3)).expect( "failed to initialize the counter that provides the values of the CUSE file handles", @@ -144,9 +141,9 @@ fn main() -> std::io::Result<()> { let vuinput_devicename = match &args.devname { None => "vuinput", - Some(devname) => devname + Some(devname) => devname, }; - let vuinput_devicename = CString::new(format!("DEVNAME={}",vuinput_devicename)).unwrap(); + let vuinput_devicename = CString::new(format!("DEVNAME={}", vuinput_devicename)).unwrap(); let mut dev_info_argv: Vec<*const c_char> = vec![ vuinput_devicename.as_ptr(), // pointer to the C string @@ -155,9 +152,9 @@ fn main() -> std::io::Result<()> { // setting dev_major and dev_minor to 0 leads to a dynamic assignment of the major and minor, very likely beginning with 234:0 // see in https://www.kernel.org/doc/Documentation/admin-guide/devices.txt - let (major,minor) = match ((&args).major, (&args).minor) { - (Some(major), Some(minor)) => (major,minor), - _ => (0,0) + let (major, minor) = match ((&args).major, (&args).minor) { + (Some(major), Some(minor)) => (major, minor), + _ => (0, 0), }; let ci = cuse_lowlevel::cuse_info { dev_major: major,