GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/ipc.cpp Lines: 2 101 2.0 %
Date: 2021-03-17 Branches: 1 86 1.2 %

Line Branch Exec Source
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
}