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 */
15 use super::translate;
16
17 use hdc::config::{self, HdcCommand};
18
19 use hdc::utils;
20 use std::collections::HashMap;
21 use std::io::{self, Error, ErrorKind};
22 use std::str::FromStr;
23
24 #[derive(Default, Debug, Clone)]
25 pub struct Parsed {
26 pub options: Vec<String>,
27 pub command: Option<HdcCommand>,
28 pub parameters: Vec<String>,
29 }
30
31 lazy_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
77 const MAX_CMD_LEN: usize = 3;
78 const MAX_CONNECTKEY_SIZE: u16 = 32;
79 const COMBINED_COMMAND_LEN: usize = 2;
80
81 // TODO: trial tree
split_opt_and_cmdnull82 pub 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
parse_commandnull127 pub 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
exchange_parsed_for_daemonnull140 pub 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)]
159 pub 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
extract_global_paramsnull170 pub 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
check_portnull242 fn 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
parse_server_listen_stringnull249 fn 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