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