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  virtual void run(void)
39  {
41  m_sock->DecrRef();
42  m_sock = NULL;
43  }
44 
47 };
48 
49 MythServer::MythServer(QObject *parent) : ServerPool(parent)
50 {
51 }
52 
54 {
55  emit newConnection(socket);
56 }
57 
59  m_server(NULL), 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 != NULL)
86  {
87  m_server->close();
88  delete m_server;
89  m_server = NULL;
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 NULL;
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  else
275  {
276  LOG(VB_SOCKET, LOG_ERR, LOC +
277  "Use of socket attempted before announcement.");
278  listline.clear();
279  listline << "ERROR" << "socket has not been announced";
280  sock->WriteStringList(listline);
281  }
282  return;
283  }
284 
285  if (command == "ANN")
286  {
287  LOG(VB_SOCKET, LOG_ERR, LOC + "ANN sent out of sequence.");
288  listline.clear();
289  listline << "ERROR" << "socket has already been announced";
290  sock->WriteStringList(listline);
291  return;
292  }
293 
294  {
295  // socket is validated and announced, handle everything else
296  QReadLocker rlock(&m_handlerLock);
297  QReadLocker slock(&m_socketLock);
298 
299  if (!m_socketMap.contains(sock))
300  {
301  LOG(VB_SOCKET, LOG_ERR, LOC + "No handler found for socket.");
302  listline.clear();
303  listline << "ERROR" << "socket handler cannot be found";
304  sock->WriteStringList(listline);
305  return;
306  }
307 
308  SocketHandler *handler = GetConnectionBySocket(sock);
309  ReferenceLocker hlock(handler);
310 
311  QMap<QString, SocketRequestHandler*>::const_iterator i
312  = m_handlerMap.constBegin();
313  while (!handled && (i != m_handlerMap.constEnd()))
314  {
315  handled = (*i)->HandleQuery(handler, tokens, listline);
316  ++i;
317  }
318 
319  if (handled)
320  LOG(VB_SOCKET, LOG_DEBUG, LOC + QString("Query handled by: %1")
321  .arg((*--i)->GetHandlerName()));
322  }
323 
324  if (!handled)
325  {
326  if (command == "BACKEND_MESSAGE")
327  // never respond to these... ever, even if they are not otherwise
328  // handled by something in m_handlerMap
329  return;
330 
331  listline.clear();
332  listline << "ERROR" << "unknown command";
333  sock->WriteStringList(listline);
334  }
335 }
336 
338  const QStringList &slist)
339 {
340  QStringList retlist;
341  QString version = slist[1];
342  if (version != MYTH_PROTO_VERSION)
343  {
344  LOG(VB_GENERAL, LOG_ERR, LOC +
345  "Client speaks protocol version " + version +
346  " but we speak " + MYTH_PROTO_VERSION + '!');
347  retlist << "REJECT" << MYTH_PROTO_VERSION;
348  socket->WriteStringList(retlist);
349  HandleDone(socket);
350  return;
351  }
352 
353  if (slist.size() < 3)
354  {
355  LOG(VB_GENERAL, LOG_ERR, LOC + "Client did not pass protocol "
356  "token. Refusing connection!");
357  retlist << "REJECT" << MYTH_PROTO_VERSION;
358  socket->WriteStringList(retlist);
359  HandleDone(socket);
360  return;
361  }
362 
363  QString token = slist[2];
364  if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
365  {
366  LOG(VB_GENERAL, LOG_ERR, LOC +
367  QString("Client sent incorrect protocol token \"%1\" for "
368  "protocol version. Refusing connection!").arg(token));
369  retlist << "REJECT" << MYTH_PROTO_VERSION;
370  socket->WriteStringList(retlist);
371  HandleDone(socket);
372  return;
373  }
374 
375  LOG(VB_SOCKET, LOG_DEBUG, LOC + "Client validated");
376  retlist << "ACCEPT" << MYTH_PROTO_VERSION;
377  socket->WriteStringList(retlist);
378  socket->m_isValidated = true;
379 }
380 
382 {
383  sock->DisconnectFromHost();
384 }
385 
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:388
void newConnection(qt_socket_fd_t sd)
void Stop(void)
void setProxy(const QNetworkProxy &proxy)
Definition: serverpool.h:97
This decrements the reference on destruction.
void connectionClosed(MythSocket *socket)
virtual void newTcpConnection(qt_socket_fd_t socket)
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:518
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:577
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:339
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:333
bool m_isValidated
Definition: mythsocket.h:112
QReadWriteLock m_handlerLock
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:38
#define LOC
MythServer(QObject *parent=0)
void ProcessRequest(MythSocket *socket)
void readyRead(MythSocket *socket)
bool Listen(int port)
void close(void)
Definition: serverpool.cpp:365
bool IsValidated(void) const
Definition: mythsocket.h:42
MThreadPool m_threadPool
bool IsConnected(void) const
Definition: mythsocket.cpp:571
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:321
SocketHandler * GetConnectionBySocket(MythSocket *socket)
virtual void run(void)
virtual QString GetHandlerName(void)
bool IsAnnounced(void) const
Definition: mythsocket.h:47
MythSocketManager & m_parent