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