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