use anyhow::{Context, Result, anyhow};
use libc::{self, CLONE_NEWNS, execvp, pid_t};
use std::ffi::CString;
use std::fs::File;
use std::io::Error as IoError;
use std::os::raw::c_char;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::ptr;
pub fn run_program_in_netns_with_path_redirect(
program: &str,
args: &[&str],
netns_name: &str,
original_path: &str,
new_path: &str,
) -> Result<u32> {
let netns_path = format!("/var/run/netns/{netns_name}");
if !Path::new(&netns_path).exists() {
return Err(anyhow!("Network namespace '{netns_name}' does not exist"));
}
let netns_fd = File::open(&netns_path)
.with_context(|| format!("Failed to open network namespace file: {netns_path}"))?;
let pid: pid_t = unsafe { libc::fork() };
match pid.cmp(&0) {
std::cmp::Ordering::Less => {
Err(anyhow!("Fork failed: {}", IoError::last_os_error()))
}
std::cmp::Ordering::Equal => {
let result = unsafe { libc::setns(netns_fd.as_raw_fd(), libc::CLONE_NEWNET) };
if result == -1 {
eprintln!(
"Failed to set network namespace: {}",
IoError::last_os_error()
);
unsafe { libc::_exit(1) };
}
let res = unsafe { libc::unshare(CLONE_NEWNS) };
if res == -1 {
eprintln!("Unshare failed: {}", IoError::last_os_error());
unsafe { libc::_exit(1) };
}
let root = CString::new("/")?;
let result = unsafe {
libc::mount(
ptr::null(),
root.as_ptr(),
ptr::null(),
libc::MS_PRIVATE | libc::MS_REC,
ptr::null(),
)
};
if result == -1 {
eprintln!("Making root private failed: {}", IoError::last_os_error());
unsafe { libc::_exit(1) };
}
let source = CString::new(new_path)?;
let target = CString::new(original_path)?;
let result = unsafe {
libc::mount(
source.as_ptr(),
target.as_ptr(),
ptr::null(),
libc::MS_BIND,
ptr::null(),
)
};
if result == -1 {
eprintln!("Bind mount failed: {}", IoError::last_os_error());
unsafe { libc::_exit(1) };
}
let program_cstr = CString::new(program)?;
let mut c_args: Vec<CString> = Vec::with_capacity(args.len() + 1);
c_args.push(program_cstr.clone());
for arg in args {
c_args.push(CString::new(*arg)?);
}
let mut arg_ptrs: Vec<*const c_char> = c_args.iter().map(|arg| arg.as_ptr()).collect();
arg_ptrs.push(std::ptr::null());
unsafe {
execvp(program_cstr.as_ptr(), arg_ptrs.as_ptr());
eprintln!("execvp failed: {}", IoError::last_os_error());
libc::_exit(1);
}
}
std::cmp::Ordering::Greater => {
Ok(pid as u32)
}
}
}