diff --git a/vuinputd/src/cuse_device/vuinput_ioctl.rs b/vuinputd/src/cuse_device/vuinput_ioctl.rs index 2ad991f..e61bf22 100644 --- a/vuinputd/src/cuse_device/vuinput_ioctl.rs +++ b/vuinputd/src/cuse_device/vuinput_ioctl.rs @@ -14,7 +14,8 @@ use uinput_ioctls::*; use crate::cuse_device::{get_vuinput_state, VuFileHandle}; use crate::job_engine::JOB_DISPATCHER; -use crate::jobs::inject_in_container_job::InjectInContainerJob; +use crate::jobs::emit_udev_event_in_container_job::EmitUdevEventInContainerJob; +use crate::jobs::mknod_device_in_container_job::MknodDeviceInContainerJob; use crate::jobs::remove_from_container_job::RemoveFromContainerJob; use crate::process_tools::SELF_NAMESPACES; use crate::{cuse_device::*, jobs}; @@ -181,25 +182,40 @@ pub unsafe extern "C" fn vuinput_ioctl( .unwrap() .equal_mnt_and_net(&vuinput_state.requesting_process.namespaces) { - let inject_job = InjectInContainerJob::new( + let mknod_job = MknodDeviceInContainerJob::new( vuinput_state.requesting_process.clone(), devnode.clone(), sysname.clone(), major, minor, ); - let awaiter = inject_job.get_awaiter_for_state(); + let awaiter = mknod_job.get_awaiter_for_state(); JOB_DISPATCHER .get() .unwrap() .lock() .unwrap() - .dispatch(Box::new(inject_job)); - awaiter(&jobs::inject_in_container_job::State::Finished); + .dispatch(Box::new(mknod_job)); + awaiter(&jobs::mknod_device_in_container_job::State::Finished); debug!( - "fh {}: injecting dev-nodes in container has been finished ", + "fh {}: mknod_device in container has been finished ", fh ); + + // we do not wait for the udev stuff + let emit_udev_event_job = EmitUdevEventInContainerJob::new( + vuinput_state.requesting_process.clone(), + devnode.clone(), + sysname.clone(), + major, + minor, + ); + JOB_DISPATCHER + .get() + .unwrap() + .lock() + .unwrap() + .dispatch(Box::new(emit_udev_event_job)); } // write a SYN-event (which is just zeros) just for validation diff --git a/vuinputd/src/jobs/inject_in_container_job.rs b/vuinputd/src/jobs/emit_udev_event_in_container_job.rs similarity index 86% rename from vuinputd/src/jobs/inject_in_container_job.rs rename to vuinputd/src/jobs/emit_udev_event_in_container_job.rs index fe5daf8..a3af1ed 100644 --- a/vuinputd/src/jobs/inject_in_container_job.rs +++ b/vuinputd/src/jobs/emit_udev_event_in_container_job.rs @@ -31,7 +31,7 @@ pub enum State { } #[derive(Clone, Debug)] -pub struct InjectInContainerJob { +pub struct EmitUdevEventInContainerJob { requesting_process: RequestingProcess, target: JobTarget, dev_path: String, @@ -41,7 +41,7 @@ pub struct InjectInContainerJob { sync_state: Arc<(Mutex, Condvar)>, } -impl InjectInContainerJob { +impl EmitUdevEventInContainerJob { pub fn new( requesting_process: RequestingProcess, dev_path: String, @@ -82,16 +82,16 @@ impl InjectInContainerJob { } } -impl Job for InjectInContainerJob { +impl Job for EmitUdevEventInContainerJob { fn desc(&self) -> &str { - "Inject input device into container" + "emit udev event into container" } fn execute_after_cancellation(&self) -> bool { false } - fn create_task(self: &InjectInContainerJob) -> Pin>> { + fn create_task(self: &EmitUdevEventInContainerJob) -> Pin>> { Box::pin(self.clone().inject_in_container()) } @@ -100,7 +100,7 @@ impl Job for InjectInContainerJob { } } -impl InjectInContainerJob { +impl EmitUdevEventInContainerJob { async fn inject_in_container(self) { // temporary hack that needs to be replaced. We try 50 times // Should be: Wait for the device to be created, the runtime data to be written and the @@ -148,16 +148,6 @@ impl InjectInContainerJob { let netlink_data = netlink_data.unwrap(); let dev_path = self.dev_path.clone(); - let mknod_device_action = Action::MknodDevice { - path: dev_path.clone(), - major: self.major, - minor: self.minor, - }; - - let child_pid_1 = - process_tools::start_action(mknod_device_action, &self.requesting_process) - .expect("subprocess should work"); - let emit_udev_event_action = Action::EmitUdevEvent { netlink_message: netlink_data.clone(), runtime_data: Some(runtime_data), @@ -165,12 +155,11 @@ impl InjectInContainerJob { minor: self.minor, }; - let child_pid_2 = + let child_pid = process_tools::start_action(emit_udev_event_action, &self.requesting_process) .expect("subprocess should work"); - let _exit_info = await_process(Pid::Pid(child_pid_1)).await.unwrap(); - let _exit_info = await_process(Pid::Pid(child_pid_2)).await.unwrap(); + let _exit_info = await_process(Pid::Pid(child_pid)).await.unwrap(); self.set_state(&State::Finished); } } diff --git a/vuinputd/src/jobs/mknod_device_in_container_job.rs b/vuinputd/src/jobs/mknod_device_in_container_job.rs new file mode 100644 index 0000000..90cc424 --- /dev/null +++ b/vuinputd/src/jobs/mknod_device_in_container_job.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +// +// Author: Johannes Leupolz + +use std::{ + collections::HashMap, + future::Future, + pin::Pin, + sync::{Arc, Condvar, Mutex}, + time::Duration, +}; + +use async_io::Timer; +use log::debug; + +use crate::{ + actions::{ + action::Action, + runtime_data::{read_udev_data}, + }, + job_engine::job::{Job, JobTarget}, + jobs::monitor_udev_job::EVENT_STORE, + process_tools::{self, await_process, Pid, RequestingProcess}, +}; + +#[derive(Clone, Debug, Copy, PartialOrd, PartialEq)] +pub enum State { + Initialized, + Started, + Finished, +} + +#[derive(Clone, Debug)] +pub struct MknodDeviceInContainerJob { + requesting_process: RequestingProcess, + target: JobTarget, + dev_path: String, + sys_path: String, + major: u64, + minor: u64, + sync_state: Arc<(Mutex, Condvar)>, +} + +impl MknodDeviceInContainerJob { + pub fn new( + requesting_process: RequestingProcess, + dev_path: String, + sys_path: String, + major: u64, + minor: u64, + ) -> Self { + Self { + requesting_process: requesting_process.clone(), + target: JobTarget::Container(requesting_process), + dev_path: dev_path, + sys_path: sys_path, + major: major, + minor: minor, + sync_state: Arc::new((Mutex::new(State::Initialized), Condvar::new())), + } + } + + fn set_state(&self, new_state: &State) -> () { + let (lock, cvar) = &*self.sync_state; + let mut current_state = lock.lock().unwrap(); + *current_state = *new_state; + // We notify the condvar that the value has changed. + cvar.notify_all(); + } + + pub fn get_awaiter_for_state(&self) -> impl FnOnce(&State) -> () { + // pattern is described on https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html + let sync_state = self.sync_state.clone(); + let awaiter = move |state: &State| { + let (lock, cvar) = &*sync_state; + let mut current_state = lock.lock().unwrap(); + while *current_state < *state { + current_state = cvar.wait(current_state).unwrap(); + } + }; + awaiter + } +} + +impl Job for MknodDeviceInContainerJob { + fn desc(&self) -> &str { + "mknod input device in container" + } + + fn execute_after_cancellation(&self) -> bool { + false + } + + fn create_task(self: &MknodDeviceInContainerJob) -> Pin>> { + Box::pin(self.clone().inject_in_container()) + } + + fn job_target(&self) -> JobTarget { + self.target.clone() + } +} + +impl MknodDeviceInContainerJob { + async fn inject_in_container(self) { + + let mknod_device_action = Action::MknodDevice { + path: self.dev_path.clone(), + major: self.major, + minor: self.minor, + }; + + let child_pid = + process_tools::start_action(mknod_device_action, &self.requesting_process) + .expect("subprocess should work"); + + let _exit_info = await_process(Pid::Pid(child_pid)).await.unwrap(); + self.set_state(&State::Finished); + } +} diff --git a/vuinputd/src/jobs/mod.rs b/vuinputd/src/jobs/mod.rs index 4adefe9..c1bb9e4 100644 --- a/vuinputd/src/jobs/mod.rs +++ b/vuinputd/src/jobs/mod.rs @@ -2,6 +2,7 @@ // // Author: Johannes Leupolz -pub mod inject_in_container_job; +pub mod mknod_device_in_container_job; +pub mod emit_udev_event_in_container_job; pub mod monitor_udev_job; pub mod remove_from_container_job;