1/* 2 * Copyright (C) 2021 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 16#include <iostream> 17#include "ext_client.h" 18#include "server.h" 19#include "server_for_client.h" 20 21#ifdef _WIN32 22#include <windows.h> 23#endif 24 25#ifndef HARMONY_PROJECT 26#include "ut_command.h" 27using namespace HdcTest; 28#endif 29 30using namespace Hdc; 31 32namespace { 33 bool g_isServerMode = false; 34 bool g_isPullServer = true; 35 bool g_isPcDebugRun = false; 36 bool g_isTCPorUSB = false; 37 bool g_isCustomLoglevel = false; 38 bool g_externalCmd = false; 39 int g_isTestMethod = 0; 40 string g_connectKey = ""; 41 string g_serverListenString = ""; 42 string g_containerInOut = ""; 43} 44 45namespace Hdc { 46// return value: 0 == not command, 1 == one command, 2 == double command 47int IsRegisterCommand(string &outCommand, const char *cmd, const char *cmdnext) 48{ 49 string sCmdNext = cmdnext == nullptr ? string("") : string(cmdnext); 50 string doubleCommand = cmd + string(" ") + sCmdNext; 51 vector<string> registerCommand; 52 registerCommand.push_back(CMDSTR_SOFTWARE_VERSION); 53 registerCommand.push_back(CMDSTR_SOFTWARE_HELP); 54 registerCommand.push_back(CMDSTR_TARGET_DISCOVER); 55 registerCommand.push_back(CMDSTR_LIST_TARGETS); 56 registerCommand.push_back(CMDSTR_CHECK_SERVER); 57 registerCommand.push_back(CMDSTR_CHECK_DEVICE); 58 registerCommand.push_back(CMDSTR_WAIT_FOR); 59 registerCommand.push_back(CMDSTR_CONNECT_ANY); 60 registerCommand.push_back(CMDSTR_CONNECT_TARGET); 61 registerCommand.push_back(CMDSTR_SHELL); 62 registerCommand.push_back(CMDSTR_FILE_SEND); 63 registerCommand.push_back(CMDSTR_FILE_RECV); 64 registerCommand.push_back(CMDSTR_FORWARD_FPORT); 65 registerCommand.push_back(CMDSTR_FORWARD_RPORT); 66 registerCommand.push_back(CMDSTR_SERVICE_KILL); 67 registerCommand.push_back(CMDSTR_SERVICE_START); 68 registerCommand.push_back(CMDSTR_GENERATE_KEY); 69 registerCommand.push_back(CMDSTR_APP_INSTALL); 70 registerCommand.push_back(CMDSTR_APP_UNINSTALL); 71 registerCommand.push_back(CMDSTR_TARGET_MOUNT); 72 registerCommand.push_back(CMDSTR_HILOG); 73 registerCommand.push_back(CMDSTR_STARTUP_MODE); 74 registerCommand.push_back(CMDSTR_BUGREPORT); 75 registerCommand.push_back(CMDSTR_TARGET_MODE); 76 registerCommand.push_back(CMDSTR_APP_SIDELOAD); 77 registerCommand.push_back(CMDSTR_TARGET_REBOOT); 78 registerCommand.push_back(CMDSTR_LIST_JDWP); 79 registerCommand.push_back(CMDSTR_TRACK_JDWP); 80 registerCommand.push_back(CMDSTR_FLASHD_UPDATE); 81 registerCommand.push_back(CMDSTR_FLASHD_FLASH); 82 registerCommand.push_back(CMDSTR_FLASHD_ERASE); 83 registerCommand.push_back(CMDSTR_FLASHD_FORMAT); 84 85 for (string v : registerCommand) { 86 if (doubleCommand == v) { 87 outCommand = doubleCommand; 88 return CMD_ARG1_COUNT; 89 } 90 if (cmd == v || !strncmp(cmd, CMDSTR_WAIT_FOR.c_str(), CMDSTR_WAIT_FOR.size())) { 91 outCommand = cmd; 92 return 1; 93 } 94 } 95 return 0; 96} 97 98void AppendCwdWhenTransfer(string &outCommand) 99{ 100 if (outCommand != CMDSTR_FILE_SEND && outCommand != CMDSTR_FILE_RECV && outCommand != CMDSTR_APP_INSTALL && 101 outCommand != CMDSTR_APP_SIDELOAD) { 102 return; 103 } 104 int value = -1; 105 char path[PATH_MAX] = ""; 106 size_t size = sizeof(path); 107 value = uv_cwd(path, &size); 108 if (value < 0) { 109 constexpr int bufSize = 1024; 110 char buf[bufSize] = { 0 }; 111 uv_strerror_r(value, buf, bufSize); 112 WRITE_LOG(LOG_FATAL, "append cwd path failed: %s", buf); 113 return; 114 } 115 if (strlen(path) >= PATH_MAX - 1) { 116 WRITE_LOG(LOG_FATAL, "append cwd path failed: buffer space max"); 117 return; 118 } 119 if (path[strlen(path) - 1] != Base::GetPathSep()) { 120 path[strlen(path)] = Base::GetPathSep(); 121 } 122 outCommand += outCommand.size() ? " " : ""; 123 outCommand += CMDSTR_REMOTE_PARAMETER; 124 outCommand += outCommand.size() ? " -cwd " : "-cwd "; 125 outCommand += Base::StringFormat("\"%s\"", path); 126} 127 128int SplitOptionAndCommand(int argc, const char **argv, string &outOption, string &outCommand) 129{ 130 bool foundCommand = false; 131 int resultChild = 0; 132 // we want to start from 1, ignore argv[0], but it has issue 133 for (int i = 0; i < argc; ++i) { 134 if (!foundCommand) { 135 resultChild = IsRegisterCommand(outCommand, argv[i], (i == argc - 1) ? nullptr : argv[i + 1]); 136 if (resultChild > 0) { 137 foundCommand = true; 138 if (resultChild == 2) { 139 ++i; 140 } 141 AppendCwdWhenTransfer(outCommand); 142 continue; 143 } 144 } 145 if (foundCommand) { 146 outCommand += outCommand.size() ? " " : ""; 147 string rawCmd = Base::UnicodeToUtf8(argv[i]); 148 outCommand += rawCmd.find(" ") == string::npos ? rawCmd : Base::StringFormat("\"%s\"", rawCmd.c_str()); 149 } else { 150 outOption += outOption.size() ? " " : ""; 151 string rawOption = Base::UnicodeToUtf8(argv[i]); 152 outOption += (i == 0) ? Base::StringFormat("\"%s\"", rawOption.c_str()) : rawOption; 153 } 154 } 155 return 0; 156} 157 158int RunServerMode(string &serverListenString) 159{ 160 if (serverListenString.empty()) { 161 return -1; 162 } 163 HdcServer server(true); 164 if (!server.Initial(serverListenString.c_str())) { 165 Base::PrintMessage("Initial failed"); 166 return -1; 167 } 168 server.WorkerPendding(); 169 return 0; 170} 171 172int RunPcDebugMode(bool isPullServer, bool isTCPorUSB, int isTestMethod) 173{ 174#ifdef HARMONY_PROJECT 175 Base::PrintMessage("Not support command..."); 176#else 177 pthread_t pt; 178 if (isPullServer) { 179 pthread_create(&pt, nullptr, TestBackgroundServerForClient, nullptr); 180 uv_sleep(200); // give time to start serverForClient,at least 200ms 181 } 182 TestRuntimeCommandSimple(isTCPorUSB, isTestMethod, true); 183 if (isPullServer) { 184 pthread_join(pt, nullptr); 185 WRITE_LOG(LOG_DEBUG, "!!!!!!!!!Server finish"); 186 } 187#endif 188 return 0; 189} 190 191int RunClientMode(string &commands, string &serverListenString, string &connectKey, bool isPullServer) 192{ 193 if (serverListenString.empty()) { 194 return -1; 195 } 196 uv_loop_t loopMain; 197 uv_loop_init(&loopMain); 198 HdcClient client(false, serverListenString, &loopMain, commands == CMDSTR_CHECK_SERVER); 199 if (!commands.size()) { 200 Base::PrintMessage("Unknown operation command..."); 201 std::cerr << TranslateCommand::Usage(); 202 return 0; 203 } 204 if (!strncmp(commands.c_str(), CMDSTR_SERVICE_START.c_str(), CMDSTR_SERVICE_START.size()) || 205 !strncmp(commands.c_str(), CMDSTR_SERVICE_KILL.c_str(), CMDSTR_SERVICE_KILL.size()) || 206 !strncmp(commands.c_str(), CMDSTR_GENERATE_KEY.c_str(), CMDSTR_GENERATE_KEY.size())) { 207 client.CtrlServiceWork(commands.c_str()); 208 return 0; 209 } 210 if (isPullServer && Base::ProgramMutex(SERVER_NAME.c_str(), true) == 0) { 211 // default pullup, just default listenstr.If want to customer listen-string, please use 'hdc -m -s lanip:port' 212 HdcServer::PullupServer(serverListenString.c_str()); 213 uv_sleep(START_SERVER_FOR_CLIENT_TIME); // give time to start serverForClient,at least 200ms 214 } 215 client.Initial(connectKey); 216 client.ExecuteCommand(commands.c_str()); 217 return 0; 218} 219 220bool ParseServerListenString(string &serverListenString, char *optarg) 221{ 222 if (strlen(optarg) > strlen("0000::0000:0000:0000:0000%interfacename:65535")) { 223 Base::PrintMessage("Unknown content of parament '-s'"); 224 return false; 225 } 226 char buf[BUF_SIZE_TINY] = ""; 227 if (strcpy_s(buf, sizeof(buf), optarg) != 0) { 228 Base::PrintMessage("strcpy_s error %d", errno); 229 return false; 230 } 231 char *p = strrchr(buf, ':'); 232 if (!p) { // Only port 233 if (strlen(buf) > PORT_MAX_LEN) { 234 Base::PrintMessage("The port-string's length must < 5"); 235 return false; 236 } 237 size_t len = strlen(buf); 238 for (size_t i = 0; i < len; i++) { 239 if (isdigit(buf[i]) == 0) { 240 Base::PrintMessage("The port must be digit buf:%s", buf); 241 return false; 242 } 243 } 244 int port = atoi(buf); 245 if (port <= 0 || port > MAX_IP_PORT) { 246 Base::PrintMessage("Port range incorrect"); 247 return false; 248 } 249 (void)snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "::ffff:127.0.0.1:%d", port); 250 serverListenString = buf; 251 } else { 252 *p = '\0'; 253 char *str = p + 1; 254 size_t len = strlen(str); 255 for (size_t i = 0; i < len; i++) { 256 if (isdigit(str[i]) == 0) { 257 Base::PrintMessage("The port must be digit str:%s", str); 258 return false; 259 } 260 } 261 int port = atoi(p + 1); 262 sockaddr_in addrv4; 263 sockaddr_in6 addrv6; 264 265 if ((port <= 0 || port > MAX_IP_PORT)) { 266 Base::PrintMessage("-s content port incorrect."); 267 return false; 268 } 269 270 if (uv_ip4_addr(buf, port, &addrv4) == 0) { 271 serverListenString = IPV4_MAPPING_PREFIX; 272 serverListenString += optarg; 273 } else if (uv_ip6_addr(buf, port, &addrv6) == 0) { 274 serverListenString = optarg; 275 } else { 276 Base::PrintMessage("-s content IP incorrect."); 277 return false; 278 } 279 } 280 return true; 281} 282 283bool GetCommandlineOptions(int optArgc, const char *optArgv[]) 284{ 285 int ch = 0; 286 bool needExit = false; 287 opterr = 0; 288 // get option parameters first 289 while ((ch = getopt(optArgc, const_cast<char *const*>(optArgv), "hvpfmncs:Sd:t:l:")) != -1) { 290 switch (ch) { 291 case 'h': { 292 string usage = Hdc::TranslateCommand::Usage(); 293 if (optind < optArgc && optind >= 0 && string(optArgv[optind]) == "verbose") { 294 usage = Hdc::TranslateCommand::Verbose(); 295 } 296 fprintf(stderr, "%s", usage.c_str()); 297 needExit = true; 298 return needExit; 299 } 300 case 'v': { 301 string ver = Base::GetVersion(); 302 fprintf(stdout, "%s\n", ver.c_str()); 303 needExit = true; 304 return needExit; 305 } 306 case 'f': { // [not-publish] 307 break; 308 } 309 case 'l': { 310 int logLevel = atoi(optarg); 311 if (logLevel < 0 || logLevel > LOG_LAST) { 312 Base::PrintMessage("Loglevel error!"); 313 needExit = true; 314 return needExit; 315 } 316 g_isCustomLoglevel = true; 317 Base::SetLogLevel(logLevel); 318 break; 319 } 320 case 'm': { // [not-publish] is server mode,or client mode 321 g_isServerMode = true; 322 break; 323 } 324 case 'n': { 325 g_containerInOut = "-n"; 326 break; 327 } 328 case 'c': { 329 g_containerInOut = "-c"; 330 break; 331 } 332 case 'p': { // [not-publish] not pullup server 333 g_isPullServer = false; 334 break; 335 } 336 case 't': { // key 337 if (strlen(optarg) > MAX_CONNECTKEY_SIZE) { 338 Base::PrintMessage("Sizeo of of parament '-t' %d is too long", strlen(optarg)); 339 needExit = true; 340 return needExit; 341 } 342 g_connectKey = optarg; 343 break; 344 } 345 case 's': { 346 if (!Hdc::ParseServerListenString(g_serverListenString, optarg)) { 347 needExit = true; 348 return needExit; 349 } 350 break; 351 } 352 case 'S': { 353 g_externalCmd = true; 354 break; 355 } 356 case 'd': // [Undisclosed parameters] debug mode 357 g_isPcDebugRun = true; 358 if (optarg[0] == 't') { 359 g_isTCPorUSB = true; 360 } else if (optarg[0] == 'u') { 361 g_isTCPorUSB = false; 362 } else { 363 Base::PrintMessage("Unknown debug parameters"); 364 needExit = true; 365 return needExit; 366 } 367 g_isTestMethod = atoi(optarg + 1); 368 break; 369 case '?': 370 break; 371 default: { 372 Base::PrintMessage("Unknown parameters"); 373 needExit = true; 374 return needExit; 375 } 376 } 377 } 378 return needExit; 379} 380 381void InitServerAddr(void) 382{ 383 int port; 384 do { 385 char *env = getenv(ENV_SERVER_PORT.c_str()); 386 if (!env) { 387 port = DEFAULT_PORT; 388 break; 389 } 390 391 size_t len = strlen(env); 392 size_t maxLen = 5; 393 if (len > maxLen) { 394 fprintf(stderr, "OHOS_HDC_SERVER_PORT %s is not in (0, 65535] range\n", env); 395 return; 396 } 397 398 for (size_t i = 0; i < len; i++) { 399 if (isdigit(env[i]) == 0) { 400 fprintf(stderr, "OHOS_HDC_SERVER_PORT %s is not digit\n", env); 401 return; 402 } 403 } 404 405 port = atoi(env); 406 if (port > MAX_IP_PORT || port <= 0) { 407 fprintf(stderr, "OHOS_HDC_SERVER_PORT %s is not in (0, 65535] range\n", env); 408 return; 409 } 410 } while (0); 411 g_serverListenString = DEFAULT_SERVER_ADDR_IP; 412 g_serverListenString += ":"; 413 g_serverListenString += std::to_string(port); 414} 415 416void RunExternalClient(string &str, string &connectKey, string &containerInOut) 417{ 418 ExtClient extClient; 419 extClient.connectKey = connectKey; 420 extClient.containerInOut = containerInOut; 421 extClient.Init(); 422 extClient.ExecuteCommand(str); 423} 424} 425 426#ifndef UNIT_TEST 427 428// hdc -l4 -m -s ip:port|hdc -l4 -m 429// hdc -l4 - s ip:port list targets 430int main(int argc, const char *argv[]) 431{ 432#ifdef _WIN32 433 SetConsoleOutputCP(CP_UTF8); 434#endif 435 string options; 436 string commands; 437 Hdc::SplitOptionAndCommand(argc, argv, options, commands); 438 uv_setup_args(argc, const_cast<char **>(argv)); 439 int optArgc = 0; 440 char **optArgv = Base::SplitCommandToArgs(options.c_str(), &optArgc); 441 bool cmdOptionResult; 442 443 InitServerAddr(); 444 cmdOptionResult = GetCommandlineOptions(optArgc, const_cast<const char **>(optArgv)); 445 delete[](reinterpret_cast<char*>(optArgv)); 446 if (cmdOptionResult) { 447 return 0; 448 } 449 Base::InitProcess(); 450 if (g_isServerMode) { 451 Base::CreateLogDir(); 452 // -m server.Run alone in the background, no -s will be listen loopback address 453 Hdc::RunServerMode(g_serverListenString); 454 } else if (g_isPcDebugRun) { 455 Hdc::RunPcDebugMode(g_isPullServer, g_isTCPorUSB, g_isTestMethod); 456 } else { 457 if (!g_isCustomLoglevel) { 458 Base::SetLogLevel(LOG_INFO); 459 } 460 461 if (!ExtClient::SharedLibraryExist()) { 462 Hdc::RunClientMode(commands, g_serverListenString, g_connectKey, g_isPullServer); 463 Hdc::Base::RemoveLogCache(); 464 _exit(0); 465 } 466 string str = "list targets"; 467 if (!strncmp(commands.c_str(), CMDSTR_LIST_TARGETS.c_str(), CMDSTR_LIST_TARGETS.size())) { 468 string lista = "list targets -a"; 469 if (!strncmp(commands.c_str(), lista.c_str(), lista.size())) { 470 str = "list targets -v"; 471 } else { 472 str = commands; 473 } 474 Hdc::RunExternalClient(str, g_connectKey, g_containerInOut); 475 Hdc::RunClientMode(str, g_serverListenString, g_connectKey, g_isPullServer); 476 } else if (!strncmp(commands.c_str(), CMDSTR_SOFTWARE_VERSION.c_str(), CMDSTR_SOFTWARE_VERSION.size()) || 477 !strncmp(commands.c_str(), CMDSTR_SOFTWARE_HELP.c_str(), CMDSTR_SOFTWARE_HELP.size()) || 478 !strncmp(commands.c_str(), CMDSTR_TARGET_DISCOVER.c_str(), CMDSTR_TARGET_DISCOVER.size()) || 479 !strncmp(commands.c_str(), CMDSTR_SERVICE_START.c_str(), CMDSTR_SERVICE_START.size()) || 480 !strncmp(commands.c_str(), CMDSTR_SERVICE_KILL.c_str(), CMDSTR_SERVICE_KILL.size()) || 481 !strncmp(commands.c_str(), CMDSTR_WAIT_FOR.c_str(), CMDSTR_WAIT_FOR.size())) { 482 Hdc::RunExternalClient(commands, g_connectKey, g_containerInOut); 483 Hdc::RunClientMode(commands, g_serverListenString, g_connectKey, g_isPullServer); 484 } else if (!strncmp(commands.c_str(), CMDSTR_CONNECT_TARGET.c_str(), CMDSTR_CONNECT_TARGET.size()) || 485 !strncmp(commands.c_str(), CMDSTR_TARGET_MODE.c_str(), CMDSTR_TARGET_MODE.size()) || g_externalCmd) { 486 Hdc::RunExternalClient(commands, g_connectKey, g_containerInOut); 487 } else { 488 g_show = false; 489 Hdc::RunExternalClient(str, g_connectKey, g_containerInOut); 490 Hdc::RunClientMode(str, g_serverListenString, g_connectKey, g_isPullServer); 491 g_show = true; 492 if (g_connectKey.empty()) { 493 if (g_lists.size() == 0) { 494 Base::PrintMessage("No any target"); 495 } else if (g_lists.size() == 1) { 496 auto iter = g_lists.begin(); 497 g_connectKey = iter->first; 498 } else { 499 Base::PrintMessage("Specify one target"); 500 } 501 } 502 if (g_lists[g_connectKey] == "external") { 503 Hdc::RunExternalClient(commands, g_connectKey, g_containerInOut); 504 } else if (g_lists[g_connectKey] == "hdc") { 505 Hdc::RunClientMode(commands, g_serverListenString, g_connectKey, g_isPullServer); 506 } 507 } 508 } 509 WRITE_LOG(LOG_DEBUG, "!!!!!!!!!Main finish main"); 510 Hdc::Base::RemoveLogCache(); 511 return 0; 512} 513#endif // no UNIT_TEST 514