/* * 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::translate; use hdc::config::{self, HdcCommand}; use hdc::utils; use std::collections::HashMap; use std::io::{self, Error, ErrorKind}; use std::str::FromStr; #[derive(Default, Debug, Clone)] pub struct Parsed { pub options: Vec, pub command: Option, pub parameters: Vec, } lazy_static! { static ref CMD_MAP: HashMap<&'static str, HdcCommand> = { let mut map = HashMap::new(); map.insert("version", HdcCommand::ClientVersion); map.insert("help", HdcCommand::KernelHelp); map.insert("discover", HdcCommand::KernelTargetDiscover); map.insert("start", HdcCommand::KernelServerStart); map.insert("kill", HdcCommand::KernelServerKill); map.insert("keygen", HdcCommand::ClientKeyGenerate); map.insert("list targets", HdcCommand::KernelTargetList); map.insert("checkserver", HdcCommand::KernelCheckServer); map.insert("checkdevice", HdcCommand::KernelCheckDevice); map.insert("wait", HdcCommand::KernelWaitFor); map.insert("tconn", HdcCommand::KernelTargetConnect); map.insert("any", HdcCommand::KernelTargetAny); map.insert("shell", HdcCommand::UnityExecute); map.insert("target boot", HdcCommand::UnityReboot); map.insert("target mount", HdcCommand::UnityRemount); map.insert("smode", HdcCommand::UnityRootrun); map.insert("tmode", HdcCommand::UnityRunmode); map.insert("bugreport", HdcCommand::UnityBugreportInit); map.insert("hilog", HdcCommand::UnityHilog); map.insert("file send", HdcCommand::FileInit); map.insert("file recv", HdcCommand::FileRecvInit); map.insert("fport", HdcCommand::ForwardInit); map.insert("rport", HdcCommand::ForwardRportInit); map.insert("rport ls", HdcCommand::ForwardRportList); map.insert("fport ls", HdcCommand::ForwardList); map.insert("fport rm", HdcCommand::ForwardRemove); map.insert("rport rm", HdcCommand::ForwardRportRemove); map.insert("install", HdcCommand::AppInit); map.insert("uninstall", HdcCommand::AppUninstall); map.insert("sideload", HdcCommand::AppSideload); map.insert("jpid", HdcCommand::JdwpList); map.insert("track-jpid", HdcCommand::JdwpTrack); map.insert("alive", HdcCommand::KernelEnableKeepalive); map.insert("update", HdcCommand::FlashdUpdateInit); map.insert("flash", HdcCommand::FlashdFlashInit); map.insert("erase", HdcCommand::FlashdErase); map.insert("format", HdcCommand::FlashdFormat); map }; } const MAX_CMD_LEN: usize = 3; const MAX_CONNECTKEY_SIZE: u16 = 32; const COMBINED_COMMAND_LEN: usize = 2; // TODO: trial tree pub fn split_opt_and_cmd(input: Vec) -> Parsed { let mut cmd_opt: Option = None; let mut cmd_index = input.len(); for st in 0..input.len() { for len in 1..MAX_CMD_LEN { if st + len > input.len() { break; } let cmd = input[st..st + len].join(" "); if let Some(command) = CMD_MAP.get(cmd.as_str()) { // if first command parsed is "fport", but next command parsed // is not "fport ls" or "fport rm", discard the new command. if cmd_opt.is_some() && (cmd_opt.unwrap() == HdcCommand::ForwardInit || cmd_opt.unwrap() == HdcCommand::ForwardRportInit) && (*command != HdcCommand::ForwardRemove && *command != HdcCommand::ForwardList && *command != HdcCommand::ForwardRportList && *command != HdcCommand::ForwardRportRemove) { break; } cmd_index = st; cmd_opt = Some(command.to_owned()); if *command == HdcCommand::ForwardInit || *command == HdcCommand::ForwardRportInit { continue; } else { break; } } } if cmd_opt.is_some() && cmd_opt.unwrap() != HdcCommand::ForwardInit && cmd_opt.unwrap() != HdcCommand::ForwardRportInit { break; } } Parsed { options: input[..cmd_index].to_vec(), command: cmd_opt, parameters: input[cmd_index..].to_vec(), } } pub fn parse_command(args: std::env::Args) -> io::Result { let input = args.collect::>()[1..].to_vec(); let parsed = split_opt_and_cmd(input); match extract_global_params(parsed.options) { Ok(parsed_cmd) => Ok(ParsedCommand { command: parsed.command, parameters: parsed.parameters, ..parsed_cmd }), Err(e) => Err(e), } } pub fn exchange_parsed_for_daemon(mut parsed: Parsed) -> Parsed { if let Some(HdcCommand::UnityReboot) = parsed.command { if parsed.parameters.len() > COMBINED_COMMAND_LEN { let valid_boot_cmd = ["-bootloader", "-recovery", "-flashd"]; for str in valid_boot_cmd { if str == parsed.parameters[COMBINED_COMMAND_LEN].as_str() { parsed.parameters.clear(); parsed.parameters.push(str.to_string()[1..].to_string()); return parsed; } } } parsed.parameters.clear(); hdc::info!("parsed parameter is {:?}", parsed.parameters); } parsed } #[derive(Default, Debug, PartialEq)] pub struct ParsedCommand { pub run_in_server: bool, pub launch_server: bool, pub spawned_server: bool, pub connect_key: String, pub log_level: usize, pub server_addr: String, pub command: Option, pub parameters: Vec, } pub fn extract_global_params(opts: Vec) -> io::Result { let mut parsed_command = ParsedCommand { launch_server: true, log_level: 3, server_addr: format!("127.0.0.1:{}", config::SERVER_DEFAULT_PORT), ..Default::default() }; let len = opts.len(); for i in 0..len { let opt = opts[i].as_str(); let arg = if opt.len() > 2 { &opt[2..] } else if i < len - 1 { opts[i + 1].as_str() } else { "" }; if opt.starts_with("-h") { if arg == "verbose" { return Err(utils::error_other(translate::verbose())); } else { return Err(utils::error_other(translate::usage())); } } else if opt.starts_with("-v") { return Err(utils::error_other(config::get_version())); } else if opt.starts_with("-l") { if let Ok(level) = arg.parse::() { if level < config::LOG_LEVEL_ORDER.len() { parsed_command.log_level = level; } else { return Err(utils::error_other(format!( "-l content loglevel incorrect\n\n{}", translate::usage() ))); } } else { return Err(utils::error_other(format!( "-l content loglevel incorrect\n\n{}", translate::usage() ))); } } else if opt.starts_with("-m") { parsed_command.run_in_server = true; } else if opt.starts_with("-p") { parsed_command.launch_server = false; } else if opt.starts_with("-t") { parsed_command.connect_key = arg.to_string(); if arg.len() > MAX_CONNECTKEY_SIZE.into() { return Err(utils::error_other(format!( "Sizeo of of parament '-t' {} is too long\n", arg.len() ))); } } else if opt.starts_with("-s") { match parse_server_listen_string(arg.to_string()) { Ok(server_addr) => parsed_command.server_addr = server_addr, Err(e) => { return Err(utils::error_other(format!( "{}\n\n{}", e, translate::usage() ))); } } } else if opt.starts_with("-b") { // server spawned by client, no stdout parsed_command.spawned_server = true; } } Ok(parsed_command) } fn check_port(port_str: String) -> io::Result { if let Ok(port) = port_str.parse::() { return Ok(port); } Err(Error::new(ErrorKind::Other, "-s content port incorrect")) } fn parse_server_listen_string(arg: String) -> io::Result { let segments: Vec<&str> = arg.split(':').collect(); let port_str = match segments.last() { Some(str) => str.to_string(), None => { return Err(Error::new(ErrorKind::Other, "-s content ip incorrect")); } }; let port_len = port_str.len(); let port = check_port(port_str)?; if segments.len() == 1 { return Ok(format!( // "{}{}:{}", // config::IPV4_MAPPING_PREFIX, "{}:{}", config::LOCAL_HOST, port )); } let ip_str = &arg[..arg.len() - port_len - 1]; match std::net::IpAddr::from_str(ip_str) { Ok(ip_addr) => { if ip_addr.is_ipv4() || ip_addr.is_ipv6() { Ok(arg) } else { Err(Error::new(ErrorKind::Other, "-s content ip incorrect")) } } _ => Err(Error::new(ErrorKind::Other, "-s content ip incorrect")), } }