• 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 
17 #include "host_updater.h"
18 #include "server.h"
19 
20 namespace Hdc {
21 bool terminalStateChange = false;
HdcClient(const bool serverOrClient,const string & addrString,uv_loop_t * loopMainIn)22 HdcClient::HdcClient(const bool serverOrClient, const string &addrString, uv_loop_t *loopMainIn)
23     : HdcChannelBase(serverOrClient, addrString, loopMainIn)
24 {
25     MallocChannel(&channel);  // free by logic
26     debugRetryCount = 0;
27 }
28 
~HdcClient()29 HdcClient::~HdcClient()
30 {
31 #ifndef _WIN32
32     if (terminalStateChange) {
33         tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
34     }
35 #endif
36     Base::TryCloseLoop(loopMain, "ExecuteCommand finish");
37 }
38 
NotifyInstanceChannelFree(HChannel hChannel)39 void HdcClient::NotifyInstanceChannelFree(HChannel hChannel)
40 {
41     if (bShellInteractive) {
42         WRITE_LOG(LOG_DEBUG, "Restore tty");
43         ModifyTty(false, &hChannel->stdinTty);
44     }
45 }
46 
GetLastPID()47 uint32_t HdcClient::GetLastPID()
48 {
49     char bufPath[BUF_SIZE_MEDIUM] = "";
50     size_t size = BUF_SIZE_MEDIUM;
51     char pidBuf[BUF_SIZE_TINY] = "";
52     // get running pid to kill it
53     if (uv_os_tmpdir(bufPath, &size) < 0) {
54         WRITE_LOG(LOG_FATAL, "Tmppath failed");
55         return 0;
56     }
57     string path = Base::StringFormat("%s%c.%s.pid", bufPath, Base::GetPathSep(), SERVER_NAME.c_str());
58     Base::ReadBinFile(path.c_str(), (void **)&pidBuf, BUF_SIZE_TINY);
59     int pid = atoi(pidBuf);  // pid  maybe 0
60     return pid;
61 }
62 
StartKillServer(const char * cmd,bool startOrKill)63 bool HdcClient::StartKillServer(const char *cmd, bool startOrKill)
64 {
65     bool isNowRunning = Base::ProgramMutex(SERVER_NAME.c_str(), true) != 0;
66     const int SIGN_NUM = 9;
67     uint32_t pid = GetLastPID();
68     if (!pid) {
69         return false;
70     }
71     if (startOrKill) {
72         if (isNowRunning) {
73             // already running
74             if (!strstr(cmd, " -r")) {
75                 return true;
76             }
77             if (pid) {
78                 uv_kill(pid, SIGN_NUM);
79             }
80         }
81         HdcServer::PullupServer(channelHostPort.c_str());
82     } else {
83         if (isNowRunning && pid) {
84             uv_kill(pid, SIGN_NUM);
85             Base::PrintMessage("Kill server finish");
86         }
87         // already running
88         if (!strstr(cmd, " -r")) {
89             return true;
90         }
91         HdcServer::PullupServer(channelHostPort.c_str());
92     }
93     return true;
94 }
95 
DoCtrlServiceWork(uv_check_t * handle)96 void HdcClient::DoCtrlServiceWork(uv_check_t *handle)
97 {
98     HdcClient *thisClass = (HdcClient *)handle->data;
99     const char *cmd = thisClass->command.c_str();
100     string &strCmd = thisClass->command;
101     while (true) {
102         if (!strncmp(cmd, CMDSTR_SERVICE_START.c_str(), CMDSTR_SERVICE_START.size())) {
103             thisClass->StartKillServer(cmd, true);
104         } else if (!strncmp(cmd, CMDSTR_SERVICE_KILL.c_str(), CMDSTR_SERVICE_KILL.size())) {
105             thisClass->StartKillServer(cmd, false);
106             // clang-format off
107         } else if (!strncmp(cmd, CMDSTR_GENERATE_KEY.c_str(), CMDSTR_GENERATE_KEY.size()) &&
108                    strCmd.find(" ") != std::string::npos) {
109             // clang-format on
110             string keyPath = strCmd.substr(CMDSTR_GENERATE_KEY.size() + 1, strCmd.size());
111             HdcAuth::GenerateKey(keyPath.c_str());
112         } else {
113             Base::PrintMessage("Unknow command");
114         }
115         break;
116     }
117     Base::TryCloseHandle((const uv_handle_t *)handle);
118 }
119 
CtrlServiceWork(const char * commandIn)120 int HdcClient::CtrlServiceWork(const char *commandIn)
121 {
122     command = commandIn;
123     ctrlServerWork.data = this;
124     uv_check_init(loopMain, &ctrlServerWork);
125     uv_check_start(&ctrlServerWork, DoCtrlServiceWork);
126     uv_run(loopMain, UV_RUN_NOWAIT);
127     return 0;
128 }
129 
AutoConnectKey(string & doCommand,const string & preConnectKey) const130 string HdcClient::AutoConnectKey(string &doCommand, const string &preConnectKey) const
131 {
132     string key = preConnectKey;
133     bool isNoTargetCommand = false;
134     vector<string> vecNoConnectKeyCommand;
135     vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_VERSION);
136     vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_HELP);
137     vecNoConnectKeyCommand.push_back(CMDSTR_TARGET_DISCOVER);
138     vecNoConnectKeyCommand.push_back(CMDSTR_LIST_TARGETS);
139     vecNoConnectKeyCommand.push_back(CMDSTR_CONNECT_TARGET);
140     vecNoConnectKeyCommand.push_back(CMDSTR_KILL_SERVER);
141     vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " ls");
142     vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " rm");
143     for (string v : vecNoConnectKeyCommand) {
144         if (!doCommand.compare(0, v.size(), v)) {
145             isNoTargetCommand = true;
146             break;
147         }
148     }
149     if (isNoTargetCommand) {
150         key = "";
151     } else {
152         if (!preConnectKey.size()) {
153             key = CMDSTR_CONNECT_ANY;
154         }
155     }
156     return key;
157 }
158 
ExecuteCommand(const string & commandIn)159 int HdcClient::ExecuteCommand(const string &commandIn)
160 {
161     char ip[BUF_SIZE_TINY] = "";
162     uint16_t port = 0;
163     if (Base::ConnectKey2IPPort(channelHostPort.c_str(), ip, &port) < 0) {
164         return -1;
165     }
166     command = commandIn;
167     connectKey = AutoConnectKey(command, connectKey);
168     ConnectServerForClient(ip, port);
169     uv_timer_init(loopMain, &waitTimeDoCmd);
170     waitTimeDoCmd.data = this;
171     uv_timer_start(&waitTimeDoCmd, CommandWorker, 10, 10); // 10 10 repeat
172     WorkerPendding();
173     return 0;
174 }
175 
Initial(const string & connectKeyIn)176 int HdcClient::Initial(const string &connectKeyIn)
177 {
178     connectKey = connectKeyIn;
179     if (!channelHostPort.size() || !channelHost.size() || !channelPort) {
180         WRITE_LOG(LOG_FATAL, "Listen string initial failed");
181         return ERR_PARM_FAIL;
182     }
183     return 0;
184 }
185 
ConnectServerForClient(const char * ip,uint16_t port)186 int HdcClient::ConnectServerForClient(const char *ip, uint16_t port)
187 {
188     if (uv_is_closing((const uv_handle_t *)&channel->hWorkTCP)) {
189         return ERR_SOCKET_FAIL;
190     }
191     WRITE_LOG(LOG_DEBUG, "Try to connect %s:%d", ip, port);
192     struct sockaddr_in dest;
193     uv_ip4_addr(ip, port, &dest);
194     uv_connect_t *conn = new uv_connect_t();
195     conn->data = this;
196     uv_tcp_connect(conn, (uv_tcp_t *)&channel->hWorkTCP, (const struct sockaddr *)&dest, Connect);
197     return 0;
198 }
199 
CommandWorker(uv_timer_t * handle)200 void HdcClient::CommandWorker(uv_timer_t *handle)
201 {
202     const uint16_t maxWaitRetry = 500;
203     HdcClient *thisClass = (HdcClient *)handle->data;
204     if (++thisClass->debugRetryCount > maxWaitRetry) {
205         uv_timer_stop(handle);
206         uv_stop(thisClass->loopMain);
207         WRITE_LOG(LOG_DEBUG, "Connect server failed");
208         return;
209     }
210     if (!thisClass->channel->handshakeOK) {
211         return;
212     }
213     uv_timer_stop(handle);
214     WRITE_LOG(LOG_DEBUG, "Connect server successful");
215     bool closeInput = false;
216     if (!HostUpdater::ConfirmCommand(thisClass->command, closeInput)) {
217         uv_timer_stop(handle);
218         uv_stop(thisClass->loopMain);
219         WRITE_LOG(LOG_DEBUG, "Cmd \'%s\' has been canceld", thisClass->command.c_str());
220         return;
221     }
222     while (closeInput) {
223 #ifndef _WIN32
224         if (tcgetattr(STDIN_FILENO, &thisClass->terminalState)) {
225             break;
226         }
227         termios tio;
228         if (tcgetattr(STDIN_FILENO, &tio)) {
229             break;
230         }
231         cfmakeraw(&tio);
232         tio.c_cc[VTIME] = 0;
233         tio.c_cc[VMIN] = 1;
234         tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
235         terminalStateChange = true;
236 #endif
237         break;
238     }
239     thisClass->Send(thisClass->channel->channelId, (uint8_t *)thisClass->command.c_str(),
240                     thisClass->command.size() + 1);
241 }
242 
AllocStdbuf(uv_handle_t * handle,size_t sizeWanted,uv_buf_t * buf)243 void HdcClient::AllocStdbuf(uv_handle_t *handle, size_t sizeWanted, uv_buf_t *buf)
244 {
245     if (sizeWanted <= 0) {
246         return;
247     }
248     HChannel context = (HChannel)handle->data;
249     int availSize = strlen(context->bufStd);
250     buf->base = (char *)context->bufStd + availSize;
251     buf->len = sizeof(context->bufStd) - availSize - 2;  // reserve 2bytes
252 }
253 
ReadStd(uv_stream_t * stream,ssize_t nread,const uv_buf_t * buf)254 void HdcClient::ReadStd(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
255 {
256     HChannel hChannel = (HChannel)stream->data;
257     HdcClient *thisClass = (HdcClient *)hChannel->clsChannel;
258     char *command = hChannel->bufStd;
259     if (nread <= 0) {
260         return;  // error
261     }
262     thisClass->Send(hChannel->channelId, (uint8_t *)command, strlen(command));
263     Base::ZeroArray(hChannel->bufStd);
264 }
265 
ModifyTty(bool setOrRestore,uv_tty_t * tty)266 void HdcClient::ModifyTty(bool setOrRestore, uv_tty_t *tty)
267 {
268     if (setOrRestore) {
269 #ifdef _WIN32
270         uv_tty_set_mode(tty, UV_TTY_MODE_RAW);
271 #else
272         if (tcgetattr(STDIN_FILENO, &terminalState)) {
273             return;
274         }
275         termios tio;
276         if (tcgetattr(STDIN_FILENO, &tio)) {
277             return;
278         }
279         cfmakeraw(&tio);
280         tio.c_cc[VTIME] = 0;
281         tio.c_cc[VMIN] = 1;
282         tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
283 #endif
284     } else {
285 #ifndef _WIN32
286         tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
287 #endif
288     }
289 }
290 
BindLocalStd(HChannel hChannel)291 void HdcClient::BindLocalStd(HChannel hChannel)
292 {
293     if (command == CMDSTR_SHELL) {
294         bShellInteractive = true;
295     }
296     if (uv_guess_handle(STDIN_FILENO) != UV_TTY) {
297         WRITE_LOG(LOG_FATAL, "Not support std mode");
298         return;
299     }
300 
301     WRITE_LOG(LOG_DEBUG, "Tty std mode");
302     if (uv_tty_init(loopMain, &hChannel->stdoutTty, STDOUT_FILENO, 0)
303         || uv_tty_init(loopMain, &hChannel->stdinTty, STDIN_FILENO, 1)) {
304         WRITE_LOG(LOG_DEBUG, "uv_tty_init failed");
305         return;
306     }
307     hChannel->stdoutTty.data = hChannel;
308     ++hChannel->uvRef;
309     hChannel->stdinTty.data = hChannel;
310     ++hChannel->uvRef;
311     if (bShellInteractive) {
312         WRITE_LOG(LOG_DEBUG, "uv_tty_init uv_tty_set_mode");
313         ModifyTty(true, &hChannel->stdinTty);
314         uv_read_start((uv_stream_t *)&hChannel->stdinTty, AllocStdbuf, ReadStd);
315     }
316 }
317 
Connect(uv_connect_t * connection,int status)318 void HdcClient::Connect(uv_connect_t *connection, int status)
319 {
320     HdcClient *thisClass = (HdcClient *)connection->data;
321     delete connection;
322     HChannel hChannel = (HChannel)thisClass->channel;
323     if (status < 0 || uv_is_closing((const uv_handle_t *)&hChannel->hWorkTCP)) {
324         WRITE_LOG(LOG_FATAL, "connect failed");
325         thisClass->FreeChannel(hChannel->channelId);
326         return;
327     }
328     thisClass->BindLocalStd(hChannel);
329     Base::SetTcpOptions((uv_tcp_t *)&hChannel->hWorkTCP);
330     uv_read_start((uv_stream_t *)&hChannel->hWorkTCP, AllocCallback, ReadStream);
331 }
332 
PreHandshake(HChannel hChannel,const uint8_t * buf)333 int HdcClient::PreHandshake(HChannel hChannel, const uint8_t *buf)
334 {
335     ChannelHandShake *hShake = (ChannelHandShake *)buf;
336     if (strncmp(hShake->banner, HANDSHAKE_MESSAGE.c_str(), HANDSHAKE_MESSAGE.size())) {
337         hChannel->availTailIndex = 0;
338         WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
339         return ERR_BUF_CHECK;
340     }
341     // sync remote session id to local
342     uint32_t unOld = hChannel->channelId;
343     hChannel->channelId = ntohl(hShake->channelId);
344     AdminChannel(OP_UPDATE, unOld, hChannel);
345     WRITE_LOG(LOG_DEBUG, "Client channel handshake finished, use connectkey:%s", connectKey.c_str());
346     // send config
347     // channel handshake step2
348     if (memset_s(hShake->connectKey, sizeof(hShake->connectKey), 0, sizeof(hShake->connectKey)) != EOK
349         || memcpy_s(hShake->connectKey, sizeof(hShake->connectKey), connectKey.c_str(), connectKey.size()) != EOK) {
350         hChannel->availTailIndex = 0;
351         WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
352         return ERR_BUF_COPY;
353     }
354     Send(hChannel->channelId, reinterpret_cast<uint8_t *>(hShake), sizeof(ChannelHandShake));
355     hChannel->handshakeOK = true;
356 #ifdef HDC_CHANNEL_KEEP_ALIVE
357     // Evaluation method, non long-term support
358     Send(hChannel->channelId, reinterpret_cast<uint8_t *>(CMDSTR_INNER_ENABLE_KEEPALIVE.c_str()),
359          CMDSTR_INNER_ENABLE_KEEPALIVE.size());
360 #endif
361     return RET_SUCCESS;
362 }
363 
364 // read serverForClient(server)TCP data
ReadChannel(HChannel hChannel,uint8_t * buf,const int bytesIO)365 int HdcClient::ReadChannel(HChannel hChannel, uint8_t *buf, const int bytesIO)
366 {
367     if (!hChannel->handshakeOK) {
368         return PreHandshake(hChannel, buf);
369     }
370 #ifdef UNIT_TEST
371     // Do not output to console when the unit test
372     return 0;
373 #endif
374     WRITE_LOG(LOG_DEBUG, "Client ReadChannel :%d", bytesIO);
375     string s(reinterpret_cast<char *>(buf), bytesIO);
376     (void)fprintf(stdout, "%s", s.c_str());
377     fflush(stdout);
378     return 0;
379 }
380 }  // namespace Hdc
381