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