blob: a2f382c94114399cf99f5767c229567d2a89ab0c [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Command Line Interface for Netsim
mod args;
mod browser;
mod pcap_handler;
mod requests;
mod response;
use std::env;
use std::fs::File;
use std::path::PathBuf;
use args::{BinaryProtobuf, GetCapture, NetsimArgs};
use clap::Parser;
use cxx::UniquePtr;
use frontend_client_cxx::ffi::{new_frontend_client, ClientResult, FrontendClient, GrpcMethod};
use frontend_client_cxx::ClientResponseReader;
use pcap_handler::CaptureHandler;
// helper function to process streaming Grpc request
fn perform_streaming_request(
client: &cxx::UniquePtr<FrontendClient>,
cmd: &GetCapture,
req: &BinaryProtobuf,
filename: &str,
) -> UniquePtr<ClientResult> {
let dir = if cmd.location.is_some() {
PathBuf::from(cmd.location.to_owned().unwrap())
} else {
env::current_dir().unwrap()
};
// Find next available file name
let mut output_file = dir.join(filename.to_string() + ".pcap");
let mut idx = 0;
while output_file.exists() {
idx += 1;
output_file = dir.join(format!("{}_{}.pcap", filename, idx));
}
client.get_capture(
req,
&ClientResponseReader {
handler: Box::new(CaptureHandler {
file: File::create(&output_file).unwrap_or_else(|_| {
panic!("Failed to create file: {}", &output_file.display())
}),
path: output_file,
}),
},
)
}
/// helper function to send the Grpc request(s) and handle the response(s) per the given command
fn perform_command(
command: &mut args::Command,
client: cxx::UniquePtr<FrontendClient>,
grpc_method: GrpcMethod,
verbose: bool,
) -> Result<(), String> {
// Get command's gRPC request(s)
let requests = match command {
args::Command::Pcap(args::Pcap::Patch(_) | args::Pcap::Get(_)) => {
command.get_requests(&client)
}
_ => vec![command.get_request_bytes()],
};
// Process each request
for (i, req) in requests.iter().enumerate() {
let result = match command {
// Continuous option sends the gRPC call every second
args::Command::Devices(ref cmd) if cmd.continuous => loop {
process_result(command, client.send_grpc(&grpc_method, req), verbose)?;
std::thread::sleep(std::time::Duration::from_secs(1));
},
// Get Pcap use streaming gRPC reader request
args::Command::Pcap(args::Pcap::Get(ref cmd)) => {
perform_streaming_request(&client, cmd, req, &cmd.filenames[i])
}
// All other commands use a single gRPC call
_ => client.send_grpc(&grpc_method, req),
};
process_result(command, result, verbose)?;
}
Ok(())
}
/// Check and handle the gRPC call result
fn process_result(
command: &args::Command,
result: UniquePtr<ClientResult>,
verbose: bool,
) -> Result<(), String> {
if result.is_ok() {
command.print_response(result.byte_vec().as_slice(), verbose);
} else {
return Err(format!("Grpc call error: {}", result.err()));
}
Ok(())
}
#[no_mangle]
/// main Rust netsim CLI function to be called by C wrapper netsim.cc
pub extern "C" fn rust_main() {
let mut args = NetsimArgs::parse();
if matches!(args.command, args::Command::Gui) {
browser::open("http://localhost:7681/");
return;
}
let grpc_method = args.command.grpc_method();
let client = new_frontend_client();
if client.is_null() {
eprintln!("Unable to create frontend client. Please ensure netsimd is running.");
return;
}
if let Err(e) = perform_command(&mut args.command, client, grpc_method, args.verbose) {
eprintln!("{e}");
}
}