xref: /developtools/hdc/hdc_rust/src/host/parser.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::translate;
16
17use hdc::config::{self, HdcCommand};
18
19use hdc::utils;
20use std::collections::HashMap;
21use std::io::{self, Error, ErrorKind};
22use std::str::FromStr;
23
24#[derive(Default, Debug, Clone)]
25pub struct Parsed {
26    pub options: Vec<String>,
27    pub command: Option<HdcCommand>,
28    pub parameters: Vec<String>,
29}
30
31lazy_static! {
32    static ref CMD_MAP: HashMap<&'static str, HdcCommand> = {
33        let mut map = HashMap::new();
34
35        map.insert("version", HdcCommand::ClientVersion);
36        map.insert("help", HdcCommand::KernelHelp);
37        map.insert("discover", HdcCommand::KernelTargetDiscover);
38        map.insert("start", HdcCommand::KernelServerStart);
39        map.insert("kill", HdcCommand::KernelServerKill);
40        map.insert("keygen", HdcCommand::ClientKeyGenerate);
41        map.insert("list targets", HdcCommand::KernelTargetList);
42        map.insert("checkserver", HdcCommand::KernelCheckServer);
43        map.insert("checkdevice", HdcCommand::KernelCheckDevice);
44        map.insert("wait", HdcCommand::KernelWaitFor);
45        map.insert("tconn", HdcCommand::KernelTargetConnect);
46        map.insert("any", HdcCommand::KernelTargetAny);
47        map.insert("shell", HdcCommand::UnityExecute);
48        map.insert("target boot", HdcCommand::UnityReboot);
49        map.insert("target mount", HdcCommand::UnityRemount);
50        map.insert("smode", HdcCommand::UnityRootrun);
51        map.insert("tmode", HdcCommand::UnityRunmode);
52        map.insert("bugreport", HdcCommand::UnityBugreportInit);
53        map.insert("hilog", HdcCommand::UnityHilog);
54        map.insert("file send", HdcCommand::FileInit);
55        map.insert("file recv", HdcCommand::FileRecvInit);
56        map.insert("fport", HdcCommand::ForwardInit);
57        map.insert("rport", HdcCommand::ForwardRportInit);
58        map.insert("rport ls", HdcCommand::ForwardRportList);
59        map.insert("fport ls", HdcCommand::ForwardList);
60        map.insert("fport rm", HdcCommand::ForwardRemove);
61        map.insert("rport rm", HdcCommand::ForwardRportRemove);
62        map.insert("install", HdcCommand::AppInit);
63        map.insert("uninstall", HdcCommand::AppUninstall);
64        map.insert("sideload", HdcCommand::AppSideload);
65        map.insert("jpid", HdcCommand::JdwpList);
66        map.insert("track-jpid", HdcCommand::JdwpTrack);
67        map.insert("alive", HdcCommand::KernelEnableKeepalive);
68        map.insert("update", HdcCommand::FlashdUpdateInit);
69        map.insert("flash", HdcCommand::FlashdFlashInit);
70        map.insert("erase", HdcCommand::FlashdErase);
71        map.insert("format", HdcCommand::FlashdFormat);
72
73        map
74    };
75}
76
77const MAX_CMD_LEN: usize = 3;
78const MAX_CONNECTKEY_SIZE: u16 = 32;
79const COMBINED_COMMAND_LEN: usize = 2;
80
81// TODO: trial tree
82pub fn split_opt_and_cmd(input: Vec<String>) -> Parsed {
83    let mut cmd_opt: Option<HdcCommand> = None;
84    let mut cmd_index = input.len();
85    for st in 0..input.len() {
86        for len in 1..MAX_CMD_LEN {
87            if st + len > input.len() {
88                break;
89            }
90            let cmd = input[st..st + len].join(" ");
91            if let Some(command) = CMD_MAP.get(cmd.as_str()) {
92                // if first command parsed is "fport", but next command parsed
93                // is not "fport ls" or "fport rm", discard the new command.
94                if cmd_opt.is_some()
95                    && (cmd_opt.unwrap() == HdcCommand::ForwardInit
96                        || cmd_opt.unwrap() == HdcCommand::ForwardRportInit)
97                    && (*command != HdcCommand::ForwardRemove
98                        && *command != HdcCommand::ForwardList
99                        && *command != HdcCommand::ForwardRportList
100                        && *command != HdcCommand::ForwardRportRemove)
101                {
102                    break;
103                }
104                cmd_index = st;
105                cmd_opt = Some(command.to_owned());
106                if *command == HdcCommand::ForwardInit || *command == HdcCommand::ForwardRportInit {
107                    continue;
108                } else {
109                    break;
110                }
111            }
112        }
113        if cmd_opt.is_some()
114            && cmd_opt.unwrap() != HdcCommand::ForwardInit
115            && cmd_opt.unwrap() != HdcCommand::ForwardRportInit
116        {
117            break;
118        }
119    }
120    Parsed {
121        options: input[..cmd_index].to_vec(),
122        command: cmd_opt,
123        parameters: input[cmd_index..].to_vec(),
124    }
125}
126
127pub fn parse_command(args: std::env::Args) -> io::Result<ParsedCommand> {
128    let input = args.collect::<Vec<_>>()[1..].to_vec();
129    let parsed = split_opt_and_cmd(input);
130    match extract_global_params(parsed.options) {
131        Ok(parsed_cmd) => Ok(ParsedCommand {
132            command: parsed.command,
133            parameters: parsed.parameters,
134            ..parsed_cmd
135        }),
136        Err(e) => Err(e),
137    }
138}
139
140pub fn exchange_parsed_for_daemon(mut parsed: Parsed) -> Parsed  {
141    if let Some(HdcCommand::UnityReboot) = parsed.command {
142        if parsed.parameters.len() > COMBINED_COMMAND_LEN {
143            let valid_boot_cmd = ["-bootloader", "-recovery", "-flashd"];
144            for str in valid_boot_cmd {
145                if str == parsed.parameters[COMBINED_COMMAND_LEN].as_str() {
146                    parsed.parameters.clear();
147                    parsed.parameters.push(str.to_string()[1..].to_string());
148                    return parsed;
149                }
150            }
151        }
152        parsed.parameters.clear();
153        hdc::info!("parsed parameter is {:?}", parsed.parameters);
154    }
155    parsed
156}
157
158#[derive(Default, Debug, PartialEq)]
159pub struct ParsedCommand {
160    pub run_in_server: bool,
161    pub launch_server: bool,
162    pub spawned_server: bool,
163    pub connect_key: String,
164    pub log_level: usize,
165    pub server_addr: String,
166    pub command: Option<HdcCommand>,
167    pub parameters: Vec<String>,
168}
169
170pub fn extract_global_params(opts: Vec<String>) -> io::Result<ParsedCommand> {
171    let mut parsed_command = ParsedCommand {
172        launch_server: true,
173        log_level: 3,
174        server_addr: format!("127.0.0.1:{}", config::SERVER_DEFAULT_PORT),
175        ..Default::default()
176    };
177    let len = opts.len();
178    for i in 0..len {
179        let opt = opts[i].as_str();
180        let arg = if opt.len() > 2 {
181            &opt[2..]
182        } else if i < len - 1 {
183            opts[i + 1].as_str()
184        } else {
185            ""
186        };
187        if opt.starts_with("-h") {
188            if arg == "verbose" {
189                return Err(utils::error_other(translate::verbose()));
190            } else {
191                return Err(utils::error_other(translate::usage()));
192            }
193        } else if opt.starts_with("-v") {
194            return Err(utils::error_other(config::get_version()));
195        } else if opt.starts_with("-l") {
196            if let Ok(level) = arg.parse::<usize>() {
197                if level < config::LOG_LEVEL_ORDER.len() {
198                    parsed_command.log_level = level;
199                } else {
200                    return Err(utils::error_other(format!(
201                        "-l content loglevel incorrect\n\n{}",
202                        translate::usage()
203                    )));
204                }
205            } else {
206                return Err(utils::error_other(format!(
207                    "-l content loglevel incorrect\n\n{}",
208                    translate::usage()
209                )));
210            }
211        } else if opt.starts_with("-m") {
212            parsed_command.run_in_server = true;
213        } else if opt.starts_with("-p") {
214            parsed_command.launch_server = false;
215        } else if opt.starts_with("-t") {
216            parsed_command.connect_key = arg.to_string();
217            if arg.len() > MAX_CONNECTKEY_SIZE.into() {
218                return Err(utils::error_other(format!(
219                    "Sizeo of of parament '-t' {} is too long\n",
220                    arg.len()
221                )));
222            }
223        } else if opt.starts_with("-s") {
224            match parse_server_listen_string(arg.to_string()) {
225                Ok(server_addr) => parsed_command.server_addr = server_addr,
226                Err(e) => {
227                    return Err(utils::error_other(format!(
228                        "{}\n\n{}",
229                        e,
230                        translate::usage()
231                    )));
232                }
233            }
234        } else if opt.starts_with("-b") {
235            // server spawned by client, no stdout
236            parsed_command.spawned_server = true;
237        }
238    }
239    Ok(parsed_command)
240}
241
242fn check_port(port_str: String) -> io::Result<u16> {
243    if let Ok(port) = port_str.parse::<u16>() {
244        return Ok(port);
245    }
246    Err(Error::new(ErrorKind::Other, "-s content port incorrect"))
247}
248
249fn parse_server_listen_string(arg: String) -> io::Result<String> {
250    let segments: Vec<&str> = arg.split(':').collect();
251    let port_str = match segments.last() {
252        Some(str) => str.to_string(),
253        None => {
254            return Err(Error::new(ErrorKind::Other, "-s content ip incorrect"));
255        }
256    };
257    let port_len = port_str.len();
258    let port = check_port(port_str)?;
259
260    if segments.len() == 1 {
261        return Ok(format!(
262            // "{}{}:{}",
263            // config::IPV4_MAPPING_PREFIX,
264            "{}:{}",
265            config::LOCAL_HOST,
266            port
267        ));
268    }
269
270    let ip_str = &arg[..arg.len() - port_len - 1];
271    match std::net::IpAddr::from_str(ip_str) {
272        Ok(ip_addr) => {
273            if ip_addr.is_ipv4() || ip_addr.is_ipv6() {
274                Ok(arg)
275            } else {
276                Err(Error::new(ErrorKind::Other, "-s content ip incorrect"))
277            }
278        }
279        _ => Err(Error::new(ErrorKind::Other, "-s content ip incorrect")),
280    }
281}
282