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 crate::bluetooth::advertise_settings as ble_advertise_settings;
16 use crate::captures::captures_handler::clear_pcap_files;
17 use crate::config::{set_dev, set_disable_wifi_p2p, set_pcap};
18 use crate::ffi::ffi_transport::{run_grpc_server_cxx, GrpcServer};
19 use crate::http_server::server::run_http_server;
20 use crate::transport::socket::run_socket_transport;
21 use crate::wireless;
22 use cxx::UniquePtr;
23 use log::{error, info, warn};
24 use netsim_common::util::ini_file::IniFile;
25 use netsim_common::util::os_utils::get_netsim_ini_filepath;
26 use netsim_common::util::zip_artifact::remove_zip_files;
27 use std::env;
28 use std::time::Duration;
29 
30 /// Module to control startup, run, and cleanup netsimd services.
31 
32 pub struct ServiceParams {
33     fd_startup_str: String,
34     no_cli_ui: bool,
35     no_web_ui: bool,
36     pcap: bool,
37     hci_port: u16,
38     instance_num: u16,
39     dev: bool,
40     disable_wifi_p2p: bool,
41     vsock: u16,
42     rust_grpc: bool,
43 }
44 
45 impl ServiceParams {
46     #[allow(clippy::too_many_arguments)]
new( fd_startup_str: String, no_cli_ui: bool, no_web_ui: bool, pcap: bool, hci_port: u16, instance_num: u16, dev: bool, disable_wifi_p2p: bool, vsock: u16, rust_grpc: bool, ) -> Self47     pub fn new(
48         fd_startup_str: String,
49         no_cli_ui: bool,
50         no_web_ui: bool,
51         pcap: bool,
52         hci_port: u16,
53         instance_num: u16,
54         dev: bool,
55         disable_wifi_p2p: bool,
56         vsock: u16,
57         rust_grpc: bool,
58     ) -> Self {
59         ServiceParams {
60             fd_startup_str,
61             no_cli_ui,
62             no_web_ui,
63             pcap,
64             hci_port,
65             instance_num,
66             dev,
67             disable_wifi_p2p,
68             vsock,
69             rust_grpc,
70         }
71     }
72 }
73 
74 pub struct Service {
75     // netsimd states, like device resource.
76     service_params: ServiceParams,
77     // grpc server
78     grpc_server: UniquePtr<GrpcServer>,
79     rust_grpc_server: Option<grpcio::Server>,
80 }
81 
82 impl Service {
83     /// # Safety
84     ///
85     /// The file descriptors in `service_params.fd_startup_str` must be valid and open, and must
86     /// remain so for as long as the `Service` exists.
new(service_params: ServiceParams) -> Service87     pub unsafe fn new(service_params: ServiceParams) -> Service {
88         Service { service_params, grpc_server: UniquePtr::null(), rust_grpc_server: None }
89     }
90 
91     /// Sets up the states for netsimd.
set_up(&self)92     pub fn set_up(&self) {
93         // Clear all zip files
94         match remove_zip_files() {
95             Ok(()) => info!("netsim generated zip files in temp directory has been removed."),
96             Err(err) => error!("{err:?}"),
97         }
98 
99         // Clear all pcap files
100         if clear_pcap_files() {
101             info!("netsim generated pcap files in temp directory has been removed.");
102         }
103 
104         set_pcap(self.service_params.pcap);
105         set_dev(self.service_params.dev);
106         set_disable_wifi_p2p(self.service_params.disable_wifi_p2p);
107     }
108 
109     /// Runs netsim gRPC server
run_grpc_server(&mut self) -> Option<u32>110     fn run_grpc_server(&mut self) -> Option<u32> {
111         // If NETSIM_GRPC_PORT is set, use the fixed port for grpc server.
112         let mut netsim_grpc_port =
113             env::var("NETSIM_GRPC_PORT").map(|val| val.parse::<u32>().unwrap_or(0)).unwrap_or(0);
114         if self.service_params.rust_grpc {
115             // Run netsim gRPC server
116             let (server, port) = crate::grpc_server::server::start(netsim_grpc_port);
117             self.rust_grpc_server = Some(server);
118             netsim_grpc_port = port.into();
119         } else {
120             let grpc_server = run_grpc_server_cxx(
121                 netsim_grpc_port,
122                 self.service_params.no_cli_ui,
123                 self.service_params.vsock,
124             );
125             match grpc_server.is_null() {
126                 true => return None,
127                 false => {
128                     self.grpc_server = grpc_server;
129                     netsim_grpc_port = self.grpc_server.get_grpc_port();
130                 }
131             }
132         }
133         Some(netsim_grpc_port)
134     }
135 
136     /// Runs netsim web server
run_web_server(&self) -> Option<u16>137     fn run_web_server(&self) -> Option<u16> {
138         // If NETSIM_NO_WEB_SERVER is set, don't start http server.
139         let no_web_server = env::var("NETSIM_NO_WEB_SERVER").is_ok_and(|v| v == "1");
140         match !no_web_server && !self.service_params.no_web_ui {
141             true => Some(run_http_server(self.service_params.instance_num)),
142             false => None,
143         }
144     }
145 
146     /// Write ports to netsim.ini file
write_ports_to_ini(&self, grpc_port: u32, web_port: Option<u16>)147     fn write_ports_to_ini(&self, grpc_port: u32, web_port: Option<u16>) {
148         let filepath = get_netsim_ini_filepath(self.service_params.instance_num);
149         let mut ini_file = IniFile::new(filepath);
150         if let Some(num) = web_port {
151             ini_file.insert("web.port", &num.to_string());
152         }
153         ini_file.insert("grpc.port", &grpc_port.to_string());
154         if let Err(err) = ini_file.write() {
155             error!("{err:?}");
156         }
157     }
158 
159     /// Runs the netsimd services.
160     #[allow(unused_unsafe)]
run(&mut self)161     pub fn run(&mut self) {
162         if !self.service_params.fd_startup_str.is_empty() {
163             // SAFETY: When the `Service` was constructed by `Service::new` the caller guaranteed
164             // that the file descriptors in `service_params.fd_startup_str` would remain valid and
165             // open.
166             unsafe {
167                 use crate::transport::fd::run_fd_transport;
168                 run_fd_transport(&self.service_params.fd_startup_str);
169             }
170         }
171 
172         let grpc_port = match self.run_grpc_server() {
173             Some(port) => port,
174             None => {
175                 error!("Failed to run netsimd because unable to start grpc server");
176                 return;
177             }
178         };
179 
180         // Run frontend web server
181         let web_port = self.run_web_server();
182 
183         // Write the port numbers to ini file
184         self.write_ports_to_ini(grpc_port, web_port);
185 
186         // Run the socket server.
187         run_socket_transport(self.service_params.hci_port);
188     }
189 
190     /// Shut down the netsimd services
shut_down(&mut self)191     pub fn shut_down(&mut self) {
192         // TODO: shutdown other services in Rust
193         if !self.grpc_server.is_null() {
194             self.grpc_server.shut_down();
195         }
196         self.rust_grpc_server.as_mut().map(|server| server.shutdown());
197         wireless::bluetooth::bluetooth_stop();
198         wireless::wifi::wifi_stop();
199     }
200 }
201 
202 /// Constructing test beacons for dev mode
new_test_beacon(idx: u32, interval: u64)203 pub fn new_test_beacon(idx: u32, interval: u64) {
204     use crate::devices::devices_handler::create_device;
205     use netsim_proto::common::ChipKind;
206     use netsim_proto::frontend::CreateDeviceRequest;
207     use netsim_proto::model::chip::ble_beacon::{
208         AdvertiseData as AdvertiseDataProto, AdvertiseSettings as AdvertiseSettingsProto,
209     };
210     use netsim_proto::model::chip_create::{
211         BleBeaconCreate as BleBeaconCreateProto, Chip as ChipProto,
212     };
213     use netsim_proto::model::ChipCreate as ChipCreateProto;
214     use netsim_proto::model::DeviceCreate as DeviceCreateProto;
215     use protobuf::MessageField;
216     use protobuf_json_mapping::print_to_string;
217 
218     let beacon_proto = BleBeaconCreateProto {
219         address: format!("be:ac:01:be:ef:{:02x}", idx),
220         settings: MessageField::some(AdvertiseSettingsProto {
221             interval: Some(
222                 ble_advertise_settings::AdvertiseMode::new(Duration::from_millis(interval))
223                     .try_into()
224                     .unwrap(),
225             ),
226             scannable: true,
227             ..Default::default()
228         }),
229         adv_data: MessageField::some(AdvertiseDataProto {
230             include_device_name: true,
231             ..Default::default()
232         }),
233         scan_response: MessageField::some(AdvertiseDataProto {
234             manufacturer_data: vec![1u8, 2, 3, 4],
235             ..Default::default()
236         }),
237         ..Default::default()
238     };
239 
240     let chip_proto = ChipCreateProto {
241         name: format!("gDevice-bt-beacon-chip-{idx}"),
242         kind: ChipKind::BLUETOOTH_BEACON.into(),
243         chip: Some(ChipProto::BleBeacon(beacon_proto)),
244         ..Default::default()
245     };
246 
247     let device_proto = DeviceCreateProto {
248         name: format!("gDevice-beacon-{idx}"),
249         chips: vec![chip_proto],
250         ..Default::default()
251     };
252 
253     let request =
254         CreateDeviceRequest { device: MessageField::some(device_proto), ..Default::default() };
255 
256     if let Err(err) = create_device(&print_to_string(&request).unwrap()) {
257         warn!("Failed to create beacon device {idx}: {err}");
258     }
259 }
260