MythTV master
mythsocketmanager.cpp
Go to the documentation of this file.
1// Qt
2#include <QMutex>
3#include <QReadWriteLock>
4#include <QReadLocker>
5#include <QWriteLocker>
6#include <QMutexLocker>
7#include <QWaitCondition>
8#include <QEvent>
9#include <QCoreApplication>
10#include <QNetworkProxy>
11#include <QRunnable>
12
13// MythTV
14#include "libmythbase/mthread.h"
16#include "libmythbase/mythversion.h"
19
20#include "mythsocketmanager.h"
21#include "sockethandler.h"
23
24#define LOC QString("MythSocketManager: ")
25
26static constexpr std::chrono::milliseconds PRT_TIMEOUT { 10ms };
27
28class ProcessRequestRunnable : public QRunnable
29{
30 public:
32 m_parent(parent), m_sock(sock)
33 {
34 m_sock->IncrRef();
35 }
36
37 void run(void) override // QRunnable
38 {
40 m_sock->DecrRef();
41 m_sock = nullptr;
42 }
43
46};
47
49 m_threadPool("MythSocketManager")
50{
51}
52
54{
56
57 QWriteLocker wlock(&m_handlerLock);
58
59 QMap<QString, SocketRequestHandler*>::iterator i;
60 for (i = m_handlerMap.begin(); i != m_handlerMap.end(); ++i)
61 delete *i;
62
63 m_handlerMap.clear();
64
65 QMutexLocker locker(&m_socketListLock);
66 for (auto iter = m_socketList.begin();
67 iter != m_socketList.end();
68 iter = m_socketList.erase(iter))
69 {
70 (*iter)->DecrRef();
71 }
72}
73
75{
76 if (m_server != nullptr)
77 {
78 m_server->close();
79 delete m_server;
80 m_server = nullptr;
81 }
82
83 m_server = new MythServer(this);
84 m_server->setProxy(QNetworkProxy::NoProxy);
85 if (!m_server->listen(port))
86 {
87 LOG(VB_GENERAL, LOG_ERR, QString("Failed to bind port %1.").arg(port));
88 return false;
89 }
90
93 return true;
94}
95
97{
98 QMutexLocker locker(&m_socketListLock);
99 auto *ms = new MythSocket(sd, this);
100 if (ms->IsConnected())
101 m_socketList.insert(ms);
102 else
103 delete ms;
104}
105
107{
108 QWriteLocker wlock(&m_handlerLock);
109
110 QString name = handler->GetHandlerName();
111 if (m_handlerMap.contains(name))
112 {
113 LOG(VB_GENERAL, LOG_WARNING, LOC + name +
114 " has already been registered.");
115 delete handler;
116 }
117 else
118 {
119 LOG(VB_GENERAL, LOG_INFO, LOC +
120 "Registering socket command handler " + name);
121 handler->SetParent(this);
122 m_handlerMap.insert(name, handler);
123 }
124}
125
127{
128 QWriteLocker wlock(&m_socketLock);
129 if (m_socketMap.contains(sock->GetSocket()))
130 return;
131
132 sock->IncrRef();
133 m_socketMap.insert(sock->GetSocket(), sock);
134}
135
137{
138 QReadLocker rlock(&m_socketLock);
139 if (!m_socketMap.contains(sock))
140 return nullptr;
141
142 SocketHandler *handler = m_socketMap.value(sock);
143 handler->IncrRef();
144 return handler;
145}
146
148{
150 new ProcessRequestRunnable(*this, sock),
151 "ServiceRequest", PRT_TIMEOUT);
152}
153
155{
156 // TODO We should delete the MythSocket's at some point
157 // prior to MythSocketManager shutdown...
158
159 {
160 QReadLocker rlock(&m_handlerLock);
161
162 QMap<QString, SocketRequestHandler*>::const_iterator i;
163 for (i = m_handlerMap.constBegin(); i != m_handlerMap.constEnd(); ++i)
164 (*i)->connectionClosed(sock);
165 }
166
167 {
168 QWriteLocker wlock(&m_socketLock);
169 if (m_socketMap.contains(sock))
170 {
171 SocketHandler *handler = m_socketMap.take(sock);
172 handler->DecrRef();
173 }
174 }
175}
176
178{
179 // used as context manager since MythSocket cannot be used directly
180 // with QMutexLocker
181
182 if (sock->IsDataAvailable())
183 {
184 ProcessRequestWork(sock);
185 }
186}
187
189{
190 QStringList listline;
191 if (!sock->ReadStringList(listline))
192 return;
193
194 QString line = listline[0].simplified();
195 QStringList tokens = line.split(' ', Qt::SkipEmptyParts);
196 QString command = tokens[0];
197
198 bool handled = false;
199
200 // DONE can be called at any time
201 if (command == "DONE")
202 {
203 HandleDone(sock);
204 return;
205 }
206
207 if (!sock->IsValidated())
208 {
209 // all sockets must be validated against the local protocol version
210 // before any subsequent commands can be run
211 if (command == "MYTH_PROTO_VERSION")
212 {
213 HandleVersion(sock, tokens);
214 }
215 else
216 {
217 LOG(VB_SOCKET, LOG_ERR, LOC +
218 "Use of socket attempted before protocol validation.");
219 listline.clear();
220 listline << "ERROR" << "socket has not been validated";
221 sock->WriteStringList(listline);
222 }
223 return;
224 }
225
226 if (!sock->IsAnnounced())
227 {
228 // all sockets must be announced before any subsequent commands can
229 // be run
230 if (command == "ANN")
231 {
232 QReadLocker rlock(&m_handlerLock);
233
234 QMap<QString, SocketRequestHandler*>::const_iterator i
235 = m_handlerMap.constBegin();
236 while (!handled && (i != m_handlerMap.constEnd()))
237 {
238 LOG(VB_SOCKET, LOG_DEBUG, LOC +
239 QString("Attempting to handle announce with: %1")
240 .arg((*i)->GetHandlerName()));
241 handled = (*i)->HandleAnnounce(sock, tokens, listline);
242 ++i;
243 }
244
245 if (handled)
246 {
247 --i;
248 LOG(VB_SOCKET, LOG_DEBUG, LOC +
249 QString("Socket announce handled by: %1")
250 .arg((*i)->GetHandlerName()));
251 for (i = m_handlerMap.constBegin();
252 i != m_handlerMap.constEnd(); ++i)
253 (*i)->connectionAnnounced(sock, tokens, listline);
254 }
255 else
256 {
257 LOG(VB_SOCKET, LOG_ERR, LOC + "Socket announce unhandled.");
258 listline.clear();
259 listline << "ERROR" << "unhandled announce";
260 sock->WriteStringList(listline);
261 }
262
263 return;
264 }
265 LOG(VB_SOCKET, LOG_ERR, LOC +
266 "Use of socket attempted before announcement.");
267 listline.clear();
268 listline << "ERROR" << "socket has not been announced";
269 sock->WriteStringList(listline);
270 return;
271 }
272
273 if (command == "ANN")
274 {
275 LOG(VB_SOCKET, LOG_ERR, LOC + "ANN sent out of sequence.");
276 listline.clear();
277 listline << "ERROR" << "socket has already been announced";
278 sock->WriteStringList(listline);
279 return;
280 }
281
282 {
283 // socket is validated and announced, handle everything else
284 QReadLocker rlock(&m_handlerLock);
285 QReadLocker slock(&m_socketLock);
286
287 if (!m_socketMap.contains(sock))
288 {
289 LOG(VB_SOCKET, LOG_ERR, LOC + "No handler found for socket.");
290 listline.clear();
291 listline << "ERROR" << "socket handler cannot be found";
292 sock->WriteStringList(listline);
293 return;
294 }
295
296 SocketHandler *handler = GetConnectionBySocket(sock);
297 ReferenceLocker hlock(handler);
298
299 QMap<QString, SocketRequestHandler*>::const_iterator i
300 = m_handlerMap.constBegin();
301 while (!handled && (i != m_handlerMap.constEnd()))
302 {
303 handled = (*i)->HandleQuery(handler, tokens, listline);
304 ++i;
305 }
306
307 if (handled)
308 LOG(VB_SOCKET, LOG_DEBUG, LOC + QString("Query handled by: %1")
309 .arg((*--i)->GetHandlerName()));
310 }
311
312 if (!handled)
313 {
314 if (command == "BACKEND_MESSAGE")
315 {
316 // never respond to these... ever, even if they are not otherwise
317 // handled by something in m_handlerMap
318 return;
319 }
320
321 listline.clear();
322 listline << "ERROR" << "unknown command";
323 sock->WriteStringList(listline);
324 }
325}
326
328 const QStringList &slist)
329{
330 QStringList retlist;
331 const QString& version = slist[1];
332 if (version != MYTH_PROTO_VERSION)
333 {
334 LOG(VB_GENERAL, LOG_ERR, LOC +
335 "Client speaks protocol version " + version +
336 " but we speak " + MYTH_PROTO_VERSION + '!');
337 retlist << "REJECT" << MYTH_PROTO_VERSION;
338 socket->WriteStringList(retlist);
339 HandleDone(socket);
340 return;
341 }
342
343 if (slist.size() < 3)
344 {
345 LOG(VB_GENERAL, LOG_ERR, LOC + "Client did not pass protocol "
346 "token. Refusing connection!");
347 retlist << "REJECT" << MYTH_PROTO_VERSION;
348 socket->WriteStringList(retlist);
349 HandleDone(socket);
350 return;
351 }
352
353 const QString& token = slist[2];
354 if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
355 {
356 LOG(VB_GENERAL, LOG_ERR, LOC +
357 QString("Client sent incorrect protocol token \"%1\" for "
358 "protocol version. Refusing connection!").arg(token));
359 retlist << "REJECT" << MYTH_PROTO_VERSION;
360 socket->WriteStringList(retlist);
361 HandleDone(socket);
362 return;
363 }
364
365 LOG(VB_SOCKET, LOG_DEBUG, LOC + "Client validated");
366 retlist << "ACCEPT" << MYTH_PROTO_VERSION;
367 socket->WriteStringList(retlist);
368 socket->m_isValidated = true;
369}
370
372{
373 sock->DisconnectFromHost();
374}
375
void startReserved(QRunnable *runnable, const QString &debugName, std::chrono::milliseconds waitForAvailMS=0ms)
void Stop(void)
void newConnection(qintptr socket)
QMap< QString, SocketRequestHandler * > m_handlerMap
bool Listen(int port)
static void HandleVersion(MythSocket *socket, const QStringList &slist)
SocketHandler * GetConnectionBySocket(MythSocket *socket)
QReadWriteLock m_handlerLock
void readyRead(MythSocket *socket) override
QReadWriteLock m_socketLock
MythServer * m_server
void AddSocketHandler(SocketHandler *socket)
~MythSocketManager() override
void ProcessRequestWork(MythSocket *socket)
void ProcessRequest(MythSocket *socket)
void RegisterHandler(SocketRequestHandler *handler)
void newConnection(qintptr sd)
static void HandleDone(MythSocket *socket)
void connectionClosed(MythSocket *socket) override
QMap< MythSocket *, SocketHandler * > m_socketMap
QSet< MythSocket * > m_socketList
MThreadPool m_threadPool
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
bool IsValidated(void) const
Definition: mythsocket.h:41
bool IsAnnounced(void) const
Definition: mythsocket.h:46
bool m_isValidated
Definition: mythsocket.h:119
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:317
bool IsDataAvailable(void)
Definition: mythsocket.cpp:561
void DisconnectFromHost(void)
Definition: mythsocket.cpp:502
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:305
void run(void) override
ProcessRequestRunnable(MythSocketManager &parent, MythSocket *sock)
MythSocketManager & m_parent
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
This decrements the reference on destruction.
bool listen(QList< QHostAddress > addrs, quint16 port, bool requireall=true, PoolServerType type=kTCPServer)
Definition: serverpool.cpp:395
void close(void)
Definition: serverpool.cpp:374
void setProxy(const QNetworkProxy &proxy)
Definition: serverpool.h:98
MythSocket * GetSocket(void) const
Definition: sockethandler.h:29
virtual void SetParent(MythSocketManager *parent)
virtual QString GetHandlerName(void)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
static constexpr std::chrono::milliseconds PRT_TIMEOUT
string version
Definition: giantbomb.py:185