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