xref: /developtools/hdc/hdc_rust/src/host/client.rs (revision cc290419)
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 */
15use super::parser::ParsedCommand;
16use super::server;
17
18use hdc::common::base;
19use hdc::common::base::Base;
20use hdc::config::{self, HdcCommand};
21use hdc::transfer;
22use hdc::utils;
23#[allow(unused)]
24use hdc::utils::hdc_log::*;
25use libc::exit;
26use std::time::Duration;
27
28use std::env;
29use std::io::{self, Error, ErrorKind, Write};
30#[cfg(not(target_os = "windows"))]
31use std::os::fd::AsRawFd;
32
33#[cfg(featrue = "host")]
34extern crate ylong_runtime_static as ylong_runtime;
35#[cfg(not(target_os = "windows"))]
36use ylong_runtime::io::AsyncReadExt;
37use ylong_runtime::io::AsyncWriteExt;
38use ylong_runtime::net::{SplitWriteHalf, TcpStream};
39
40#[cfg(target_os = "windows")]
41use crate::tty_utility::*;
42
43#[cfg(target_os = "windows")]
44extern "C" {
45    fn getch() -> libc::c_int;
46}
47
48
49#[allow(unused)]
50pub struct Client {
51    command: HdcCommand,
52    params: Vec<String>,
53    connect_key: String,
54    wr: SplitWriteHalf,
55}
56
57pub async fn run_client_mode(parsed_cmd: ParsedCommand) -> io::Result<()> {
58    match parsed_cmd.command {
59        Some(HdcCommand::KernelServerStart) => {
60            if parsed_cmd.parameters.contains(&"-r".to_string()) {
61                server::server_kill().await;
62            }
63            server::server_fork(parsed_cmd.server_addr.clone(), parsed_cmd.log_level).await;
64            return Ok(());
65        }
66        Some(HdcCommand::KernelServerKill) => {
67            server::server_kill().await;
68            if parsed_cmd.parameters.contains(&"-r".to_string()) {
69                server::server_fork(parsed_cmd.server_addr.clone(), parsed_cmd.log_level).await;
70            }
71            return Ok(());
72        }
73        _ => {}
74    };
75
76    if parsed_cmd.launch_server && Base::program_mutex(base::GLOBAL_SERVER_NAME, true) {
77        server::server_fork(parsed_cmd.server_addr.clone(), parsed_cmd.log_level).await;
78    }
79
80    // TODO: other cmd before initial client
81
82    let mut client = Client::new(parsed_cmd).await?;
83
84    if let Err(e) = client.handshake().await {
85        hdc::error!("handshake with server failed: {e:?}");
86        return Err(e);
87    }
88    client.execute_command().await
89}
90
91impl Client {
92    pub async fn new(parsed_cmd: ParsedCommand) -> io::Result<Self> {
93        let Some(command) = parsed_cmd.command else {
94            return Err(Error::new(ErrorKind::Other, "command is None"));
95        };
96        let connect_key = auto_connect_key(parsed_cmd.connect_key, command);
97
98        let stream = match TcpStream::connect(parsed_cmd.server_addr).await {
99            Ok(stream) => stream,
100            Err(_) => return Err(Error::new(ErrorKind::Other, "Connect to server failed")),
101        };
102
103        let (rd, wr) = stream.into_split();
104
105        transfer::ChannelMap::start(rd).await;
106
107        Ok(Self {
108            command,
109            params: parsed_cmd.parameters,
110            connect_key,
111            wr,
112        })
113    }
114
115    async fn execute_command(&mut self) -> io::Result<()> {
116        let entire_cmd = self.params.join(" ");
117        hdc::debug!("execute command params: {}", &entire_cmd);
118
119        match self.command {
120            HdcCommand::KernelTargetList
121            | HdcCommand::KernelTargetConnect
122            | HdcCommand::UnityHilog => self.general_task().await,
123            HdcCommand::FileInit | HdcCommand::FileCheck | HdcCommand::FileRecvInit => {
124                self.file_send_task().await
125            }
126            HdcCommand::AppInit => self.app_install_task().await,
127            HdcCommand::AppUninstall => self.app_uninstall_task().await,
128            HdcCommand::UnityRunmode
129            | HdcCommand::UnityReboot
130            | HdcCommand::UnityRemount => self.unity_task().await,
131            HdcCommand::UnityRootrun => self.unity_root_run_task().await,
132            HdcCommand::UnityExecute => self.shell_task().await,
133            HdcCommand::KernelWaitFor => self.wait_task().await,
134            HdcCommand::UnityBugreportInit => self.bug_report_task().await,
135            HdcCommand::ForwardInit
136            | HdcCommand::ForwardRportInit
137            | HdcCommand::ForwardList
138            | HdcCommand::ForwardRportList
139            | HdcCommand::ForwardRemove
140            | HdcCommand::ForwardRportRemove => {
141                self.forward_task().await
142            }
143            HdcCommand::JdwpList | HdcCommand::JdwpTrack => self.jdwp_task().await,
144            HdcCommand::KernelCheckServer => self.check_server_task().await,
145            _ => Err(Error::new(
146                ErrorKind::Other,
147                format!("unknown command: {}", self.command as u32),
148            )),
149        }
150    }
151
152    pub async fn handshake(&mut self) -> io::Result<()> {
153        let recv = self.recv().await?;
154        let msg = match std::str::from_utf8(&recv[..config::HANDSHAKE_MESSAGE.len()]) {
155            Ok(msg) => msg,
156            Err(err) => {
157                return Err(Error::new(ErrorKind::Other, format!("handshake from_utf8 error : {err}")));
158            }
159        };
160        if msg != config::HANDSHAKE_MESSAGE {
161            return Err(Error::new(ErrorKind::Other, "Recv server-hello failed"));
162        }
163        let buf = [
164            config::HANDSHAKE_MESSAGE.as_bytes(),
165            vec![0_u8; config::BANNER_SIZE - config::HANDSHAKE_MESSAGE.len()].as_slice(),
166            self.connect_key.as_bytes(),
167            vec![0_u8; config::KEY_MAX_SIZE - self.connect_key.len()].as_slice(),
168        ]
169        .concat();
170
171        self.send(buf.as_slice()).await;
172
173        Ok(())
174    }
175
176    async fn send(&mut self, buf: &[u8]) {
177        hdc::debug!("channel send buf: {:#?}", buf);
178        let msg = [u32::to_be_bytes(buf.len() as u32).as_slice(), buf].concat();
179        let _ = self.wr.write_all(msg.as_slice()).await;
180    }
181
182    async fn recv(&mut self) -> io::Result<Vec<u8>> {
183        hdc::debug!("channel recv buf");
184        transfer::ChannelMap::recv().await
185    }
186
187    async fn unity_task(&mut self) -> io::Result<()> {
188        self.send(self.params.join(" ").as_bytes()).await;
189        self.loop_recv().await
190    }
191
192    async fn wait_task(&mut self) -> io::Result<()> {
193        self.send(self.params.join(" ").as_bytes()).await;
194        self.loop_recv_waitfor().await
195    }
196
197    async fn unity_root_run_task(&mut self) -> io::Result<()> {
198        if self.params.len() >= 2 && self.params[1].starts_with("-r") {
199            self.params[1] = "r".to_string();
200        }
201        self.send(self.params.join(" ").as_bytes()).await;
202        self.loop_recv().await
203    }
204    async fn jdwp_task(&mut self) -> io::Result<()> {
205        self.send(self.params.join(" ").as_bytes()).await;
206        self.loop_recv().await
207    }
208
209
210    #[cfg(target_os = "windows")]
211    async fn  shell_task(&mut self) -> io::Result<()> {
212        let cmd = match self.params.len() {
213            1 => "shell\0".to_string(),
214            _ => self.params.join(" "),
215        };
216
217        self.send(cmd.as_bytes()).await;
218
219        let _handle = ylong_runtime::spawn(async move {
220            loop {
221                match transfer::ChannelMap::recv().await {
222                    Ok(recv) => {
223                        let _ = utils::print_msg(recv).await;
224                    }
225                    Err(_) => {
226                        std::process::exit(0);
227                    }
228                }
229            }
230        });
231
232        loop {
233            let c = unsafe { getch() };
234
235            // 判断如果是ctrl_D,发送给serer端,client直接退出
236            if c == 0x4 {
237                self.send([c as u8].as_slice()).await;
238                break;
239            }
240
241            // win下的控制字符以0xe0开头,转换后发送给server,
242            if c == 0xe0 {
243                let control_code = convert_to_control_code();
244                self.send(control_code.as_slice()).await;
245                continue;
246            }
247
248            let unicode_byte = unicode_assemble(c);
249            hdc::info!("unicode_byte is {:?}", unicode_byte);
250            self.send(unicode_byte.as_slice()).await;
251        }
252        Ok(())
253    }
254
255    #[cfg(not(target_os = "windows"))]
256    async fn shell_task(&mut self) -> io::Result<()> {
257        let cmd = match self.params.len() {
258            1 => "shell\0".to_string(),
259            _ => self.params.join(" "),
260        };
261
262        self.send(cmd.as_bytes()).await;
263
264        let termios = setup_raw_terminal()?;
265        let termios_clone = termios;
266
267        let _handle = ylong_runtime::spawn(async move {
268            loop {
269                match transfer::ChannelMap::recv().await {
270                    Ok(recv) => {
271                        let _ = utils::print_msg(recv).await;
272                    }
273                    Err(_) => {
274                        let _ = recover_terminal(termios_clone);
275
276                        std::process::exit(0);
277                    }
278                }
279            }
280        });
281
282        let mut buf = [0_u8; 1];
283        let mut stdin = ylong_runtime::io::stdin();
284
285        while let Ok(bytes) = stdin.read(&mut buf).await {
286            self.send(&buf[..bytes]).await;
287            if buf[..bytes].contains(&0x4_u8) {
288                break;
289            }
290        }
291
292        let _ = recover_terminal(termios);
293        Ok(())
294    }
295
296    async fn forward_task(&mut self) -> io::Result<()> {
297        if (self.command == HdcCommand::ForwardRemove
298            || self.command == HdcCommand::ForwardRportRemove)
299            && self.params.len() < 3
300        {
301            return Err(Error::new(
302                ErrorKind::Other,
303                "Too few arguments.".to_string()
304            ));
305        }
306        if (self.command == HdcCommand::ForwardInit
307            || self.command == HdcCommand::ForwardRportInit)
308            && self.params.len() < 3
309        {
310            return Err(Error::new(
311                ErrorKind::Other,
312                "Too few arguments.".to_string()
313            ));
314        }
315        self.send(self.params.join(" ").as_bytes()).await;
316        self.loop_recv().await
317    }
318
319    async fn general_task(&mut self) -> io::Result<()> {
320        self.send(self.params.join(" ").as_bytes()).await;
321        loop {
322            let recv = self.recv().await?;
323            hdc::debug!(
324                "general_task recv: {:#?}",
325                recv.iter()
326                    .map(|c| format!("{c:02x}"))
327                    .collect::<Vec<_>>()
328                    .join(" ")
329            );
330            let _ = utils::print_msg(recv).await;
331        }
332    }
333
334    async fn bug_report_task(&mut self) -> io::Result<()> {
335        if self.params.len() <= 1 {
336            return self.general_task().await;
337        }
338        self.send(self.params.join(" ").as_bytes()).await;
339        let mut file = std::fs::File::create(self.params[1].as_str())?;
340        loop {
341            let recv = self.recv().await?;
342            file.write_all(&recv)?;
343        }
344    }
345
346    async fn file_send_task(&mut self) -> io::Result<()> {
347        let mut params = self.params.clone();
348        if self.command == HdcCommand::FileInit || self.command == HdcCommand::FileRecvInit {
349            let command_field_count = 2;
350            let current_dir = env::current_dir()?;
351            let mut s = current_dir.display().to_string();
352            s.push(Base::get_path_sep());
353            params.insert(command_field_count, "-cwd".to_string());
354            params.insert(command_field_count + 1, s.clone());
355        }
356
357        self.send(params.join(" ").as_bytes()).await;
358        self.loop_recv().await
359    }
360
361    async fn loop_recv(&mut self) -> io::Result<()> {
362        loop {
363            let recv = self.recv().await;
364            match recv {
365                Ok(recv) => {
366                    hdc::debug!(
367                        "recv: {:#?}",
368                        recv.iter()
369                            .map(|c| format!("{c:02x}"))
370                            .collect::<Vec<_>>()
371                            .join(" ")
372                    );
373                    match String::from_utf8(recv) {
374                        Ok(msg) => print!("{msg}"),
375                        Err(err) => return Err(Error::new(ErrorKind::Other, format!("recv data to str failed, {err}"))),
376                    }
377                }
378                Err(e) => {
379                    return Err(e);
380                }
381            }
382        }
383    }
384
385    async fn loop_recv_waitfor(&mut self) -> io::Result<()> {
386        loop {
387            let recv = self.recv().await;
388            match recv {
389                Ok(recv) => {
390                    hdc::debug!(
391                        "recv: {:#?}",
392                        recv.iter()
393                            .map(|c| format!("{c:02x}"))
394                            .collect::<Vec<_>>()
395                            .join(" ")
396                    );
397                    if let HdcCommand::KernelWaitFor = self.command {
398                        let wait_for = "No connected target\r\n".to_string();
399                        if wait_for == String::from_utf8(recv).expect("invalid UTF-8") {
400                            self.send(self.params.join(" ").as_bytes()).await;
401                            hdc::debug!("WaitFor sleep a second");
402                            let wait_interval = 1000;
403                            ylong_runtime::time::sleep(Duration::from_millis(wait_interval)).await;
404                        } else {
405                            hdc::debug!("exit client");
406                            unsafe {
407                                exit(0);
408                            }
409                        }
410                    }
411                }
412                Err(e) => {
413                    return Err(e);
414                }
415            }
416        }
417    }
418
419    async fn app_install_task(&mut self) -> io::Result<()> {
420        let mut params = self.params.clone();
421        let command_field_count = 1;
422        let current_dir = env::current_dir()?;
423        let mut s = current_dir.display().to_string();
424        s.push(Base::get_path_sep());
425        params.insert(command_field_count, "-cwd".to_string());
426        params.insert(command_field_count + 1, s.clone());
427
428        self.send(params.join(" ").as_bytes()).await;
429
430        loop {
431            let recv = self.recv().await;
432            match recv {
433                Ok(recv) => {
434                    hdc::debug!(
435                        "app_install_task recv: {:#?}",
436                        recv.iter()
437                            .map(|c| format!("{c:02x}"))
438                            .collect::<Vec<_>>()
439                            .join(" ")
440                    );
441                    match String::from_utf8(recv) {
442                        Ok(msg) => print!("{}", msg),
443                        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{e}"))),
444                    }
445                }
446                Err(e) => {
447                    return Err(e);
448                }
449            }
450        }
451    }
452
453    async fn app_uninstall_task(&mut self) -> io::Result<()> {
454        let params = self.params.clone();
455        self.send(params.join(" ").as_bytes()).await;
456
457        loop {
458            let recv = self.recv().await;
459            match recv {
460                Ok(recv) => {
461                    hdc::debug!(
462                        "app_uninstall_task recv: {:#?}",
463                        recv.iter()
464                            .map(|c| format!("{c:02x}"))
465                            .collect::<Vec<_>>()
466                            .join(" ")
467                    );
468                    match String::from_utf8(recv) {
469                        Ok(msg) => println!("{msg}"),
470                        Err(e) => {
471                            return Err(io::Error::new(io::ErrorKind::Other, format!("{e}")));
472                        }
473                    }
474                }
475                Err(e) => {
476                    return Err(e);
477                }
478            }
479        }
480    }
481
482    async fn check_server_task(&mut self) -> io::Result<()> {
483        let params = self.params.clone();
484        self.send(params.join(" ").as_bytes()).await;
485
486        let recv = self.recv().await;
487        match recv {
488            Ok(recv) => {
489                hdc::debug!(
490                    "check_server_task recv: {:#?}",
491                    recv.iter()
492                        .map(|c| format!("{c:02x}"))
493                        .collect::<Vec<_>>()
494                        .join(" ")
495                );
496
497                const CMD_U8_LEN: usize = 2;
498                if recv.len() < CMD_U8_LEN {
499                    return Err(Error::new(io::ErrorKind::Other, "recv failed"));
500                }
501
502                let (cmd_slice, version_slice) = recv.split_at(CMD_U8_LEN);
503                let Ok(cmd) = HdcCommand::try_from(u16::from_le_bytes(cmd_slice.try_into().unwrap_or_default()) as u32)
504                else {
505                    return Err(Error::new(io::ErrorKind::Other, "HdcCommand::try_from failed"));
506                };
507                if HdcCommand::KernelCheckServer != cmd {
508                    return Err(Error::new(io::ErrorKind::Other, "recv cmd error"));
509                }
510                match String::from_utf8(version_slice.to_vec()) {
511                    Ok(s_ver) => println!("Client version:{}, server version:{}", config::get_version(), s_ver),
512                    Err(err) => {
513                        return Err(Error::new(io::ErrorKind::Other, format!("from_utf8 failed, {err}")));
514                    }
515                }
516                Ok(())
517            }
518            Err(e) => Err(e),
519        }
520    }
521}
522
523fn auto_connect_key(key: String, cmd: HdcCommand) -> String {
524    match cmd {
525        HdcCommand::ClientVersion
526        | HdcCommand::KernelHelp
527        | HdcCommand::KernelTargetDiscover
528        | HdcCommand::KernelTargetList
529        | HdcCommand::KernelCheckServer
530        | HdcCommand::KernelTargetConnect
531        | HdcCommand::KernelCheckDevice
532        | HdcCommand::KernelServerKill => "".to_string(),
533        _ => {
534            if key.is_empty() {
535                "any".to_string()
536            } else {
537                key
538            }
539        }
540    }
541}
542
543#[cfg(not(target_os = "windows"))]
544fn setup_raw_terminal() -> io::Result<libc::termios> {
545    unsafe {
546        let tty;
547        let fd = if libc::isatty(libc::STDIN_FILENO) == 1 {
548            libc::STDIN_FILENO
549        } else {
550            tty = std::fs::File::open("/dev/tty")?;
551            tty.as_raw_fd()
552        };
553
554        let mut ptr = core::mem::MaybeUninit::uninit();
555
556        if libc::tcgetattr(fd, ptr.as_mut_ptr()) == 0 {
557            let termios = ptr.assume_init();
558            let mut termios_copy = termios;
559            let c_oflag = termios.c_oflag;
560            libc::cfmakeraw(&mut termios_copy);
561            termios_copy.c_oflag = c_oflag;
562
563            if libc::tcsetattr(fd, libc::TCSADRAIN, &termios_copy) == 0 {
564                return Ok(termios);
565            }
566        }
567    }
568
569    Err(io::Error::last_os_error())
570}
571
572#[cfg(not(target_os = "windows"))]
573fn recover_terminal(termios: libc::termios) -> io::Result<()> {
574    unsafe {
575        let tty;
576        let fd = if libc::isatty(libc::STDIN_FILENO) == 1 {
577            libc::STDIN_FILENO
578        } else {
579            tty = std::fs::File::open("/dev/tty")?;
580            tty.as_raw_fd()
581        };
582        if libc::tcsetattr(fd, libc::TCSADRAIN, &termios) == 0 {
583            Ok(())
584        } else {
585            Err(io::Error::last_os_error())
586        }
587    }
588}
589