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