mirror of
https://github.com/joleuger/vuinputd.git
synced 2026-07-18 00:45:07 +00:00
Only write to uinput when we have a complete message
This commit is contained in:
parent
b645f73dc0
commit
e66347135d
2 changed files with 85 additions and 53 deletions
|
|
@ -96,7 +96,7 @@ enum VuError {
|
|||
static VUINPUT_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
|
||||
static VUINPUT_STATE: OnceLock<RwLock<HashMap<VuFileHandle, Arc<Mutex<VuInputState>>>>> = OnceLock::new();
|
||||
static JOB_DISPATCHER: OnceLock<Mutex<Dispatcher>>= OnceLock::new();
|
||||
static SELF_REQUESTING_PROCESSES: OnceLock<RequestingProcess>= OnceLock::new();
|
||||
static VUINPUTD_NAMESPACES: OnceLock<Namespaces>= OnceLock::new();
|
||||
|
||||
// For log limiting. Idea: Move to log_limit crate
|
||||
static DEDUP_LAST_ERROR: OnceLock<Mutex<Option<(u64,VuError)>>> = OnceLock::new();
|
||||
|
|
@ -193,8 +193,8 @@ unsafe extern "C" fn vuinput_open(
|
|||
let fh = get_fresh_filehandle();
|
||||
let ctx = fuse_lowlevel::fuse_req_ctx(_req);
|
||||
debug!("fh {}: opened by process id {} (host view)", fh, (*ctx).pid);
|
||||
let namespaces = get_namespaces(Pid::Pid((*ctx).pid));
|
||||
debug!("fh {}: namespaces {}", fh, namespaces);
|
||||
let requesting_process = get_requesting_process(Pid::Pid((*ctx).pid));
|
||||
debug!("fh {}: namespaces {}", fh, requesting_process);
|
||||
// namespaces net:4026531840, uts:4026531838, ipc:4026531839, pid:4026531836, pid_for_children:4026531836, user:4026531837, mnt:4026531841, cgroup:4026531835, time:4026531834, time_for_children:4026531834
|
||||
(*_fi).fh = fh;
|
||||
// Open the path in read-only mode, returns `io::Result<File>`
|
||||
|
|
@ -210,7 +210,7 @@ unsafe extern "C" fn vuinput_open(
|
|||
&VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap()),
|
||||
VuInputState {
|
||||
file: v,
|
||||
requesting_process: namespaces,
|
||||
requesting_process,
|
||||
input_device: None
|
||||
},
|
||||
)
|
||||
|
|
@ -238,6 +238,7 @@ 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();
|
||||
|
|
@ -281,12 +282,20 @@ unsafe extern "C" fn vuinput_write(
|
|||
return;
|
||||
}
|
||||
|
||||
let result = vuinput_state.file.write_all(slice);
|
||||
let chunksize = std::mem::size_of::<libc::input_event>();
|
||||
|
||||
let mut bytes = 0;
|
||||
let mut result = Result::Ok(());
|
||||
|
||||
while bytes + chunksize <= _size && result.is_ok() {
|
||||
result = vuinput_state.file.write_all(&slice[bytes..bytes + chunksize]);
|
||||
bytes += chunksize;
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
trace!("wrote {} bytes", _size);
|
||||
fuse_lowlevel::fuse_reply_write(_req, _size);
|
||||
debug!("wrote {} bytes", bytes);
|
||||
fuse_lowlevel::fuse_reply_write(_req, bytes);
|
||||
}
|
||||
Err(e) => {
|
||||
let mut last_error = DEDUP_LAST_ERROR.get().unwrap().lock().unwrap();
|
||||
|
|
@ -317,7 +326,7 @@ 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_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) {
|
||||
if input_device.is_some() && ! VUINPUTD_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);
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job));
|
||||
|
|
@ -485,7 +494,7 @@ 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_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) {
|
||||
if ! VUINPUTD_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);
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(inject_job));
|
||||
}
|
||||
|
|
@ -500,7 +509,7 @@ 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_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) {
|
||||
if input_device.is_some() && ! VUINPUTD_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);
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job));
|
||||
|
|
@ -724,7 +733,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_REQUESTING_PROCESSES.set(get_namespaces(Pid::SelfPid)).unwrap();
|
||||
VUINPUTD_NAMESPACES.set(get_namespace(Pid::SelfPid)).unwrap();
|
||||
DEDUP_LAST_ERROR.set(Mutex::new(None)).unwrap();
|
||||
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(MonitorBackgroundLoop::new()));
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Pid {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||
struct Namespaces {
|
||||
pub struct Namespaces {
|
||||
pub net: Option<u64>,
|
||||
pub uts: Option<u64>,
|
||||
pub ipc: Option<u64>,
|
||||
|
|
@ -46,27 +46,34 @@ struct Namespaces {
|
|||
}
|
||||
|
||||
/// Returns true if the process with `pid` is a 32-bit (compat) process. None, if unsure.
|
||||
pub fn is_compat_process(pid: i32) -> Option<bool> {
|
||||
const EI_CLASS: usize = 4;
|
||||
const ELFCLASS32: u8 = 1;
|
||||
const ELFCLASS64: u8 = 2;
|
||||
pub fn is_compat_process(pid: Pid) -> Option<bool> {
|
||||
|
||||
let exe_path = format!("/proc/{}/exe", pid);
|
||||
let mut buf = [0u8; 5];
|
||||
match pid {
|
||||
Pid::Pid(pid) => {
|
||||
const EI_CLASS: usize = 4;
|
||||
const ELFCLASS32: u8 = 1;
|
||||
const ELFCLASS64: u8 = 2;
|
||||
|
||||
match File::open(&exe_path).and_then(|mut f| f.read_exact(&mut buf)) {
|
||||
Ok(()) => {
|
||||
// ELF magic check
|
||||
if &buf[0..4] != b"\x7FELF" {
|
||||
return None;
|
||||
}
|
||||
match buf[EI_CLASS] {
|
||||
ELFCLASS32 => Some(true),
|
||||
ELFCLASS64 => Some(false),
|
||||
_ => None,
|
||||
let exe_path = format!("/proc/{}/exe", pid);
|
||||
let mut buf = [0u8; 5];
|
||||
|
||||
match File::open(&exe_path).and_then(|mut f| f.read_exact(&mut buf)) {
|
||||
Ok(()) => {
|
||||
// ELF magic check
|
||||
if &buf[0..4] != b"\x7FELF" {
|
||||
return None;
|
||||
}
|
||||
match buf[EI_CLASS] {
|
||||
ELFCLASS32 => Some(true),
|
||||
ELFCLASS64 => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
Pid::SelfPid =>
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +82,8 @@ pub fn is_compat_process(pid: i32) -> Option<bool> {
|
|||
pub struct RequestingProcess {
|
||||
pub nspath: String,
|
||||
pub nsroot: String,
|
||||
nsinodes: Namespaces,
|
||||
pub namespaces: Namespaces,
|
||||
pub is_compat: bool,
|
||||
}
|
||||
|
||||
impl Namespaces {
|
||||
|
|
@ -86,28 +94,32 @@ impl Namespaces {
|
|||
|
||||
impl RequestingProcess {
|
||||
pub fn equal_mnt_and_net(&self, other: &RequestingProcess) -> bool {
|
||||
self.nsinodes.equal_mnt_and_net(&other.nsinodes)
|
||||
self.namespaces.equal_mnt_and_net(&other.namespaces)
|
||||
}
|
||||
|
||||
pub fn equal_mnt_and_net_ns(&self, other: &Namespaces) -> bool {
|
||||
self.namespaces.equal_mnt_and_net(&other)
|
||||
}
|
||||
}
|
||||
|
||||
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)?;
|
||||
writeln!(f, " uts: {:?}", self.nsinodes.uts)?;
|
||||
writeln!(f, " ipc: {:?}", self.nsinodes.ipc)?;
|
||||
writeln!(f, " pid: {:?}", self.nsinodes.pid)?;
|
||||
writeln!(f, " pid_for_children: {:?}", self.nsinodes.pid_for_children)?;
|
||||
writeln!(f, " user: {:?}", self.nsinodes.user)?;
|
||||
writeln!(f, " mnt: {:?}", self.nsinodes.mnt)?;
|
||||
writeln!(f, " cgroup: {:?}", self.nsinodes.cgroup)?;
|
||||
writeln!(f, " time: {:?}", self.nsinodes.time)?;
|
||||
writeln!(f, " time_for_children: {:?}", self.nsinodes.time_for_children)?;
|
||||
writeln!(f, " net: {:?}", self.namespaces.net)?;
|
||||
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, " 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)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_namespace_inodes(pid: Pid) -> Namespaces {
|
||||
pub fn get_namespace(pid: Pid) -> Namespaces {
|
||||
let pid: String = match pid {
|
||||
Pid::Pid(pid) => pid.to_string(),
|
||||
Pid::SelfPid => "self".to_string(),
|
||||
|
|
@ -171,21 +183,37 @@ fn get_ppid(pid: Pid) -> Option<Pid> {
|
|||
|
||||
|
||||
|
||||
pub fn get_namespaces(pid: Pid) -> RequestingProcess {
|
||||
pub fn get_requesting_process(pid: Pid) -> RequestingProcess {
|
||||
|
||||
match pid {
|
||||
Pid::Pid(_) =>
|
||||
{
|
||||
let is_compat = match is_compat_process(pid) {
|
||||
Some(true) => {
|
||||
debug!("identified process {} as 64 bit process",pid.path());
|
||||
false
|
||||
},
|
||||
Some(false) => {
|
||||
debug!("identified process {} as 32 bit process",pid.path());
|
||||
true
|
||||
},
|
||||
None => {
|
||||
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_inodes(pid);
|
||||
let nsinodes = get_namespace(pid);
|
||||
loop {
|
||||
let candidate_ppid = get_ppid(ppid);
|
||||
match candidate_ppid {
|
||||
None => break,
|
||||
Some(candidate_ppid) =>
|
||||
{
|
||||
let ppid_nsinodes = get_namespace_inodes(candidate_ppid);
|
||||
let ppid_nsinodes = get_namespace(candidate_ppid);
|
||||
if nsinodes.equal_mnt_and_net(&ppid_nsinodes) {
|
||||
ppid=candidate_ppid;
|
||||
} else {
|
||||
|
|
@ -202,18 +230,13 @@ pub fn get_namespaces(pid: Pid) -> RequestingProcess {
|
|||
RequestingProcess {
|
||||
nspath: nspath,
|
||||
nsroot: nsroot,
|
||||
nsinodes: nsinodes,
|
||||
namespaces: nsinodes,
|
||||
is_compat: is_compat
|
||||
}
|
||||
},
|
||||
Pid::SelfPid =>
|
||||
{
|
||||
let nsinodes = get_namespace_inodes(pid);
|
||||
let nspath = format!("{}/ns", pid.path());
|
||||
RequestingProcess {
|
||||
nspath: nspath.clone(),
|
||||
nsroot: nspath,
|
||||
nsinodes: nsinodes,
|
||||
}
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue