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