* 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<String>,
pub command: Option<HdcCommand>,
pub parameters: Vec<String>,
}
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;
pub fn split_opt_and_cmd(input: Vec<String>) -> Parsed {
let mut cmd_opt: Option<HdcCommand> = 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 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<ParsedCommand> {
let input = args.collect::<Vec<_>>()[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<HdcCommand>,
pub parameters: Vec<String>,
}
pub fn extract_global_params(opts: Vec<String>) -> io::Result<ParsedCommand> {
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::<usize>() {
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") {
parsed_command.spawned_server = true;
}
}
Ok(parsed_command)
}
fn check_port(port_str: String) -> io::Result<u16> {
if let Ok(port) = port_str.parse::<u16>() {
return Ok(port);
}
Err(Error::new(ErrorKind::Other, "-s content port incorrect"))
}
fn parse_server_listen_string(arg: String) -> io::Result<String> {
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::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")),
}
}