mirror of
https://github.com/joleuger/vuinputd.git
synced 2026-07-18 00:45:07 +00:00
Improved error handling; also implemented some cleanup
This commit is contained in:
parent
6d096c2e62
commit
53bc1ff153
7 changed files with 77 additions and 44 deletions
|
|
@ -81,7 +81,7 @@ impl InjectInContainerJob {
|
|||
number_of_attempt+=1;
|
||||
// wait a maximum of 5 seconds == 50 attempts
|
||||
Timer::after(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
if (netlink_data.is_none() || runtime_data.is_none()) {
|
||||
if netlink_data.is_none() {
|
||||
debug!("Give up reading netlink data");
|
||||
|
|
@ -98,11 +98,16 @@ impl InjectInContainerJob {
|
|||
let runtime_data = runtime_data.unwrap();
|
||||
let netlink_data = netlink_data.unwrap();
|
||||
|
||||
|
||||
let child_pid = run_in_net_and_mnt_namespace(self.namespaces, Box::new(move || {
|
||||
|
||||
ensure_input_device(self.dev_path.clone(), self.major, self.minor).unwrap();
|
||||
|
||||
if let Err(e) = ensure_input_device(self.dev_path.clone(), self.major, self.minor) {
|
||||
debug!("Error creating input device {}: {e}",self.dev_path.clone());
|
||||
};
|
||||
ensure_udev_structure().unwrap();
|
||||
write_udev_data(runtime_data.as_str(), major, minor).unwrap();
|
||||
if let Err(e) = write_udev_data(runtime_data.as_str(), major, minor) {
|
||||
debug!("Error writing udev data for device {}: {e}",self.dev_path.clone());
|
||||
};
|
||||
send_udev_monitor_message_with_properties(netlink_data.clone());
|
||||
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@ use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
|||
use std::path::Path;
|
||||
|
||||
pub fn ensure_input_device(dev_path: String, major: u64, minor: u64) -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let input_dir = Path::new("/dev/input");
|
||||
// Create directory like `mkdir -p`
|
||||
if !input_dir.exists() {
|
||||
println!("Create /dev/input");
|
||||
fs::create_dir_all(input_dir)?;
|
||||
}
|
||||
|
||||
let path = Path::new(&dev_path);
|
||||
let expected_dev = makedev(major, minor);
|
||||
let expected_mode = 0o666;
|
||||
|
|
@ -64,14 +72,25 @@ pub fn ensure_input_device(dev_path: String, major: u64, minor: u64) -> Result<(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
fn main() {
|
||||
let name = "/dev/input/example0";
|
||||
let major = 13; // example values
|
||||
let minor = 37;
|
||||
pub fn remove_input_device(dev_path: String, major: u64, minor: u64) -> Result<(), Box<dyn Error>> {
|
||||
let path = Path::new(&dev_path);
|
||||
let expected_dev = makedev(major, minor);
|
||||
|
||||
if let Err(e) = ensure_input_device(name, major, minor) {
|
||||
eprintln!("Error: {}", e);
|
||||
// --- Step 1: Ensure it is the correct device ---
|
||||
if !path.exists() {
|
||||
return Err("Device does not exist".into());
|
||||
}
|
||||
}
|
||||
*/
|
||||
match stat(path) {
|
||||
Ok(st) => {
|
||||
let is_char = (st.st_mode & libc::S_IFMT as u32) == libc::S_IFCHR as u32;
|
||||
let dev_ok = st.st_rdev == expected_dev;
|
||||
if !(is_char && dev_ok) {
|
||||
return Err("Device that should be deleted has wrong major and minor".into())
|
||||
}
|
||||
}
|
||||
Err(x) => return Err("Could not execute stat on device file".into())
|
||||
}
|
||||
|
||||
let _ = fs::remove_file(path);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -9,22 +9,28 @@ use async_pidfd::AsyncPidFd;
|
|||
use log::debug;
|
||||
|
||||
use crate::{
|
||||
container::{mknod_input_device::ensure_input_device, netlink_message::send_udev_monitor_message_with_properties, runtime_data::{self, ensure_udev_structure, read_udev_data, write_udev_data}}, jobs::job::{Job, JobTarget}, monitor_udev::EVENT_STORE, namespace::{run_in_net_and_mnt_namespace, Namespaces}
|
||||
container::{mknod_input_device::{ensure_input_device, remove_input_device}, netlink_message::send_udev_monitor_message_with_properties, runtime_data::{self, delete_udev_data, ensure_udev_structure, read_udev_data, write_udev_data}}, jobs::job::{Job, JobTarget}, monitor_udev::EVENT_STORE, namespace::{Namespaces, run_in_net_and_mnt_namespace}
|
||||
};
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct RemoveFromContainerJob {
|
||||
namespaces: Namespaces,
|
||||
target: JobTarget,
|
||||
dev_path: String,
|
||||
sys_path: String,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
}
|
||||
|
||||
impl RemoveFromContainerJob {
|
||||
pub fn new(namespaces: Namespaces,sys_path: String) -> Self {
|
||||
pub fn new(namespaces: Namespaces,dev_path: String, sys_path: String, major: u64, minor: u64) -> Self {
|
||||
Self {
|
||||
namespaces: namespaces.clone(),
|
||||
target: JobTarget::Container(namespaces),
|
||||
dev_path: dev_path,
|
||||
sys_path: sys_path,
|
||||
major: major ,
|
||||
minor: minor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +55,6 @@ impl Job for RemoveFromContainerJob {
|
|||
|
||||
impl RemoveFromContainerJob {
|
||||
async fn remove_from_container(self) {
|
||||
// TODO: Here is a race with inject in container.
|
||||
let netlink_event = match EVENT_STORE.get().unwrap().lock().unwrap().take(&self.sys_path) {
|
||||
Some(netlink_event) => netlink_event,
|
||||
None => {
|
||||
|
|
@ -66,11 +71,21 @@ impl RemoveFromContainerJob {
|
|||
|
||||
// define for capturing
|
||||
let mut netlink_data = netlink_data.unwrap().clone();
|
||||
let major = self.major;
|
||||
let minor=self.minor;
|
||||
|
||||
let _ = netlink_data.insert("ACTION".to_string(),"remove".to_string());
|
||||
let child_pid = run_in_net_and_mnt_namespace(self.namespaces, Box::new(move || {
|
||||
// TODO: we should keep the same order as event_execute_rules_on_remove in
|
||||
// https://github.com/systemd/systemd/blob/main/src/udev/udev-event.c
|
||||
|
||||
send_udev_monitor_message_with_properties(netlink_data.clone());
|
||||
if let Err(e) = delete_udev_data(major,minor) {
|
||||
debug!("Error deleting udev data for {}:{}: {e}",major,minor);
|
||||
}
|
||||
if let Err(e) = remove_input_device(self.dev_path.clone(), self.major, self.minor) {
|
||||
debug!("Error removing input device {}: {e}",self.dev_path.clone());
|
||||
};
|
||||
|
||||
}))
|
||||
.expect("subprocess should work");
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@ pub fn write_udev_data(content: &str, major: u64, minor: u64) -> io::Result<()>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete udev data for a given major/minor number
|
||||
/// - `major`, `minor` = device numbers
|
||||
pub fn delete_udev_data(major: u64, minor: u64) -> io::Result<()> {
|
||||
|
||||
let path = format!("/run/udev/data/c{}:{}", major, minor);
|
||||
fs::remove_file(&path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_udev_data(major: u64, minor: u64) -> io::Result<String> {
|
||||
let path = format!("/run/udev/data/c{}:{}", major, minor);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use async_channel::{Receiver, Sender};
|
|||
use futures::executor::{LocalPool, LocalSpawner};
|
||||
use futures::future::RemoteHandle;
|
||||
use futures::task::LocalSpawnExt;
|
||||
use log::debug;
|
||||
use std::sync::Mutex;
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc};
|
||||
|
|
@ -109,7 +110,9 @@ impl Dispatcher {
|
|||
|
||||
pub fn close(&mut self) {
|
||||
self.tx = None;
|
||||
debug!("Checking for running jobs before shutdown");
|
||||
self.future_handles.lock().unwrap().clear();
|
||||
debug!("Pending jobs canceled");
|
||||
}
|
||||
|
||||
pub fn wait_until_finished(&mut self) {
|
||||
|
|
|
|||
|
|
@ -13,36 +13,11 @@
|
|||
// renaming
|
||||
// use in container
|
||||
// cancellation token
|
||||
// device removal (udev netlink message)
|
||||
// distinguish between cleanup jobs that must not be cancelled and other jobs (especially background jobs)
|
||||
// naming: dev_path vs dev_node. I guess I mean the same.
|
||||
// Send warning, if udev monitor does not exist
|
||||
|
||||
|
||||
// test scenario:
|
||||
// vuinputd
|
||||
// mouse-advanced
|
||||
// ls -lh /dev/input/event9
|
||||
// cat /run/udev/data/c13:73
|
||||
// machinectl shell johannes@gamestreamingserver
|
||||
// systemctl --user stop headless-labwc.service
|
||||
// machinectl shell gamestreamingserver
|
||||
// mkdir -p /run/udev/data/
|
||||
// touch /run/udev/control
|
||||
// mknod /dev/input/event9 c 13 73
|
||||
// vim /run/udev/data/c13:73
|
||||
// machinectl shell johannes@gamestreamingserver
|
||||
// systemctl --user start headless-labwc.service
|
||||
// systemctl --user start wayvnc.service
|
||||
|
||||
|
||||
//libinput backend
|
||||
// seatd
|
||||
//libinput-tools
|
||||
// groups
|
||||
// 777 for input9
|
||||
// char-input to systemd
|
||||
|
||||
use libc::O_CLOEXEC;
|
||||
use libc::{iovec, off_t, size_t, EBADRQC, EIO, ENOENT};
|
||||
use libc::{uinput_abs_setup, uinput_ff_erase, uinput_ff_upload, uinput_setup};
|
||||
|
|
@ -287,7 +262,8 @@ unsafe extern "C" fn vuinput_release(
|
|||
|
||||
// remove device in container, if the request was really from another namespace
|
||||
if ! SELF_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.ns_of_requestor) {
|
||||
let remove_job=RemoveFromContainerJob::new(vuinput_state.ns_of_requestor.clone(),vuinput_state.input_device.as_ref().unwrap().syspath.clone());
|
||||
let input_device = vuinput_state.input_device.as_ref().unwrap();
|
||||
let remove_job=RemoveFromContainerJob::new(vuinput_state.ns_of_requestor.clone(),input_device.devnode.clone(),input_device.syspath.clone(),input_device.major,input_device.minor);
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job));
|
||||
}
|
||||
|
||||
|
|
@ -468,7 +444,8 @@ unsafe extern "C" fn vuinput_ioctl(
|
|||
|
||||
// Remove device in container, if the request was really from another namespace
|
||||
if ! SELF_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.ns_of_requestor) {
|
||||
let remove_job=RemoveFromContainerJob::new(vuinput_state.ns_of_requestor.clone(),vuinput_state.input_device.as_ref().unwrap().syspath.clone());
|
||||
let input_device = vuinput_state.input_device.as_ref().unwrap();
|
||||
let remove_job=RemoveFromContainerJob::new(vuinput_state.ns_of_requestor.clone(),input_device.devnode.clone(),input_device.syspath.clone(),input_device.major,input_device.minor);
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use log::debug;
|
||||
use nix::{
|
||||
sched::{setns, CloneFlags},
|
||||
unistd::{fork, ForkResult, Pid},
|
||||
};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
os::fd::AsFd,
|
||||
os::fd::AsFd, path::{self, Path},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||
|
|
@ -106,6 +107,11 @@ pub fn run_in_net_and_mnt_namespace(ns: Namespaces, func: Box<dyn Fn()>) -> nix:
|
|||
}
|
||||
ForkResult::Child => {
|
||||
// enter namespace
|
||||
let path: &Path = Path::new(ns.nspath.as_str());
|
||||
if !fs::exists(path).unwrap() {
|
||||
debug!("the process whose namespaces we want to enter does not exist anymore!");
|
||||
std::process::exit(0);
|
||||
}
|
||||
let net = File::open(ns.nspath.clone() + "/net").expect("net not found");
|
||||
let mnt = File::open(ns.nspath.clone() + "/mnt").expect("mnt not found");
|
||||
setns(net.as_fd(), CloneFlags::CLONE_NEWNET).expect("couldn't enter net");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue