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