From d86a90e2b1a203e8d76301fc70836c43391fb041 Mon Sep 17 00:00:00 2001 From: Johannes Leupolz Date: Wed, 17 Dec 2025 21:12:53 +0000 Subject: [PATCH] Some refactorings to prepare the switch from calling a lambda function from the forked process to reinvoking the whole process with the job description as json --- vuinputd-tests/src/bin/test-keyboard.rs | 2 - vuinputd/Cargo.toml | 2 + vuinputd/src/actions/action.rs | 27 +++++++++ vuinputd/src/actions/handle_action.rs | 30 ++++++++++ .../{jobs => actions}/mknod_input_device.rs | 0 vuinputd/src/actions/mod.rs | 9 +++ .../src/{jobs => actions}/netlink_message.rs | 0 .../src/{jobs => actions}/runtime_data.rs | 0 vuinputd/src/job_engine/closure_job.rs | 60 ++++++++++--------- vuinputd/src/jobs/inject_in_container_job.rs | 9 +-- vuinputd/src/jobs/mod.rs | 3 - .../src/jobs/remove_from_container_job.rs | 7 +-- vuinputd/src/main.rs | 22 +++++++ 13 files changed, 125 insertions(+), 46 deletions(-) create mode 100644 vuinputd/src/actions/action.rs create mode 100644 vuinputd/src/actions/handle_action.rs rename vuinputd/src/{jobs => actions}/mknod_input_device.rs (100%) create mode 100644 vuinputd/src/actions/mod.rs rename vuinputd/src/{jobs => actions}/netlink_message.rs (100%) rename vuinputd/src/{jobs => actions}/runtime_data.rs (100%) diff --git a/vuinputd-tests/src/bin/test-keyboard.rs b/vuinputd-tests/src/bin/test-keyboard.rs index 1891d31..18eda1f 100644 --- a/vuinputd-tests/src/bin/test-keyboard.rs +++ b/vuinputd-tests/src/bin/test-keyboard.rs @@ -13,8 +13,6 @@ use std::mem::{self, size_of, zeroed}; use std::os::fd::AsRawFd; use std::os::raw::{c_char, c_void}; use std::ptr; -use std::thread::sleep; -use std::time::Duration; pub use uinput_ioctls::*; // Constants (same numeric values as in linux headers) diff --git a/vuinputd/Cargo.toml b/vuinputd/Cargo.toml index 78c488b..b182b69 100644 --- a/vuinputd/Cargo.toml +++ b/vuinputd/Cargo.toml @@ -27,3 +27,5 @@ futures = "0.3.31" async-io = "2.6.0" anyhow = "1.0.100" clap = { version = "4", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/vuinputd/src/actions/action.rs b/vuinputd/src/actions/action.rs new file mode 100644 index 0000000..fa57f01 --- /dev/null +++ b/vuinputd/src/actions/action.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// +// Author: Johannes Leupolz + +use serde::{Serialize, Deserialize}; + +#[derive(Serialize,Deserialize)] +#[serde(tag = "action")] +pub enum Action { + #[serde(rename = "mknod-device")] + MknodDevice { + path: String, + major: u32, + minor: u32, + mode: u32, + }, + + #[serde(rename = "announce-via-netlink")] + AnnounceViaNetlink { + message: String, + }, + + #[serde(rename = "remove-device")] + RemoveDevice { + path: String, + }, +} diff --git a/vuinputd/src/actions/handle_action.rs b/vuinputd/src/actions/handle_action.rs new file mode 100644 index 0000000..aea5ec3 --- /dev/null +++ b/vuinputd/src/actions/handle_action.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// +// Author: Johannes Leupolz + +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); + }); + 0 +} + +fn handle_action(action: Action) -> Result<(), String> { + + match action { + Action::MknodDevice { .. } => { + Ok(()) + } + Action::AnnounceViaNetlink { .. } => { + Ok(()) + } + Action::RemoveDevice { .. } => { + Ok(()) + } + } +} diff --git a/vuinputd/src/jobs/mknod_input_device.rs b/vuinputd/src/actions/mknod_input_device.rs similarity index 100% rename from vuinputd/src/jobs/mknod_input_device.rs rename to vuinputd/src/actions/mknod_input_device.rs diff --git a/vuinputd/src/actions/mod.rs b/vuinputd/src/actions/mod.rs new file mode 100644 index 0000000..af3b886 --- /dev/null +++ b/vuinputd/src/actions/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// +// Author: Johannes Leupolz + +pub mod action; +pub mod handle_action; +pub mod mknod_input_device; +pub mod netlink_message; +pub mod runtime_data; diff --git a/vuinputd/src/jobs/netlink_message.rs b/vuinputd/src/actions/netlink_message.rs similarity index 100% rename from vuinputd/src/jobs/netlink_message.rs rename to vuinputd/src/actions/netlink_message.rs diff --git a/vuinputd/src/jobs/runtime_data.rs b/vuinputd/src/actions/runtime_data.rs similarity index 100% rename from vuinputd/src/jobs/runtime_data.rs rename to vuinputd/src/actions/runtime_data.rs diff --git a/vuinputd/src/job_engine/closure_job.rs b/vuinputd/src/job_engine/closure_job.rs index 5e0fb65..810cbe9 100644 --- a/vuinputd/src/job_engine/closure_job.rs +++ b/vuinputd/src/job_engine/closure_job.rs @@ -5,7 +5,7 @@ use std::future::Future; use std::pin::Pin; -use crate::job_engine::job::{Dispatcher, Job, JobTarget}; +use super::job::{Job, JobTarget}; pub struct ClosureJob { desc: String, @@ -55,34 +55,40 @@ impl Job for ClosureJob { } } -/// Example usage -#[test] -pub fn example() { - let mut dispatcher = Dispatcher::new(); +#[cfg(test)] +mod tests { + use super::super::job::{Dispatcher, JobTarget}; + use super::ClosureJob; - // Send a Host job - dispatcher.dispatch(Box::new(ClosureJob::new( - "Host maintenance", - JobTarget::Host, - false, - Box::new(|job: &ClosureJob| { - let target = job.target.clone(); - Box::pin(async move { - println!("Running host job on {:?}", target); - }) - }), - ))); + /// Example usage + #[test] + pub fn example() { + let mut dispatcher = Dispatcher::new(); - // Sending a Container job works the same - // dispatcher.dispatch(Job::new(JobTarget::Container(ns.clone()), "Container task", false, |target| async move { - // println!("Running container job for {:?}", target); - // })); + // Send a Host job + dispatcher.dispatch(Box::new(ClosureJob::new( + "Host maintenance", + JobTarget::Host, + false, + Box::new(|job: &ClosureJob| { + let target = job.target.clone(); + Box::pin(async move { + println!("Running host job on {:?}", target); + }) + }), + ))); - // - // JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(ClosureJob::new("Monitor udev events", JobTarget::BackgroundLoop,false, - // Box::new(move |_target| Box::pin(monitor_udev::udev_monitor_loop(cancel_token.clone())))))); + // Sending a Container job works the same + // dispatcher.dispatch(Job::new(JobTarget::Container(ns.clone()), "Container task", false, |target| async move { + // println!("Running container job for {:?}", target); + // })); - // Allow loops to run briefly before dropping all senders -> graceful shutdown - dispatcher.close(); - dispatcher.wait_until_finished(); + // + // JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(ClosureJob::new("Monitor udev events", JobTarget::BackgroundLoop,false, + // Box::new(move |_target| Box::pin(monitor_udev::udev_monitor_loop(cancel_token.clone())))))); + + // Allow loops to run briefly before dropping all senders -> graceful shutdown + dispatcher.close(); + dispatcher.wait_until_finished(); + } } diff --git a/vuinputd/src/jobs/inject_in_container_job.rs b/vuinputd/src/jobs/inject_in_container_job.rs index e78cad1..e557a8d 100644 --- a/vuinputd/src/jobs/inject_in_container_job.rs +++ b/vuinputd/src/jobs/inject_in_container_job.rs @@ -14,14 +14,7 @@ use async_io::Timer; use log::debug; use crate::{ - job_engine::job::{Job, JobTarget}, - jobs::{ - mknod_input_device::ensure_input_device, - monitor_udev_job::EVENT_STORE, - netlink_message::send_udev_monitor_message_with_properties, - runtime_data::{ensure_udev_structure, read_udev_data, write_udev_data}, - }, - process_tools::{await_process, run_in_net_and_mnt_namespace, Pid, RequestingProcess}, + 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} }; #[derive(Clone, Debug, Copy, PartialOrd, PartialEq)] diff --git a/vuinputd/src/jobs/mod.rs b/vuinputd/src/jobs/mod.rs index 1580916..4adefe9 100644 --- a/vuinputd/src/jobs/mod.rs +++ b/vuinputd/src/jobs/mod.rs @@ -3,8 +3,5 @@ // Author: Johannes Leupolz pub mod inject_in_container_job; -pub mod mknod_input_device; pub mod monitor_udev_job; -pub mod netlink_message; pub mod remove_from_container_job; -pub mod runtime_data; diff --git a/vuinputd/src/jobs/remove_from_container_job.rs b/vuinputd/src/jobs/remove_from_container_job.rs index e4b0077..c299047 100644 --- a/vuinputd/src/jobs/remove_from_container_job.rs +++ b/vuinputd/src/jobs/remove_from_container_job.rs @@ -11,12 +11,7 @@ use std::{ use log::debug; use crate::{ - job_engine::job::{Job, JobTarget}, - jobs::{ - mknod_input_device::remove_input_device, monitor_udev_job::EVENT_STORE, - netlink_message::send_udev_monitor_message_with_properties, runtime_data::delete_udev_data, - }, - process_tools::{await_process, run_in_net_and_mnt_namespace, Pid, RequestingProcess}, + 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} }; #[derive(Clone, Debug, Copy, PartialOrd, PartialEq)] diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index 544fb39..c73b723 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -37,6 +37,8 @@ pub mod job_engine; use crate::job_engine::{job::*, JOB_DISPATCHER}; use crate::process_tools::*; +pub mod actions; + pub mod jobs; use clap::Parser; @@ -58,9 +60,24 @@ struct Args { /// Device name (without /dev/) #[arg(long)] devname: Option, + + /// Action to execute (JSON encoded). Note that this excludes all other options. + #[arg(long, value_name = "JSON")] + pub action: Option, } 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) => {}, + _ => { + return Err( + "--action must not be used in combination with any other argument".into(), + ); + } + } + // major/minor must appear together match (&args.major, &args.minor) { (Some(_), Some(_)) | (None, None) => {} @@ -97,6 +114,11 @@ fn main() -> std::io::Result<()> { std::process::exit(2); } + if args.action.is_some() { + 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(