xref: /aosp_15_r20/tools/netsim/rust/daemon/src/rust_main.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1 // Copyright 2023 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 use clap::Parser;
16 use grpcio::{ChannelBuilder, Deadline, EnvBuilder};
17 use log::warn;
18 use log::{error, info};
19 use netsim_common::system::netsimd_temp_dir;
20 use netsim_common::util::os_utils::{
21     get_hci_port, get_instance, get_instance_name, get_server_address, redirect_std_stream,
22     remove_netsim_ini,
23 };
24 use netsim_common::util::zip_artifact::zip_artifacts;
25 use netsim_proto::frontend_grpc::FrontendServiceClient;
26 
27 use crate::captures::capture::spawn_capture_event_subscriber;
28 use crate::config_file;
29 use crate::devices::devices_handler::spawn_shutdown_publisher;
30 use crate::events;
31 use crate::events::{Event, ShutDown};
32 use crate::session::Session;
33 use crate::version::get_version;
34 use crate::wireless;
35 use netsim_common::util::netsim_logger;
36 
37 use crate::args::NetsimdArgs;
38 use crate::ffi::ffi_util;
39 use crate::service::{new_test_beacon, Service, ServiceParams};
40 use netsim_proto::config::{Bluetooth as BluetoothConfig, Capture, Config};
41 use std::env;
42 use std::ffi::{c_char, c_int};
43 use std::sync::mpsc::Receiver;
44 
45 /// Wireless network simulator for android (and other) emulated devices.
46 ///
47 /// # Safety
48 ///
49 /// The file descriptors passed in `NetsimdArgs::fd_startup_str` must remain valid and open for as
50 /// long as the program runs.
51 #[no_mangle]
rust_main(argc: c_int, argv: *const *const c_char)52 pub unsafe extern "C" fn rust_main(argc: c_int, argv: *const *const c_char) {
53     // enable Rust backtrace by setting env RUST_BACKTRACE=full
54     env::set_var("RUST_BACKTRACE", "full");
55     ffi_util::set_up_crash_report();
56     let netsimd_args = get_netsimd_args(argc, argv);
57     netsim_logger::init("netsimd", netsimd_args.verbose);
58     run_netsimd_with_args(netsimd_args);
59 }
60 
61 #[allow(unused)]
get_netsimd_args(argc: c_int, argv: *const *const c_char) -> NetsimdArgs62 fn get_netsimd_args(argc: c_int, argv: *const *const c_char) -> NetsimdArgs {
63     let env_args_or_err = env::var("NETSIM_ARGS");
64 
65     #[cfg(feature = "cuttlefish")]
66     {
67         // TODO: Use NetsimdArgs::parse() after netsimd binary is built with netsimd.rs.
68         // In linux arm64 in aosp-main, it can't access CLI arguments by std::env::args() with netsimd.cc wrapper.
69         let mut argv: Vec<_> = (0..argc)
70             .map(|i|
71                 // SAFETY: argc and argv will remain valid as long as the program runs.
72                 unsafe {
73                     std::ffi::CStr::from_ptr(*argv.add(i as usize)).to_str().unwrap().to_owned()
74                 })
75             .collect();
76         if let Ok(env_args) = env_args_or_err {
77             env_args.split(' ').for_each(|arg| argv.push(arg.to_string()));
78         }
79         NetsimdArgs::parse_from(argv)
80     }
81     #[cfg(not(feature = "cuttlefish"))]
82     {
83         let mut argv = env::args().collect::<Vec<String>>();
84         if let Ok(env_args) = env_args_or_err {
85             env_args.split(' ').for_each(|arg| argv.push(arg.to_string()));
86         }
87         NetsimdArgs::parse_from(argv)
88     }
89 }
90 
run_netsimd_with_args(args: NetsimdArgs)91 fn run_netsimd_with_args(args: NetsimdArgs) {
92     // Log version and terminate netsimd
93     if args.version {
94         println!("Netsimd Version: {}", get_version());
95         return;
96     }
97 
98     // Log where netsim artifacts are located
99     info!("netsim artifacts path: {:?}", netsimd_temp_dir());
100 
101     // Log all args
102     info!("{:#?}", args);
103 
104     if !args.logtostderr {
105         if let Err(err) =
106             redirect_std_stream(&get_instance_name(args.instance, args.connector_instance))
107         {
108             error!("{err:?}");
109         }
110         // Duplicating the previous two logs to be included in netsim_stderr.log
111         info!("netsim artifacts path: {:?}", netsimd_temp_dir());
112         info!("{:#?}", args);
113     }
114 
115     match args.connector_instance {
116         #[cfg(feature = "cuttlefish")]
117         Some(connector_instance) => run_netsimd_connector(args, connector_instance),
118         _ => run_netsimd_primary(args),
119     }
120 }
121 
122 /// Forwards packets to another netsim daemon.
123 #[cfg(feature = "cuttlefish")]
run_netsimd_connector(args: NetsimdArgs, instance: u16)124 fn run_netsimd_connector(args: NetsimdArgs, instance: u16) {
125     if args.fd_startup_str.is_none() {
126         error!("Failed to start netsimd forwarder, missing `-s` arg");
127         return;
128     }
129     let fd_startup = args.fd_startup_str.unwrap();
130 
131     let mut server: Option<String> = None;
132     // Attempts multiple time for fetching netsim.ini
133     for second in [1, 2, 4, 8, 0] {
134         server = get_server_address(instance);
135         if server.is_some() {
136             break;
137         } else {
138             warn!("Unable to find ini file for instance {}, retrying", instance);
139             std::thread::sleep(std::time::Duration::from_secs(second));
140         }
141     }
142     if server.is_none() {
143         error!("Failed to run netsimd connector");
144         return;
145     }
146     let server = server.unwrap();
147     // TODO: Make this function returns Result to use `?` instead of unwrap().
148     info!("Starting in Connector mode to {}", server.as_str());
149     crate::transport::fd::run_fd_connector(&fd_startup, server.as_str())
150         .map_err(|e| error!("Failed to run fd connector: {}", e))
151         .unwrap();
152 }
153 
154 // loop until ShutDown event is received, then log and return.
main_loop(events_rx: Receiver<Event>)155 fn main_loop(events_rx: Receiver<Event>) {
156     loop {
157         // events_rx.recv() will wait until the event is received.
158         // TODO(b/305536480): Remove built-in devices during shutdown.
159         if let Ok(Event::ShutDown(ShutDown { reason })) = events_rx.recv() {
160             info!("Netsim is shutdown: {reason}");
161             return;
162         }
163     }
164 }
165 
166 // Disambiguate config and command line args and store merged setting in config
disambiguate_args(args: &mut NetsimdArgs, config: &mut Config)167 fn disambiguate_args(args: &mut NetsimdArgs, config: &mut Config) {
168     // Command line override config file arguments
169 
170     // Currently capture cannot be specified off explicitly with command line.
171     // Enable capture if enabled by command line arg
172     if args.pcap {
173         match config.capture.as_mut() {
174             Some(capture) => {
175                 capture.enabled = Some(true);
176             }
177             None => {
178                 let mut capture = Capture::new();
179                 capture.enabled = Some(true);
180                 config.capture = Some(capture).into();
181             }
182         }
183     }
184 
185     // Ensure Bluetooth config is initialized
186     let bt_config = match config.bluetooth.as_mut() {
187         Some(existing_bt_config) => existing_bt_config,
188         None => {
189             config.bluetooth = Some(BluetoothConfig::new()).into();
190             config.bluetooth.as_mut().unwrap()
191         }
192     };
193 
194     // Set disable_address_reuse as needed
195     if args.disable_address_reuse {
196         bt_config.disable_address_reuse = Some(true);
197     }
198 
199     // Determine test beacons configuration, default true for cuttlefish
200     // TODO: remove default for cuttlefish by adding flag to tests
201     bt_config.test_beacons = match (args.test_beacons, args.no_test_beacons) {
202         (true, false) => Some(true),
203         (false, true) => Some(false),
204         (false, false) => match bt_config.test_beacons {
205             Some(test_beacons) => Some(test_beacons),
206             None => Some(cfg!(feature = "cuttlefish")),
207         },
208         (true, true) => panic!("unexpected flag combination"),
209     };
210 }
211 
run_netsimd_primary(mut args: NetsimdArgs)212 fn run_netsimd_primary(mut args: NetsimdArgs) {
213     info!(
214         "Netsim Version: {}, OS: {}, Arch: {}",
215         get_version(),
216         std::env::consts::OS,
217         std::env::consts::ARCH
218     );
219 
220     let fd_startup_str = args.fd_startup_str.clone().unwrap_or_default();
221     let instance_num = get_instance(args.instance);
222     let hci_port: u16 =
223         get_hci_port(args.hci_port.unwrap_or_default(), instance_num - 1).try_into().unwrap();
224 
225     #[cfg(feature = "cuttlefish")]
226     if fd_startup_str.is_empty() {
227         warn!("Warning: netsimd startup flag -s is empty, waiting for gRPC connections.");
228     }
229 
230     if is_netsimd_alive(instance_num) {
231         warn!("Failed to start netsim daemon because a netsim daemon is already running");
232         return;
233     }
234 
235     // Load config file
236     let mut config = Config::new();
237     if let Some(ref filename) = args.config {
238         match config_file::new_from_file(filename) {
239             Ok(config_from_file) => {
240                 config = config_from_file;
241             }
242             Err(e) => {
243                 error!("Skipping config in {}: {:?}", filename, e);
244             }
245         }
246     }
247     // Disambiguate conflicts between cmdline args and config file
248     disambiguate_args(&mut args, &mut config);
249 
250     // Print config file settings
251     info!("{:#?}", config);
252 
253     if let Some(host_dns) = args.host_dns {
254         config.wifi.mut_or_insert_default().slirp_options.mut_or_insert_default().host_dns =
255             host_dns;
256     }
257 
258     if let Some(http_proxy) = args.http_proxy {
259         config.wifi.mut_or_insert_default().slirp_options.mut_or_insert_default().http_proxy =
260             http_proxy;
261     }
262 
263     let service_params = ServiceParams::new(
264         fd_startup_str,
265         args.no_cli_ui,
266         args.no_web_ui,
267         hci_port,
268         instance_num,
269         args.dev,
270         args.vsock.unwrap_or_default(),
271     );
272 
273     // SAFETY: The caller guaranteed that the file descriptors in `fd_startup_str` would remain
274     // valid and open for as long as the program runs.
275     let mut service = unsafe { Service::new(service_params) };
276     service.set_up();
277 
278     // Create all Event Receivers
279     let capture_events_rx = events::subscribe();
280     let device_events_rx = events::subscribe();
281     let main_events_rx = events::subscribe();
282     let session_events_rx = events::subscribe();
283 
284     // Start Session Event listener
285     let mut session = Session::new();
286     session.start(session_events_rx);
287 
288     // Pass all event receivers to each modules
289     let capture = config.capture.enabled.unwrap_or_default();
290     spawn_capture_event_subscriber(capture_events_rx, capture);
291 
292     if !args.no_shutdown {
293         spawn_shutdown_publisher(device_events_rx);
294     }
295 
296     // Start radio facades
297     wireless::bluetooth::bluetooth_start(&config.bluetooth, instance_num);
298     wireless::wifi::wifi_start(&config.wifi, args.forward_host_mdns);
299     wireless::uwb::uwb_start();
300 
301     // Create test beacons if required
302     if config.bluetooth.test_beacons == Some(true) {
303         new_test_beacon(1, 1000);
304         new_test_beacon(2, 1000);
305     }
306 
307     // Run all netsimd services (grpc, socket, web)
308     service.run();
309 
310     // Runs a synchronous main loop
311     main_loop(main_events_rx);
312 
313     // Gracefully shutdown netsimd services
314     service.shut_down();
315 
316     // write out session stats
317     let _ = session.stop();
318 
319     // zip all artifacts
320     if let Err(err) = zip_artifacts() {
321         error!("Failed to zip artifacts: {err:?}");
322     }
323 
324     // Once shutdown is complete, delete the netsim ini file
325     remove_netsim_ini(instance_num);
326 }
327 
is_netsimd_alive(instance_num: u16) -> bool328 fn is_netsimd_alive(instance_num: u16) -> bool {
329     match get_server_address(instance_num) {
330         Some(address) => {
331             // Check if grpc server has started
332             let channel = ChannelBuilder::new(std::sync::Arc::new(EnvBuilder::new().build()))
333                 .connect(&address);
334             let client = FrontendServiceClient::new(channel);
335             let deadline = Deadline::from(std::time::Duration::from_secs(1));
336             futures::executor::block_on(client.client.channel().wait_for_connected(deadline))
337         }
338         None => false,
339     }
340 }
341