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