1 |
|
|
/* |
2 |
|
|
* The ManaPlus Client |
3 |
|
|
* Copyright (C) 2014-2019 The ManaPlus Developers |
4 |
|
|
* Copyright (C) 2019-2021 Andrei Karas |
5 |
|
|
* |
6 |
|
|
* This file is part of The ManaPlus Client. |
7 |
|
|
* |
8 |
|
|
* This program is free software; you can redistribute it and/or modify |
9 |
|
|
* it under the terms of the GNU General Public License as published by |
10 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
11 |
|
|
* any later version. |
12 |
|
|
* |
13 |
|
|
* This program is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
|
|
* GNU General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* You should have received a copy of the GNU General Public License |
19 |
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
#include "net/ipc.h" |
23 |
|
|
|
24 |
|
|
#include "gui/windows/chatwindow.h" |
25 |
|
|
|
26 |
|
|
#ifndef DYECMD |
27 |
|
|
#include "input/inputmanager.h" |
28 |
|
|
#endif // DYECMD |
29 |
|
|
|
30 |
|
|
#include "utils/delete2.h" |
31 |
|
|
#include "utils/foreach.h" |
32 |
|
|
#include "utils/sdlhelper.h" |
33 |
|
|
#include "utils/stringutils.h" |
34 |
|
|
|
35 |
|
|
#include "debug.h" |
36 |
|
|
|
37 |
|
|
IPC *ipc = nullptr; |
38 |
|
|
|
39 |
|
|
IPC::IPC() : |
40 |
|
|
mNumReqs(0), |
41 |
|
|
mSocket(nullptr), |
42 |
|
|
mDelayedCommands(), |
43 |
|
|
mThread(nullptr), |
44 |
|
|
mMutex(SDL_CreateMutex()), |
45 |
|
|
mPort(44007U), |
46 |
|
|
mThreadLocked(false), |
47 |
|
|
mListen(false) |
48 |
|
|
{ |
49 |
|
|
} |
50 |
|
|
|
51 |
|
|
IPC::~IPC() |
52 |
|
|
{ |
53 |
|
|
mListen = false; |
54 |
|
|
if (mSocket != nullptr) |
55 |
|
|
{ |
56 |
|
|
TcpNet::closeSocket(mSocket); |
57 |
|
|
mSocket = nullptr; |
58 |
|
|
} |
59 |
|
|
SDL_DestroyMutex(mMutex); |
60 |
|
|
mMutex = nullptr; |
61 |
|
|
SDL::WaitThread(mThread); |
62 |
|
|
mThread = nullptr; |
63 |
|
|
} |
64 |
|
|
|
65 |
|
|
bool IPC::init() |
66 |
|
|
{ |
67 |
|
|
IPaddress ip; |
68 |
|
|
|
69 |
|
|
if (TcpNet::resolveHost(&ip, nullptr, mPort) == -1) |
70 |
|
|
{ |
71 |
|
|
logger->log("IPC: resolveHost error: %s\n", TcpNet::getError()); |
72 |
|
|
return false; |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
mSocket = TcpNet::open(&ip); |
76 |
|
|
if (mSocket == nullptr) |
77 |
|
|
{ |
78 |
|
|
logger->log("IPC: open error: %s", TcpNet::getError()); |
79 |
|
|
return false; |
80 |
|
|
} |
81 |
|
|
|
82 |
|
|
mThread = SDL::createThread(&acceptLoop, "ipc", this); |
83 |
|
|
if (mThread == nullptr) |
84 |
|
|
{ |
85 |
|
|
logger->log("IPC: unable to create acceptLoop thread"); |
86 |
|
|
return false; |
87 |
|
|
} |
88 |
|
|
return true; |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
int IPC::acceptLoop(void *ptr) |
92 |
|
|
{ |
93 |
|
|
if (ptr == nullptr) |
94 |
|
|
return 1; |
95 |
|
|
|
96 |
|
|
IPC *const ipc1 = reinterpret_cast<IPC*>(ptr); |
97 |
|
|
const int max_length = 1024; |
98 |
|
|
const TcpNet::SocketSet set = TcpNet::allocSocketSet(1); |
99 |
|
|
TcpNet::addSocket(set, ipc1->mSocket); |
100 |
|
|
ipc1->mListen = true; |
101 |
|
|
while (ipc1->mListen) |
102 |
|
|
{ |
103 |
|
|
TcpNet::checkSockets(set, 250); |
104 |
|
|
if (TcpNet::socketReady(ipc1->mSocket) == 0) |
105 |
|
|
continue; |
106 |
|
|
|
107 |
|
|
const TcpNet::Socket sock = TcpNet::accept(ipc1->mSocket); |
108 |
|
|
if (sock == nullptr) |
109 |
|
|
{ |
110 |
|
|
logger->log_r("IPC: unable to accept connection"); |
111 |
|
|
continue; |
112 |
|
|
} |
113 |
|
|
char data[max_length] = {0}; |
114 |
|
|
int result = TcpNet::recv(sock, data, max_length); |
115 |
|
|
if (result <= 0) |
116 |
|
|
{ |
117 |
|
|
logger->log_r("IPC: unable to accept connection"); |
118 |
|
|
TcpNet::closeSocket(sock); |
119 |
|
|
continue; |
120 |
|
|
} |
121 |
|
|
|
122 |
|
|
std::string req(data); |
123 |
|
|
trim(req); |
124 |
|
|
logger->log_r("IPC command: %s", req.c_str()); |
125 |
|
|
|
126 |
|
|
ipc1->mThreadLocked = true; |
127 |
|
|
SDL_mutexP(ipc1->mMutex); |
128 |
|
|
ipc1->mDelayedCommands.push_back(req); |
129 |
|
|
SDL_mutexV(ipc1->mMutex); |
130 |
|
|
ipc1->mThreadLocked = false; |
131 |
|
|
|
132 |
|
|
ipc1->mNumReqs ++; |
133 |
|
|
const std::string resp = strprintf("[%u] %s\n", |
134 |
|
|
ipc1->mNumReqs, req.c_str()); |
135 |
|
|
|
136 |
|
|
const char *const respc = resp.c_str(); |
137 |
|
|
const int len = CAST_S32(strlen(respc)) + 1; |
138 |
|
|
result = TcpNet::send(sock, respc, len); |
139 |
|
|
if (result < len) |
140 |
|
|
{ |
141 |
|
|
logger->log_r("IPC: send error: %s\n", TcpNet::getError()); |
142 |
|
|
TcpNet::closeSocket(sock); |
143 |
|
|
continue; |
144 |
|
|
} |
145 |
|
|
TcpNet::closeSocket(sock); |
146 |
|
|
} |
147 |
|
|
TcpNet::closeSocket(ipc1->mSocket); |
148 |
|
|
TcpNet::freeSocketSet(set); |
149 |
|
|
ipc1->mSocket = nullptr; |
150 |
|
|
ipc1->mThread = nullptr; |
151 |
|
|
return 0; |
152 |
|
|
} |
153 |
|
|
|
154 |
|
215 |
void IPC::stop() |
155 |
|
|
{ |
156 |
✗✓ |
215 |
if (ipc == nullptr) |
157 |
|
|
return; |
158 |
|
|
|
159 |
|
|
logger->log("Stopping IPC..."); |
160 |
|
|
delete2(ipc) |
161 |
|
|
} |
162 |
|
|
|
163 |
|
|
void IPC::start() |
164 |
|
|
{ |
165 |
|
|
if (ipc != nullptr) |
166 |
|
|
return; |
167 |
|
|
|
168 |
|
|
unsigned short port(44007); |
169 |
|
|
const char *const portStr = getenv("IPC_PORT"); |
170 |
|
|
if (portStr != nullptr) |
171 |
|
|
port = static_cast<unsigned short>(atoi(portStr)); |
172 |
|
|
|
173 |
|
|
logger->log("Starting IPC..."); |
174 |
|
|
ipc = new IPC; |
175 |
|
|
for (int f = port; f < 65535; f ++) |
176 |
|
|
{ |
177 |
|
|
ipc->setPort(static_cast<unsigned short>(f)); |
178 |
|
|
logger->log(" -> trying port %d...", f); |
179 |
|
|
if (ipc->init()) |
180 |
|
|
{ |
181 |
|
|
logger->log(" -> Port %d selected", f); |
182 |
|
|
return; |
183 |
|
|
} |
184 |
|
|
|
185 |
|
|
port ++; |
186 |
|
|
} |
187 |
|
|
delete2(ipc) |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
void IPC::flush() |
191 |
|
|
{ |
192 |
|
|
if (!mThreadLocked) |
193 |
|
|
{ |
194 |
|
|
SDL_mutexP(mMutex); |
195 |
|
|
#ifndef DYECMD |
196 |
|
|
// probably need enable only commands in tool |
197 |
|
|
if (chatWindow != nullptr) |
198 |
|
|
{ |
199 |
|
|
FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, |
200 |
|
|
mDelayedCommands) |
201 |
|
|
{ |
202 |
|
|
chatWindow->chatInput(*it); |
203 |
|
|
} |
204 |
|
|
} |
205 |
|
|
else |
206 |
|
|
{ |
207 |
|
|
FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, |
208 |
|
|
mDelayedCommands) |
209 |
|
|
{ |
210 |
|
|
std::string msg = *it; |
211 |
|
|
if (msg.empty() || msg[0] != '/') |
212 |
|
|
continue; |
213 |
|
|
msg = msg.substr(1); |
214 |
|
|
const size_t pos = msg.find(' '); |
215 |
|
|
const std::string type(msg, 0, pos); |
216 |
|
|
std::string args(msg, |
217 |
|
|
pos == std::string::npos ? msg.size() : pos + 1); |
218 |
|
|
args = trim(args); |
219 |
|
|
inputManager.executeChatCommand(type, args, nullptr); |
220 |
|
|
} |
221 |
|
|
} |
222 |
|
|
#endif // DYECMD |
223 |
|
|
|
224 |
|
|
mDelayedCommands.clear(); |
225 |
|
|
SDL_mutexV(mMutex); |
226 |
|
|
} |
227 |
|
|
} |