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(&current_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