use pelagos::sandbox::{create_sandbox, list_sandboxes, remove_sandbox, SandboxState};
#[derive(Debug, clap::Subcommand)]
pub enum SandboxCmd {
Create {
#[clap(long)]
name: Option<String>,
},
Ls {
#[clap(long)]
json: bool,
},
Rm {
id: String,
},
#[clap(hide = true, name = "__pause__")]
Pause {
ns_name: String,
},
}
pub fn cmd_sandbox(cmd: SandboxCmd) -> Result<(), Box<dyn std::error::Error>> {
match cmd {
SandboxCmd::Create { name } => cmd_sandbox_create(name.as_deref()),
SandboxCmd::Ls { json } => cmd_sandbox_ls(json),
SandboxCmd::Rm { id } => cmd_sandbox_rm(&id),
SandboxCmd::Pause { ns_name } => cmd_sandbox_pause(&ns_name),
}
}
fn cmd_sandbox_create(name: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
let state = create_sandbox(name)?;
println!("{}", state.id);
Ok(())
}
fn cmd_sandbox_ls(json: bool) -> Result<(), Box<dyn std::error::Error>> {
let sandboxes = list_sandboxes();
if json {
let alive: Vec<&SandboxState> = sandboxes.iter().filter(|s| s.is_alive()).collect();
println!(
"{}",
serde_json::to_string_pretty(&alive)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?
);
return Ok(());
}
if sandboxes.is_empty() {
return Ok(());
}
println!(
"{:<18} {:<20} {:<8} {:<16} NS_NAME",
"ID", "NAME", "STATUS", "IP"
);
for s in &sandboxes {
let status = if s.is_alive() { "running" } else { "dead" };
let name = s.name.as_deref().unwrap_or("-");
println!(
"{:<18} {:<20} {:<8} {:<16} {}",
s.id, name, status, s.container_ip, s.ns_name
);
}
Ok(())
}
fn cmd_sandbox_rm(id: &str) -> Result<(), Box<dyn std::error::Error>> {
remove_sandbox(id)?;
Ok(())
}
fn cmd_sandbox_pause(ns_name: &str) -> Result<(), Box<dyn std::error::Error>> {
let netns_path = format!("/run/netns/{}", ns_name);
let netns_c = std::ffi::CString::new(netns_path.as_bytes())
.map_err(|e| format!("invalid netns path: {}", e))?;
let fd = unsafe { libc::open(netns_c.as_ptr(), libc::O_RDONLY | libc::O_CLOEXEC) };
if fd < 0 {
return Err(format!(
"open netns '{}': {}",
netns_path,
std::io::Error::last_os_error()
)
.into());
}
let rc = unsafe { libc::setns(fd, libc::CLONE_NEWNET) };
unsafe { libc::close(fd) };
if rc != 0 {
return Err(format!(
"setns netns '{}': {}",
netns_path,
std::io::Error::last_os_error()
)
.into());
}
let rc = unsafe { libc::unshare(libc::CLONE_NEWIPC | libc::CLONE_NEWUTS) };
if rc != 0 {
return Err(format!("unshare IPC/UTS: {}", std::io::Error::last_os_error()).into());
}
unsafe { libc::pause() };
Ok(())
}