ManaPlus
ipc.cpp
Go to the documentation of this file.
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 
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 
52 {
53  mListen = false;
54  if (mSocket != nullptr)
55  {
57  mSocket = nullptr;
58  }
59  SDL_DestroyMutex(mMutex);
60  mMutex = nullptr;
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;
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  }
149  ipc1->mSocket = nullptr;
150  ipc1->mThread = nullptr;
151  return 0;
152 }
153 
154 void IPC::stop()
155 {
156  if (ipc == nullptr)
157  return;
158 
159  logger->log("Stopping IPC...");
160  delete2(ipc)
161 }
162 
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 
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,
201  {
202  chatWindow->chatInput(*it);
203  }
204  }
205  else
206  {
207  FOR_EACH (STD_VECTOR<std::string>::const_iterator, it,
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 }
#define CAST_S32
Definition: cast.h:30
ChatWindow * chatWindow
Definition: chatwindow.cpp:94
void chatInput(const std::string &msg) const
Definition: chatwindow.cpp:665
Definition: ipc.h:37
IPC()
Definition: ipc.cpp:39
unsigned short mPort
Definition: ipc.h:73
bool mListen
Definition: ipc.h:75
unsigned int mNumReqs
Definition: ipc.h:68
~IPC()
Definition: ipc.cpp:51
SDL_Thread * mThread
Definition: ipc.h:71
static int acceptLoop(void *ptr)
Definition: ipc.cpp:91
static void stop()
Definition: ipc.cpp:154
TcpNet::Socket mSocket
Definition: ipc.h:69
void setPort(const unsigned short port)
Definition: ipc.h:65
void flush()
Definition: ipc.cpp:190
bool init()
Definition: ipc.cpp:65
SDL_mutex * mMutex
Definition: ipc.h:72
static void start()
Definition: ipc.cpp:163
volatile bool mThreadLocked
Definition: ipc.h:74
std::vector< std::string > mDelayedCommands
Definition: ipc.h:70
bool executeChatCommand(const std::string &cmd, const std::string &args, ChatTab *const tab)
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log_r(const char *const log_text,...)
Definition: logger.cpp:365
#define delete2(var)
Definition: delete2.h:25
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
InputManager inputManager
IPC * ipc
Definition: ipc.cpp:37
#define nullptr
Definition: localconsts.h:45
Logger * logger
Definition: logger.cpp:89
uint32_t data
bool msg(InputEvent &event)
Definition: chat.cpp:39
std::string trim(std::string const &str)
SDL_Thread * createThread(int(*fn)(void *), const char *const name, void *const data)
Definition: sdlhelper.cpp:118
void WaitThread(SDL_Thread *const thread)
Definition: sdlhelper.cpp:195
::SDLNet_SocketSet SocketSet
Definition: sdltcpnet.h:37
int send(const TcpNet::Socket sock, const void *const data, const int len)
Definition: sdltcpnet.cpp:93
int resolveHost(IPaddress *const address, const char *const host, const Uint16 port)
Definition: sdltcpnet.cpp:104
void freeSocketSet(const TcpNet::SocketSet set)
Definition: sdltcpnet.cpp:193
int socketReady(const TcpNet::Socket sock)
Definition: sdltcpnet.cpp:164
TcpNet::Socket open(IPaddress *const ip)
Definition: sdltcpnet.cpp:110
::TCPsocket Socket
Definition: sdltcpnet.h:38
int addSocket(const TcpNet::SocketSet set, const TcpNet::Socket sock)
Definition: sdltcpnet.cpp:156
SocketSet allocSocketSet(const int maxsockets)
Definition: sdltcpnet.cpp:151
const char * getError()
Definition: sdltcpnet.cpp:99
int checkSockets(const TcpNet::SocketSet set, const Uint32 timeout)
Definition: sdltcpnet.cpp:175
void closeSocket(const TcpNet::Socket socket)
Definition: sdltcpnet.cpp:88
int recv(const TcpNet::Socket sock, void *const data, const int maxlen)
Definition: sdltcpnet.cpp:180
TcpNet::Socket accept(const TcpNet::Socket sock)
Definition: sdltcpnet.cpp:198
std::string strprintf(const char *const format,...)