GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/ipc.cpp Lines: 2 103 1.9 %
Date: 2017-11-29 Branches: 1 84 1.2 %

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