diff --git a/vuinputd/src/container/inject_in_container_job.rs b/vuinputd/src/container/inject_in_container_job.rs index 8783462..a566c67 100644 --- a/vuinputd/src/container/inject_in_container_job.rs +++ b/vuinputd/src/container/inject_in_container_job.rs @@ -9,12 +9,12 @@ 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, 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, requesting_process::{run_in_net_and_mnt_namespace, RequestingProcess} }; #[derive(Clone,Debug)] pub struct InjectInContainerJob { - namespaces: Namespaces, + requesting_process: RequestingProcess, target: JobTarget, dev_path: String, sys_path: String, @@ -23,10 +23,10 @@ pub struct InjectInContainerJob { } impl InjectInContainerJob { - pub fn new(namespaces: Namespaces,dev_path: String, sys_path: String, major: u64, minor: u64) -> Self { + pub fn new(requesting_process: RequestingProcess,dev_path: String, sys_path: String, major: u64, minor: u64) -> Self { Self { - namespaces: namespaces.clone(), - target: JobTarget::Container(namespaces), + requesting_process: requesting_process.clone(), + target: JobTarget::Container(requesting_process), dev_path: dev_path, sys_path: sys_path, major: major , @@ -99,7 +99,7 @@ impl InjectInContainerJob { let netlink_data = netlink_data.unwrap(); - let child_pid = run_in_net_and_mnt_namespace(self.namespaces, Box::new(move || { + let child_pid = run_in_net_and_mnt_namespace(self.requesting_process, Box::new(move || { 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()); diff --git a/vuinputd/src/container/remove_from_container_job.rs b/vuinputd/src/container/remove_from_container_job.rs index d918a03..88098ae 100644 --- a/vuinputd/src/container/remove_from_container_job.rs +++ b/vuinputd/src/container/remove_from_container_job.rs @@ -9,12 +9,12 @@ use async_pidfd::AsyncPidFd; use log::debug; use crate::{ - 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} + 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, requesting_process::{RequestingProcess, run_in_net_and_mnt_namespace} }; #[derive(Clone,Debug)] pub struct RemoveFromContainerJob { - namespaces: Namespaces, + requesting_process: RequestingProcess, target: JobTarget, dev_path: String, sys_path: String, @@ -23,10 +23,10 @@ pub struct RemoveFromContainerJob { } impl RemoveFromContainerJob { - pub fn new(namespaces: Namespaces,dev_path: String, sys_path: String, major: u64, minor: u64) -> Self { + pub fn new(requesting_process: RequestingProcess,dev_path: String, sys_path: String, major: u64, minor: u64) -> Self { Self { - namespaces: namespaces.clone(), - target: JobTarget::Container(namespaces), + requesting_process: requesting_process.clone(), + target: JobTarget::Container(requesting_process), dev_path: dev_path, sys_path: sys_path, major: major , @@ -75,7 +75,7 @@ impl RemoveFromContainerJob { 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 || { + let child_pid = run_in_net_and_mnt_namespace(self.requesting_process, 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 diff --git a/vuinputd/src/jobs/job.rs b/vuinputd/src/jobs/job.rs index abedec6..051bce8 100644 --- a/vuinputd/src/jobs/job.rs +++ b/vuinputd/src/jobs/job.rs @@ -11,7 +11,7 @@ use std::sync::Mutex; use std::thread::{self, JoinHandle}; use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; -use crate::namespace::Namespaces; +use crate::requesting_process::RequestingProcess; // To discuss: // what we handle here, could also be named Task. The decision for job was more or less @@ -25,7 +25,7 @@ pub enum JobTarget { Host, BackgroundLoop, /// A specific container or namespace target. - Container(Namespaces), + Container(RequestingProcess), } pub trait Job: Send + 'static { diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index 853d477..5053ef9 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -37,12 +37,12 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex, OnceLock, RwLock}; use uinput_ioctls::*; -pub mod namespace; +pub mod requesting_process; pub mod monitor_udev; use crate::container::inject_in_container_job::InjectInContainerJob; use crate::container::remove_from_container_job::RemoveFromContainerJob; use crate::monitor_udev::MonitorBackgroundLoop; -use crate::namespace::*; +use crate::requesting_process::*; pub mod jobs; use crate::jobs::job::*; @@ -64,7 +64,7 @@ struct VuInputDevice { #[derive(Debug)] struct VuInputState { file: File, - ns_of_requestor: Namespaces, + requesting_process: RequestingProcess, input_device: Option } @@ -96,7 +96,7 @@ enum VuError { static VUINPUT_COUNTER: OnceLock = OnceLock::new(); static VUINPUT_STATE: OnceLock>>>> = OnceLock::new(); static JOB_DISPATCHER: OnceLock>= OnceLock::new(); -static SELF_NAMESPACES: OnceLock= OnceLock::new(); +static SELF_REQUESTING_PROCESSES: OnceLock= OnceLock::new(); // For log limiting. Idea: Move to log_limit crate static DEDUP_LAST_ERROR: OnceLock>> = OnceLock::new(); @@ -210,7 +210,7 @@ unsafe extern "C" fn vuinput_open( &VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap()), VuInputState { file: v, - ns_of_requestor: namespaces, + requesting_process: namespaces, input_device: None }, ) @@ -317,9 +317,9 @@ unsafe extern "C" fn vuinput_release( // Only do this in case it has not already been done by the ioctl UI_DEV_DESTROY // this here is relevant if the process was killed and didn't have the chance to send the // ioctl UI_DEV_DESTROY. - if input_device.is_some() && ! SELF_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.ns_of_requestor) { + if input_device.is_some() && ! SELF_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) { let input_device = input_device.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); + let remove_job=RemoveFromContainerJob::new(vuinput_state.requesting_process.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)); } @@ -485,8 +485,8 @@ unsafe extern "C" fn vuinput_ioctl( vuinput_state.input_device = Some(VuInputDevice {cuse_fh:*fh, major: major, minor: minor, syspath: sysname.clone(), devnode: devnode.clone(), runtime_data: None, netlink_data: None }); // Create 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 inject_job=InjectInContainerJob::new(vuinput_state.ns_of_requestor.clone(),devnode.clone(),sysname.clone(),major,minor); + if ! SELF_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) { + let inject_job=InjectInContainerJob::new(vuinput_state.requesting_process.clone(),devnode.clone(),sysname.clone(),major,minor); JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(inject_job)); } @@ -500,9 +500,9 @@ unsafe extern "C" fn vuinput_ioctl( let input_device = vuinput_state.input_device.take(); // Remove device in container, if the request was really from another namespace - if input_device.is_some() && ! SELF_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.ns_of_requestor) { + if input_device.is_some() && ! SELF_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) { let input_device = input_device.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); + let remove_job=RemoveFromContainerJob::new(vuinput_state.requesting_process.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)); } @@ -702,6 +702,18 @@ fn check_permissions() -> Result<(), std::io::Error> { }) } +// this is static for the architecture +pub fn compat_uses_64bit_time() -> bool { + let uname = nix::sys::utsname::uname().unwrap(); + let arch = uname.machine().to_str().unwrap(); + + match arch { + "x86_64" => false, + "ppc64" => false, // some setups still 32-bit time_t + _ => true, // arm64, riscv64, s390x all use 64-bit + } +} + fn main() -> std::io::Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init(); @@ -712,7 +724,7 @@ fn main() -> std::io::Result<()> { VUINPUT_STATE.set(RwLock::new(HashMap::new())).unwrap(); VUINPUT_COUNTER.set(AtomicU64::new(3)).unwrap(); JOB_DISPATCHER.set(Mutex::new(Dispatcher::new())).unwrap(); - SELF_NAMESPACES.set(get_namespaces(Pid::SelfPid)).unwrap(); + SELF_REQUESTING_PROCESSES.set(get_namespaces(Pid::SelfPid)).unwrap(); DEDUP_LAST_ERROR.set(Mutex::new(None)).unwrap(); JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(MonitorBackgroundLoop::new())); diff --git a/vuinputd/src/namespace.rs b/vuinputd/src/requesting_process.rs similarity index 89% rename from vuinputd/src/namespace.rs rename to vuinputd/src/requesting_process.rs index 4bee9eb..fd1a5d2 100644 --- a/vuinputd/src/namespace.rs +++ b/vuinputd/src/requesting_process.rs @@ -31,7 +31,7 @@ impl Pid { } #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] -struct NamespaceInodes { +struct Namespaces { pub net: Option, pub uts: Option, pub ipc: Option, @@ -45,18 +45,6 @@ struct NamespaceInodes { } -// this is static for the architecture -fn compat_uses_64bit_time() -> bool { - let uname = nix::sys::utsname::uname().unwrap(); - let arch = uname.machine().to_str().unwrap(); - - match arch { - "x86_64" => false, - "ppc64" => false, // some setups still 32-bit time_t - _ => true, // arm64, riscv64, s390x all use 64-bit - } -} - /// Returns true if the process with `pid` is a 32-bit (compat) process. None, if unsure. pub fn is_compat_process(pid: i32) -> Option { const EI_CLASS: usize = 4; @@ -84,25 +72,25 @@ pub fn is_compat_process(pid: i32) -> Option { // TODO: Rename to capture all relevant process information #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] -pub struct Namespaces { +pub struct RequestingProcess { pub nspath: String, pub nsroot: String, - nsinodes: NamespaceInodes, -} - -impl NamespaceInodes { - pub fn equal_mnt_and_net(&self, other: &NamespaceInodes) -> bool { - self.mnt == other.mnt && self.net == other.net - } + nsinodes: Namespaces, } impl Namespaces { pub fn equal_mnt_and_net(&self, other: &Namespaces) -> bool { + self.mnt == other.mnt && self.net == other.net + } +} + +impl RequestingProcess { + pub fn equal_mnt_and_net(&self, other: &RequestingProcess) -> bool { self.nsinodes.equal_mnt_and_net(&other.nsinodes) } } -impl std::fmt::Display for Namespaces { +impl std::fmt::Display for RequestingProcess { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "Namespaces:")?; writeln!(f, " net: {:?}", self.nsinodes.net)?; @@ -119,14 +107,14 @@ impl std::fmt::Display for Namespaces { } } -fn get_namespace_inodes(pid: Pid) -> NamespaceInodes { +fn get_namespace_inodes(pid: Pid) -> Namespaces { let pid: String = match pid { Pid::Pid(pid) => pid.to_string(), Pid::SelfPid => "self".to_string(), }; let nspath = format!("/proc/{}/ns", pid); - let mut ns = NamespaceInodes { + let mut ns = Namespaces { net: None, uts: None, ipc: None, @@ -183,7 +171,7 @@ fn get_ppid(pid: Pid) -> Option { -pub fn get_namespaces(pid: Pid) -> Namespaces { +pub fn get_namespaces(pid: Pid) -> RequestingProcess { match pid { Pid::Pid(_) => @@ -211,7 +199,7 @@ pub fn get_namespaces(pid: Pid) -> Namespaces { let nspath = format!("{}/ns", pid.path()); let nsroot = format!("{}/ns", ppid.path()); - Namespaces { + RequestingProcess { nspath: nspath, nsroot: nsroot, nsinodes: nsinodes, @@ -221,7 +209,7 @@ pub fn get_namespaces(pid: Pid) -> Namespaces { { let nsinodes = get_namespace_inodes(pid); let nspath = format!("{}/ns", pid.path()); - Namespaces { + RequestingProcess { nspath: nspath.clone(), nsroot: nspath, nsinodes: nsinodes, @@ -232,7 +220,7 @@ pub fn get_namespaces(pid: Pid) -> Namespaces { /// Runs a function inside the given network and mount namespaces. /// Returns the child PID so the caller can `waitpid` on it. -pub fn run_in_net_and_mnt_namespace(ns: Namespaces, func: Box) -> nix::Result { +pub fn run_in_net_and_mnt_namespace(ns: RequestingProcess, func: Box) -> nix::Result { //Note: The child process is created with a single thread—the one that called fork(). match unsafe { fork()? } {