Try to get root process id of the process that tries to create a input

device. This should be more stable, because the process itself more
likely vanishes that its parent process.
This commit is contained in:
Johannes Leupolz 2025-10-28 21:16:03 +00:00
parent 53bc1ff153
commit f04550d9d5
2 changed files with 122 additions and 26 deletions

View file

@ -187,7 +187,7 @@ 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(Some((*ctx).pid));
let namespaces = get_namespaces(Pid::Pid((*ctx).pid));
debug!("fh {}: namespaces {}", fh, namespaces);
// 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;
@ -645,7 +645,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(None)).unwrap();
SELF_NAMESPACES.set(get_namespaces(Pid::SelfPid)).unwrap();
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(MonitorBackgroundLoop::new()));
info!("Starting vuinputd");

View file

@ -5,16 +5,34 @@
use log::debug;
use nix::{
sched::{setns, CloneFlags},
unistd::{fork, ForkResult, Pid},
unistd::{fork, ForkResult},
};
use std::{
fs::{self, File},
os::fd::AsFd, path::{self, Path},
};
use std::io::{self, BufRead};
use std::path::PathBuf;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Pid {
SelfPid,
Pid(i32),
}
impl Pid {
pub fn path(&self) -> String {
match self {
Pid::SelfPid => "/proc/self".to_string(),
Pid::Pid(pid_no) => format!("/proc/{}",pid_no)
}
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct Namespaces {
pub nspath: String,
struct NamespaceInodes {
pub net: Option<u64>,
pub uts: Option<u64>,
pub ipc: Option<u64>,
@ -25,40 +43,53 @@ pub struct Namespaces {
pub cgroup: Option<u64>,
pub time: Option<u64>,
pub time_for_children: Option<u64>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct Namespaces {
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
}
}
impl Namespaces {
pub fn equal_mnt_and_net(&self, other: &Namespaces) -> bool {
self.mnt == other.mnt && self.net == other.net
self.nsinodes.equal_mnt_and_net(&other.nsinodes)
}
}
impl std::fmt::Display for Namespaces {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Namespaces:")?;
writeln!(f, " net: {:?}", self.net)?;
writeln!(f, " uts: {:?}", self.uts)?;
writeln!(f, " ipc: {:?}", self.ipc)?;
writeln!(f, " pid: {:?}", self.pid)?;
writeln!(f, " pid_for_children: {:?}", self.pid_for_children)?;
writeln!(f, " user: {:?}", self.user)?;
writeln!(f, " mnt: {:?}", self.mnt)?;
writeln!(f, " cgroup: {:?}", self.cgroup)?;
writeln!(f, " time: {:?}", self.time)?;
writeln!(f, " time_for_children: {:?}", self.time_for_children)?;
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)?;
Ok(())
}
}
pub fn get_namespaces(pid: Option<i32>) -> Namespaces {
fn get_namespace_inodes(pid: Pid) -> NamespaceInodes {
let pid: String = match pid {
Some(pid) => pid.to_string(),
None => "self".to_string(),
Pid::Pid(pid) => pid.to_string(),
Pid::SelfPid => "self".to_string(),
};
let nspath = format!("/proc/{}/ns", pid);
let mut ns = Namespaces {
nspath: nspath.clone(),
let mut ns = NamespaceInodes {
net: None,
uts: None,
ipc: None,
@ -96,9 +127,74 @@ pub fn get_namespaces(pid: Option<i32>) -> Namespaces {
ns
}
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
.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))
}
}
pub fn get_namespaces(pid: Pid) -> Namespaces {
match pid {
Pid::Pid(_) =>
{
// go up the parent hierarchy until we find a parent with different namespaces
let mut ppid = pid;
let nsinodes = get_namespace_inodes(pid);
loop {
let candidate_ppid = get_ppid(ppid);
match candidate_ppid {
None => break,
Some(candidate_ppid) =>
{
let ppid_nsinodes = get_namespace_inodes(candidate_ppid);
if nsinodes.equal_mnt_and_net(&ppid_nsinodes) {
ppid=candidate_ppid;
} else {
break;
}
}
}
}
let nspath = format!("{}/ns", pid.path());
let nsroot = format!("{}/ns", ppid.path());
Namespaces {
nspath: nspath,
nsroot: nsroot,
nsinodes: nsinodes,
}
},
Pid::SelfPid =>
{
let nsinodes = get_namespace_inodes(pid);
let nspath = format!("{}/ns", pid.path());
Namespaces {
nspath: nspath.clone(),
nsroot: nspath,
nsinodes: nsinodes,
}
},
}
}
/// 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<dyn Fn()>) -> nix::Result<Pid> {
pub fn run_in_net_and_mnt_namespace(ns: Namespaces, 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()? } {
ForkResult::Parent { child } => {
@ -107,13 +203,13 @@ 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());
let path: &Path = Path::new(ns.nsroot.as_str());
if !fs::exists(path).unwrap() {
debug!("the process whose namespaces we want to enter does not exist anymore!");
debug!("the root process of the container 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");
let net = File::open(ns.nsroot.clone() + "/net").expect("net not found");
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