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