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 
100 #if (QT_VERSION >= 0x050000)
103 #else
104  connect(m_server, SIGNAL(newConnection(qt_socket_fd_t)),
105  this, SLOT(newConnection(qt_socket_fd_t)));
106 #endif
107  return true;
108 }
109 
111 {
112  QMutexLocker locker(&m_socketListLock);
113  MythSocket *ms = new MythSocket(sd, this);
114  if (ms->IsConnected())
115  m_socketList.insert(ms);
116  else
117  delete ms;
118 }
119 
121 {
122  QWriteLocker wlock(&m_handlerLock);
123 
124  QString name = handler->GetHandlerName();
125  if (m_handlerMap.contains(name))
126  {
127  LOG(VB_GENERAL, LOG_WARNING, LOC + name +
128  " has already been registered.");
129  delete handler;
130  }
131  else
132  {
133  LOG(VB_GENERAL, LOG_INFO, LOC +
134  "Registering socket command handler " + name);
135  handler->SetParent(this);
136  m_handlerMap.insert(name, handler);
137  }
138 }
139 
141 {
142  QWriteLocker wlock(&m_socketLock);
143  if (m_socketMap.contains(sock->GetSocket()))
144  return;
145 
146  sock->IncrRef();
147  m_socketMap.insert(sock->GetSocket(), sock);
148 }
149 
151 {
152  QReadLocker rlock(&m_socketLock);
153  if (!m_socketMap.contains(sock))
154  return NULL;
155 
156  SocketHandler *handler = m_socketMap[sock];
157  handler->IncrRef();
158  return handler;
159 }
160 
162 {
164  new ProcessRequestRunnable(*this, sock),
165  "ServiceRequest", PRT_TIMEOUT);
166 }
167 
169 {
170  // TODO We should delete the MythSocket's at some point
171  // prior to MythSocketManager shutdown...
172 
173  {
174  QReadLocker rlock(&m_handlerLock);
175 
176  QMap<QString, SocketRequestHandler*>::const_iterator i;
177  for (i = m_handlerMap.constBegin(); i != m_handlerMap.constEnd(); ++i)
178  (*i)->connectionClosed(sock);
179  }
180 
181  {
182  QWriteLocker wlock(&m_socketLock);
183  if (m_socketMap.contains(sock))
184  {
185  SocketHandler *handler = m_socketMap.take(sock);
186  handler->DecrRef();
187  }
188  }
189 }
190 
192 {
193  // used as context manager since MythSocket cannot be used directly
194  // with QMutexLocker
195 
196  if (sock->IsDataAvailable())
197  {
198  ProcessRequestWork(sock);
199  }
200 }
201 
203 {
204  QStringList listline;
205  if (!sock->ReadStringList(listline))
206  return;
207 
208  QString line = listline[0].simplified();
209  QStringList tokens = line.split(' ', QString::SkipEmptyParts);
210  QString command = tokens[0];
211 
212  bool handled = false;
213 
214  // DONE can be called at any time
215  if (command == "DONE")
216  {
217  HandleDone(sock);
218  return;
219  }
220 
221  if (!sock->IsValidated())
222  {
223  // all sockets must be validated against the local protocol version
224  // before any subsequent commands can be run
225  if (command == "MYTH_PROTO_VERSION")
226  {
227  HandleVersion(sock, tokens);
228  }
229  else
230  {
231  LOG(VB_SOCKET, LOG_ERR, LOC +
232  "Use of socket attempted before protocol validation.");
233  listline.clear();
234  listline << "ERROR" << "socket has not been validated";
235  sock->WriteStringList(listline);
236  }
237  return;
238  }
239 
240  if (!sock->IsAnnounced())
241  {
242  // all sockets must be announced before any subsequent commands can
243  // be run
244  if (command == "ANN")
245  {
246  QReadLocker rlock(&m_handlerLock);
247 
248  QMap<QString, SocketRequestHandler*>::const_iterator i
249  = m_handlerMap.constBegin();
250  while (!handled && (i != m_handlerMap.constEnd()))
251  {
252  LOG(VB_SOCKET, LOG_DEBUG, LOC +
253  QString("Attempting to handle announce with: %1")
254  .arg((*i)->GetHandlerName()));
255  handled = (*i)->HandleAnnounce(sock, tokens, listline);
256  ++i;
257  }
258 
259  if (handled)
260  {
261  --i;
262  LOG(VB_SOCKET, LOG_DEBUG, LOC +
263  QString("Socket announce handled by: %1")
264  .arg((*i)->GetHandlerName()));
265  for (i = m_handlerMap.constBegin();
266  i != m_handlerMap.constEnd(); ++i)
267  (*i)->connectionAnnounced(sock, tokens, listline);
268  }
269  else
270  {
271  LOG(VB_SOCKET, LOG_ERR, LOC + "Socket announce unhandled.");
272  listline.clear();
273  listline << "ERROR" << "unhandled announce";
274  sock->WriteStringList(listline);
275  }
276 
277  return;
278  }
279  else
280  {
281  LOG(VB_SOCKET, LOG_ERR, LOC +
282  "Use of socket attempted before announcement.");
283  listline.clear();
284  listline << "ERROR" << "socket has not been announced";
285  sock->WriteStringList(listline);
286  }
287  return;
288  }
289 
290  if (command == "ANN")
291  {
292  LOG(VB_SOCKET, LOG_ERR, LOC + "ANN sent out of sequence.");
293  listline.clear();
294  listline << "ERROR" << "socket has already been announced";
295  sock->WriteStringList(listline);
296  return;
297  }
298 
299  {
300  // socket is validated and announced, handle everything else
301  QReadLocker rlock(&m_handlerLock);
302  QReadLocker slock(&m_socketLock);
303 
304  if (!m_socketMap.contains(sock))
305  {
306  LOG(VB_SOCKET, LOG_ERR, LOC + "No handler found for socket.");
307  listline.clear();
308  listline << "ERROR" << "socket handler cannot be found";
309  sock->WriteStringList(listline);
310  return;
311  }
312 
313  SocketHandler *handler = GetConnectionBySocket(sock);
314  ReferenceLocker hlock(handler);
315 
316  QMap<QString, SocketRequestHandler*>::const_iterator i
317  = m_handlerMap.constBegin();
318  while (!handled && (i != m_handlerMap.constEnd()))
319  {
320  handled = (*i)->HandleQuery(handler, tokens, listline);
321  ++i;
322  }
323 
324  if (handled)
325  LOG(VB_SOCKET, LOG_DEBUG, LOC + QString("Query handled by: %1")
326  .arg((*--i)->GetHandlerName()));
327  }
328 
329  if (!handled)
330  {
331  if (command == "BACKEND_MESSAGE")
332  // never respond to these... ever, even if they are not otherwise
333  // handled by something in m_handlerMap
334  return;
335 
336  listline.clear();
337  listline << "ERROR" << "unknown command";
338  sock->WriteStringList(listline);
339  }
340 }
341 
343  const QStringList &slist)
344 {
345  QStringList retlist;
346  QString version = slist[1];
347  if (version != MYTH_PROTO_VERSION)
348  {
349  LOG(VB_GENERAL, LOG_ERR, LOC +
350  "Client speaks protocol version " + version +
351  " but we speak " + MYTH_PROTO_VERSION + '!');
352  retlist << "REJECT" << MYTH_PROTO_VERSION;
353  socket->WriteStringList(retlist);
354  HandleDone(socket);
355  return;
356  }
357 
358  if (slist.size() < 3)
359  {
360  LOG(VB_GENERAL, LOG_ERR, LOC + "Client did not pass protocol "
361  "token. Refusing connection!");
362  retlist << "REJECT" << MYTH_PROTO_VERSION;
363  socket->WriteStringList(retlist);
364  HandleDone(socket);
365  return;
366  }
367 
368  QString token = slist[2];
369  if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
370  {
371  LOG(VB_GENERAL, LOG_ERR, LOC +
372  QString("Client sent incorrect protocol token \"%1\" for "
373  "protocol version. Refusing connection!").arg(token));
374  retlist << "REJECT" << MYTH_PROTO_VERSION;
375  socket->WriteStringList(retlist);
376  HandleDone(socket);
377  return;
378  }
379 
380  LOG(VB_SOCKET, LOG_DEBUG, LOC + "Client validated");
381  retlist << "ACCEPT" << MYTH_PROTO_VERSION;
382  socket->WriteStringList(retlist);
383  socket->m_isValidated = true;
384 }
385 
387 {
388  sock->DisconnectFromHost();
389 }
390 
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)
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.
const char * name
Definition: ParseText.cpp:338
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
struct v4l2_mpeg_vbi_itv0_line line[35]
Definition: videodev2.h:1039
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void ProcessRequestWork(MythSocket *socket)
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
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
static const QString LOC
void newConnection(qt_socket_fd_t socket)
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:321
SocketHandler * GetConnectionBySocket(MythSocket *socket)
qintptr qt_socket_fd_t
Definition: mythqtcompat.h:5
virtual void run(void)
virtual QString GetHandlerName(void)
bool IsAnnounced(void) const
Definition: mythsocket.h:47
MythSocketManager & m_parent