| // Copyright 2023 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. |
| |
| use clap::Parser; |
| use log::warn; |
| use log::{error, info}; |
| #[cfg(feature = "cuttlefish")] |
| use netsim_common::system::netsimd_temp_dir; |
| use netsim_common::util::os_utils::{get_hci_port, get_instance, remove_netsim_ini}; |
| use netsim_common::util::zip_artifact::zip_artifacts; |
| |
| use crate::bluetooth as bluetooth_facade; |
| use crate::captures::capture::spawn_capture_event_subscriber; |
| use crate::config_file; |
| use crate::devices::devices_handler::wait_devices; |
| use crate::events::Event; |
| use crate::resource; |
| use crate::wifi as wifi_facade; |
| use netsim_common::util::netsim_logger; |
| |
| use crate::args::NetsimdArgs; |
| use crate::ffi::ffi_util; |
| use crate::service::{Service, ServiceParams}; |
| #[cfg(feature = "cuttlefish")] |
| use netsim_common::util::os_utils::get_server_address; |
| use netsim_proto::config::Config; |
| use std::env; |
| use std::ffi::{c_char, c_int}; |
| use std::sync::mpsc::Receiver; |
| |
| /// Wireless network simulator for android (and other) emulated devices. |
| /// |
| /// # Safety |
| /// |
| /// The file descriptors passed in `NetsimdArgs::fd_startup_str` must remain valid and open for as |
| /// long as the program runs. |
| #[no_mangle] |
| pub unsafe extern "C" fn rust_main(argc: c_int, argv: *const *const c_char) { |
| ffi_util::set_up_crash_report(); |
| netsim_logger::init("netsimd"); |
| let netsimd_args = get_netsimd_args(argc, argv); |
| run_netsimd_with_args(netsimd_args); |
| } |
| |
| #[allow(unused)] |
| fn get_netsimd_args(argc: c_int, argv: *const *const c_char) -> NetsimdArgs { |
| let env_args_or_err = env::var("NETSIM_ARGS"); |
| |
| #[cfg(feature = "cuttlefish")] |
| { |
| // TODO: Use NetsimdArgs::parse() after netsimd binary is built with netsimd.rs. |
| // In linux arm64 in aosp-main, it can't access CLI arguments by std::env::args() with netsimd.cc wrapper. |
| let mut argv: Vec<_> = (0..argc) |
| .map(|i| |
| // SAFETY: argc and argv will remain valid as long as the program runs. |
| unsafe { |
| std::ffi::CStr::from_ptr(*argv.add(i as usize)).to_str().unwrap().to_owned() |
| }) |
| .collect(); |
| if let Ok(env_args) = env_args_or_err { |
| env_args.split(' ').for_each(|arg| argv.push(arg.to_string())); |
| } |
| NetsimdArgs::parse_from(argv) |
| } |
| #[cfg(not(feature = "cuttlefish"))] |
| { |
| let mut argv = env::args().collect::<Vec<String>>(); |
| if let Ok(env_args) = env_args_or_err { |
| env_args.split(' ').for_each(|arg| argv.push(arg.to_string())); |
| } |
| NetsimdArgs::parse_from(argv) |
| } |
| } |
| |
| fn run_netsimd_with_args(args: NetsimdArgs) { |
| // Log where netsim artifacts are located |
| #[cfg(feature = "cuttlefish")] |
| info!("netsim artifacts path: {}", netsimd_temp_dir().display()); |
| |
| // Redirect stdout and stderr to files only if netsimd is not invoked |
| // by Cuttlefish. Some Cuttlefish builds fail when writing logs to files. |
| #[cfg(not(feature = "cuttlefish"))] |
| if !args.logtostderr { |
| cxx::let_cxx_string!(netsimd_temp_dir = netsim_common::system::netsimd_temp_dir_string()); |
| ffi_util::redirect_std_stream(&netsimd_temp_dir); |
| } |
| |
| match args.connector_instance { |
| #[cfg(feature = "cuttlefish")] |
| Some(connector_instance) => run_netsimd_connector(args, connector_instance), |
| _ => run_netsimd_primary(args), |
| } |
| } |
| |
| /// Forwards packets to another netsim daemon. |
| #[cfg(feature = "cuttlefish")] |
| fn run_netsimd_connector(args: NetsimdArgs, instance: u16) { |
| if args.fd_startup_str.is_none() { |
| error!("Failed to start netsimd forwarder, missing `-s` arg"); |
| return; |
| } |
| let fd_startup = args.fd_startup_str.unwrap(); |
| |
| let mut server: Option<String> = None; |
| // Attempts multiple time for fetching netsim.ini |
| for second in [1, 2, 4, 8, 0] { |
| server = get_server_address(instance); |
| if server.is_some() { |
| break; |
| } else { |
| warn!("Unable to find ini file for instance {}, retrying", instance); |
| std::thread::sleep(std::time::Duration::from_secs(second)); |
| } |
| } |
| if server.is_none() { |
| error!("Failed to run netsimd connector"); |
| return; |
| } |
| let server = server.unwrap(); |
| // TODO: Make this function returns Result to use `?` instead of unwrap(). |
| info!("Starting in Connector mode to {}", server.as_str()); |
| crate::transport::fd::run_fd_connector(&fd_startup, server.as_str()) |
| .map_err(|e| error!("Failed to run fd connector: {}", e)) |
| .unwrap(); |
| } |
| |
| // loop until ShutDown event is received, then log and return. |
| fn main_loop(events_rx: Receiver<Event>) { |
| loop { |
| // events_rx.recv() will wait until the event is received. |
| if let Ok(Event::ShutDown { reason }) = events_rx.recv() { |
| info!("Netsim is shutdown: {reason}"); |
| return; |
| } |
| } |
| } |
| |
| fn run_netsimd_primary(args: NetsimdArgs) { |
| let fd_startup_str = args.fd_startup_str.unwrap_or_default(); |
| let instance_num = get_instance(args.instance); |
| let hci_port: u16 = |
| get_hci_port(args.hci_port.unwrap_or_default(), instance_num - 1).try_into().unwrap(); |
| |
| #[cfg(feature = "cuttlefish")] |
| if fd_startup_str.is_empty() { |
| warn!("Failed to start netsim daemon because fd startup flag `-s` is empty"); |
| return; |
| } |
| |
| if ffi_util::is_netsimd_alive(instance_num) { |
| warn!("Failed to start netsim daemon because a netsim daemon is already running"); |
| return; |
| } |
| |
| let mut config = Config::new(); |
| if let Some(filename) = args.config { |
| match config_file::new_from_file(&filename) { |
| Ok(config_from_file) => { |
| info!("Using config in {}", config); |
| config = config_from_file; |
| } |
| Err(e) => { |
| error!("Skipping config in {}: {:?}", filename, e); |
| } |
| } |
| } |
| |
| let service_params = ServiceParams::new( |
| fd_startup_str, |
| args.no_cli_ui, |
| args.no_web_ui, |
| args.pcap, |
| hci_port, |
| instance_num, |
| args.dev, |
| args.vsock.unwrap_or_default(), |
| ); |
| |
| // SAFETY: The caller guaranteed that the file descriptors in `fd_startup_str` would remain |
| // valid and open for as long as the program runs. |
| let mut service = unsafe { Service::new(service_params) }; |
| service.set_up(); |
| |
| // Create all Event Receivers |
| let capture_events_rx = resource::clone_events().lock().unwrap().subscribe(); |
| let device_events_rx = resource::clone_events().lock().unwrap().subscribe(); |
| let main_events_rx = resource::clone_events().lock().unwrap().subscribe(); |
| |
| // Pass all event receivers to each modules |
| spawn_capture_event_subscriber(capture_events_rx); |
| wait_devices(device_events_rx); |
| |
| // Start radio facades |
| bluetooth_facade::bluetooth_start(&config.bluetooth, instance_num, args.disable_address_reuse); |
| wifi_facade::wifi_start(&config.wifi); |
| |
| // Run all netsimd services (grpc, socket, web) |
| service.run(); |
| |
| // Runs a synchronous main loop |
| main_loop(main_events_rx); |
| |
| // Gracefully shutdown netsimd services |
| service.shut_down(); |
| |
| // Once service.run is complete, delete the netsim ini file |
| // and zip all artifacts |
| remove_netsim_ini(instance_num); |
| if let Err(err) = zip_artifacts() { |
| error!("Failed to zip artifacts: {err:?}"); |
| } |
| } |