• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Command Line Interface for Netsim
16 
17 mod args;
18 mod browser;
19 mod pcap_handler;
20 mod requests;
21 mod response;
22 
23 use std::env;
24 use std::fs::File;
25 use std::path::PathBuf;
26 
27 use args::{BinaryProtobuf, GetCapture, NetsimArgs};
28 use clap::Parser;
29 use cxx::UniquePtr;
30 use frontend_client_cxx::ffi::{new_frontend_client, ClientResult, FrontendClient, GrpcMethod};
31 use frontend_client_cxx::ClientResponseReader;
32 use pcap_handler::CaptureHandler;
33 
34 // helper function to process streaming Grpc request
perform_streaming_request( client: &cxx::UniquePtr<FrontendClient>, cmd: &GetCapture, req: &BinaryProtobuf, filename: &str, ) -> UniquePtr<ClientResult>35 fn perform_streaming_request(
36     client: &cxx::UniquePtr<FrontendClient>,
37     cmd: &GetCapture,
38     req: &BinaryProtobuf,
39     filename: &str,
40 ) -> UniquePtr<ClientResult> {
41     let dir = if cmd.location.is_some() {
42         PathBuf::from(cmd.location.to_owned().unwrap())
43     } else {
44         env::current_dir().unwrap()
45     };
46     // Find next available file name
47     let mut output_file = dir.join(filename.to_string() + ".pcap");
48     let mut idx = 0;
49     while output_file.exists() {
50         idx += 1;
51         output_file = dir.join(format!("{}_{}.pcap", filename, idx));
52     }
53     client.get_capture(
54         req,
55         &ClientResponseReader {
56             handler: Box::new(CaptureHandler {
57                 file: File::create(&output_file).unwrap_or_else(|_| {
58                     panic!("Failed to create file: {}", &output_file.display())
59                 }),
60                 path: output_file,
61             }),
62         },
63     )
64 }
65 
66 /// helper function to send the Grpc request(s) and handle the response(s) per the given command
perform_command( command: &mut args::Command, client: cxx::UniquePtr<FrontendClient>, grpc_method: GrpcMethod, verbose: bool, ) -> Result<(), String>67 fn perform_command(
68     command: &mut args::Command,
69     client: cxx::UniquePtr<FrontendClient>,
70     grpc_method: GrpcMethod,
71     verbose: bool,
72 ) -> Result<(), String> {
73     // Get command's gRPC request(s)
74     let requests = match command {
75         args::Command::Pcap(args::Pcap::Patch(_) | args::Pcap::Get(_)) => {
76             command.get_requests(&client)
77         }
78         _ => vec![command.get_request_bytes()],
79     };
80 
81     // Process each request
82     for (i, req) in requests.iter().enumerate() {
83         let result = match command {
84             // Continuous option sends the gRPC call every second
85             args::Command::Devices(ref cmd) if cmd.continuous => loop {
86                 process_result(command, client.send_grpc(&grpc_method, req), verbose)?;
87                 std::thread::sleep(std::time::Duration::from_secs(1));
88             },
89             // Get Pcap use streaming gRPC reader request
90             args::Command::Pcap(args::Pcap::Get(ref cmd)) => {
91                 perform_streaming_request(&client, cmd, req, &cmd.filenames[i])
92             }
93             // All other commands use a single gRPC call
94             _ => client.send_grpc(&grpc_method, req),
95         };
96         process_result(command, result, verbose)?;
97     }
98     Ok(())
99 }
100 
101 /// Check and handle the gRPC call result
process_result( command: &args::Command, result: UniquePtr<ClientResult>, verbose: bool, ) -> Result<(), String>102 fn process_result(
103     command: &args::Command,
104     result: UniquePtr<ClientResult>,
105     verbose: bool,
106 ) -> Result<(), String> {
107     if result.is_ok() {
108         command.print_response(result.byte_vec().as_slice(), verbose);
109     } else {
110         return Err(format!("Grpc call error: {}", result.err()));
111     }
112     Ok(())
113 }
114 #[no_mangle]
115 /// main Rust netsim CLI function to be called by C wrapper netsim.cc
rust_main()116 pub extern "C" fn rust_main() {
117     let mut args = NetsimArgs::parse();
118     if matches!(args.command, args::Command::Gui) {
119         browser::open("http://localhost:7681/");
120         return;
121     }
122     let grpc_method = args.command.grpc_method();
123     let client = new_frontend_client();
124     if client.is_null() {
125         eprintln!("Unable to create frontend client. Please ensure netsimd is running.");
126         return;
127     }
128     if let Err(e) = perform_command(&mut args.command, client, grpc_method, args.verbose) {
129         eprintln!("{e}");
130     }
131 }
132