use crate::error::{NucleusError, Result};
use std::os::unix::process::CommandExt;
use std::process::Command;
pub fn in_netns<F, R>(pid: u32, f: F) -> Result<R>
where
F: FnOnce() -> Result<R> + Send,
R: Send,
{
let ns_path = format!("/proc/{}/ns/net", pid);
let ns_file = std::fs::File::open(&ns_path).map_err(|e| {
NucleusError::NetworkError(format!("failed to open netns for PID {}: {}", pid, e))
})?;
std::thread::scope(|scope| {
scope
.spawn(|| {
nix::sched::setns(&ns_file, nix::sched::CloneFlags::CLONE_NEWNET).map_err(|e| {
NucleusError::NetworkError(format!("setns(CLONE_NEWNET): {}", e))
})?;
f()
})
.join()
.map_err(|_| NucleusError::NetworkError("netns worker thread panicked".to_string()))?
})
}
pub fn exec_in_netns(pid: u32, program: &str, args: &[&str]) -> Result<()> {
let ns_path = format!("/proc/{}/ns/net", pid);
let ns_file = std::fs::File::open(&ns_path).map_err(|e| {
NucleusError::NetworkError(format!("failed to open netns for PID {}: {}", pid, e))
})?;
let output = unsafe {
Command::new(program)
.args(args)
.pre_exec(move || {
nix::sched::setns(&ns_file, nix::sched::CloneFlags::CLONE_NEWNET)
.map_err(std::io::Error::other)
})
.output()
}
.map_err(|e| {
NucleusError::NetworkError(format!("exec {} in netns({}): {}", program, pid, e))
})?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(NucleusError::NetworkError(format!(
"{} {:?} failed in netns({}): {}",
program,
args,
pid,
stderr.trim()
)));
}
Ok(())
}