mirror of
https://github.com/joleuger/vuinputd.git
synced 2026-07-17 16:36:03 +00:00
cargo fmt
This commit is contained in:
parent
b3d0a0225d
commit
f0bff128a6
22 changed files with 429 additions and 347 deletions
|
|
@ -4,7 +4,7 @@
|
|||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
//
|
||||
// This library is heavily baased on https://github.com/richard-w/libfuse-sys
|
||||
// but adopted to only provide the low-level modules of fuse and cuse.
|
||||
// but adopted to only provide the low-level modules of fuse and cuse.
|
||||
|
||||
extern crate bindgen;
|
||||
extern crate pkg_config;
|
||||
|
|
@ -15,7 +15,6 @@ use std::path::PathBuf;
|
|||
|
||||
const FUSE_USE_VERSION: u32 = 314; //fuse version of ubuntu 24.04
|
||||
|
||||
|
||||
fn fuse_binding_filter(builder: bindgen::Builder) -> bindgen::Builder {
|
||||
let builder = builder
|
||||
// Whitelist "fuse_*" symbols and blocklist everything else
|
||||
|
|
@ -103,18 +102,13 @@ fn main() {
|
|||
let mut pkgcfg = pkg_config::Config::new();
|
||||
|
||||
// Find libfuse
|
||||
let fuse3_lib = pkgcfg.cargo_metadata(true).probe("fuse3").expect("Failed to find pkg-config module fuse3");
|
||||
|
||||
let fuse3_lib = pkgcfg
|
||||
.cargo_metadata(true)
|
||||
.probe("fuse3")
|
||||
.expect("Failed to find pkg-config module fuse3");
|
||||
|
||||
// Generate lowlevel bindings
|
||||
generate_fuse_bindings(
|
||||
"fuse_lowlevel.h",
|
||||
&fuse3_lib,
|
||||
fuse_binding_filter,
|
||||
);
|
||||
generate_fuse_bindings("fuse_lowlevel.h", &fuse3_lib, fuse_binding_filter);
|
||||
// Generate lowlevel cuse bindings
|
||||
generate_fuse_bindings(
|
||||
"cuse_lowlevel.h",
|
||||
&fuse3_lib,
|
||||
cuse_binding_filter,
|
||||
);
|
||||
}
|
||||
generate_fuse_bindings("cuse_lowlevel.h", &fuse3_lib, cuse_binding_filter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
//
|
||||
// This library is heavily baased on https://github.com/richard-w/libfuse-sys
|
||||
// but adopted to only provide the low-level modules of fuse and cuse.
|
||||
// but adopted to only provide the low-level modules of fuse and cuse.
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
use libc::*;
|
||||
|
||||
|
||||
pub mod fuse_lowlevel {
|
||||
use super::*;
|
||||
include!(concat!(env!("OUT_DIR"), "/fuse_lowlevel.rs"));
|
||||
|
|
@ -24,8 +23,8 @@ pub mod fuse_lowlevel {
|
|||
pub mod cuse_lowlevel {
|
||||
use super::*;
|
||||
include!(concat!(env!("OUT_DIR"), "/cuse_lowlevel.rs"));
|
||||
|
||||
|
||||
use fuse_lowlevel::{
|
||||
fuse_args, fuse_conn_info, fuse_file_info, fuse_pollhandle, fuse_req_t, fuse_session,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ const REL_Y: i32 = 1;
|
|||
const SYN_REPORT: i32 = 0;
|
||||
const BUS_USB: u16 = 0x03;
|
||||
|
||||
///
|
||||
|
||||
///
|
||||
|
||||
fn emit(fd: c_int, ev_type: i32, code: i32, val: i32) -> io::Result<()> {
|
||||
// libc's input_event struct layout:
|
||||
|
|
@ -62,11 +61,10 @@ fn main() -> io::Result<()> {
|
|||
// open device - matches: open("/dev/uinput-test", O_WRONLY | O_NONBLOCK);
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let device=
|
||||
match args.len() {
|
||||
2 => args[1].clone(),
|
||||
_ => "/dev/uinput".to_string()
|
||||
};
|
||||
let device = match args.len() {
|
||||
2 => args[1].clone(),
|
||||
_ => "/dev/uinput".to_string(),
|
||||
};
|
||||
|
||||
let path = CString::new(device).unwrap();
|
||||
let fd = unsafe { open(path.as_ptr(), O_WRONLY | O_NONBLOCK) };
|
||||
|
|
@ -97,19 +95,16 @@ fn main() -> io::Result<()> {
|
|||
std::process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
ui_set_evbit(fd, EV_REL.try_into().unwrap()).unwrap_or_else(|e| {
|
||||
eprintln!("ui_set_evbit(EV_REL) failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
ui_set_relbit(fd, REL_X.try_into().unwrap()).unwrap_or_else(|e| {
|
||||
eprintln!("ui_set_relbit(REL_X) failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
ui_set_relbit(fd, REL_Y.try_into().unwrap()).unwrap_or_else(|e| {
|
||||
eprintln!("ui_set_relbit(REL_Y) failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ const REL_Y: i32 = 1;
|
|||
const SYN_REPORT: i32 = 0;
|
||||
const BUS_USB: u16 = 0x03;
|
||||
|
||||
///
|
||||
|
||||
///
|
||||
|
||||
fn emit(fd: c_int, ev_type: i32, code: i32, val: i32) -> io::Result<()> {
|
||||
// libc's input_event struct layout:
|
||||
|
|
@ -62,11 +61,10 @@ fn main() -> io::Result<()> {
|
|||
// open device - matches: open("/dev/uinput-test", O_WRONLY | O_NONBLOCK);
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let device=
|
||||
match args.len() {
|
||||
2 => args[1].clone(),
|
||||
_ => "/dev/uinput".to_string()
|
||||
};
|
||||
let device = match args.len() {
|
||||
2 => args[1].clone(),
|
||||
_ => "/dev/uinput".to_string(),
|
||||
};
|
||||
|
||||
let path = CString::new(device).unwrap();
|
||||
let fd = unsafe { open(path.as_ptr(), O_WRONLY | O_NONBLOCK) };
|
||||
|
|
@ -97,19 +95,16 @@ fn main() -> io::Result<()> {
|
|||
std::process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
ui_set_evbit(fd, EV_REL.try_into().unwrap()).unwrap_or_else(|e| {
|
||||
eprintln!("ui_set_evbit(EV_REL) failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
ui_set_relbit(fd, REL_X.try_into().unwrap()).unwrap_or_else(|e| {
|
||||
eprintln!("ui_set_relbit(REL_X) failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
ui_set_relbit(fd, REL_Y.try_into().unwrap()).unwrap_or_else(|e| {
|
||||
eprintln!("ui_set_relbit(REL_Y) failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
|
|
@ -217,7 +212,6 @@ fn main() -> io::Result<()> {
|
|||
sleep(Duration::from_millis(300));
|
||||
}
|
||||
|
||||
|
||||
// Give userspace time to read events
|
||||
sleep(Duration::from_secs(5));
|
||||
|
||||
|
|
|
|||
|
|
@ -4,20 +4,19 @@
|
|||
|
||||
pub mod state;
|
||||
pub mod vuinput_ioctl;
|
||||
pub mod vuinput_write;
|
||||
pub mod vuinput_release;
|
||||
pub mod vuinput_open;
|
||||
pub mod vuinput_release;
|
||||
pub mod vuinput_write;
|
||||
|
||||
use std::{fs, io};
|
||||
use std::io::ErrorKind;
|
||||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||
use std::io::{ErrorKind};
|
||||
use std::{fs, io};
|
||||
|
||||
use ::cuse_lowlevel::*;
|
||||
use state::*;
|
||||
|
||||
pub const BUS_USB: u16 = 0x03;
|
||||
|
||||
|
||||
// Instance of cuse_lowlevel_ops with all stubs assigned.
|
||||
// Setting to None leads to e.g. "write error: Function not implemented".
|
||||
// You can find the implementations of the uinput default (open, release ,read, write, poll,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File};
|
||||
use std::fs::File;
|
||||
use std::sync::{Arc, Mutex, OnceLock, RwLock};
|
||||
|
||||
use ::cuse_lowlevel::*;
|
||||
|
|
@ -12,8 +12,8 @@ use crate::process_tools::RequestingProcess;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct VuInputDevice {
|
||||
pub major : u64,
|
||||
pub minor : u64,
|
||||
pub major: u64,
|
||||
pub minor: u64,
|
||||
pub syspath: String,
|
||||
pub devnode: String,
|
||||
}
|
||||
|
|
@ -22,12 +22,12 @@ pub struct VuInputDevice {
|
|||
pub struct VuInputState {
|
||||
pub file: File,
|
||||
pub requesting_process: RequestingProcess,
|
||||
pub input_device: Option<VuInputDevice>
|
||||
pub input_device: Option<VuInputDevice>,
|
||||
}
|
||||
|
||||
#[derive(Debug,Eq, Hash, PartialEq, Clone)]
|
||||
#[derive(Debug, Eq, Hash, PartialEq, Clone)]
|
||||
pub enum VuFileHandle {
|
||||
Fh(u64)
|
||||
Fh(u64),
|
||||
}
|
||||
|
||||
impl VuFileHandle {
|
||||
|
|
@ -39,15 +39,13 @@ impl VuFileHandle {
|
|||
impl std::fmt::Display for VuFileHandle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VuFileHandle::Fh(fh) => writeln!(f, "VuFileHandle({:?})",fh)?,
|
||||
VuFileHandle::Fh(fh) => writeln!(f, "VuFileHandle({:?})", fh)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vuinput_state(
|
||||
fh:&VuFileHandle,
|
||||
) -> Result<Arc<Mutex<VuInputState>>, String> {
|
||||
pub fn get_vuinput_state(fh: &VuFileHandle) -> Result<Arc<Mutex<VuInputState>>, String> {
|
||||
let map = VUINPUT_STATE
|
||||
.get()
|
||||
.ok_or("global not initialized".to_string())?;
|
||||
|
|
@ -58,11 +56,7 @@ pub fn get_vuinput_state(
|
|||
.ok_or("handle not opened".to_string())
|
||||
}
|
||||
|
||||
|
||||
pub fn insert_vuinput_state(
|
||||
fh:&VuFileHandle,
|
||||
state: VuInputState,
|
||||
) -> Result<(), String> {
|
||||
pub fn insert_vuinput_state(fh: &VuFileHandle, state: VuInputState) -> Result<(), String> {
|
||||
let map = VUINPUT_STATE
|
||||
.get()
|
||||
.ok_or("global not initialized".to_string())?;
|
||||
|
|
@ -79,9 +73,7 @@ pub fn insert_vuinput_state(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_vuinput_state(
|
||||
fh:&VuFileHandle,
|
||||
) -> Result<Arc<Mutex<VuInputState>>, String> {
|
||||
pub fn remove_vuinput_state(fh: &VuFileHandle) -> Result<Arc<Mutex<VuInputState>>, String> {
|
||||
let map = VUINPUT_STATE
|
||||
.get()
|
||||
.ok_or("global not initialized".to_string())?;
|
||||
|
|
@ -91,22 +83,24 @@ pub fn remove_vuinput_state(
|
|||
}
|
||||
|
||||
pub fn initialize_vuinput_state() {
|
||||
VUINPUT_STATE.set(RwLock::new(HashMap::new())).expect("failed to initialize global state");
|
||||
VUINPUT_STATE
|
||||
.set(RwLock::new(HashMap::new()))
|
||||
.expect("failed to initialize global state");
|
||||
}
|
||||
|
||||
pub fn initialize_dedup_last_error() {
|
||||
DEDUP_LAST_ERROR.set(Mutex::new(None)).expect("failed to initialize the log deduplication state");
|
||||
DEDUP_LAST_ERROR
|
||||
.set(Mutex::new(None))
|
||||
.expect("failed to initialize the log deduplication state");
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VuError {
|
||||
WriteError
|
||||
WriteError,
|
||||
}
|
||||
|
||||
|
||||
pub static VUINPUT_STATE: OnceLock<RwLock<HashMap<VuFileHandle, Arc<Mutex<VuInputState>>>>> = OnceLock::new();
|
||||
pub static VUINPUT_STATE: OnceLock<RwLock<HashMap<VuFileHandle, Arc<Mutex<VuInputState>>>>> =
|
||||
OnceLock::new();
|
||||
|
||||
// For log limiting. Idea: Move to log_limit crate
|
||||
pub static DEDUP_LAST_ERROR: OnceLock<Mutex<Option<(u64,VuError)>>> = OnceLock::new();
|
||||
|
||||
pub static DEDUP_LAST_ERROR: OnceLock<Mutex<Option<(u64, VuError)>>> = OnceLock::new();
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use ::cuse_lowlevel::*;
|
||||
use libc::{iovec, size_t, EBADRQC};
|
||||
use libc::{uinput_abs_setup, uinput_ff_erase, uinput_ff_upload, uinput_setup};
|
||||
use ::cuse_lowlevel::*;
|
||||
use log::{debug};
|
||||
use std::ffi::{CStr};
|
||||
use log::debug;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Write;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::raw::{c_char, c_int, c_uint, c_void};
|
||||
use uinput_ioctls::*;
|
||||
|
||||
use crate::cuse_device::{VuFileHandle, get_vuinput_state};
|
||||
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::remove_from_container_job::RemoveFromContainerJob;
|
||||
|
|
@ -46,7 +46,7 @@ pub unsafe extern "C" fn vuinput_ioctl(
|
|||
//UI_ABS_SETUP => UI_ABS_SETUP_WITHOUT_SIZE,
|
||||
_ => cmd_u64,
|
||||
};
|
||||
let vufh= VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap());
|
||||
let vufh = VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap());
|
||||
let vuinput_state_mutex = get_vuinput_state(&vufh).unwrap();
|
||||
let fh = &(*_fi).fh;
|
||||
let mut vuinput_state = vuinput_state_mutex.lock().unwrap();
|
||||
|
|
@ -166,21 +166,44 @@ pub unsafe extern "C" fn vuinput_ioctl(
|
|||
debug!("fh {}: syspath: {}", fh, sysname);
|
||||
let devnode = fetch_device_node(&sysname).unwrap();
|
||||
debug!("fh {}: devnode: {}", fh, devnode);
|
||||
let (major,minor) = fetch_major_minor(&devnode).unwrap();
|
||||
debug!("fh {}: major: {} minor: {} ", fh, major,minor);
|
||||
vuinput_state.input_device = Some(VuInputDevice {major: major, minor: minor, syspath: sysname.clone(), devnode: devnode.clone() });
|
||||
let (major, minor) = fetch_major_minor(&devnode).unwrap();
|
||||
debug!("fh {}: major: {} minor: {} ", fh, major, minor);
|
||||
vuinput_state.input_device = Some(VuInputDevice {
|
||||
major: major,
|
||||
minor: minor,
|
||||
syspath: sysname.clone(),
|
||||
devnode: devnode.clone(),
|
||||
});
|
||||
|
||||
// Create device in container, if the request was really from another namespace
|
||||
if ! SELF_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process.namespaces) {
|
||||
let inject_job=InjectInContainerJob::new(vuinput_state.requesting_process.clone(),devnode.clone(),sysname.clone(),major,minor);
|
||||
if !SELF_NAMESPACES
|
||||
.get()
|
||||
.unwrap()
|
||||
.equal_mnt_and_net(&vuinput_state.requesting_process.namespaces)
|
||||
{
|
||||
let inject_job = InjectInContainerJob::new(
|
||||
vuinput_state.requesting_process.clone(),
|
||||
devnode.clone(),
|
||||
sysname.clone(),
|
||||
major,
|
||||
minor,
|
||||
);
|
||||
let awaiter = inject_job.get_awaiter_for_state();
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(inject_job));
|
||||
JOB_DISPATCHER
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dispatch(Box::new(inject_job));
|
||||
awaiter(&jobs::inject_in_container_job::State::Finished);
|
||||
debug!("fh {}: injecting dev-nodes in container has been finished ", fh);
|
||||
debug!(
|
||||
"fh {}: injecting dev-nodes in container has been finished ",
|
||||
fh
|
||||
);
|
||||
}
|
||||
|
||||
// write a SYN-event (which is just zeros) just for validation
|
||||
let syn_event : [u8; 24] = [0; 24];
|
||||
let syn_event: [u8; 24] = [0; 24];
|
||||
vuinput_state.file.write_all(&syn_event).unwrap();
|
||||
|
||||
fuse_lowlevel::fuse_reply_ioctl(_req, 0, std::ptr::null(), 0);
|
||||
|
|
@ -190,13 +213,32 @@ pub 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.requesting_process.namespaces) {
|
||||
if input_device.is_some()
|
||||
&& !SELF_NAMESPACES
|
||||
.get()
|
||||
.unwrap()
|
||||
.equal_mnt_and_net(&vuinput_state.requesting_process.namespaces)
|
||||
{
|
||||
let input_device = input_device.unwrap();
|
||||
let remove_job=RemoveFromContainerJob::new(vuinput_state.requesting_process.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,
|
||||
);
|
||||
let awaiter = remove_job.get_awaiter_for_state();
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job));
|
||||
JOB_DISPATCHER
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dispatch(Box::new(remove_job));
|
||||
awaiter(&jobs::remove_from_container_job::State::Finished);
|
||||
debug!("fh {}: removing dev-nodes from container has been finished ", fh);
|
||||
debug!(
|
||||
"fh {}: removing dev-nodes from container has been finished ",
|
||||
fh
|
||||
);
|
||||
}
|
||||
|
||||
ui_dev_destroy(fd).unwrap();
|
||||
|
|
@ -364,7 +406,6 @@ pub unsafe extern "C" fn vuinput_ioctl(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn fetch_device_node(path: &str) -> io::Result<String> {
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?; // propagate per-entry errors
|
||||
|
|
@ -396,4 +437,3 @@ pub fn fetch_major_minor(path: &str) -> io::Result<(u64, u64)> {
|
|||
|
||||
Ok((major, minor))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use std::sync::OnceLock;
|
||||
use libc::{O_CLOEXEC};
|
||||
use libc::{ENOENT};
|
||||
use ::cuse_lowlevel::*;
|
||||
use libc::ENOENT;
|
||||
use libc::O_CLOEXEC;
|
||||
use log::{debug, error};
|
||||
use std::fs::{OpenOptions};
|
||||
use std::os::unix::fs::{OpenOptionsExt};
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::process_tools::{Pid, get_requesting_process};
|
||||
use crate::cuse_device::*;
|
||||
use crate::process_tools::{get_requesting_process, Pid};
|
||||
|
||||
pub static VUINPUT_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ pub unsafe extern "C" fn vuinput_open(
|
|||
VuInputState {
|
||||
file: v,
|
||||
requesting_process,
|
||||
input_device: None
|
||||
input_device: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -58,4 +58,4 @@ pub unsafe extern "C" fn vuinput_open(
|
|||
fuse_lowlevel::fuse_reply_err(_req, ENOENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,21 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use ::cuse_lowlevel::*;
|
||||
use log::{debug};
|
||||
use std::sync::{Arc};
|
||||
use crate::{cuse_device::*, jobs};
|
||||
use crate::job_engine::JOB_DISPATCHER;
|
||||
use crate::jobs::remove_from_container_job::RemoveFromContainerJob;
|
||||
use crate::process_tools::SELF_NAMESPACES;
|
||||
use crate::{cuse_device::*, jobs};
|
||||
use ::cuse_lowlevel::*;
|
||||
use log::debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub unsafe extern "C" fn vuinput_release(
|
||||
_req: fuse_lowlevel::fuse_req_t,
|
||||
_fi: *mut fuse_lowlevel::fuse_file_info,
|
||||
) {
|
||||
let fh = &(*_fi).fh;
|
||||
let vuinput_state_mutex = remove_vuinput_state(&VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap())).unwrap();
|
||||
let vuinput_state_mutex =
|
||||
remove_vuinput_state(&VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap())).unwrap();
|
||||
|
||||
let mut vuinput_state = vuinput_state_mutex.lock().unwrap();
|
||||
let input_device = vuinput_state.input_device.take();
|
||||
|
|
@ -24,11 +25,27 @@ pub 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.requesting_process.namespaces) {
|
||||
if input_device.is_some()
|
||||
&& !SELF_NAMESPACES
|
||||
.get()
|
||||
.unwrap()
|
||||
.equal_mnt_and_net(&vuinput_state.requesting_process.namespaces)
|
||||
{
|
||||
let input_device = input_device.unwrap();
|
||||
let remove_job=RemoveFromContainerJob::new(vuinput_state.requesting_process.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,
|
||||
);
|
||||
let awaiter = remove_job.get_awaiter_for_state();
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job));
|
||||
JOB_DISPATCHER
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dispatch(Box::new(remove_job));
|
||||
awaiter(&jobs::remove_from_container_job::State::Finished);
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +57,7 @@ pub unsafe extern "C" fn vuinput_release(
|
|||
Arc::strong_count(&vuinput_state_mutex)
|
||||
);
|
||||
drop(vuinput_state_mutex); // this also closes the file when no other references are open
|
||||
// TODO: maybe also ensure that nothing is left in the containers
|
||||
// TODO: maybe also ensure that nothing is left in the containers
|
||||
|
||||
// Note: For CUSE, the kernel always issues RELEASE via fuse_sync_release(),
|
||||
// which forces a *synchronous* request (fuse_simple_request()).
|
||||
|
|
@ -53,4 +70,3 @@ pub unsafe extern "C" fn vuinput_release(
|
|||
// `fuse_reply_err(req, 0)` is enough to wake the kernel and is safe here.
|
||||
fuse_lowlevel::fuse_reply_err(_req, 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,16 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use crate::cuse_device::*;
|
||||
use ::cuse_lowlevel::*;
|
||||
use libc::{__s32, __u16, input_event};
|
||||
use libc::{off_t, size_t, EIO};
|
||||
use libc::{uinput_abs_setup, uinput_setup};
|
||||
use ::cuse_lowlevel::*;
|
||||
use log::{debug, trace};
|
||||
use std::io::Write;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::raw::{c_char};
|
||||
use std::os::raw::c_char;
|
||||
use uinput_ioctls::*;
|
||||
use libc::{__s32, __u16, input_event};
|
||||
use crate::cuse_device::*;
|
||||
|
||||
|
||||
// TODO: compat-mode+ ensure sizeof(struct input_event)
|
||||
pub unsafe extern "C" fn vuinput_write(
|
||||
|
|
@ -27,14 +26,18 @@ pub unsafe extern "C" fn vuinput_write(
|
|||
"vuinput_write: offset needs to be 0 but is {}",
|
||||
_off
|
||||
);
|
||||
|
||||
|
||||
let fh = &(*_fi).fh;
|
||||
let slice = std::slice::from_raw_parts(_buf as *const u8, _size);
|
||||
let vuinput_state_mutex = get_vuinput_state(&VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap())).unwrap();
|
||||
let vuinput_state_mutex =
|
||||
get_vuinput_state(&VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap())).unwrap();
|
||||
let mut vuinput_state = vuinput_state_mutex.lock().unwrap();
|
||||
|
||||
|
||||
if vuinput_state.input_device.is_none() {
|
||||
debug!("{}: legacy device setup recognized! Ignore the data and use hardcoded values",fh);
|
||||
debug!(
|
||||
"{}: legacy device setup recognized! Ignore the data and use hardcoded values",
|
||||
fh
|
||||
);
|
||||
|
||||
assert!(_size == std::mem::size_of::<libc::uinput_user_dev>());
|
||||
let legacy_uinput_user_dev = _buf as *const libc::uinput_user_dev;
|
||||
|
|
@ -45,8 +48,8 @@ pub unsafe extern "C" fn vuinput_write(
|
|||
usetup.id.vendor = 0x1209;
|
||||
usetup.id.product = 0x5020;
|
||||
usetup.id.version = (*legacy_uinput_user_dev).id.version;
|
||||
usetup.ff_effects_max=(*legacy_uinput_user_dev).ff_effects_max;
|
||||
usetup.name=(*legacy_uinput_user_dev).name;
|
||||
usetup.ff_effects_max = (*legacy_uinput_user_dev).ff_effects_max;
|
||||
usetup.name = (*legacy_uinput_user_dev).name;
|
||||
|
||||
// Call IOCTLs to setup and create the device
|
||||
// Assuming your wrappers accept (fd, ptr_to_usetup) etc.
|
||||
|
|
@ -56,10 +59,12 @@ pub unsafe extern "C" fn vuinput_write(
|
|||
ui_dev_setup(fd, usetup_ptr).unwrap();
|
||||
|
||||
// setup abs
|
||||
for code in 0..libc::ABS_CNT{
|
||||
if (*legacy_uinput_user_dev).absmax[code] != 0 || (*legacy_uinput_user_dev).absmin[code] != 0 {
|
||||
for code in 0..libc::ABS_CNT {
|
||||
if (*legacy_uinput_user_dev).absmax[code] != 0
|
||||
|| (*legacy_uinput_user_dev).absmin[code] != 0
|
||||
{
|
||||
let mut abs_setup: uinput_abs_setup = unsafe { std::mem::zeroed() };
|
||||
abs_setup.code=code.try_into().unwrap();
|
||||
abs_setup.code = code.try_into().unwrap();
|
||||
abs_setup.absinfo.maximum = (*legacy_uinput_user_dev).absmax[code];
|
||||
abs_setup.absinfo.minimum = (*legacy_uinput_user_dev).absmin[code];
|
||||
abs_setup.absinfo.fuzz = (*legacy_uinput_user_dev).absfuzz[code];
|
||||
|
|
@ -77,50 +82,50 @@ pub unsafe extern "C" fn vuinput_write(
|
|||
let mut bytes = 0;
|
||||
let mut result = Result::Ok(0);
|
||||
|
||||
let compat_size= std::mem::size_of::<input_event_compat>();
|
||||
let normal_size= std::mem::size_of::<libc::input_event>();
|
||||
let compat_size = std::mem::size_of::<input_event_compat>();
|
||||
let normal_size = std::mem::size_of::<libc::input_event>();
|
||||
let is_compat = vuinput_state.requesting_process.is_compat;
|
||||
// TODO: ARM: && !compat_uses_64bit_time()
|
||||
|
||||
|
||||
if !is_compat {
|
||||
while bytes + normal_size <= _size && result.is_ok() {
|
||||
result = vuinput_state.file.write(&slice[bytes..bytes + normal_size]);
|
||||
bytes += normal_size;
|
||||
bytes += normal_size;
|
||||
}
|
||||
} else {
|
||||
while bytes + compat_size <= _size && result.is_ok() {
|
||||
let position= _buf.byte_add(bytes);
|
||||
let position = _buf.byte_add(bytes);
|
||||
let compat = position as *const input_event_compat;
|
||||
let normal = map_to_64_bit(&*compat);
|
||||
let normal_ptr=(&normal as *const libc::input_event) as *const u8;
|
||||
let slice = std::slice::from_raw_parts(normal_ptr,normal_size);
|
||||
let normal_ptr = (&normal as *const libc::input_event) as *const u8;
|
||||
let slice = std::slice::from_raw_parts(normal_ptr, normal_size);
|
||||
result = vuinput_state.file.write(&slice);
|
||||
bytes += compat_size;
|
||||
bytes += compat_size;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
trace!("wrote {} of {} bytes (compat {})", bytes,_size,is_compat);
|
||||
trace!("wrote {} of {} bytes (compat {})", bytes, _size, is_compat);
|
||||
fuse_lowlevel::fuse_reply_write(_req, bytes);
|
||||
}
|
||||
Err(e) => {
|
||||
let mut last_error = DEDUP_LAST_ERROR.get().unwrap().lock().unwrap();
|
||||
|
||||
|
||||
match *last_error {
|
||||
Some((last_fh,VuError::WriteError)) if *fh == last_fh => {},
|
||||
_ => {debug!("fh {}: error writing to uinput: {e:?}",fh);}
|
||||
Some((last_fh, VuError::WriteError)) if *fh == last_fh => {}
|
||||
_ => {
|
||||
debug!("fh {}: error writing to uinput: {e:?}", fh);
|
||||
}
|
||||
}
|
||||
|
||||
*last_error = Some((*fh,VuError::WriteError));
|
||||
|
||||
|
||||
*last_error = Some((*fh, VuError::WriteError));
|
||||
|
||||
fuse_lowlevel::fuse_reply_err(_req, EIO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
pub struct input_event_compat {
|
||||
pub input_event_sec: u32,
|
||||
|
|
@ -138,17 +143,17 @@ pub fn compat_uses_64bit_time() -> bool {
|
|||
match arch {
|
||||
"x86_64" => false,
|
||||
"ppc64" => false, // some setups still 32-bit time_t
|
||||
_ => true, // arm64, riscv64, s390x all use 64-bit
|
||||
_ => true, // arm64, riscv64, s390x all use 64-bit
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_to_64_bit(compat: &input_event_compat) -> input_event{
|
||||
pub fn map_to_64_bit(compat: &input_event_compat) -> input_event {
|
||||
let mut mapped: input_event = unsafe { std::mem::zeroed() };
|
||||
mapped.time.tv_sec=compat.input_event_sec.into();
|
||||
mapped.time.tv_usec=compat.input_event_usec.into();
|
||||
mapped.type_=compat.type_;
|
||||
mapped.code=compat.code;
|
||||
mapped.value=compat.value;
|
||||
mapped.time.tv_sec = compat.input_event_sec.into();
|
||||
mapped.time.tv_usec = compat.input_event_usec.into();
|
||||
mapped.type_ = compat.type_;
|
||||
mapped.code = compat.code;
|
||||
mapped.value = compat.value;
|
||||
|
||||
mapped
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use std::pin::Pin;
|
|||
|
||||
use crate::job_engine::job::{Dispatcher, Job, JobTarget};
|
||||
|
||||
|
||||
pub struct ClosureJob {
|
||||
desc: String,
|
||||
execute_after_cancellation: bool,
|
||||
|
|
@ -79,11 +78,10 @@ pub fn example() {
|
|||
// println!("Running container job for {:?}", target);
|
||||
// }));
|
||||
|
||||
//
|
||||
// JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(ClosureJob::new("Monitor udev events", JobTarget::BackgroundLoop,false,
|
||||
//
|
||||
// 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();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ use crate::job_engine::job::Dispatcher;
|
|||
pub mod closure_job;
|
||||
pub mod job;
|
||||
|
||||
pub static JOB_DISPATCHER: OnceLock<Mutex<Dispatcher>>= OnceLock::new();
|
||||
pub static JOB_DISPATCHER: OnceLock<Mutex<Dispatcher>> = OnceLock::new();
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
use crate::job_engine::closure_job::ClosureJob;
|
||||
use crate::job_engine::job::{Dispatcher, JobTarget};
|
||||
|
||||
|
|
@ -32,11 +30,10 @@ fn test_job_ordering() {
|
|||
let c1 = c1.clone();
|
||||
Box::pin(async move {
|
||||
*c1.lock().unwrap() = 5;
|
||||
})
|
||||
})
|
||||
}),
|
||||
)));
|
||||
|
||||
|
||||
// job 2: increment to 6
|
||||
let c2 = c.clone();
|
||||
dispatcher.dispatch(Box::new(ClosureJob::new(
|
||||
|
|
@ -47,7 +44,7 @@ fn test_job_ordering() {
|
|||
let c2 = c2.clone();
|
||||
Box::pin(async move {
|
||||
*c2.lock().unwrap() += 1;
|
||||
})
|
||||
})
|
||||
}),
|
||||
)));
|
||||
|
||||
|
|
@ -365,4 +362,4 @@ fn test_cleanup_when_target_disappears() {
|
|||
// Should run normally
|
||||
assert!(buf.contains(&"alive-job".into()));
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,22 +2,36 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use std::{collections::HashMap, future::Future, pin::Pin, sync::{Arc, Condvar, Mutex}, time::Duration};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
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::{Pid, RequestingProcess, await_process, run_in_net_and_mnt_namespace}};
|
||||
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},
|
||||
};
|
||||
|
||||
|
||||
#[derive(Clone,Debug,Copy,PartialOrd,PartialEq)]
|
||||
#[derive(Clone, Debug, Copy, PartialOrd, PartialEq)]
|
||||
pub enum State {
|
||||
Initialized,
|
||||
Started,
|
||||
Finished,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InjectInContainerJob {
|
||||
requesting_process: RequestingProcess,
|
||||
target: JobTarget,
|
||||
|
|
@ -25,17 +39,23 @@ pub struct InjectInContainerJob {
|
|||
sys_path: String,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
sync_state: Arc<(Mutex<State>,Condvar)>,
|
||||
sync_state: Arc<(Mutex<State>, Condvar)>,
|
||||
}
|
||||
|
||||
impl InjectInContainerJob {
|
||||
pub fn new(requesting_process: RequestingProcess,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 {
|
||||
requesting_process: requesting_process.clone(),
|
||||
target: JobTarget::Container(requesting_process),
|
||||
dev_path: dev_path,
|
||||
sys_path: sys_path,
|
||||
major: major ,
|
||||
major: major,
|
||||
minor: minor,
|
||||
sync_state: Arc::new((Mutex::new(State::Initialized), Condvar::new())),
|
||||
}
|
||||
|
|
@ -52,10 +72,10 @@ impl InjectInContainerJob {
|
|||
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 awaiter = move |state: &State| {
|
||||
let (lock, cvar) = &*sync_state;
|
||||
let mut current_state = lock.lock().unwrap();
|
||||
while *current_state < *state {
|
||||
while *current_state < *state {
|
||||
current_state = cvar.wait(current_state).unwrap();
|
||||
}
|
||||
};
|
||||
|
|
@ -87,30 +107,33 @@ impl InjectInContainerJob {
|
|||
// Should be: Wait for the device to be created, the runtime data to be written and the
|
||||
// netlink message to be sent
|
||||
self.set_state(&State::Started);
|
||||
let mut netlink_data: Option<HashMap<String,String>> = None;
|
||||
let mut netlink_data: Option<HashMap<String, String>> = None;
|
||||
let mut runtime_data: Option<String> = None;
|
||||
let mut number_of_attempt = 1;
|
||||
while number_of_attempt<=50 && !(netlink_data.is_some() && runtime_data.is_some()) {
|
||||
|
||||
while number_of_attempt <= 50 && !(netlink_data.is_some() && runtime_data.is_some()) {
|
||||
if netlink_data.is_none() {
|
||||
|
||||
if let Some(netlink_event)=EVENT_STORE.get().unwrap().lock().unwrap().take(&self.sys_path) {
|
||||
if let Some(netlink_event) = EVENT_STORE
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take(&self.sys_path)
|
||||
{
|
||||
if netlink_event.tombstone || netlink_event.remove_data.is_some() {
|
||||
debug!("do nothing, because the device has already been removed in the meantime");
|
||||
return;
|
||||
}
|
||||
netlink_data=netlink_event.add_data;
|
||||
netlink_data = netlink_event.add_data;
|
||||
};
|
||||
}
|
||||
if runtime_data.is_none() {
|
||||
runtime_data = read_udev_data(self.major,self.minor).ok();
|
||||
|
||||
runtime_data = read_udev_data(self.major, self.minor).ok();
|
||||
}
|
||||
|
||||
number_of_attempt+=1;
|
||||
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");
|
||||
|
|
@ -124,27 +147,29 @@ impl InjectInContainerJob {
|
|||
|
||||
// define for capturing
|
||||
let major = self.major;
|
||||
let minor=self.minor;
|
||||
let minor = self.minor;
|
||||
let runtime_data = runtime_data.unwrap();
|
||||
let netlink_data = netlink_data.unwrap();
|
||||
let dev_path = self.dev_path.clone();
|
||||
|
||||
|
||||
let child_pid = run_in_net_and_mnt_namespace(&self.requesting_process, Box::new(move || {
|
||||
|
||||
if let Err(e) = ensure_input_device(dev_path.clone(), self.major, self.minor) {
|
||||
debug!("Error creating input device {}: {e}",dev_path.clone());
|
||||
};
|
||||
ensure_udev_structure().unwrap();
|
||||
if let Err(e) = write_udev_data(runtime_data.as_str(), major, minor) {
|
||||
debug!("Error writing udev data for device {}: {e}",dev_path.clone());
|
||||
};
|
||||
send_udev_monitor_message_with_properties(netlink_data.clone());
|
||||
|
||||
}))
|
||||
let child_pid = run_in_net_and_mnt_namespace(
|
||||
&self.requesting_process,
|
||||
Box::new(move || {
|
||||
if let Err(e) = ensure_input_device(dev_path.clone(), self.major, self.minor) {
|
||||
debug!("Error creating input device {}: {e}", dev_path.clone());
|
||||
};
|
||||
ensure_udev_structure().unwrap();
|
||||
if let Err(e) = write_udev_data(runtime_data.as_str(), major, minor) {
|
||||
debug!(
|
||||
"Error writing udev data for device {}: {e}",
|
||||
dev_path.clone()
|
||||
);
|
||||
};
|
||||
send_udev_monitor_message_with_properties(netlink_data.clone());
|
||||
}),
|
||||
)
|
||||
.expect("subprocess should work");
|
||||
let _exit_info = await_process(Pid::Pid(child_pid.as_raw())).await.unwrap();
|
||||
self.set_state(&State::Finished);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,17 @@
|
|||
use nix::sys::stat::{makedev, mknod, stat, Mode, SFlag};
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::{PermissionsExt};
|
||||
use std::os::unix::fs::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;
|
||||
|
|
@ -84,12 +83,12 @@ pub fn remove_input_device(dev_path: String, major: u64, minor: u64) -> Result<(
|
|||
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())
|
||||
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())
|
||||
Err(_x) => return Err("Could not execute stat on device file".into()),
|
||||
}
|
||||
|
||||
let _ = fs::remove_file(path);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
pub mod inject_in_container_job;
|
||||
pub mod remove_from_container_job;
|
||||
pub mod monitor_udev_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;
|
||||
pub mod netlink_message;
|
||||
|
|
@ -129,12 +129,10 @@ impl EventStore {
|
|||
|
||||
pub static EVENT_STORE: OnceLock<Arc<Mutex<EventStore>>> = OnceLock::new();
|
||||
|
||||
pub struct MonitorBackgroundLoop {
|
||||
}
|
||||
pub struct MonitorBackgroundLoop {}
|
||||
impl MonitorBackgroundLoop {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
}
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,16 +206,14 @@ pub async fn udev_monitor_loop(cancel_token: Arc<AtomicBool>) {
|
|||
let key = match key.as_str() {
|
||||
"ID_VUINPUT_KEYBOARD" => "ID_INPUT_KEYBOARD".to_string(),
|
||||
"ID_VUINPUT_MOUSE" => "ID_INPUT_MOUSE".to_string(),
|
||||
_ => key
|
||||
_ => key,
|
||||
};
|
||||
|
||||
let value: String = property.value().to_str().unwrap().to_string();
|
||||
if key!="ID_SEAT" {
|
||||
if key != "ID_SEAT" {
|
||||
properties.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let value_of_devpath = properties.get("DEVPATH").unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ use std::collections::HashMap;
|
|||
use std::mem;
|
||||
use std::os::fd::{AsRawFd, OwnedFd};
|
||||
|
||||
use std::io::{IoSlice};
|
||||
use std::io::IoSlice;
|
||||
|
||||
use log::debug;
|
||||
use nix::sys::socket::{
|
||||
bind, sendmsg, socket, AddressFamily, MsgFlags, NetlinkAddr, SockFlag, SockProtocol, SockType
|
||||
bind, sendmsg, socket, AddressFamily, MsgFlags, NetlinkAddr, SockFlag, SockProtocol, SockType,
|
||||
};
|
||||
|
||||
/// Netlink constants
|
||||
|
|
@ -79,7 +79,7 @@ pub fn string_hash32(s: &str) -> u32 {
|
|||
match s {
|
||||
"input" => 3248653424,
|
||||
"" => 0,
|
||||
_ => panic!("uncovered use case")
|
||||
_ => panic!("uncovered use case"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,12 +95,10 @@ fn open_netlink(groups: u32) -> Result<OwnedFd, String> {
|
|||
.map_err(|e| format!("Could not create netlink socket: {}", e))?;
|
||||
|
||||
// pid 0 => the kernel takes care of assigning it.
|
||||
let sockaddr=NetlinkAddr::new(0, groups);
|
||||
let raw_fd= fd.as_raw_fd();
|
||||
let sockaddr = NetlinkAddr::new(0, groups);
|
||||
let raw_fd = fd.as_raw_fd();
|
||||
|
||||
bind(raw_fd, &sockaddr).map_err(|e| {
|
||||
format!("Could not bind netlink socket: {}", e)
|
||||
})?;
|
||||
bind(raw_fd, &sockaddr).map_err(|e| format!("Could not bind netlink socket: {}", e))?;
|
||||
|
||||
Ok(fd)
|
||||
}
|
||||
|
|
@ -128,16 +126,19 @@ pub fn send_udev_monitor_message(
|
|||
let fd = open_netlink(groups)?;
|
||||
|
||||
// prepare iovecs
|
||||
let iov = [
|
||||
IoSlice::new(&header_bytes),
|
||||
IoSlice::new(payload),
|
||||
];
|
||||
let iov = [IoSlice::new(&header_bytes), IoSlice::new(payload)];
|
||||
|
||||
// destination sockaddr (NULL nl_pid => kernel / multicast)
|
||||
let sockaddr = NetlinkAddr::new(0, groups);
|
||||
|
||||
let _rc = sendmsg(fd.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sockaddr))
|
||||
.map_err(|e| format!("Could not send message: {}", e));
|
||||
let _rc = sendmsg(
|
||||
fd.as_raw_fd(),
|
||||
&iov,
|
||||
&[],
|
||||
MsgFlags::empty(),
|
||||
Some(&sockaddr),
|
||||
)
|
||||
.map_err(|e| format!("Could not send message: {}", e));
|
||||
debug!("udev message sent");
|
||||
|
||||
// ensure cleanup
|
||||
|
|
@ -146,29 +147,27 @@ pub fn send_udev_monitor_message(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_udev_monitor_message_with_properties(properties:HashMap<String, String>) {
|
||||
pub fn send_udev_monitor_message_with_properties(properties: HashMap<String, String>) {
|
||||
let device_name = match properties.get("DEVNAME") {
|
||||
Some(name) => name,
|
||||
None => "unknown device"
|
||||
None => "unknown device",
|
||||
};
|
||||
debug!("Sending udev message over netlink for {}",device_name);
|
||||
let mut payload:Vec<u8> = Vec::new();
|
||||
for (key,value) in properties.iter() {
|
||||
debug!("Sending udev message over netlink for {}", device_name);
|
||||
let mut payload: Vec<u8> = Vec::new();
|
||||
for (key, value) in properties.iter() {
|
||||
payload.extend(key.as_bytes());
|
||||
payload.extend("=".as_bytes());
|
||||
payload.extend(value.as_bytes());
|
||||
payload.push(0);
|
||||
}
|
||||
|
||||
send_udev_monitor_message(&payload,Some("input"),None,UDEV_EVENT_MODE).unwrap();
|
||||
|
||||
send_udev_monitor_message(&payload, Some("input"), None, UDEV_EVENT_MODE).unwrap();
|
||||
}
|
||||
|
||||
// println!("{:02X?}", payload);
|
||||
// 746573743D76616C75650043555252454E545F544147533D3A736561745F7675696E7075743A00544147533D3A736561745F7675696E7075743A00444556504154483D2F646576696365732F7669727475616C2F696E7075742F696E7075743133382F6576656E74390049445F5655494E5055545F4D4F5553453D31004D494E4F523D37330049445F494E5055543D31002E494E5055545F434C4153533D6D6F757365005345514E554D3D3134393231002E484156455F485744425F50524F504552544945533D31004D414A4F523D313300414354494F4E3D6164640049445F53455249414C3D6E6F73657269616C004445564E414D453D2F6465762F696E7075742F6576656E743900555345435F494E495449414C495A45443D31373337373733353034373139320049445F5655494E5055543D310049445F534541543D736561745F7675696E7075740053554253595354454D3D696E70757400
|
||||
// dGVzdD12YWx1ZQBDVVJSRU5UX1RBR1M9OnNlYXRfdnVpbnB1dDoAVEFHUz06c2VhdF92dWlucHV0OgBERVZQQVRIPS9kZXZpY2VzL3ZpcnR1YWwvaW5wdXQvaW5wdXQxMzgvZXZlbnQ5AElEX1ZVSU5QVVRfTU9VU0U9MQBNSU5PUj03MwBJRF9JTlBVVD0xAC5JTlBVVF9DTEFTUz1tb3VzZQBTRVFOVU09MTQ5MjEALkhBVkVfSFdEQl9QUk9QRVJUSUVTPTEATUFKT1I9MTMAQUNUSU9OPWFkZABJRF9TRVJJQUw9bm9zZXJpYWwAREVWTkFNRT0vZGV2L2lucHV0L2V2ZW50OQBVU0VDX0lOSVRJQUxJWkVEPTE3Mzc3NzM1MDQ3MTkyAElEX1ZVSU5QVVQ9MQBJRF9TRUFUPXNlYXRfdnVpbnB1dABTVUJTWVNURU09aW5wdXQA
|
||||
|
||||
|
||||
|
||||
/*
|
||||
UDEV [16427452.069342] add /devices/virtual/input/input97 (input)
|
||||
ACTION=add
|
||||
|
|
@ -211,4 +210,4 @@ TAGS=:seat_vuinput:power-switch:
|
|||
CURRENT_TAGS=:seat_vuinput:power-switch:
|
||||
|
||||
|
||||
*/
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,23 +2,31 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use std::{future::Future, pin::Pin, sync::{Arc, Condvar, Mutex}};
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
};
|
||||
|
||||
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::{Pid, RequestingProcess, await_process, run_in_net_and_mnt_namespace}};
|
||||
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},
|
||||
};
|
||||
|
||||
|
||||
|
||||
#[derive(Clone,Debug,Copy,PartialOrd,PartialEq)]
|
||||
#[derive(Clone, Debug, Copy, PartialOrd, PartialEq)]
|
||||
pub enum State {
|
||||
Initialized,
|
||||
Started,
|
||||
Finished,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoveFromContainerJob {
|
||||
requesting_process: RequestingProcess,
|
||||
target: JobTarget,
|
||||
|
|
@ -26,17 +34,23 @@ pub struct RemoveFromContainerJob {
|
|||
sys_path: String,
|
||||
major: u64,
|
||||
minor: u64,
|
||||
sync_state: Arc<(Mutex<State>,Condvar)>,
|
||||
sync_state: Arc<(Mutex<State>, Condvar)>,
|
||||
}
|
||||
|
||||
impl RemoveFromContainerJob {
|
||||
pub fn new(requesting_process: RequestingProcess,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 {
|
||||
requesting_process: requesting_process.clone(),
|
||||
target: JobTarget::Container(requesting_process),
|
||||
dev_path: dev_path,
|
||||
sys_path: sys_path,
|
||||
major: major ,
|
||||
major: major,
|
||||
minor: minor,
|
||||
sync_state: Arc::new((Mutex::new(State::Initialized), Condvar::new())),
|
||||
}
|
||||
|
|
@ -52,7 +66,7 @@ impl RemoveFromContainerJob {
|
|||
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 awaiter = move |state: &State| {
|
||||
let (lock, cvar) = &*sync_state;
|
||||
let mut current_state = lock.lock().unwrap();
|
||||
while *current_state < *state {
|
||||
|
|
@ -61,7 +75,6 @@ impl RemoveFromContainerJob {
|
|||
};
|
||||
awaiter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Job for RemoveFromContainerJob {
|
||||
|
|
@ -86,7 +99,13 @@ impl RemoveFromContainerJob {
|
|||
async fn remove_from_container(self) {
|
||||
self.set_state(&State::Started);
|
||||
|
||||
let netlink_event = match EVENT_STORE.get().unwrap().lock().unwrap().take(&self.sys_path) {
|
||||
let netlink_event = match EVENT_STORE
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take(&self.sys_path)
|
||||
{
|
||||
Some(netlink_event) => netlink_event,
|
||||
None => {
|
||||
debug!("do nothing, because the device has never been announced via netlink");
|
||||
|
|
@ -100,31 +119,32 @@ impl RemoveFromContainerJob {
|
|||
self.set_state(&State::Finished);
|
||||
return;
|
||||
}
|
||||
let netlink_data=netlink_event.add_data;
|
||||
let netlink_data = netlink_event.add_data;
|
||||
|
||||
// define for capturing
|
||||
let mut netlink_data = netlink_data.unwrap().clone();
|
||||
let major = self.major;
|
||||
let minor=self.minor;
|
||||
let minor = self.minor;
|
||||
let dev_path = self.dev_path.clone();
|
||||
|
||||
let _ = netlink_data.insert("ACTION".to_string(),"remove".to_string());
|
||||
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
|
||||
|
||||
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(dev_path.clone(), self.major, self.minor) {
|
||||
debug!("Error removing input device {}: {e}",dev_path.clone());
|
||||
};
|
||||
let _ = netlink_data.insert("ACTION".to_string(), "remove".to_string());
|
||||
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
|
||||
|
||||
}))
|
||||
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(dev_path.clone(), self.major, self.minor) {
|
||||
debug!("Error removing input device {}: {e}", dev_path.clone());
|
||||
};
|
||||
}),
|
||||
)
|
||||
.expect("subprocess should work");
|
||||
let _exit_info = await_process(Pid::Pid(child_pid.as_raw())).await;
|
||||
self.set_state(&State::Finished);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use std::path::Path;
|
|||
/// Ensure required udev directories and files exist
|
||||
pub fn ensure_udev_structure() -> io::Result<()> {
|
||||
// TODO: this _must_ exist, before a service using libinput is run. The time of device creation might be too late
|
||||
|
||||
|
||||
let data_dir = Path::new("/run/udev/data");
|
||||
let control_file = Path::new("/run/udev/control");
|
||||
|
|
@ -53,7 +52,6 @@ pub fn write_udev_data(content: &str, major: u64, minor: u64) -> io::Result<()>
|
|||
cleaned.push_str(&line);
|
||||
cleaned.push('\n');
|
||||
}
|
||||
|
||||
|
||||
let path = format!("/run/udev/data/c{}:{}", major, minor);
|
||||
let mut file = File::create(&path)?;
|
||||
|
|
@ -65,7 +63,6 @@ pub fn write_udev_data(content: &str, major: u64, minor: u64) -> io::Result<()>
|
|||
/// 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(())
|
||||
|
|
|
|||
|
|
@ -20,26 +20,25 @@
|
|||
use ::cuse_lowlevel::*;
|
||||
use log::info;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::{c_char};
|
||||
use std::sync::atomic::{AtomicU64};
|
||||
use std::sync::{Mutex};
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub mod cuse_device;
|
||||
|
||||
use crate::cuse_device::state::{initialize_dedup_last_error, initialize_vuinput_state};
|
||||
use crate::cuse_device::vuinput_make_cuse_ops;
|
||||
use crate::cuse_device::vuinput_open::VUINPUT_COUNTER;
|
||||
use crate::cuse_device::{vuinput_make_cuse_ops};
|
||||
use crate::jobs::monitor_udev_job::MonitorBackgroundLoop;
|
||||
|
||||
pub mod process_tools;
|
||||
|
||||
pub mod job_engine;
|
||||
use crate::job_engine::{JOB_DISPATCHER, job::*};
|
||||
use crate::job_engine::{job::*, JOB_DISPATCHER};
|
||||
use crate::process_tools::*;
|
||||
|
||||
pub mod jobs;
|
||||
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
|
||||
|
||||
|
|
@ -48,11 +47,22 @@ fn main() -> std::io::Result<()> {
|
|||
let args: Vec<String> = std::env::args().collect();
|
||||
|
||||
initialize_vuinput_state();
|
||||
VUINPUT_COUNTER.set(AtomicU64::new(3)).expect("failed to initialize the counter that provides the values of the CUSE file handles"); // 3, because 1 and 2 are usually STDOUT and STDERR
|
||||
JOB_DISPATCHER.set(Mutex::new(Dispatcher::new())).expect("failed to initialize the job dispatcher");
|
||||
SELF_NAMESPACES.set(get_namespace(Pid::SelfPid)).expect("failed to retrieve the namespaces of the vuinputd process");
|
||||
VUINPUT_COUNTER.set(AtomicU64::new(3)).expect(
|
||||
"failed to initialize the counter that provides the values of the CUSE file handles",
|
||||
); // 3, because 1 and 2 are usually STDOUT and STDERR
|
||||
JOB_DISPATCHER
|
||||
.set(Mutex::new(Dispatcher::new()))
|
||||
.expect("failed to initialize the job dispatcher");
|
||||
SELF_NAMESPACES
|
||||
.set(get_namespace(Pid::SelfPid))
|
||||
.expect("failed to retrieve the namespaces of the vuinputd process");
|
||||
initialize_dedup_last_error();
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(MonitorBackgroundLoop::new()));
|
||||
JOB_DISPATCHER
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dispatch(Box::new(MonitorBackgroundLoop::new()));
|
||||
|
||||
info!("Starting vuinputd");
|
||||
|
||||
|
|
@ -62,7 +72,7 @@ fn main() -> std::io::Result<()> {
|
|||
|
||||
let mut dev_info_argv: Vec<*const c_char> = vec![
|
||||
vuinput_devicename.as_ptr(), // pointer to the C string
|
||||
std::ptr::null(), // null terminator, often required by C APIs
|
||||
std::ptr::null(), // null terminator, often required by C APIs
|
||||
];
|
||||
|
||||
// setting dev_major and dev_minor to 0 leads to a dynamic assignment of the major and minor, very likely beginning with 234:0
|
||||
|
|
@ -104,7 +114,12 @@ fn main() -> std::io::Result<()> {
|
|||
}
|
||||
info!("Stopping vuinputd");
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().close();
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().wait_until_finished();
|
||||
JOB_DISPATCHER
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.wait_until_finished();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@ use nix::{
|
|||
unistd::{fork, ForkResult},
|
||||
};
|
||||
use std::{
|
||||
fs::{self, File}, io::Read, os::fd::{AsFd, FromRawFd, OwnedFd, RawFd}, path::{Path}, process, sync::OnceLock
|
||||
fs::{self, File},
|
||||
io::Read,
|
||||
os::fd::{AsFd, FromRawFd, OwnedFd, RawFd},
|
||||
path::Path,
|
||||
process,
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use std::io;
|
||||
|
||||
pub static SELF_NAMESPACES: OnceLock<Namespaces>= OnceLock::new();
|
||||
|
||||
|
||||
pub static SELF_NAMESPACES: OnceLock<Namespaces> = OnceLock::new();
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Pid {
|
||||
|
|
@ -28,7 +31,7 @@ impl Pid {
|
|||
pub fn path(&self) -> String {
|
||||
match self {
|
||||
Pid::SelfPid => "/proc/self".to_string(),
|
||||
Pid::Pid(pid_no) => format!("/proc/{}",pid_no)
|
||||
Pid::Pid(pid_no) => format!("/proc/{}", pid_no),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,12 +48,10 @@ pub struct Namespaces {
|
|||
pub cgroup: Option<u64>,
|
||||
pub time: Option<u64>,
|
||||
pub time_for_children: Option<u64>,
|
||||
|
||||
}
|
||||
|
||||
/// Returns true if the process with `pid` is a 32-bit (compat) process. None, if unsure.
|
||||
pub fn is_compat_process(pid: Pid) -> Option<bool> {
|
||||
|
||||
match pid {
|
||||
Pid::Pid(pid) => {
|
||||
const EI_CLASS: usize = 4;
|
||||
|
|
@ -75,8 +76,7 @@ pub fn is_compat_process(pid: Pid) -> Option<bool> {
|
|||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
Pid::SelfPid =>
|
||||
unreachable!()
|
||||
Pid::SelfPid => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,12 +112,20 @@ impl std::fmt::Display for RequestingProcess {
|
|||
writeln!(f, " uts: {:?}", self.namespaces.uts)?;
|
||||
writeln!(f, " ipc: {:?}", self.namespaces.ipc)?;
|
||||
writeln!(f, " pid: {:?}", self.namespaces.pid)?;
|
||||
writeln!(f, " pid_for_children: {:?}", self.namespaces.pid_for_children)?;
|
||||
writeln!(
|
||||
f,
|
||||
" pid_for_children: {:?}",
|
||||
self.namespaces.pid_for_children
|
||||
)?;
|
||||
writeln!(f, " user: {:?}", self.namespaces.user)?;
|
||||
writeln!(f, " mnt: {:?}", self.namespaces.mnt)?;
|
||||
writeln!(f, " cgroup: {:?}", self.namespaces.cgroup)?;
|
||||
writeln!(f, " time: {:?}", self.namespaces.time)?;
|
||||
writeln!(f, " time_for_children: {:?}", self.namespaces.time_for_children)?;
|
||||
writeln!(
|
||||
f,
|
||||
" time_for_children: {:?}",
|
||||
self.namespaces.time_for_children
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -168,45 +176,42 @@ pub fn get_namespace(pid: Pid) -> Namespaces {
|
|||
}
|
||||
|
||||
fn get_ppid(pid: Pid) -> Option<Pid> {
|
||||
let content =
|
||||
match pid {
|
||||
Pid::SelfPid => fs::read_to_string(format!("/proc/self/status")).ok()?,
|
||||
Pid::Pid(pid) => fs::read_to_string(format!("/proc/{}/status", pid)).ok()?
|
||||
};
|
||||
let ppid=content
|
||||
let content = match pid {
|
||||
Pid::SelfPid => fs::read_to_string(format!("/proc/self/status")).ok()?,
|
||||
Pid::Pid(pid) => fs::read_to_string(format!("/proc/{}/status", pid)).ok()?,
|
||||
};
|
||||
let ppid = content
|
||||
.lines()
|
||||
.find(|line| line.starts_with("PPid:"))
|
||||
.and_then(|line| line.split_whitespace().nth(1))
|
||||
.and_then(|ppid| ppid.parse::<i32>().ok());
|
||||
match ppid {
|
||||
None => None,
|
||||
Some(ppid)=> Some(Pid::Pid(ppid))
|
||||
Some(ppid) => Some(Pid::Pid(ppid)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn get_requesting_process(pid: Pid) -> RequestingProcess {
|
||||
|
||||
match pid {
|
||||
Pid::Pid(_) =>
|
||||
{
|
||||
Pid::Pid(_) => {
|
||||
let is_compat = match is_compat_process(pid) {
|
||||
Some(false) => {
|
||||
debug!("identified process {} as 64 bit process",pid.path());
|
||||
debug!("identified process {} as 64 bit process", pid.path());
|
||||
false
|
||||
},
|
||||
}
|
||||
Some(true) => {
|
||||
debug!("identified process {} as 32 bit process",pid.path());
|
||||
debug!("identified process {} as 32 bit process", pid.path());
|
||||
true
|
||||
},
|
||||
}
|
||||
None => {
|
||||
debug!("could not identify bitness of process {}. Assume 64 bit process",pid.path());
|
||||
debug!(
|
||||
"could not identify bitness of process {}. Assume 64 bit process",
|
||||
pid.path()
|
||||
);
|
||||
false
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// go up the parent hierarchy until we find a parent with different namespaces
|
||||
let mut ppid = pid;
|
||||
let nsinodes = get_namespace(pid);
|
||||
|
|
@ -214,19 +219,21 @@ pub fn get_requesting_process(pid: Pid) -> RequestingProcess {
|
|||
let candidate_ppid = get_ppid(ppid);
|
||||
match candidate_ppid {
|
||||
None => break,
|
||||
Some(candidate_ppid) =>
|
||||
{
|
||||
Some(candidate_ppid) => {
|
||||
let ppid_nsinodes = get_namespace(candidate_ppid);
|
||||
if nsinodes.equal_mnt_and_net(&ppid_nsinodes) {
|
||||
ppid=candidate_ppid;
|
||||
ppid = candidate_ppid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
debug!("identified process {} as root of process id {}",ppid.path(),pid.path());
|
||||
debug!(
|
||||
"identified process {} as root of process id {}",
|
||||
ppid.path(),
|
||||
pid.path()
|
||||
);
|
||||
|
||||
let nspath = format!("{}/ns", pid.path());
|
||||
let nsroot = format!("{}/ns", ppid.path());
|
||||
|
|
@ -234,19 +241,21 @@ pub fn get_requesting_process(pid: Pid) -> RequestingProcess {
|
|||
nspath: nspath,
|
||||
nsroot: nsroot,
|
||||
namespaces: nsinodes,
|
||||
is_compat: is_compat
|
||||
is_compat: is_compat,
|
||||
}
|
||||
},
|
||||
Pid::SelfPid =>
|
||||
{
|
||||
}
|
||||
Pid::SelfPid => {
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: &RequestingProcess, func: Box<dyn Fn()>) -> nix::Result<nix::unistd::Pid> {
|
||||
pub fn run_in_net_and_mnt_namespace(
|
||||
ns: &RequestingProcess,
|
||||
func: Box<dyn Fn()>,
|
||||
) -> nix::Result<nix::unistd::Pid> {
|
||||
//Note: The child process is created with a single thread—the one that called fork().
|
||||
|
||||
match unsafe { fork()? } {
|
||||
|
|
@ -255,7 +264,7 @@ pub fn run_in_net_and_mnt_namespace(ns: &RequestingProcess, func: Box<dyn Fn()>)
|
|||
Ok(child)
|
||||
}
|
||||
ForkResult::Child => {
|
||||
debug!("Start new process {}",process::id());
|
||||
debug!("Start new process {}", process::id());
|
||||
// enter namespace
|
||||
let path: &Path = Path::new(ns.nsroot.as_str());
|
||||
debug!("Entering namespaces of process {}. We assume this is the root process of the container.",ns.nsroot.clone());
|
||||
|
|
@ -267,7 +276,7 @@ pub fn run_in_net_and_mnt_namespace(ns: &RequestingProcess, func: Box<dyn Fn()>)
|
|||
let mnt = File::open(ns.nsroot.clone() + "/mnt").expect("mnt not found");
|
||||
setns(net.as_fd(), CloneFlags::CLONE_NEWNET).expect("couldn't enter net");
|
||||
setns(mnt.as_fd(), CloneFlags::CLONE_NEWNS).expect("couldn't enter mnt");
|
||||
|
||||
|
||||
// execute your function
|
||||
func();
|
||||
std::process::exit(0);
|
||||
|
|
@ -276,15 +285,13 @@ pub fn run_in_net_and_mnt_namespace(ns: &RequestingProcess, func: Box<dyn Fn()>)
|
|||
}
|
||||
|
||||
pub async fn await_process(pid: Pid) -> io::Result<i32> {
|
||||
|
||||
match pid {
|
||||
Pid::Pid(pid) =>
|
||||
{
|
||||
unsafe {
|
||||
Pid::Pid(pid) => {
|
||||
unsafe {
|
||||
// Use pidfd_open() (libc) to get a real FD
|
||||
let pidfd = libc::syscall(libc::SYS_pidfd_open, pid, 0);
|
||||
if pidfd == -1 {
|
||||
return Err(io::Error::last_os_error())
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
let owned_fd = OwnedFd::from_raw_fd(pidfd as RawFd);
|
||||
|
||||
|
|
@ -294,35 +301,28 @@ pub async fn await_process(pid: Pid) -> io::Result<i32> {
|
|||
|
||||
// Retrieve the exit code using waitid()
|
||||
let mut si: libc::siginfo_t = std::mem::zeroed();
|
||||
let r = libc::waitid(
|
||||
libc::P_PID,
|
||||
pid as u32,
|
||||
&mut si,
|
||||
libc::WEXITED,
|
||||
);
|
||||
let r = libc::waitid(libc::P_PID, pid as u32, &mut si, libc::WEXITED);
|
||||
if r != 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(si.si_status())
|
||||
}
|
||||
},
|
||||
Pid::SelfPid =>
|
||||
{
|
||||
}
|
||||
Pid::SelfPid => {
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn check_permissions() -> Result<(), std::io::Error> {
|
||||
let path = Path::new("/proc/self/status");
|
||||
debug!("Capabilities of vuinputd process:");
|
||||
fs::read_to_string(path).
|
||||
and_then(|status_file| {
|
||||
status_file.lines()
|
||||
.filter(|line| line.starts_with("Cap"))
|
||||
.for_each(move |x| debug!("{}",x));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
fs::read_to_string(path).and_then(|status_file| {
|
||||
status_file
|
||||
.lines()
|
||||
.filter(|line| line.starts_with("Cap"))
|
||||
.for_each(move |x| debug!("{}", x));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue