use std::env;
use std::process::exit;
use netmap_rs::prelude::*;
fn usage(prog: &str) -> ! {
eprintln!(
"usage: {prog} -a vale0:port [-a vale0:port]... add ports\n\
\x20 {prog} -r vale0:port [-r vale0:port]... remove ports\n\
\x20 {prog} -A vale0:port -i vale0:ifname attach port {{\n\
\n\
Manage VALE virtual switches (no physical NIC is touched).\n\
Ports are created on first open and destroyed when the last\n\
netmap file descriptor closes. 'add' then 'remove' order is\n\
preserved: flags are processed left to right."
);
exit(1);
}
fn main() -> Result<(), Error> {
let args: Vec<String> = env::args().skip(1).collect();
if args.is_empty() {
usage(&env::args().next().unwrap_or_else(|| "vale-ctl".into()));
}
let mut i = 0;
let mut found_action = false;
while i < args.len() {
let arg = &args[i];
match arg.as_str() {
"-a" | "-add" => {
found_action = true;
let name = next(&args, &mut i, "-a");
add_port(&name)?;
}
"-r" | "-del" | "-d" | "-rm" => {
found_action = true;
let name = next(&args, &mut i, "-r");
remove_port(&name)?;
}
"-A" => {
found_action = true;
let name = next(&args, &mut i, "-A");
add_port(&name)?;
}
"-i" | "-ifname" => {
let _name = next(&args, &mut i, "-i");
}
"-h" | "--help" => usage("vale-ctl"),
other => {
eprintln!("warning: ignoring unknown argument '{other}'");
}
}
i += 1;
}
if !found_action {
usage("vale-ctl");
}
Ok(())
}
fn next(args: &[String], i: &mut usize, flag: &str) -> String {
let v = args.get(*i + 1).cloned().unwrap_or_else(|| {
eprintln!("missing argument after '{flag}'");
exit(1);
});
*i += 1;
v
}
fn add_port(name: &str) -> Result<(), Error> {
let qualified = qualify(name);
let nm = NetmapBuilder::new(&qualified).build()?;
println!(
"added {qualified}: {} TX rings, {} RX rings",
nm.num_tx_rings(),
nm.num_rx_rings()
);
Ok(())
}
fn remove_port(name: &str) -> Result<(), Error> {
let qualified = qualify(name);
let nm = NetmapBuilder::new(&qualified).build()?;
println!(
"removed {qualified}: {} TX rings, {} RX rings unregistered on close",
nm.num_tx_rings(),
nm.num_rx_rings()
);
drop(nm);
Ok(())
}
fn qualify(name: &str) -> String {
if name.starts_with("netmap:") {
name.to_string()
} else {
format!("netmap:{name}")
}
}