1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
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 * http://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::parser;
16 use crate::task;
17 #[cfg(target_os = "windows")]
18 use libc::{c_char, c_int};
19 #[cfg(target_os = "windows")]
20 use std::ffi::CString;
21
22 use crate::task::ConnectMap;
23 use hdc::config;
24 use hdc::config::HdcCommand;
25 use hdc::host_transfer::host_usb;
26 use hdc::transfer;
27 use hdc::utils;
28 #[allow(unused)]
29 use hdc::utils::hdc_log::*;
30 use std::io::{self, Error, ErrorKind};
31 use std::process;
32 use std::str::FromStr;
33 use std::time::Duration;
34
35 #[cfg(feature = "host")]
36 extern crate ylong_runtime_static as ylong_runtime;
37 use ylong_runtime::net::{SplitReadHalf, SplitWriteHalf, TcpListener, TcpStream};
38
39 #[cfg(target_os = "windows")]
40 extern "C" {
LaunchServerWin32null41 fn LaunchServerWin32(
42 runPath: *const c_char,
43 listenString: *const c_char,
44 logLevel: c_int,
45 ) -> bool;
46 }
47
48 #[derive(PartialEq)]
49 enum TargetStatus {
50 NotReadyAndUnknown,
51 NotReadyAndknown,
52 Ready
53 }
54
55 static mut IS_FIRST_NO_TARGETS: TargetStatus = TargetStatus::NotReadyAndUnknown;
56 const WAIT_TIME_MS: u64 = 200;
57
set_target_statusnull58 fn set_target_status(value: TargetStatus) {
59 unsafe {
60 IS_FIRST_NO_TARGETS = value;
61 }
62 }
63
is_target_status_equalnull64 fn is_target_status_equal(value: TargetStatus) -> bool {
65 unsafe {
66 IS_FIRST_NO_TARGETS == value
67 }
68 }
69
70 pub async fn run_server_mode(addr_str: String) -> io::Result<()> {
71 utils::spawn(start_usb_server());
72 start_client_listen(addr_str).await
73 }
74
75 async fn start_usb_server() {
76 let ptr = host_usb::init_host_usb() as u64;
77 loop {
78 let device_list = host_usb::get_ready_usb_devices_string(ptr);
79 match device_list {
80 Ok(str) => {
81 if str.is_empty() {
82 std::thread::sleep(Duration::from_millis(WAIT_TIME_MS));
83 continue;
84 }
85 for sn in str.split(' ') {
86 if sn.is_empty() {
87 continue;
88 }
89 hdc::info!("start_usb_server sn:{}", sn);
90 task::start_usb_device_loop(ptr, sn.to_string()).await;
91 }
92 std::thread::sleep(Duration::from_millis(WAIT_TIME_MS));
93 }
94 Err(_) => {
95 break;
96 }
97 }
98 }
99 host_usb::stop(ptr);
100 }
101
102 async fn start_client_listen(addr_str: String) -> io::Result<()> {
103 let saddr = addr_str;
104 let listener = TcpListener::bind(saddr.clone()).await?;
105 hdc::info!("server binds on {saddr}");
106
107 loop {
108 let (stream, addr) = listener.accept().await?;
109 hdc::info!("accepted client {addr}");
110 ylong_runtime::spawn(handle_client(stream));
111 }
112 }
113
114 pub async fn get_process_pids() -> Vec<u32> {
115 let mut pids: Vec<u32> = Vec::new();
116 if cfg!(target_os = "windows") {
117 let output_vec = match utils::execute_cmd("tasklist | findstr hdc".to_owned()) {
118 Ok(output) => [output.stdout, output.stderr].concat(),
119 Err(e) => e.to_string().into_bytes(),
120 };
121 let output_str = String::from_utf8_lossy(&output_vec);
122 let mut get_pid = false;
123 for token in output_str.split_whitespace() {
124 if get_pid {
125 match u32::from_str(token) {
126 Ok(pid) => {
127 pids.push(pid);
128 get_pid = false;
129 },
130 Err(err) => {
131 hdc::error!("'{token}' to u32 failed, {err}");
132 continue;
133 },
134 }
135 }
136 if token.contains("exe") {
137 get_pid = true;
138 }
139 }
140 } else {
141 let output_vec = match utils::execute_cmd(
142 "pgrep -x hdc".to_owned(),
143 ) {
144 Ok(output) => [output.stdout, output.stderr].concat(),
145 Err(e) => e.to_string().into_bytes(),
146 };
147 let output_str = String::from_utf8_lossy(&output_vec);
148 for pid_str in output_str.split_whitespace() {
149 let Ok(pid) = u32::from_str(pid_str) else {
150 hdc::error!("'{pid_str}' to u32 error");
151 continue;
152 };
153 pids.push(pid);
154 }
155 }
156 pids
157 }
158
159 // 跨平台命令
160 #[cfg(target_os = "windows")]
161 pub async fn server_fork(addr_str: String, log_level: usize) {
162 let current_exe = match std::env::current_exe() {
163 Ok(current_exe) => current_exe,
164 Err(err) => {
165 hdc::error!("server_fork, {err}");
166 return;
167 }
168 };
169 let Ok(run_path) = CString::new(current_exe.display().to_string()) else {
170 hdc::error!("server_fork CString::new fail");
171 return;
172 };
173 let Ok(listen_string) = CString::new(addr_str) else {
174 hdc::error!("server_fork CString::new fail");
175 return;
176 };
177 if unsafe {
178 LaunchServerWin32(
179 run_path.as_ptr(),
180 listen_string.as_ptr(),
181 log_level as c_int,
182 )
183 } {
184 ylong_runtime::time::sleep(Duration::from_millis(1000)).await
185 } else {
186 hdc::info!("server fork failed")
187 }
188 }
189
190 #[cfg(not(target_os = "windows"))]
191 pub async fn server_fork(addr_str: String, log_level: usize) {
192 let current_exe = match std::env::current_exe() {
193 Ok(current_exe) => current_exe.display().to_string(),
194 Err(err) => {
195 hdc::error!("server_fork, {err}");
196 return;
197 }
198 };
199 let result = process::Command::new(¤t_exe)
200 .args([
201 "-b",
202 "-m",
203 "-l",
204 log_level.to_string().as_str(),
205 "-s",
206 addr_str.as_str(),
207 ])
208 .spawn();
209 match result {
210 Ok(_) => ylong_runtime::time::sleep(Duration::from_millis(1000)).await,
211 Err(_) => hdc::info!("server fork failed"),
212 }
213 }
214
215 pub async fn server_kill() {
216 // TODO: check mac & win
217 let pids = get_process_pids().await;
218
219 for pid in pids {
220 if pid != process::id() {
221 if cfg!(target_os = "windows") {
222 match utils::execute_cmd(format!("taskkill /pid {} /f", pid)) {
223 Ok(_) => println!("Kill server finish"),
224 Err(e) => hdc::info!("Kill server error {}", e.to_string()),
225 };
226 } else {
227 match utils::execute_cmd(format!("kill -9 {}", pid)) {
228 Ok(_) => println!("Kill server finish"),
229 Err(e) => hdc::info!("Kill server error {}", e.to_string()),
230 };
231 }
232 }
233 }
234 }
235
236 #[allow(unused)]
237 #[derive(PartialEq)]
238 enum ChannelState {
239 InteractiveShell,
240 File,
241 App,
242 None,
243 }
244
245 async fn handle_client(stream: TcpStream) -> io::Result<()> {
246 let (mut rd, wr) = stream.into_split();
247 let (connect_key, channel_id) = handshake_with_client(&mut rd, wr).await?;
248 let mut channel_state = ChannelState::None;
249
250 let mut retry_count = 0;
251 const RETRY_MAX_COUNT: usize = 20;
252 loop {
253 let target_list = ConnectMap::get_list(true).await;
254 if target_list.is_empty() && is_target_status_equal(TargetStatus::Ready) {
255 set_target_status(TargetStatus::NotReadyAndUnknown);
256 }
257 if target_list.is_empty() && is_target_status_equal(TargetStatus::NotReadyAndUnknown) {
258 hdc::warn!("found no targets.");
259 std::thread::sleep(Duration::from_millis(WAIT_TIME_MS));
260 retry_count += 1;
261 if retry_count >= RETRY_MAX_COUNT {
262 retry_count = 0;
263 set_target_status(TargetStatus::NotReadyAndknown);
264 } else {
265 continue;
266 }
267 } else if !target_list.is_empty() {
268 set_target_status(TargetStatus::Ready);
269 }
270 let recv = match transfer::tcp::recv_channel_message(&mut rd).await {
271 Ok(recv) => recv,
272 Err(err) => {
273 hdc::error!("recv_channel_message failed, {err}");
274 return Ok(());
275 }
276 };
277 hdc::debug!("recv hex: {}", recv.iter().map(|c| format!("{c:02x}")).collect::<Vec<_>>().join(" "));
278
279 let recv_str = String::from_utf8_lossy(&recv.clone()).into_owned();
280 hdc::debug!("recv str: {}", recv_str.clone());
281 let mut parsed = parser::split_opt_and_cmd(
282 String::from_utf8_lossy(&recv)
283 .into_owned()
284 .split(' ')
285 .map(|s| s.trim_end_matches('\0').to_string())
286 .collect::<Vec<_>>(),
287 );
288
289 if channel_state == ChannelState::InteractiveShell {
290 parsed.command = Some(HdcCommand::ShellData);
291 parsed.parameters = vec![recv_str];
292 }
293
294 if parsed.command == Some(HdcCommand::UnityExecute) && parsed.parameters.len() == 1 {
295 channel_state = ChannelState::InteractiveShell;
296 parsed.command = Some(HdcCommand::ShellInit);
297 }
298
299 parsed = parser::exchange_parsed_for_daemon(parsed);
300
301 hdc::debug!("parsed cmd: {:#?}", parsed);
302
303 if let Some(cmd) = parsed.command {
304 if let Err(e) = task::channel_task_dispatch(task::TaskInfo {
305 command: cmd,
306 connect_key: connect_key.clone(),
307 channel_id,
308 params: parsed.parameters,
309 })
310 .await
311 {
312 hdc::error!("{e}");
313 }
314 } else {
315 return Err(Error::new(ErrorKind::Other, "command not found"));
316 }
317 }
318 }
319
320 async fn handshake_with_client(
321 rd: &mut SplitReadHalf,
322 wr: SplitWriteHalf,
323 ) -> io::Result<(String, u32)> {
324 let channel_id = utils::get_pseudo_random_u32();
325 transfer::TcpMap::start(channel_id, wr).await;
326
327 let buf = [
328 config::HANDSHAKE_MESSAGE.as_bytes(),
329 vec![0_u8; config::BANNER_SIZE - config::HANDSHAKE_MESSAGE.len()].as_slice(),
330 u32::to_le_bytes(channel_id).as_slice(),
331 vec![0_u8; config::KEY_MAX_SIZE - std::mem::size_of::<u32>()].as_slice(),
332 ]
333 .concat();
334
335 transfer::send_channel_data(channel_id, buf).await;
336 let recv = transfer::tcp::recv_channel_message(rd).await?;
337 let connect_key = unpack_channel_handshake(recv)?;
338 Ok((connect_key, channel_id))
339 }
340
unpack_channel_handshakenull341 fn unpack_channel_handshake(recv: Vec<u8>) -> io::Result<String> {
342 let Ok(msg) = std::str::from_utf8(&recv[..config::HANDSHAKE_MESSAGE.len()]) else {
343 return Err(Error::new(ErrorKind::Other, "not utf-8 chars."));
344 };
345 if msg != config::HANDSHAKE_MESSAGE {
346 return Err(Error::new(ErrorKind::Other, "Recv server-hello failed"));
347 }
348 let key_buf = &recv[config::BANNER_SIZE..];
349 let pos = match key_buf.iter().position(|c| *c == 0) {
350 Some(p) => p,
351 None => key_buf.len(),
352 };
353 if let Ok(connect_key) = String::from_utf8(key_buf[..pos].to_vec()) {
354 Ok(connect_key)
355 } else {
356 Err(Error::new(ErrorKind::Other, "unpack connect key failed"))
357 }
358 }
359