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 #include "client.h"
16 #ifndef TEST_HASH
17 #include "hdc_hash_gen.h"
18 #endif
19 #include "host_updater.h"
20 #include "server.h"
21 #include "file.h"
22
23 std::map<std::string, std::string> g_lists;
24 bool g_show = true;
25
26 namespace Hdc {
27 bool g_terminalStateChange = false;
HdcClient(const bool serverOrClient, const string &addrString, uv_loop_t *loopMainIn, bool checkVersion)28 HdcClient::HdcClient(const bool serverOrClient, const string &addrString, uv_loop_t *loopMainIn, bool checkVersion)
29 : HdcChannelBase(serverOrClient, addrString, loopMainIn)
30 {
31 MallocChannel(&channel); // free by logic
32 debugRetryCount = 0;
33 #ifndef _WIN32
34 Base::ZeroStruct(terminalState);
35 #endif
36 isCheckVersionCmd = checkVersion;
37 }
38
~HdcClient()39 HdcClient::~HdcClient()
40 {
41 #ifndef _WIN32
42 if (g_terminalStateChange) {
43 tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
44 }
45 #endif
46 Base::TryCloseLoop(loopMain, "ExecuteCommand finish");
47 }
48
NotifyInstanceChannelFree(HChannel hChannel)49 void HdcClient::NotifyInstanceChannelFree(HChannel hChannel)
50 {
51 if (bShellInteractive) {
52 WRITE_LOG(LOG_DEBUG, "Restore tty");
53 ModifyTty(false, &hChannel->stdinTty);
54 }
55 }
56
GetLastPID()57 uint32_t HdcClient::GetLastPID()
58 {
59 char bufPath[BUF_SIZE_MEDIUM] = "";
60 size_t size = BUF_SIZE_MEDIUM;
61 char pidBuf[BUF_SIZE_TINY] = "";
62 // get running pid to kill it
63 if (uv_os_tmpdir(bufPath, &size) < 0) {
64 WRITE_LOG(LOG_FATAL, "Tmppath failed");
65 return 0;
66 }
67 string path = Base::StringFormat("%s%c.%s.pid", bufPath, Base::GetPathSep(), SERVER_NAME.c_str());
68 Base::ReadBinFile(path.c_str(), reinterpret_cast<void **>(&pidBuf), BUF_SIZE_TINY);
69 int pid = atoi(pidBuf); // pid maybe 0
70 return pid;
71 }
72
StartServer(const string &cmd)73 bool HdcClient::StartServer(const string &cmd)
74 {
75 int serverStatus = Base::ProgramMutex(SERVER_NAME.c_str(), true);
76 if (serverStatus < 0) {
77 WRITE_LOG(LOG_DEBUG, "get server status failed, serverStatus:%d", serverStatus);
78 return false;
79 }
80
81 // server is not running
82 if (serverStatus == 0) {
83 HdcServer::PullupServer(channelHostPort.c_str());
84 return true;
85 }
86
87 // server is running
88 if (cmd.find(" -r") == std::string::npos) {
89 return true;
90 }
91
92 // restart server
93 uint32_t pid = GetLastPID();
94 if (pid == 0) {
95 Base::PrintMessage(TERMINAL_HDC_PROCESS_FAILED.c_str());
96 return false;
97 }
98 int rc = uv_kill(pid, SIGKILL);
99 WRITE_LOG(LOG_DEBUG, "uv_kill rc:%d", rc);
100 HdcServer::PullupServer(channelHostPort.c_str());
101 return true;
102 }
103
KillServer(const string &cmd)104 bool HdcClient::KillServer(const string &cmd)
105 {
106 int serverStatus = Base::ProgramMutex(SERVER_NAME.c_str(), true);
107 if (serverStatus < 0) {
108 WRITE_LOG(LOG_DEBUG, "get server status failed, serverStatus:%d", serverStatus);
109 return false;
110 }
111
112 // server is not running
113 if (serverStatus == 0) {
114 if (cmd.find(" -r") != std::string::npos) {
115 HdcServer::PullupServer(channelHostPort.c_str());
116 }
117 return true;
118 }
119
120 // server is running
121 uint32_t pid = GetLastPID();
122 if (pid == 0) {
123 Base::PrintMessage(TERMINAL_HDC_PROCESS_FAILED.c_str());
124 return false;
125 }
126 int rc = uv_kill(pid, SIGKILL);
127 if (rc == 0) {
128 Base::PrintMessage("Kill server finish");
129 } else {
130 constexpr int size = 1024;
131 char buf[size] = { 0 };
132 uv_strerror_r(rc, buf, size);
133 Base::PrintMessage("Kill server failed %s", buf);
134 }
135 if (cmd.find(" -r") != std::string::npos) {
136 HdcServer::PullupServer(channelHostPort.c_str());
137 }
138 return true;
139 }
140
DoCtrlServiceWork(uv_check_t *handle)141 void HdcClient::DoCtrlServiceWork(uv_check_t *handle)
142 {
143 HdcClient *thisClass = (HdcClient *)handle->data;
144 string &strCmd = thisClass->command;
145 if (!strncmp(thisClass->command.c_str(), CMDSTR_SERVICE_START.c_str(), CMDSTR_SERVICE_START.size())) {
146 thisClass->StartServer(strCmd);
147 } else if (!strncmp(thisClass->command.c_str(), CMDSTR_SERVICE_KILL.c_str(), CMDSTR_SERVICE_KILL.size())) {
148 thisClass->KillServer(strCmd);
149 // clang-format off
150 } else if (!strncmp(thisClass->command.c_str(), CMDSTR_GENERATE_KEY.c_str(), CMDSTR_GENERATE_KEY.size()) &&
151 strCmd.find(" ") != std::string::npos) {
152 // clang-format on
153 string keyPath = strCmd.substr(CMDSTR_GENERATE_KEY.size() + 1, strCmd.size());
154 HdcAuth::GenerateKey(keyPath.c_str());
155 } else {
156 Base::PrintMessage("Unknown command");
157 }
158 Base::TryCloseHandle((const uv_handle_t *)handle);
159 }
160
CtrlServiceWork(const char *commandIn)161 int HdcClient::CtrlServiceWork(const char *commandIn)
162 {
163 command = commandIn;
164 ctrlServerWork.data = this;
165 uv_check_init(loopMain, &ctrlServerWork);
166 uv_check_start(&ctrlServerWork, DoCtrlServiceWork);
167 uv_run(loopMain, UV_RUN_NOWAIT);
168 return 0;
169 }
170
AutoConnectKey(string &doCommand, const string &preConnectKey) const171 string HdcClient::AutoConnectKey(string &doCommand, const string &preConnectKey) const
172 {
173 string key = preConnectKey;
174 bool isNoTargetCommand = false;
175 vector<string> vecNoConnectKeyCommand;
176 vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_VERSION);
177 vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_HELP);
178 vecNoConnectKeyCommand.push_back(CMDSTR_TARGET_DISCOVER);
179 vecNoConnectKeyCommand.push_back(CMDSTR_LIST_TARGETS);
180 vecNoConnectKeyCommand.push_back(CMDSTR_CHECK_SERVER);
181 vecNoConnectKeyCommand.push_back(CMDSTR_CONNECT_TARGET);
182 vecNoConnectKeyCommand.push_back(CMDSTR_CHECK_DEVICE);
183 vecNoConnectKeyCommand.push_back(CMDSTR_WAIT_FOR);
184 vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " ls");
185 vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " rm");
186 for (string v : vecNoConnectKeyCommand) {
187 if (!doCommand.compare(0, v.size(), v)) {
188 isNoTargetCommand = true;
189 break;
190 }
191 }
192 if (isNoTargetCommand) {
193 if (this->command != CMDSTR_WAIT_FOR) {
194 key = "";
195 }
196 } else {
197 if (!preConnectKey.size()) {
198 key = CMDSTR_CONNECT_ANY;
199 }
200 }
201 return key;
202 }
203
204 #ifdef _WIN32
ReadFileThreadFunc(void* arg)205 static void ReadFileThreadFunc(void* arg)
206 {
207 char buffer[BUF_SIZE_DEFAULT] = { 0 };
208 DWORD bytesRead = 0;
209
210 HANDLE* read = reinterpret_cast<HANDLE*>(arg);
211 while (true) {
212 if (!ReadFile(*read, buffer, BUF_SIZE_DEFAULT - 1, &bytesRead, NULL)) {
213 break;
214 }
215 string str = std::to_string(bytesRead);
216 const char* zero = "0";
217 if (!strncmp(zero, str.c_str(), strlen(zero))) {
218 return;
219 }
220 printf("%s", buffer);
221 if (memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)) != EOK) {
222 return;
223 }
224 }
225 }
226
GetHilogPath()227 string HdcClient::GetHilogPath()
228 {
229 string hdcPath = Base::GetHdcAbsolutePath();
230 int index = hdcPath.find_last_of(Base::GetPathSep());
231 string exePath = hdcPath.substr(0, index) + Base::GetPathSep() + HILOG_NAME;
232
233 return exePath;
234 }
235
RunCommandWin32(const string& cmd)236 void HdcClient::RunCommandWin32(const string& cmd)
237 {
238 HANDLE hSubWrite;
239 HANDLE hParentRead;
240 HANDLE hParentWrite;
241 HANDLE hSubRead;
242 STARTUPINFO si;
243 PROCESS_INFORMATION pi;
244 SECURITY_ATTRIBUTES sa;
245 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
246 sa.lpSecurityDescriptor = NULL;
247 sa.bInheritHandle = true;
248
249 if (!CreatePipe(&hParentRead, &hSubWrite, &sa, 0) ||
250 !CreatePipe(&hSubRead, &hParentWrite, &sa, 0) ||
251 !SetHandleInformation(hParentRead, HANDLE_FLAG_INHERIT, 0) ||
252 !SetHandleInformation(hParentWrite, HANDLE_FLAG_INHERIT, 0)) {
253 return;
254 }
255
256 ZeroMemory(&si, sizeof(STARTUPINFO));
257 si.cb = sizeof(STARTUPINFO);
258 GetStartupInfo(&si);
259 si.hStdError = hSubWrite;
260 si.hStdOutput = hSubWrite;
261 si.hStdInput = hSubRead;
262 si.wShowWindow = SW_HIDE;
263 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
264
265 const char *msg = cmd.c_str();
266 char buffer[BUF_SIZE_SMALL] = {0};
267 if (strcpy_s(buffer, sizeof(buffer), msg) != EOK) {
268 return;
269 }
270 const string exePath = GetHilogPath();
271 if (!CreateProcess(_T(exePath.c_str()), _T(buffer), NULL, NULL, true, NULL, NULL, NULL, &si, &pi)) {
272 WRITE_LOG(LOG_INFO, "create process failed, error:%d", GetLastError());
273 return;
274 }
275 auto thread = std::thread([&hParentRead]() {
276 ReadFileThreadFunc(&hParentRead);
277 });
278 WaitForSingleObject(pi.hProcess, INFINITE);
279 CloseHandle(hSubWrite);
280 CloseHandle(pi.hProcess);
281 CloseHandle(pi.hThread);
282 thread.join();
283 CloseHandle(hParentRead);
284 CloseHandle(hParentWrite);
285 CloseHandle(hSubRead);
286 }
287 #else
RunCommand(const string& cmd)288 void HdcClient::RunCommand(const string& cmd)
289 {
290 FILE *procFileInfo = nullptr;
291 procFileInfo = popen(cmd.c_str(), "r");
292 if (procFileInfo == nullptr) {
293 perror("popen execute failed");
294 return;
295 }
296 char resultBufShell[BUF_SIZE_DEFAULT] = {0};
297 while (fgets(resultBufShell, sizeof(resultBufShell), procFileInfo) != nullptr) {
298 printf("%s", resultBufShell);
299 if (memset_s(resultBufShell, sizeof(resultBufShell), 0, sizeof(resultBufShell)) != EOK) {
300 break;
301 }
302 }
303 pclose(procFileInfo);
304 }
305 #endif
306
RunExecuteCommand(const string& cmd)307 void HdcClient::RunExecuteCommand(const string& cmd)
308 {
309 #ifdef _WIN32
310 RunCommandWin32(cmd);
311 #else
312 RunCommand(cmd);
313 #endif
314 }
315
IsCaptureCommand(const string& cmd)316 bool IsCaptureCommand(const string& cmd)
317 {
318 int index = string(CMDSTR_HILOG).length();
319 int length = cmd.length();
320 const string captureOption = "parse";
321 while (index < length) {
322 if (cmd[index] == ' ') {
323 index++;
324 continue;
325 }
326 if (!strncmp(cmd.c_str() + index, captureOption.c_str(), captureOption.size())) {
327 return true;
328 } else {
329 return false;
330 }
331 }
332 return false;
333 }
334
ExecuteCommand(const string &commandIn)335 int HdcClient::ExecuteCommand(const string &commandIn)
336 {
337 char ip[BUF_SIZE_TINY] = "";
338 uint16_t port = 0;
339 int ret = Base::ConnectKey2IPPort(channelHostPort.c_str(), ip, &port, sizeof(ip));
340 if (ret < 0) {
341 WRITE_LOG(LOG_FATAL, "ConnectKey2IPPort %s failed with %d",
342 channelHostPort.c_str(), ret);
343 return -1;
344 }
345
346 if (!strncmp(commandIn.c_str(), CMDSTR_HILOG.c_str(), CMDSTR_HILOG.size()) &&
347 IsCaptureCommand(commandIn)) {
348 RunExecuteCommand(commandIn);
349 return 0;
350 }
351
352 if (!strncmp(commandIn.c_str(), CMDSTR_FILE_SEND.c_str(), CMDSTR_FILE_SEND.size()) ||
353 !strncmp(commandIn.c_str(), CMDSTR_FILE_RECV.c_str(), CMDSTR_FILE_RECV.size())) {
354 WRITE_LOG(LOG_DEBUG, "Set file send mode");
355 channel->remote = RemoteType::REMOTE_FILE;
356 }
357 if (!strncmp(commandIn.c_str(), CMDSTR_APP_INSTALL.c_str(), CMDSTR_APP_INSTALL.size())) {
358 channel->remote = RemoteType::REMOTE_APP;
359 }
360 command = commandIn;
361 connectKey = AutoConnectKey(command, connectKey);
362 ConnectServerForClient(ip, port);
363 uv_timer_init(loopMain, &waitTimeDoCmd);
364 waitTimeDoCmd.data = this;
365 uv_timer_start(&waitTimeDoCmd, CommandWorker, UV_START_TIMEOUT, UV_START_REPEAT);
366 WorkerPendding();
367 return 0;
368 }
369
Initial(const string &connectKeyIn)370 int HdcClient::Initial(const string &connectKeyIn)
371 {
372 connectKey = connectKeyIn;
373 if (!channelHostPort.size() || !channelHost.size() || !channelPort) {
374 WRITE_LOG(LOG_FATAL, "Listen string initial failed");
375 return ERR_PARM_FAIL;
376 }
377 return 0;
378 }
379
ConnectServerForClient(const char *ip, uint16_t port)380 int HdcClient::ConnectServerForClient(const char *ip, uint16_t port)
381 {
382 if (uv_is_closing((const uv_handle_t *)&channel->hWorkTCP)) {
383 WRITE_LOG(LOG_FATAL, "ConnectServerForClient uv_is_closing");
384 return ERR_SOCKET_FAIL;
385 }
386 WRITE_LOG(LOG_DEBUG, "Try to connect %s:%d", ip, port);
387 uv_connect_t *conn = new(std::nothrow) uv_connect_t();
388 if (conn == nullptr) {
389 WRITE_LOG(LOG_FATAL, "ConnectServerForClient new conn failed");
390 return ERR_GENERIC;
391 }
392 conn->data = this;
393 tcpConnectRetryCount = 0;
394 uv_timer_init(loopMain, &retryTcpConnTimer);
395 retryTcpConnTimer.data = this;
396 if (strchr(ip, '.')) {
397 isIpV4 = true;
398 std::string s = ip;
399 size_t index = s.find(IPV4_MAPPING_PREFIX);
400 size_t size = IPV4_MAPPING_PREFIX.size();
401 if (index != std::string::npos) {
402 s = s.substr(index + size);
403 }
404 WRITE_LOG(LOG_DEBUG, "ConnectServerForClient ipv4 %s:%d", s.c_str(), port);
405 uv_ip4_addr(s.c_str(), port, &destv4);
406 uv_tcp_connect(conn, (uv_tcp_t *)&channel->hWorkTCP, (const struct sockaddr *)&destv4, Connect);
407 } else {
408 isIpV4 = false;
409 WRITE_LOG(LOG_DEBUG, "ConnectServerForClient ipv6 %s:%d", ip, port);
410 uv_ip6_addr(ip, port, &dest);
411 uv_tcp_connect(conn, (uv_tcp_t *)&channel->hWorkTCP, (const struct sockaddr *)&dest, Connect);
412 }
413 return 0;
414 }
415
CommandWorker(uv_timer_t *handle)416 void HdcClient::CommandWorker(uv_timer_t *handle)
417 {
418 const uint16_t maxWaitRetry = 1200; // client socket try 12s
419 HdcClient *thisClass = (HdcClient *)handle->data;
420 if (++thisClass->debugRetryCount > maxWaitRetry) {
421 uv_timer_stop(handle);
422 uv_stop(thisClass->loopMain);
423 WRITE_LOG(LOG_DEBUG, "Connect server failed");
424 fprintf(stderr, "Connect server failed\n");
425 return;
426 }
427 if (!thisClass->channel->handshakeOK) {
428 return;
429 }
430 uv_timer_stop(handle);
431 WRITE_LOG(LOG_DEBUG, "Connect server successful");
432 bool closeInput = false;
433 if (!HostUpdater::ConfirmCommand(thisClass->command, closeInput)) {
434 uv_timer_stop(handle);
435 uv_stop(thisClass->loopMain);
436 WRITE_LOG(LOG_DEBUG, "Cmd \'%s\' has been canceld", thisClass->command.c_str());
437 return;
438 }
439 while (closeInput) {
440 #ifndef _WIN32
441 if (tcgetattr(STDIN_FILENO, &thisClass->terminalState)) {
442 break;
443 }
444 termios tio;
445 if (tcgetattr(STDIN_FILENO, &tio)) {
446 break;
447 }
448 cfmakeraw(&tio);
449 tio.c_cc[VTIME] = 0;
450 tio.c_cc[VMIN] = 1;
451 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
452 g_terminalStateChange = true;
453 #endif
454 break;
455 }
456 thisClass->Send(thisClass->channel->channelId,
457 const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(thisClass->command.c_str())),
458 thisClass->command.size() + 1);
459 }
460
AllocStdbuf(uv_handle_t *handle, size_t sizeWanted, uv_buf_t *buf)461 void HdcClient::AllocStdbuf(uv_handle_t *handle, size_t sizeWanted, uv_buf_t *buf)
462 {
463 if (sizeWanted <= 0) {
464 return;
465 }
466 HChannel context = (HChannel)handle->data;
467 int availSize = strlen(context->bufStd);
468 buf->base = (char *)context->bufStd + availSize;
469 buf->len = sizeof(context->bufStd) - availSize - 2; // reserve 2bytes
470 }
471
ReadStd(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)472 void HdcClient::ReadStd(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
473 {
474 HChannel hChannel = (HChannel)stream->data;
475 HdcClient *thisClass = (HdcClient *)hChannel->clsChannel;
476 char *cmd = hChannel->bufStd;
477 if (nread <= 0) {
478 WRITE_LOG(LOG_FATAL, "ReadStd error nread:%zd", nread);
479 return; // error
480 }
481 thisClass->Send(hChannel->channelId, reinterpret_cast<uint8_t *>(cmd), strlen(cmd));
482 Base::ZeroArray(hChannel->bufStd);
483 }
484
ModifyTty(bool setOrRestore, uv_tty_t *tty)485 void HdcClient::ModifyTty(bool setOrRestore, uv_tty_t *tty)
486 {
487 if (setOrRestore) {
488 #ifdef _WIN32
489 uv_tty_set_mode(tty, UV_TTY_MODE_RAW);
490 #else
491 if (tcgetattr(STDIN_FILENO, &terminalState)) {
492 return;
493 }
494 termios tio;
495 if (tcgetattr(STDIN_FILENO, &tio)) {
496 return;
497 }
498 cfmakeraw(&tio);
499 tio.c_cc[VTIME] = 0;
500 tio.c_cc[VMIN] = 1;
501 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
502 #endif
503 } else {
504 #ifndef _WIN32
505 tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
506 #endif
507 }
508 }
509
BindLocalStd(HChannel hChannel)510 void HdcClient::BindLocalStd(HChannel hChannel)
511 {
512 if (command == CMDSTR_SHELL) {
513 bShellInteractive = true;
514 }
515 if (bShellInteractive && uv_guess_handle(STDIN_FILENO) != UV_TTY) {
516 WRITE_LOG(LOG_WARN, "Not support stdio TTY mode");
517 return;
518 }
519
520 WRITE_LOG(LOG_DEBUG, "setup stdio TTY mode");
521 if (uv_tty_init(loopMain, &hChannel->stdoutTty, STDOUT_FILENO, 0)
522 || uv_tty_init(loopMain, &hChannel->stdinTty, STDIN_FILENO, 1)) {
523 WRITE_LOG(LOG_DEBUG, "uv_tty_init failed");
524 return;
525 }
526 hChannel->stdoutTty.data = hChannel;
527 ++hChannel->uvHandleRef;
528 hChannel->stdinTty.data = hChannel;
529 ++hChannel->uvHandleRef;
530 if (bShellInteractive) {
531 WRITE_LOG(LOG_DEBUG, "uv_tty_init uv_tty_set_mode");
532 ModifyTty(true, &hChannel->stdinTty);
533 uv_read_start((uv_stream_t *)&hChannel->stdinTty, AllocStdbuf, ReadStd);
534 }
535 }
536
Connect(uv_connect_t *connection, int status)537 void HdcClient::Connect(uv_connect_t *connection, int status)
538 {
539 WRITE_LOG(LOG_DEBUG, "Enter Connect, status:%d", status);
540 HdcClient *thisClass = (HdcClient *)connection->data;
541 delete connection;
542 HChannel hChannel = reinterpret_cast<HChannel>(thisClass->channel);
543 if (uv_is_closing((const uv_handle_t *)&hChannel->hWorkTCP)) {
544 WRITE_LOG(LOG_DEBUG, "uv_is_closing...");
545 thisClass->FreeChannel(hChannel->channelId);
546 return;
547 }
548
549 // connect success
550 if (status == 0) {
551 thisClass->BindLocalStd(hChannel);
552 Base::SetTcpOptions((uv_tcp_t *)&hChannel->hWorkTCP);
553 WRITE_LOG(LOG_DEBUG, "uv_read_start");
554 uv_read_start((uv_stream_t *)&hChannel->hWorkTCP, AllocCallback, ReadStream);
555 return;
556 }
557
558 // connect failed, start timer and retry
559 WRITE_LOG(LOG_DEBUG, "retry count:%d", thisClass->tcpConnectRetryCount);
560 if (thisClass->tcpConnectRetryCount >= TCP_CONNECT_MAX_RETRY_COUNT) {
561 WRITE_LOG(LOG_DEBUG, "stop retry for connect");
562 thisClass->FreeChannel(hChannel->channelId);
563 return;
564 }
565 thisClass->tcpConnectRetryCount++;
566 uv_timer_start(&(thisClass->retryTcpConnTimer), thisClass->RetryTcpConnectWorker, TCP_CONNECT_RETRY_TIME_MS, 0);
567 }
568
RetryTcpConnectWorker(uv_timer_t *handle)569 void HdcClient::RetryTcpConnectWorker(uv_timer_t *handle)
570 {
571 HdcClient *thisClass = (HdcClient *)handle->data;
572 HChannel hChannel = reinterpret_cast<HChannel>(thisClass->channel);
573 uv_connect_t *connection = new(std::nothrow) uv_connect_t();
574 if (connection == nullptr) {
575 WRITE_LOG(LOG_FATAL, "RetryTcpConnectWorker new conn failed");
576 thisClass->FreeChannel(hChannel->channelId);
577 return;
578 }
579 connection->data = thisClass;
580 WRITE_LOG(LOG_DEBUG, "RetryTcpConnectWorker start tcp connect");
581 if (thisClass->isIpV4) {
582 uv_tcp_connect(connection, &(thisClass->channel->hWorkTCP),
583 (const struct sockaddr *)&(thisClass->destv4), thisClass->Connect);
584 } else {
585 uv_tcp_connect(connection, &(thisClass->channel->hWorkTCP),
586 (const struct sockaddr *)&(thisClass->dest), thisClass->Connect);
587 }
588 }
589
PreHandshake(HChannel hChannel, const uint8_t *buf)590 int HdcClient::PreHandshake(HChannel hChannel, const uint8_t *buf)
591 {
592 ChannelHandShake *hShake = reinterpret_cast<ChannelHandShake *>(const_cast<uint8_t *>(buf));
593 if (strncmp(hShake->banner, HANDSHAKE_MESSAGE.c_str(), HANDSHAKE_MESSAGE.size())) {
594 hChannel->availTailIndex = 0;
595 WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
596 return ERR_BUF_CHECK;
597 }
598 hChannel->isStableBuf = (hShake->banner[BANNER_FEATURE_TAG_OFFSET] != HUGE_BUF_TAG);
599 WRITE_LOG(LOG_DEBUG, "Channel PreHandshake isStableBuf:%d", hChannel->isStableBuf);
600 if (this->command == CMDSTR_WAIT_FOR && !connectKey.empty()) {
601 hShake->banner[WAIT_TAG_OFFSET] = WAIT_DEVICE_TAG;
602 }
603 // sync remote session id to local
604 uint32_t unOld = hChannel->channelId;
605 hChannel->channelId = ntohl(hShake->channelId);
606 AdminChannel(OP_UPDATE, unOld, hChannel);
607 WRITE_LOG(LOG_DEBUG, "Client channel handshake finished, use connectkey:%s",
608 Hdc::MaskString(connectKey).c_str());
609 // send config
610 // channel handshake step2
611 if (memset_s(hShake->connectKey, sizeof(hShake->connectKey), 0, sizeof(hShake->connectKey)) != EOK
612 || memcpy_s(hShake->connectKey, sizeof(hShake->connectKey), connectKey.c_str(), connectKey.size()) != EOK) {
613 hChannel->availTailIndex = 0;
614 WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
615 return ERR_BUF_COPY;
616 }
617
618 #ifdef HDC_VERSION_CHECK
619 // add check version
620 if (!isCheckVersionCmd) { // do not check version cause user want to get server version
621 string clientVer = Base::GetVersion() + HDC_MSG_HASH;
622 string serverVer(hShake->version);
623
624 if (clientVer != serverVer) {
625 serverVer = serverVer.substr(0, Base::GetVersion().size());
626 WRITE_LOG(LOG_FATAL, "Client version:%s, server version:%s", clientVer.c_str(), serverVer.c_str());
627 hChannel->availTailIndex = 0;
628 return ERR_CHECK_VERSION;
629 }
630 }
631 Send(hChannel->channelId, reinterpret_cast<uint8_t *>(hShake), sizeof(ChannelHandShake));
632 #else
633 // do not send version message if check feature disable
634 Send(hChannel->channelId, reinterpret_cast<uint8_t *>(hShake), offsetof(struct ChannelHandShake, version));
635 #endif
636 hChannel->handshakeOK = true;
637 #ifdef HDC_CHANNEL_KEEP_ALIVE
638 // Evaluation method, non long-term support
639 Send(hChannel->channelId,
640 reinterpret_cast<uint8_t *>(const_cast<char*>(CMDSTR_INNER_ENABLE_KEEPALIVE.c_str())),
641 CMDSTR_INNER_ENABLE_KEEPALIVE.size());
642 #endif
643 return RET_SUCCESS;
644 }
645
646 // read serverForClient(server)TCP data
ReadChannel(HChannel hChannel, uint8_t *buf, const int bytesIO)647 int HdcClient::ReadChannel(HChannel hChannel, uint8_t *buf, const int bytesIO)
648 {
649 if (!hChannel->handshakeOK) {
650 return PreHandshake(hChannel, buf);
651 }
652 #ifdef UNIT_TEST
653 // Do not output to console when the unit test
654 return 0;
655 #endif
656 WRITE_LOG(LOG_DEBUG, "Client ReadChannel :%d", bytesIO);
657
658 uint16_t cmd = 0;
659 bool bOffset = false;
660 if (bytesIO >= static_cast<int>(sizeof(uint16_t))) {
661 cmd = *reinterpret_cast<uint16_t *>(buf);
662 bOffset = IsOffset(cmd);
663 }
664 if (cmd == CMD_CHECK_SERVER && isCheckVersionCmd) {
665 WRITE_LOG(LOG_DEBUG, "recieve CMD_CHECK_VERSION command");
666 string version(reinterpret_cast<char *>(buf + sizeof(uint16_t)), bytesIO - sizeof(uint16_t));
667 fprintf(stdout, "Client version:%s, server version:%s\n", Base::GetVersion().c_str(), version.c_str());
668 fflush(stdout);
669 return 0;
670 }
671 if (hChannel->remote > RemoteType::REMOTE_NONE && bOffset) {
672 // file command
673 if (hChannel->remote == RemoteType::REMOTE_FILE) {
674 if (fileTask == nullptr) {
675 HTaskInfo hTaskInfo = GetRemoteTaskInfo(hChannel);
676 hTaskInfo->masterSlave = (cmd == CMD_FILE_INIT);
677 fileTask = std::make_unique<HdcFile>(hTaskInfo);
678 }
679 if (!fileTask->CommandDispatch(cmd, buf + sizeof(uint16_t), bytesIO - sizeof(uint16_t))) {
680 fileTask->TaskFinish();
681 }
682 }
683 // app command
684 if (hChannel->remote == RemoteType::REMOTE_APP) {
685 if (appTask == nullptr) {
686 HTaskInfo hTaskInfo = GetRemoteTaskInfo(hChannel);
687 hTaskInfo->masterSlave = (cmd == CMD_APP_INIT);
688 appTask = std::make_unique<HdcHostApp>(hTaskInfo);
689 }
690 if (!appTask->CommandDispatch(cmd, buf + sizeof(uint16_t), bytesIO - sizeof(uint16_t))) {
691 appTask->TaskFinish();
692 }
693 }
694 return 0;
695 }
696
697 string s(reinterpret_cast<char *>(buf), bytesIO);
698 if (WaitFor(s)) {
699 return 0;
700 }
701 s = ListTargetsAll(s);
702 if (g_show) {
703 #ifdef _WIN32
704 fprintf(stdout, "%s", s.c_str());
705 fflush(stdout);
706 #else
707 constexpr int len = 512;
708 int size = s.size() / len;
709 int left = s.size() % len;
710 for (int i = 0; i <= size; i++) {
711 int cnt = len;
712 const char *p = reinterpret_cast<char *>(buf) + i * cnt;
713 if (i == size) {
714 cnt = left;
715 }
716 fprintf(stdout, "%.*s", cnt, p);
717 fflush(stdout);
718 std::this_thread::sleep_for(std::chrono::milliseconds(1));
719 }
720 #endif
721 }
722 return 0;
723 }
724
WaitFor(const string &str)725 bool HdcClient::WaitFor(const string &str)
726 {
727 bool wait = false;
728 if (!strncmp(this->command.c_str(), CMDSTR_WAIT_FOR.c_str(), CMDSTR_WAIT_FOR.size())) {
729 const string waitFor = "[Fail]No any connected target";
730 if (!strncmp(str.c_str(), waitFor.c_str(), waitFor.size())) {
731 Send(this->channel->channelId, reinterpret_cast<uint8_t *>(const_cast<char *>(this->command.c_str())),
732 this->command.size() + 1);
733 constexpr int timeout = 1;
734 std::this_thread::sleep_for(std::chrono::seconds(timeout));
735 wait = true;
736 } else {
737 _exit(0);
738 }
739 }
740 return wait;
741 }
742
ListTargetsAll(const string &str)743 string HdcClient::ListTargetsAll(const string &str)
744 {
745 string all = str;
746 const string lists = "list targets -v";
747 if (!strncmp(this->command.c_str(), lists.c_str(), lists.size())) {
748 UpdateList(str);
749 all = Base::ReplaceAll(all, "\n", "\thdc\n");
750 } else if (!strncmp(this->command.c_str(), CMDSTR_LIST_TARGETS.c_str(), CMDSTR_LIST_TARGETS.size())) {
751 UpdateList(str);
752 }
753 if (!strncmp(this->command.c_str(), CMDSTR_LIST_TARGETS.c_str(), CMDSTR_LIST_TARGETS.size())) {
754 if (g_lists.size() > 0 && !strncmp(str.c_str(), EMPTY_ECHO.c_str(), EMPTY_ECHO.size())) {
755 all = "";
756 }
757 }
758 return all;
759 }
760
UpdateList(const string &str)761 void HdcClient::UpdateList(const string &str)
762 {
763 if (!strncmp(str.c_str(), EMPTY_ECHO.c_str(), EMPTY_ECHO.size())) {
764 return;
765 }
766 vector<string> devs;
767 Base::SplitString(str, "\n", devs);
768 for (size_t i = 0; i < devs.size(); i++) {
769 string::size_type pos = devs[i].find("\t");
770 if (pos != string::npos) {
771 string key = devs[i].substr(0, pos);
772 g_lists[key] = "hdc";
773 } else {
774 string key = devs[i];
775 g_lists[key] = "hdc";
776 }
777 }
778 }
779
IsOffset(uint16_t cmd)780 bool HdcClient::IsOffset(uint16_t cmd)
781 {
782 return (cmd == CMD_CHECK_SERVER) ||
783 (cmd == CMD_FILE_INIT) ||
784 (cmd == CMD_FILE_CHECK) ||
785 (cmd == CMD_FILE_BEGIN) ||
786 (cmd == CMD_FILE_DATA) ||
787 (cmd == CMD_FILE_FINISH) ||
788 (cmd == CMD_FILE_MODE) ||
789 (cmd == CMD_DIR_MODE) ||
790 (cmd == CMD_APP_INIT) ||
791 (cmd == CMD_APP_CHECK) ||
792 (cmd == CMD_APP_BEGIN) ||
793 (cmd == CMD_APP_DATA) ||
794 (cmd == CMD_APP_FINISH);
795 }
796
GetRemoteTaskInfo(HChannel hChannel)797 HTaskInfo HdcClient::GetRemoteTaskInfo(HChannel hChannel)
798 {
799 HTaskInfo hTaskInfo = new TaskInformation();
800 hTaskInfo->channelId = hChannel->channelId;
801 hTaskInfo->runLoop = loopMain;
802 hTaskInfo->serverOrDaemon = true;
803 hTaskInfo->channelTask = true;
804 hTaskInfo->channelClass = this;
805 hTaskInfo->isStableBuf = hChannel->isStableBuf;
806 hTaskInfo->isCleared = false;
807 return hTaskInfo;
808 };
809 } // namespace Hdc
810