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