MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
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 
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  m_socketList.insert(new MythSocket(sd, this));
114 }
115 
117 {
118  QWriteLocker wlock(&m_handlerLock);
119 
120  QString name = handler->GetHandlerName();
121  if (m_handlerMap.contains(name))
122  {
123  LOG(VB_GENERAL, LOG_WARNING, LOC + name +
124  " has already been registered.");
125  delete handler;
126  }
127  else
128  {
129  LOG(VB_GENERAL, LOG_INFO, LOC +
130  "Registering socket command handler " + name);
131  handler->SetParent(this);
132  m_handlerMap.insert(name, handler);
133  }
134 }
135 
137 {
138  QWriteLocker wlock(&m_socketLock);
139  if (m_socketMap.contains(sock->GetSocket()))
140  return;
141 
142  sock->IncrRef();
143  m_socketMap.insert(sock->GetSocket(), sock);
144 }
145 
147 {
148  QReadLocker rlock(&m_socketLock);
149  if (!m_socketMap.contains(sock))
150  return NULL;
151 
152  SocketHandler *handler = m_socketMap[sock];
153  handler->IncrRef();
154  return handler;
155 }
156 
158 {
160  new ProcessRequestRunnable(*this, sock),
161  "ServiceRequest", PRT_TIMEOUT);
162 }
163 
165 {
166  // TODO We should delete the MythSocket's at some point
167  // prior to MythSocketManager shutdown...
168 
169  {
170  QReadLocker rlock(&m_handlerLock);
171 
172  QMap<QString, SocketRequestHandler*>::const_iterator i;
173  for (i = m_handlerMap.constBegin(); i != m_handlerMap.constEnd(); ++i)
174  (*i)->connectionClosed(sock);
175  }
176 
177  {
178  QWriteLocker wlock(&m_socketLock);
179  if (m_socketMap.contains(sock))
180  {
181  SocketHandler *handler = m_socketMap.take(sock);
182  handler->DecrRef();
183  }
184  }
185 }
186 
188 {
189  // used as context manager since MythSocket cannot be used directly
190  // with QMutexLocker
191 
192  if (sock->IsDataAvailable())
193  {
194  ProcessRequestWork(sock);
195  }
196 }
197 
199 {
200  QStringList listline;
201  if (!sock->ReadStringList(listline))
202  return;
203 
204  QString line = listline[0].simplified();
205  QStringList tokens = line.split(' ', QString::SkipEmptyParts);
206  QString command = tokens[0];
207 
208  bool handled = false;
209 
210  // DONE can be called at any time
211  if (command == "DONE")
212  {
213  HandleDone(sock);
214  return;
215  }
216 
217  if (!sock->IsValidated())
218  {
219  // all sockets must be validated against the local protocol version
220  // before any subsequent commands can be run
221  if (command == "MYTH_PROTO_VERSION")
222  {
223  HandleVersion(sock, tokens);
224  }
225  else
226  {
227  LOG(VB_SOCKET, LOG_ERR, LOC +
228  "Use of socket attempted before protocol validation.");
229  listline.clear();
230  listline << "ERROR" << "socket has not been validated";
231  sock->WriteStringList(listline);
232  }
233  return;
234  }
235 
236  if (!sock->IsAnnounced())
237  {
238  // all sockets must be announced before any subsequent commands can
239  // be run
240  if (command == "ANN")
241  {
242  QReadLocker rlock(&m_handlerLock);
243 
244  QMap<QString, SocketRequestHandler*>::const_iterator i
245  = m_handlerMap.constBegin();
246  while (!handled && (i != m_handlerMap.constEnd()))
247  {
248  LOG(VB_SOCKET, LOG_DEBUG, LOC +
249  QString("Attempting to handle announce with: %1")
250  .arg((*i)->GetHandlerName()));
251  handled = (*i)->HandleAnnounce(sock, tokens, listline);
252  ++i;
253  }
254 
255  if (handled)
256  {
257  --i;
258  LOG(VB_SOCKET, LOG_DEBUG, LOC +
259  QString("Socket announce handled by: %1")
260  .arg((*i)->GetHandlerName()));
261  for (i = m_handlerMap.constBegin();
262  i != m_handlerMap.constEnd(); ++i)
263  (*i)->connectionAnnounced(sock, tokens, listline);
264  }
265  else
266  {
267  LOG(VB_SOCKET, LOG_ERR, LOC + "Socket announce unhandled.");
268  listline.clear();
269  listline << "ERROR" << "unhandled announce";
270  sock->WriteStringList(listline);
271  }
272 
273  return;
274  }
275  else
276  {
277  LOG(VB_SOCKET, LOG_ERR, LOC +
278  "Use of socket attempted before announcement.");
279  listline.clear();
280  listline << "ERROR" << "socket has not been announced";
281  sock->WriteStringList(listline);
282  }
283  return;
284  }
285 
286  if (command == "ANN")
287  {
288  LOG(VB_SOCKET, LOG_ERR, LOC + "ANN sent out of sequence.");
289  listline.clear();
290  listline << "ERROR" << "socket has already been announced";
291  sock->WriteStringList(listline);
292  return;
293  }
294 
295  {
296  // socket is validated and announced, handle everything else
297  QReadLocker rlock(&m_handlerLock);
298  QReadLocker slock(&m_socketLock);
299 
300  if (!m_socketMap.contains(sock))
301  {
302  LOG(VB_SOCKET, LOG_ERR, LOC + "No handler found for socket.");
303  listline.clear();
304  listline << "ERROR" << "socket handler cannot be found";
305  sock->WriteStringList(listline);
306  return;
307  }
308 
309  SocketHandler *handler = GetConnectionBySocket(sock);
310  ReferenceLocker hlock(handler);
311 
312  QMap<QString, SocketRequestHandler*>::const_iterator i
313  = m_handlerMap.constBegin();
314  while (!handled && (i != m_handlerMap.constEnd()))
315  {
316  handled = (*i)->HandleQuery(handler, tokens, listline);
317  ++i;
318  }
319 
320  if (handled)
321  LOG(VB_SOCKET, LOG_DEBUG, LOC + QString("Query handled by: %1")
322  .arg((*--i)->GetHandlerName()));
323  }
324 
325  if (!handled)
326  {
327  if (command == "BACKEND_MESSAGE")
328  // never respond to these... ever, even if they are not otherwise
329  // handled by something in m_handlerMap
330  return;
331 
332  listline.clear();
333  listline << "ERROR" << "unknown command";
334  sock->WriteStringList(listline);
335  }
336 }
337 
339  const QStringList &slist)
340 {
341  QStringList retlist;
342  QString version = slist[1];
343  if (version != MYTH_PROTO_VERSION)
344  {
345  LOG(VB_GENERAL, LOG_ERR, LOC +
346  "Client speaks protocol version " + version +
347  " but we speak " + MYTH_PROTO_VERSION + '!');
348  retlist << "REJECT" << MYTH_PROTO_VERSION;
349  socket->WriteStringList(retlist);
350  HandleDone(socket);
351  return;
352  }
353 
354  if (slist.size() < 3)
355  {
356  LOG(VB_GENERAL, LOG_ERR, LOC + "Client did not pass protocol "
357  "token. Refusing connection!");
358  retlist << "REJECT" << MYTH_PROTO_VERSION;
359  socket->WriteStringList(retlist);
360  HandleDone(socket);
361  return;
362  }
363 
364  QString token = slist[2];
365  if (token != MYTH_PROTO_TOKEN)
366  {
367  LOG(VB_GENERAL, LOG_ERR, LOC + "Client sent incorrect protocol token "
368  "for protocol version. Refusing connection!");
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)
void newConnection(qt_socket_fd_t sd)
void Stop(void)
bool IsAnnounced(void) const
Definition: mythsocket.h:47
void setProxy(const QNetworkProxy &proxy)
Definition: serverpool.h:78
AllMusic * parent
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)
bool IsDataAvailable(void) const
Definition: mythsocket.cpp:558
QSet< MythSocket * > m_socketList
QReadWriteLock m_socketLock
void DisconnectFromHost(void)
Definition: mythsocket.cpp:499
virtual int IncrRef(void)
Increments reference count.
ProcessRequestRunnable(MythSocketManager &parent, MythSocket *sock)
QMap< QString, SocketRequestHandler * > m_handlerMap
MythServer * m_server
QMap< MythSocket *, SocketHandler * > m_socketMap
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void ProcessRequestWork(MythSocket *socket)
bool IsValidated(void) const
Definition: mythsocket.h:42
const char * name
Definition: ParseText.cpp:338
MythSocket * GetSocket(void)
Definition: sockethandler.h:29
Manages a collection of sockets listening on different ports.
Definition: serverpool.h:43
void RegisterHandler(SocketRequestHandler *handler)
bool ReadStringList(QStringList &list, uint timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:317
bool m_isValidated
Definition: mythsocket.h:112
QReadWriteLock m_handlerLock
MythServer(QObject *parent=0)
bool listen(QList< QHostAddress > addrs, quint16 port, bool requireall=true)
Definition: serverpool.cpp:377
void ProcessRequest(MythSocket *socket)
void readyRead(MythSocket *socket)
bool Listen(int port)
void close(void)
Definition: serverpool.cpp:354
MThreadPool m_threadPool
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:305
SocketHandler * GetConnectionBySocket(MythSocket *socket)
qintptr qt_socket_fd_t
Definition: mythqtcompat.h:5
virtual void run(void)
virtual QString GetHandlerName(void)
MythSocketManager & m_parent