1/*
2 * Copyright (c) 2024 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 "tooling/client/tcpServer/tcp_server.h"
16
17#include "tooling/utils/utils.h"
18
19namespace OHOS::ArkCompiler::Toolchain {
20uv_async_t* g_inputSignal;
21uv_async_t* g_releaseHandle;
22std::string g_inputStr = "";
23std::vector<std::string> g_allCmds = { "allocationtrack", "at", "allocationtrack-stop", "at-stop", "heapdump", "hd",
24    "heapprofiler-enable", "hp-enable", "heapprofiler-disable", "hp-disable", "sampling", "sampling", "sampling-stop",
25    "sampling-stop", "collectgarbage", "gc", "cpuprofile", "cp", "cpuprofile-stop", "cp-stop", "cpuprofile-enable",
26    "cp-enable", "cpuprofile-disable", "cp-disable", "cpuprofile-show", "cp-show", "cpuprofile-setSamplingInterval",
27    "cp-ssi", "runtime-enable", "rt-enable", "heapusage", "hu", "break", "b", "backtrack", "bt", "continue", "c",
28    "delete", "d", "disable", "disable", "display", "display", "enable", "enable", "finish", "fin", "frame", "f",
29    "help", "h", "ignore", "ig", "infobreakpoints", "infob", "infosource", "infos", "jump", "j", "list", "l", "next",
30    "n", "print", "p", "ptype", "ptype", "run", "r", "setvar", "sv", "step", "s", "undisplay", "undisplay", "watch",
31    "wa", "resume", "resume", "showstack", "ss", "step-into", "si", "step-out", "so", "step-over", "sov",
32    "runtime-disable", "rt-disable" };
33std::vector<std::string> g_noRecvCmds = { "cpuprofile-enable", "cp-enable", "cpuprofile-disable", "cp-disable",
34    "cpuprofile-stop", "cp-stop" };
35std::vector<std::string> g_inputOnMessages = { "b", "break", "bt", "backtrack", "d", "delete", "display", "fin",
36    "finish", "f", "frame", "h", "help", "ig", "ignore", "infob", "infobreakpoints", "infos", "infosource", "j", "jump",
37    "l", "list", "n", "next", "ptype", "s", "step", "ss", "showstack", "watch", "wa" };
38
39void CreateServer(void* arg)
40{
41    TcpServer::getInstance().StartTcpServer(arg);
42}
43
44void TcpServer::CloseServer()
45{
46    if (uv_is_active(reinterpret_cast<uv_handle_t*>(g_releaseHandle))) {
47        uv_async_send(g_releaseHandle);
48    }
49}
50
51void TcpServer::ServerConnect()
52{
53    isServerActive = 1;
54    struct sockaddr_in saddr;
55    saddr.sin_family = AF_INET;
56    saddr.sin_addr.s_addr = INADDR_ANY;
57    saddr.sin_port = htons(9999); // 9999: tcp bind port
58
59    lfd = socket(AF_INET, SOCK_STREAM, 0);
60    if (lfd == -1) {
61        std::cout << "socket failed" << std::endl;
62        CloseServer();
63        return;
64    }
65
66    int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));
67    if (ret == -1) {
68        std::cout << "bind failed" << std::endl;
69        CloseServer();
70        return;
71    }
72
73    ret = listen(lfd, 6); // 6: Number of backlogs
74    if (ret == -1) {
75        std::cout << "listen failed" << std::endl;
76        CloseServer();
77        return;
78    }
79
80    struct sockaddr_in clientaddr;
81    socklen_t len = sizeof(clientaddr);
82    cfd = accept(lfd, (struct sockaddr*)&clientaddr, &len);
83    if (cfd == -1) {
84        std::cout << "accept failed" << std::endl;
85        CloseServer();
86        return;
87    }
88}
89
90void TcpServer::SendCommand(std::string inputStr)
91{
92    inputStr.erase(0, inputStr.find_first_not_of(" "));
93    if (inputStr.empty()) {
94        std::cout << "cmd is empty" << std::endl;
95        return;
96    }
97
98    if (uv_is_active(reinterpret_cast<uv_handle_t*>(g_inputSignal))) {
99        uint32_t len = inputStr.length();
100        if (len < 0) {
101            CloseServer();
102            return;
103        }
104        char* msg = (char*)malloc(len + 1);
105        if (msg == nullptr) {
106            return;
107        }
108        if (strncpy_s(msg, len + 1, inputStr.c_str(), len) != 0) {
109            free(msg);
110            CloseServer();
111            return;
112        }
113        g_inputSignal->data = std::move(msg);
114        uv_async_send(g_inputSignal);
115    }
116    return;
117}
118
119void TcpServer::StartTcpServer([[maybe_unused]] void* arg)
120{
121    ServerConnect();
122
123    const char* data = "connect success";
124    write(cfd, data, strlen(data));
125    int num = 0;
126    do {
127        char recvBuf[1024] = { 0 };
128        num = read(cfd, recvBuf, sizeof(recvBuf));
129        if (num < 0) {
130            std::cout << "read failed" << std::endl;
131        } else if (num > 0) {
132            g_inputStr = std::string(recvBuf);
133            SendCommand(recvBuf);
134            TcpServerWrite();
135        } else if (num == 0) {
136            std::cout << "clinet closed" << std::endl;
137        }
138    } while (num > 0);
139
140    close(cfd);
141    close(lfd);
142
143    CloseServer();
144    return;
145}
146
147void TcpServer::TcpServerWrite(std::string msg)
148{
149    if (g_inputStr.empty()) {
150        return;
151    }
152
153    std::vector<std::string> cliCmdStr = Utils::SplitString(g_inputStr, " ");
154    if (!FindCommand(g_allCmds, cliCmdStr[0])) {
155        g_inputStr = "error cmd";
156        write(cfd, g_inputStr.c_str(), strlen(g_inputStr.c_str()));
157        g_inputStr.clear();
158        return;
159    }
160    if (msg == "inner") {
161        if (FindCommand(g_noRecvCmds, cliCmdStr[0])) {
162            write(cfd, g_inputStr.c_str(), strlen(g_inputStr.c_str()));
163            g_inputStr.clear();
164        }
165    } else if (msg == "InputOnMessage") {
166        if (FindCommand(g_inputOnMessages, cliCmdStr[0])) {
167            write(cfd, g_inputStr.c_str(), strlen(g_inputStr.c_str()));
168            g_inputStr.clear();
169        }
170    } else if (msg == "SocketOnMessage") {
171        if (FindCommand(g_inputOnMessages, cliCmdStr[0])) {
172            return;
173        }
174        if (g_inputStr.empty()) {
175            return;
176        }
177        write(cfd, g_inputStr.c_str(), strlen(g_inputStr.c_str()));
178        g_inputStr.clear();
179    }
180    return;
181}
182
183int TcpServer::CreateTcpServer([[maybe_unused]] void* arg)
184{
185    uv_thread_create(&serverTid_, CreateServer, nullptr);
186    return 0;
187}
188
189bool TcpServer::FindCommand(std::vector<std::string> cmds, std::string cmd)
190{
191    return find(cmds.begin(), cmds.end(), cmd) != cmds.end();
192}
193} // namespace OHOS::ArkCompiler::Toolchain