Split inject_in_container into emit_udev_event and mknod_device

This commit is contained in:
Johannes Leupolz 2025-12-18 19:46:55 +00:00
parent 32119fbf92
commit 25623bf636
4 changed files with 151 additions and 26 deletions

View file

@ -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

View file

@ -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<State>, 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<Box<dyn Future<Output = ()>>> {
fn create_task(self: &EmitUdevEventInContainerJob) -> Pin<Box<dyn Future<Output = ()>>> {
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);
}
}

View file

@ -0,0 +1,119 @@
// SPDX-License-Identifier: MIT
//
// Author: Johannes Leupolz <dev@leupolz.eu>
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<State>, 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<dyn Future<Output = ()>>> {
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);
}
}

View file

@ -2,6 +2,7 @@
//
// Author: Johannes Leupolz <dev@leupolz.eu>
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;