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"
27 using namespace HdcTest;
28 #endif
29 
30 using namespace Hdc;
31 
32 namespace {
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 
45 namespace Hdc {
46 // return value: 0 == not command, 1 == one command, 2 == double command
IsRegisterCommand(string &outCommand, const char *cmd, const char *cmdnext)47 int 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 
AppendCwdWhenTransfer(string &outCommand)98 void 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 
SplitOptionAndCommand(int argc, const char **argv, string &outOption, string &outCommand)128 int 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 
RunServerMode(string &serverListenString)158 int 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 
RunPcDebugMode(bool isPullServer, bool isTCPorUSB, int isTestMethod)172 int 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 
RunClientMode(string &commands, string &serverListenString, string &connectKey, bool isPullServer)191 int 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 
ParseServerListenString(string &serverListenString, char *optarg)220 bool 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 
GetCommandlineOptions(int optArgc, const char *optArgv[])283 bool 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 
InitServerAddr(void)381 void 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 
RunExternalClient(string &str, string &connectKey, string &containerInOut)416 void 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
main(int argc, const char *argv[])430 int 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