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
16#include "distributed_major.h"
17
18#include <cstring>
19#include <map>
20#include <cerrno>
21#include <cassert>
22#include <cstdio>
23#include <cstdlib>
24
25#include <unistd.h>
26#include <poll.h>
27#include <sys/socket.h>
28#include <netinet/in.h>
29#include <arpa/inet.h>
30#include "securec.h"
31
32namespace OHOS {
33namespace DistributeSystemTest {
34using namespace std;
35using namespace testing;
36using namespace OHOS::HiviewDFX;
37DistributeTestEnvironment  *g_pDistributetestEnv = nullptr;
38namespace {
39    const int CONNECT_TIME = 3;
40    const int SLEEP_TIME = 1000;
41    const int HALF_BUF_LEN = 2;
42    const int CMD_LENGTH = 50;
43}
44
45DistributeTestEnvironment::DistributeTestEnvironment() : serverPort_(DEFAULT_AGENT_PORT)
46{
47}
48
49DistributeTestEnvironment::DistributeTestEnvironment(std::string cfgFile) : serverPort_(DEFAULT_AGENT_PORT)
50{
51    Init(cfgFile);
52}
53
54void DistributeTestEnvironment::Init(std::string fileName)
55{
56    clientCfg_.OpenCfg(fileName);
57    std::string iplist;
58    if (!clientCfg_.GetCfgVal("agentlist", iplist)) {
59        return;
60    }
61    std::string::size_type posend = 0;
62    std::string::size_type pos = 0;
63    do {
64        std::string ipaddr;
65        posend = iplist.find(",", pos);
66        if (posend != std::string::npos) {
67            ipaddr = iplist.substr(pos, posend - pos);
68        } else {
69            ipaddr = iplist.substr(pos);
70        }
71        AddClient(ipaddr);
72        if (posend >= iplist.length()) {
73            break;
74        }
75        pos = posend + 1;
76    } while (posend != std::string::npos);
77    std::string strPort;
78    if (!clientCfg_.GetCfgVal("agentport", strPort)) {
79        return;
80    }
81    if (sscanf_s(strPort.c_str(), "%d", &serverPort_) < 1) {
82        serverPort_ = DEFAULT_AGENT_PORT;
83    }
84    HiLog::Info(DistributeTestEnvironment::LABEL, "get device port :  %d", serverPort_);
85}
86
87DistributeTestEnvironment::~DistributeTestEnvironment()
88{
89}
90
91int DistributeTestEnvironment::GetSerial()
92{
93    static int serialno = 0;
94    return serialno++;
95}
96
97int DistributeTestEnvironment::AddClient(std::string ipAddr)
98{
99    int count = clientList_.size();
100    struct sockaddr_in addr;
101    if (inet_pton(AF_INET, ipAddr.c_str(), &addr.sin_addr) == 1) {
102        DistDeviceInfo dev;
103        dev.devNo = count;
104        dev.ipAddr = ipAddr;
105        dev.fd = -1;
106        clientList_.push_back(dev);
107        count++;
108    } else {
109        return 0;
110    }
111    return 1;
112}
113
114int DistributeTestEnvironment::ConnectAgent(size_t devNo)
115{
116    if (devNo >= clientList_.size()) {
117        return 0;
118    }
119    std::string serverIp = clientList_[devNo].ipAddr;
120    struct sockaddr_in addr;
121    int clientSockFd = socket(AF_INET, SOCK_STREAM, 0);
122    if (clientSockFd < 0) {
123        return -1;
124    }
125    bzero(&addr, sizeof(addr));
126    addr.sin_family = AF_INET;
127    inet_pton(AF_INET, serverIp.c_str(), &addr.sin_addr);
128    addr.sin_port = htons(serverPort_);
129    int connectCount = 0;
130    for (connectCount = 0; connectCount < CONNECT_TIME; connectCount++) {  // try connect to agent 3 times.
131        if (!connect(clientSockFd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr))) {
132            break;
133        }
134        std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));  // delay 10ms
135    }
136    if (connectCount >= CONNECT_TIME) {
137        HiLog::Error(DistributeTestEnvironment::LABEL, "connect to agent %s fail.", serverIp.c_str());
138        close(clientSockFd);
139        clientSockFd = -1;
140        return 0;
141    }
142    HiLog::Info(DistributeTestEnvironment::LABEL, "connect to agent %s success.", serverIp.c_str());
143    clientList_[devNo].fd = clientSockFd;
144    return 1;
145}
146
147void DistributeTestEnvironment::SetUp()
148{
149    // initial connect agent;
150    size_t clientNo;
151    for (clientNo = 0; clientNo < clientList_.size(); clientNo++) {
152        // create connect to agent of ipaddress is , port is 8642.
153        ConnectAgent(clientNo);
154    }
155}
156
157void DistributeTestEnvironment::TearDown()
158{
159    size_t clientNo;
160    for (clientNo = 0; clientNo < clientList_.size(); clientNo++) {
161        close(clientList_[clientNo].fd); // close socket
162        clientList_[clientNo].fd = -1;
163    }
164}
165
166bool DistributeTestEnvironment::SendToAgent(size_t devNo, int cmdType, void *pstrMsg, int len,
167    std::function<bool(const std::string &, int)> onProcessReturn)
168{
169    static int globalCommandNo = 0;
170    bool breturn = false;
171    devNo = devNo - 1;
172    if (devNo >= clientList_.size()) {
173        HiLog::Info(DistributeTestEnvironment::LABEL, "can not find no %zu device.", devNo);
174        return breturn;
175    }
176    if (clientList_[devNo].fd <= 0) {
177        HiLog::Info(DistributeTestEnvironment::LABEL, "connect is failure %zu device.", devNo);
178        return breturn;
179    }
180    if (pstrMsg == nullptr) {
181        return false;
182    }
183    globalCommandNo++;
184    char szrbuf[MAX_BUFF_LEN] = {0};
185    auto pCmdMsg = reinterpret_cast<DistributedMsg *>(pstrMsg);
186    pCmdMsg->no = globalCommandNo;
187    pCmdMsg->cmdTestType = htons(cmdType);
188    pCmdMsg->len = htons(len);
189    int rlen = send(clientList_[devNo].fd, pCmdMsg, static_cast<size_t>(len + DST_COMMAND_HEAD_LEN), 0);
190    if (rlen <= 0) {
191        HiLog::Error(DistributeTestEnvironment::LABEL, "agent socket is closed.");
192        return breturn;
193    }
194    // get ret value ;
195    switch (cmdType) {
196        case DST_COMMAND_CALL:
197        case DST_COMMAND_MSG: {
198            int times = CONNECT_TIME;
199            while (times > 0) {
200                rlen = recv(clientList_[devNo].fd, szrbuf, DST_COMMAND_HEAD_LEN, 0);
201                if (static_cast<unsigned long>(rlen) >= DST_COMMAND_HEAD_LEN) {
202                    auto pCmdTest = reinterpret_cast<DistributedMsg *>(szrbuf);
203                    pCmdTest->cmdTestType = ntohs(pCmdTest->cmdTestType);
204                    pCmdTest->len = ntohs(pCmdTest->len);
205                    if (pCmdTest->len <= 0) {
206                        times--;
207                        continue;
208                    }
209                    recv(clientList_[devNo].fd, pCmdTest->alignmentCmd, pCmdTest->len, 0);
210                    HiLog::Info(DistributeTestEnvironment::LABEL, "recv agent data : No.%d command type :%d length :%d",
211                        pCmdTest->no, pCmdTest->cmdTestType, pCmdTest->len);
212                    if ((globalCommandNo == pCmdTest->no) && (cmdType == pCmdTest->cmdTestType)) {
213                        // get ret value ;
214                        if (onProcessReturn != nullptr) {
215                            breturn = onProcessReturn(pCmdTest->alignmentCmd, pCmdTest->len);
216                        } else {
217                            breturn = true;
218                        }
219                        break;
220                    } else {
221                        HiLog::Error(DistributeTestEnvironment::LABEL, "get error message. type is :%d",
222                            pCmdTest->cmdTestType);
223                    }
224                } else {
225                    if (!rlen) {
226                        // peer socket is closed.
227                        HiLog::Error(DistributeTestEnvironment::LABEL, "device socket close.");
228                        break;
229                    }
230                }
231                usleep(SLEEP_TIME);
232                times--;
233            }
234            break;
235        }
236        default: {
237            breturn = true;
238            break;
239        }
240    }
241    return breturn;
242}
243
244bool DistributeTestEnvironment::RunTestCmd(size_t devNo, const std::string &strCommand, int cmdLen,
245    const std::string &strExpectValue, int expectValueLen,
246    std::function<bool(const std::string &, int)> onProcessReturn)
247{
248    // send command data length
249    char szbuf[MAX_BUFF_LEN];
250    bool breturn = false;
251    size_t lenptr = 0;
252    errno_t ret = EOK;
253    ret = memset_s(szbuf, sizeof(szbuf), 0, MAX_BUFF_LEN);
254    if (ret != EOK) {
255        return breturn;
256    }
257    // add 2 '\0'
258    size_t rlen = cmdLen + expectValueLen + DST_COMMAND_HEAD_LEN + sizeof(int) * HALF_BUF_LEN + HALF_BUF_LEN;
259    if (rlen <= MAX_BUFF_LEN) {
260        auto pCmdTest = reinterpret_cast<DistributedMsg *>(szbuf);
261        pCmdTest->cmdTestType = DST_COMMAND_CALL;
262
263        // alignmentCmd buff format:
264        // cmd_size:int, cmd string, '\0' expectvalue_size:int
265        // expectvalue string, '\0'
266        lenptr = 0;
267        *reinterpret_cast<int *>(pCmdTest->alignmentCmd + lenptr) = htons(cmdLen);
268        lenptr += sizeof(int);
269        ret = memcpy_s(pCmdTest->alignmentCmd + lenptr, MAX_BUFF_LEN - DST_COMMAND_HEAD_LEN - lenptr,
270            strCommand.c_str(), cmdLen);
271        if (ret != EOK) {
272            return breturn;
273        }
274        lenptr += cmdLen + 1;
275        *reinterpret_cast<int *>(pCmdTest->alignmentCmd + lenptr) = htons(expectValueLen);
276        lenptr += sizeof(int);
277        ret = memcpy_s(pCmdTest->alignmentCmd + lenptr, MAX_BUFF_LEN - DST_COMMAND_HEAD_LEN - lenptr,
278            strExpectValue.c_str(), expectValueLen);
279        if (ret != EOK) {
280            return breturn;
281        }
282        lenptr += expectValueLen + 1;
283        pCmdTest->len =  lenptr;
284        breturn = SendToAgent(devNo, DST_COMMAND_CALL, pCmdTest, pCmdTest->len, onProcessReturn);
285    } else {
286        HiLog::Error(DistributeTestEnvironment::LABEL, "command data is too long \n");
287    }
288    return breturn;
289};
290
291bool DistributeTestEnvironment::SendMessage(size_t devNo, const std::string &strMsg, int msgLen,
292    std::function<bool(const std::string &, int)> onProcessReturnMsg)
293{
294    bool breturn = false;
295    if ((msgLen + DST_COMMAND_HEAD_LEN) <= MAX_BUFF_LEN) {
296        char szbuf[MAX_BUFF_LEN] = {0};
297        auto pCmdTest = reinterpret_cast<DistributedMsg *>(szbuf);
298        pCmdTest->cmdTestType = DST_COMMAND_MSG;
299        errno_t ret = memcpy_s(pCmdTest->alignmentCmd, MAX_BUFF_LEN - DST_COMMAND_HEAD_LEN, strMsg.c_str(), msgLen);
300        if (ret != EOK) {
301            return breturn;
302        }
303        pCmdTest->len = msgLen;
304        breturn = SendToAgent(devNo, DST_COMMAND_MSG, pCmdTest, msgLen, onProcessReturnMsg);
305    } else {
306        HiLog::Info(DistributeTestEnvironment::LABEL, "message data is too long.\n");
307    }
308    return breturn;
309}
310
311bool DistributeTestEnvironment::Notify(size_t devNo, const std::string &strMsg, int msgLen)
312{
313    int dstMax = MAX_BUFF_LEN - DST_COMMAND_HEAD_LEN;
314    if (msgLen < 0 || msgLen >= dstMax) {
315        return false;
316    }
317
318    bool breturn = false;
319    if ((msgLen + DST_COMMAND_HEAD_LEN) <= MAX_BUFF_LEN) {
320        char szbuf[MAX_BUFF_LEN] = {0};
321        auto pCmdTest = reinterpret_cast<DistributedMsg *>(szbuf);
322        pCmdTest->cmdTestType = DST_COMMAND_NOTIFY;
323        errno_t ret = memcpy_s(pCmdTest->alignmentCmd, dstMax, strMsg.c_str(), msgLen);
324        if (ret != EOK) {
325            return breturn;
326        }
327        pCmdTest->alignmentCmd[msgLen] = 0;
328        pCmdTest->len = msgLen;
329        breturn = SendToAgent(devNo, DST_COMMAND_NOTIFY, pCmdTest, msgLen, nullptr);
330    } else {
331        HiLog::Info(DistributeTestEnvironment::LABEL, "notify data is too long.\n");
332    }
333    return breturn;
334}
335
336DistributeTest::DistributeTest()
337{
338    returnVal_ = 0;
339}
340
341DistributeTest::~DistributeTest()
342{
343}
344
345int DistributeTest::CheckStatus()
346{
347    return 0;
348}
349
350/*
351 * function : the testcase execute command on agent device, tell device something, agent process it.
352 *     this interface is opened for user.
353 * param :
354 *     devNo: agent device serial number.
355 *     strCommand: command of the testcase to send.
356 *     cmdLen : length of command
357 *     strExpectValue: expected return value
358 *     expectValueLen: real length of return value
359 * return : if false is return, execute operation failed.
360 */
361bool DistributeTest::RunCmdOnAgent(AGENT_NO devNo, const std::string &strCommand, int cmdLen,
362    const std::string &strExpectValue, int expectValueLen)
363{
364    if (g_pDistributetestEnv != nullptr) {
365        return g_pDistributetestEnv->RunTestCmd(devNo, strCommand, cmdLen, strExpectValue, expectValueLen,
366            [&](const std::string &strReturn, int strLen)->bool {
367                return OnProcessValue(strReturn, strLen);
368            });
369    }
370    return false;
371}
372
373/*
374 * function : the testcase execute command on agent device, include command parameter.
375 *     this interface is opened for user.
376 * param :
377 *     devNo: agent device serial number.
378 *     strCmd: command of the testcase to send.
379 *     strArgs: command parameter.
380 *     strExpectValue: expected return value
381 * return : if false is return, execute operation failed.
382 */
383bool DistributeTest::RunCmdOnAgent(AGENT_NO devNo, const std::string &strCmd, const std::string &strArgs,
384    const std::string &strExpectValue)
385{
386    if (g_pDistributetestEnv != nullptr) {
387        std::string strBuf = strCmd + ":" + strArgs;
388        int cmdLen = strBuf.length() + 1;
389        int expectValueLen = strExpectValue.length() + 1;
390        return g_pDistributetestEnv->RunTestCmd(devNo, strBuf, cmdLen, strExpectValue, expectValueLen,
391            [&](const std::string &strReturn, int strLen)->bool {
392                return OnProcessValue(strReturn, strLen);
393            });
394    }
395    return false;
396}
397
398/*
399 * function : the testcase execute command on agent device, include command parameter and callback.
400 *     this interface is opened for user.
401 * param :
402 *     devNo: agent device serial number.
403 *     strCmd: command of the testcase to send.
404 *     strArgs: command parameter.
405 *     strExpectValue: expected return value
406 *     onReturnCall: callback function to handle return value and real length of return value.
407 * return : if false is return, execute operation failed.
408 */
409bool DistributeTest::RunCmdOnAgent(AGENT_NO devNo, const std::string &strCmd, const std::string &strArgs,
410    const std::string &strExpectValue, std::function<bool(const std::string &, int)> onReturnCall)
411{
412    if (g_pDistributetestEnv != nullptr) {
413        std::string strBuf = strCmd + ":" + strArgs;
414        int cmdLen = strBuf.length() + 1;
415        int expectValueLen = strExpectValue.length() + 1;
416        return g_pDistributetestEnv->RunTestCmd(devNo, strBuf, cmdLen, strExpectValue, expectValueLen, onReturnCall);
417    }
418    return false;
419}
420
421bool DistributeTest::OnProcessValue(const std::string &szbuf, int len)
422{
423    if (szbuf == "") {
424        return false;
425    }
426    if (sscanf_s(szbuf.c_str(), "%d", &returnVal_) < 1) {
427        return false;
428    }
429    return true;
430}
431
432int DistributeTest::GetReturnVal()
433{
434    return returnVal_;
435}
436
437/*
438 * function : testcase send message to agent device, tell agent device something, agent process it.
439 *     this interface is opened for user.
440 * param :
441 *     devNo: the serial number of agent device.
442 *     msg : message of the testcase sent to the agent
443 *     len: length of strMsg
444 * return : if false is return, send operation failed.
445 */
446bool DistributeTest::SendMessage(AGENT_NO devNo, const std::string &msg, int len)
447{
448    if (g_pDistributetestEnv != nullptr) {
449        return g_pDistributetestEnv->SendMessage(devNo, msg, len,
450            [&](const std::string &szreturnbuf, int rlen)->bool {
451                HiLog::Info(DistributeTestEnvironment::LABEL, "onprocessmsg len :%d.", rlen);
452                return OnMsgProc(szreturnbuf, rlen);
453            });
454    }
455    return false;
456}
457
458/*
459 * function : testcase send message to agent device, include callback.
460 *     this interface is opened for user.
461 * param :
462 *     devNo: the serial number of agent device.
463 *     msg : message of the testcase sent to the agent
464 *     len: length of message
465 *     onProcessReturnMsg: callback function that handles the agent device return message and real
466 *                         length of return value
467 * return : if false is return, send operation failed.
468 */
469bool DistributeTest::SendMessage(AGENT_NO devNo, const std::string &msg, int len,
470    std::function<bool(const std::string &, int)> onProcessReturnMsg)
471{
472    if (g_pDistributetestEnv != nullptr) {
473        return g_pDistributetestEnv->SendMessage(devNo, msg, len, onProcessReturnMsg);
474    }
475    return false;
476}
477
478bool DistributeTest::OnMsgProc(const std::string &szbuf, int len)
479{
480    return (szbuf == "") ? false : true;
481}
482
483/*
484 * function : testcase send message to agent device, no return value from agent device.
485 *     this interface is opened for user.
486 * param :
487 *     devNo: the serial number of agent device.
488 *     notifyType : message of the testcase notify the agent
489 *     msg: message of the testcase notify the agent, message format: type:message
490 *     msgLen: length of message
491 * return : if false is return, notify operation failed.
492 */
493bool DistributeTest::Notify(AGENT_NO devNo, const std::string &notifyType, const std::string &msg, int msgLen)
494{
495    // maybe need justify if the length of notifytype+msg is bigger than MAX_BUFF_LEN/2;
496    // the length of notifytype must be less than 50;
497    if (notifyType.size() < CMD_LENGTH) {
498        if (g_pDistributetestEnv != nullptr) {
499            std::string strBuf = notifyType + ":" + msg;
500            return g_pDistributetestEnv->Notify(devNo, strBuf, strBuf.length() + 1);
501        }
502    }
503    return false;
504}
505} // namespace DistributeSystemTest
506} // namespace OHOS
507