/* * Copyright (C) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use super::parser::ParsedCommand; use super::server; use hdc::common::base; use hdc::common::base::Base; use hdc::config::{self, HdcCommand}; use hdc::transfer; use hdc::utils; #[allow(unused)] use hdc::utils::hdc_log::*; use libc::exit; use std::time::Duration; use std::env; use std::io::{self, Error, ErrorKind, Write}; #[cfg(not(target_os = "windows"))] use std::os::fd::AsRawFd; #[cfg(featrue = "host")] extern crate ylong_runtime_static as ylong_runtime; #[cfg(not(target_os = "windows"))] use ylong_runtime::io::AsyncReadExt; use ylong_runtime::io::AsyncWriteExt; use ylong_runtime::net::{SplitWriteHalf, TcpStream}; #[cfg(target_os = "windows")] use crate::tty_utility::*; #[cfg(target_os = "windows")] extern "C" { fn getch() -> libc::c_int; } #[allow(unused)] pub struct Client { command: HdcCommand, params: Vec, connect_key: String, wr: SplitWriteHalf, } pub async fn run_client_mode(parsed_cmd: ParsedCommand) -> io::Result<()> { match parsed_cmd.command { Some(HdcCommand::KernelServerStart) => { if parsed_cmd.parameters.contains(&"-r".to_string()) { server::server_kill().await; } server::server_fork(parsed_cmd.server_addr.clone(), parsed_cmd.log_level).await; return Ok(()); } Some(HdcCommand::KernelServerKill) => { server::server_kill().await; if parsed_cmd.parameters.contains(&"-r".to_string()) { server::server_fork(parsed_cmd.server_addr.clone(), parsed_cmd.log_level).await; } return Ok(()); } _ => {} }; if parsed_cmd.launch_server && Base::program_mutex(base::GLOBAL_SERVER_NAME, true) { server::server_fork(parsed_cmd.server_addr.clone(), parsed_cmd.log_level).await; } // TODO: other cmd before initial client let mut client = Client::new(parsed_cmd).await?; if let Err(e) = client.handshake().await { hdc::error!("handshake with server failed: {e:?}"); return Err(e); } client.execute_command().await } impl Client { pub async fn new(parsed_cmd: ParsedCommand) -> io::Result { let Some(command) = parsed_cmd.command else { return Err(Error::new(ErrorKind::Other, "command is None")); }; let connect_key = auto_connect_key(parsed_cmd.connect_key, command); let stream = match TcpStream::connect(parsed_cmd.server_addr).await { Ok(stream) => stream, Err(_) => return Err(Error::new(ErrorKind::Other, "Connect to server failed")), }; let (rd, wr) = stream.into_split(); transfer::ChannelMap::start(rd).await; Ok(Self { command, params: parsed_cmd.parameters, connect_key, wr, }) } async fn execute_command(&mut self) -> io::Result<()> { let entire_cmd = self.params.join(" "); hdc::debug!("execute command params: {}", &entire_cmd); match self.command { HdcCommand::KernelTargetList | HdcCommand::KernelTargetConnect | HdcCommand::UnityHilog => self.general_task().await, HdcCommand::FileInit | HdcCommand::FileCheck | HdcCommand::FileRecvInit => { self.file_send_task().await } HdcCommand::AppInit => self.app_install_task().await, HdcCommand::AppUninstall => self.app_uninstall_task().await, HdcCommand::UnityRunmode | HdcCommand::UnityReboot | HdcCommand::UnityRemount => self.unity_task().await, HdcCommand::UnityRootrun => self.unity_root_run_task().await, HdcCommand::UnityExecute => self.shell_task().await, HdcCommand::KernelWaitFor => self.wait_task().await, HdcCommand::UnityBugreportInit => self.bug_report_task().await, HdcCommand::ForwardInit | HdcCommand::ForwardRportInit | HdcCommand::ForwardList | HdcCommand::ForwardRportList | HdcCommand::ForwardRemove | HdcCommand::ForwardRportRemove => { self.forward_task().await } HdcCommand::JdwpList | HdcCommand::JdwpTrack => self.jdwp_task().await, HdcCommand::KernelCheckServer => self.check_server_task().await, _ => Err(Error::new( ErrorKind::Other, format!("unknown command: {}", self.command as u32), )), } } pub async fn handshake(&mut self) -> io::Result<()> { let recv = self.recv().await?; let msg = match std::str::from_utf8(&recv[..config::HANDSHAKE_MESSAGE.len()]) { Ok(msg) => msg, Err(err) => { return Err(Error::new(ErrorKind::Other, format!("handshake from_utf8 error : {err}"))); } }; if msg != config::HANDSHAKE_MESSAGE { return Err(Error::new(ErrorKind::Other, "Recv server-hello failed")); } let buf = [ config::HANDSHAKE_MESSAGE.as_bytes(), vec![0_u8; config::BANNER_SIZE - config::HANDSHAKE_MESSAGE.len()].as_slice(), self.connect_key.as_bytes(), vec![0_u8; config::KEY_MAX_SIZE - self.connect_key.len()].as_slice(), ] .concat(); self.send(buf.as_slice()).await; Ok(()) } async fn send(&mut self, buf: &[u8]) { hdc::debug!("channel send buf: {:#?}", buf); let msg = [u32::to_be_bytes(buf.len() as u32).as_slice(), buf].concat(); let _ = self.wr.write_all(msg.as_slice()).await; } async fn recv(&mut self) -> io::Result> { hdc::debug!("channel recv buf"); transfer::ChannelMap::recv().await } async fn unity_task(&mut self) -> io::Result<()> { self.send(self.params.join(" ").as_bytes()).await; self.loop_recv().await } async fn wait_task(&mut self) -> io::Result<()> { self.send(self.params.join(" ").as_bytes()).await; self.loop_recv_waitfor().await } async fn unity_root_run_task(&mut self) -> io::Result<()> { if self.params.len() >= 2 && self.params[1].starts_with("-r") { self.params[1] = "r".to_string(); } self.send(self.params.join(" ").as_bytes()).await; self.loop_recv().await } async fn jdwp_task(&mut self) -> io::Result<()> { self.send(self.params.join(" ").as_bytes()).await; self.loop_recv().await } #[cfg(target_os = "windows")] async fn shell_task(&mut self) -> io::Result<()> { let cmd = match self.params.len() { 1 => "shell\0".to_string(), _ => self.params.join(" "), }; self.send(cmd.as_bytes()).await; let _handle = ylong_runtime::spawn(async move { loop { match transfer::ChannelMap::recv().await { Ok(recv) => { let _ = utils::print_msg(recv).await; } Err(_) => { std::process::exit(0); } } } }); loop { let c = unsafe { getch() }; // 判断如果是ctrl_D,发送给serer端,client直接退出 if c == 0x4 { self.send([c as u8].as_slice()).await; break; } // win下的控制字符以0xe0开头,转换后发送给server, if c == 0xe0 { let control_code = convert_to_control_code(); self.send(control_code.as_slice()).await; continue; } let unicode_byte = unicode_assemble(c); hdc::info!("unicode_byte is {:?}", unicode_byte); self.send(unicode_byte.as_slice()).await; } Ok(()) } #[cfg(not(target_os = "windows"))] async fn shell_task(&mut self) -> io::Result<()> { let cmd = match self.params.len() { 1 => "shell\0".to_string(), _ => self.params.join(" "), }; self.send(cmd.as_bytes()).await; let termios = setup_raw_terminal()?; let termios_clone = termios; let _handle = ylong_runtime::spawn(async move { loop { match transfer::ChannelMap::recv().await { Ok(recv) => { let _ = utils::print_msg(recv).await; } Err(_) => { let _ = recover_terminal(termios_clone); std::process::exit(0); } } } }); let mut buf = [0_u8; 1]; let mut stdin = ylong_runtime::io::stdin(); while let Ok(bytes) = stdin.read(&mut buf).await { self.send(&buf[..bytes]).await; if buf[..bytes].contains(&0x4_u8) { break; } } let _ = recover_terminal(termios); Ok(()) } async fn forward_task(&mut self) -> io::Result<()> { if (self.command == HdcCommand::ForwardRemove || self.command == HdcCommand::ForwardRportRemove) && self.params.len() < 3 { return Err(Error::new( ErrorKind::Other, "Too few arguments.".to_string() )); } if (self.command == HdcCommand::ForwardInit || self.command == HdcCommand::ForwardRportInit) && self.params.len() < 3 { return Err(Error::new( ErrorKind::Other, "Too few arguments.".to_string() )); } self.send(self.params.join(" ").as_bytes()).await; self.loop_recv().await } async fn general_task(&mut self) -> io::Result<()> { self.send(self.params.join(" ").as_bytes()).await; loop { let recv = self.recv().await?; hdc::debug!( "general_task recv: {:#?}", recv.iter() .map(|c| format!("{c:02x}")) .collect::>() .join(" ") ); let _ = utils::print_msg(recv).await; } } async fn bug_report_task(&mut self) -> io::Result<()> { if self.params.len() <= 1 { return self.general_task().await; } self.send(self.params.join(" ").as_bytes()).await; let mut file = std::fs::File::create(self.params[1].as_str())?; loop { let recv = self.recv().await?; file.write_all(&recv)?; } } async fn file_send_task(&mut self) -> io::Result<()> { let mut params = self.params.clone(); if self.command == HdcCommand::FileInit || self.command == HdcCommand::FileRecvInit { let command_field_count = 2; let current_dir = env::current_dir()?; let mut s = current_dir.display().to_string(); s.push(Base::get_path_sep()); params.insert(command_field_count, "-cwd".to_string()); params.insert(command_field_count + 1, s.clone()); } self.send(params.join(" ").as_bytes()).await; self.loop_recv().await } async fn loop_recv(&mut self) -> io::Result<()> { loop { let recv = self.recv().await; match recv { Ok(recv) => { hdc::debug!( "recv: {:#?}", recv.iter() .map(|c| format!("{c:02x}")) .collect::>() .join(" ") ); match String::from_utf8(recv) { Ok(msg) => print!("{msg}"), Err(err) => return Err(Error::new(ErrorKind::Other, format!("recv data to str failed, {err}"))), } } Err(e) => { return Err(e); } } } } async fn loop_recv_waitfor(&mut self) -> io::Result<()> { loop { let recv = self.recv().await; match recv { Ok(recv) => { hdc::debug!( "recv: {:#?}", recv.iter() .map(|c| format!("{c:02x}")) .collect::>() .join(" ") ); if let HdcCommand::KernelWaitFor = self.command { let wait_for = "No connected target\r\n".to_string(); if wait_for == String::from_utf8(recv).expect("invalid UTF-8") { self.send(self.params.join(" ").as_bytes()).await; hdc::debug!("WaitFor sleep a second"); let wait_interval = 1000; ylong_runtime::time::sleep(Duration::from_millis(wait_interval)).await; } else { hdc::debug!("exit client"); unsafe { exit(0); } } } } Err(e) => { return Err(e); } } } } async fn app_install_task(&mut self) -> io::Result<()> { let mut params = self.params.clone(); let command_field_count = 1; let current_dir = env::current_dir()?; let mut s = current_dir.display().to_string(); s.push(Base::get_path_sep()); params.insert(command_field_count, "-cwd".to_string()); params.insert(command_field_count + 1, s.clone()); self.send(params.join(" ").as_bytes()).await; loop { let recv = self.recv().await; match recv { Ok(recv) => { hdc::debug!( "app_install_task recv: {:#?}", recv.iter() .map(|c| format!("{c:02x}")) .collect::>() .join(" ") ); match String::from_utf8(recv) { Ok(msg) => print!("{}", msg), Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{e}"))), } } Err(e) => { return Err(e); } } } } async fn app_uninstall_task(&mut self) -> io::Result<()> { let params = self.params.clone(); self.send(params.join(" ").as_bytes()).await; loop { let recv = self.recv().await; match recv { Ok(recv) => { hdc::debug!( "app_uninstall_task recv: {:#?}", recv.iter() .map(|c| format!("{c:02x}")) .collect::>() .join(" ") ); match String::from_utf8(recv) { Ok(msg) => println!("{msg}"), Err(e) => { return Err(io::Error::new(io::ErrorKind::Other, format!("{e}"))); } } } Err(e) => { return Err(e); } } } } async fn check_server_task(&mut self) -> io::Result<()> { let params = self.params.clone(); self.send(params.join(" ").as_bytes()).await; let recv = self.recv().await; match recv { Ok(recv) => { hdc::debug!( "check_server_task recv: {:#?}", recv.iter() .map(|c| format!("{c:02x}")) .collect::>() .join(" ") ); const CMD_U8_LEN: usize = 2; if recv.len() < CMD_U8_LEN { return Err(Error::new(io::ErrorKind::Other, "recv failed")); } let (cmd_slice, version_slice) = recv.split_at(CMD_U8_LEN); let Ok(cmd) = HdcCommand::try_from(u16::from_le_bytes(cmd_slice.try_into().unwrap_or_default()) as u32) else { return Err(Error::new(io::ErrorKind::Other, "HdcCommand::try_from failed")); }; if HdcCommand::KernelCheckServer != cmd { return Err(Error::new(io::ErrorKind::Other, "recv cmd error")); } match String::from_utf8(version_slice.to_vec()) { Ok(s_ver) => println!("Client version:{}, server version:{}", config::get_version(), s_ver), Err(err) => { return Err(Error::new(io::ErrorKind::Other, format!("from_utf8 failed, {err}"))); } } Ok(()) } Err(e) => Err(e), } } } fn auto_connect_key(key: String, cmd: HdcCommand) -> String { match cmd { HdcCommand::ClientVersion | HdcCommand::KernelHelp | HdcCommand::KernelTargetDiscover | HdcCommand::KernelTargetList | HdcCommand::KernelCheckServer | HdcCommand::KernelTargetConnect | HdcCommand::KernelCheckDevice | HdcCommand::KernelServerKill => "".to_string(), _ => { if key.is_empty() { "any".to_string() } else { key } } } } #[cfg(not(target_os = "windows"))] fn setup_raw_terminal() -> io::Result { unsafe { let tty; let fd = if libc::isatty(libc::STDIN_FILENO) == 1 { libc::STDIN_FILENO } else { tty = std::fs::File::open("/dev/tty")?; tty.as_raw_fd() }; let mut ptr = core::mem::MaybeUninit::uninit(); if libc::tcgetattr(fd, ptr.as_mut_ptr()) == 0 { let termios = ptr.assume_init(); let mut termios_copy = termios; let c_oflag = termios.c_oflag; libc::cfmakeraw(&mut termios_copy); termios_copy.c_oflag = c_oflag; if libc::tcsetattr(fd, libc::TCSADRAIN, &termios_copy) == 0 { return Ok(termios); } } } Err(io::Error::last_os_error()) } #[cfg(not(target_os = "windows"))] fn recover_terminal(termios: libc::termios) -> io::Result<()> { unsafe { let tty; let fd = if libc::isatty(libc::STDIN_FILENO) == 1 { libc::STDIN_FILENO } else { tty = std::fs::File::open("/dev/tty")?; tty.as_raw_fd() }; if libc::tcsetattr(fd, libc::TCSADRAIN, &termios) == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } }