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_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  auto *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 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
205  QStringList tokens = line.split(' ', QString::SkipEmptyParts);
206 #else
207  QStringList tokens = line.split(' ', Qt::SkipEmptyParts);
208 #endif
209  QString command = tokens[0];
210 
211  bool handled = false;
212 
213  // DONE can be called at any time
214  if (command == "DONE")
215  {
216  HandleDone(sock);
217  return;
218  }
219 
220  if (!sock->IsValidated())
221  {
222  // all sockets must be validated against the local protocol version
223  // before any subsequent commands can be run
224  if (command == "MYTH_PROTO_VERSION")
225  {
226  HandleVersion(sock, tokens);
227  }
228  else
229  {
230  LOG(VB_SOCKET, LOG_ERR, LOC +
231  "Use of socket attempted before protocol validation.");
232  listline.clear();
233  listline << "ERROR" << "socket has not been validated";
234  sock->WriteStringList(listline);
235  }
236  return;
237  }
238 
239  if (!sock->IsAnnounced())
240  {
241  // all sockets must be announced before any subsequent commands can
242  // be run
243  if (command == "ANN")
244  {
245  QReadLocker rlock(&m_handlerLock);
246 
247  QMap<QString, SocketRequestHandler*>::const_iterator i
248  = m_handlerMap.constBegin();
249  while (!handled && (i != m_handlerMap.constEnd()))
250  {
251  LOG(VB_SOCKET, LOG_DEBUG, LOC +
252  QString("Attempting to handle announce with: %1")
253  .arg((*i)->GetHandlerName()));
254  handled = (*i)->HandleAnnounce(sock, tokens, listline);
255  ++i;
256  }
257 
258  if (handled)
259  {
260  --i;
261  LOG(VB_SOCKET, LOG_DEBUG, LOC +
262  QString("Socket announce handled by: %1")
263  .arg((*i)->GetHandlerName()));
264  for (i = m_handlerMap.constBegin();
265  i != m_handlerMap.constEnd(); ++i)
266  (*i)->connectionAnnounced(sock, tokens, listline);
267  }
268  else
269  {
270  LOG(VB_SOCKET, LOG_ERR, LOC + "Socket announce unhandled.");
271  listline.clear();
272  listline << "ERROR" << "unhandled announce";
273  sock->WriteStringList(listline);
274  }
275 
276  return;
277  }
278  LOG(VB_SOCKET, LOG_ERR, LOC +
279  "Use of socket attempted before announcement.");
280  listline.clear();
281  listline << "ERROR" << "socket has not been announced";
282  sock->WriteStringList(listline);
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  {
329  // never respond to these... ever, even if they are not otherwise
330  // handled by something in m_handlerMap
331  return;
332  }
333 
334  listline.clear();
335  listline << "ERROR" << "unknown command";
336  sock->WriteStringList(listline);
337  }
338 }
339 
341  const QStringList &slist)
342 {
343  QStringList retlist;
344  QString version = slist[1];
346  {
347  LOG(VB_GENERAL, LOG_ERR, LOC +
348  "Client speaks protocol version " + version +
349  " but we speak " + MYTH_PROTO_VERSION + '!');
350  retlist << "REJECT" << MYTH_PROTO_VERSION;
351  socket->WriteStringList(retlist);
352  HandleDone(socket);
353  return;
354  }
355 
356  if (slist.size() < 3)
357  {
358  LOG(VB_GENERAL, LOG_ERR, LOC + "Client did not pass protocol "
359  "token. Refusing connection!");
360  retlist << "REJECT" << MYTH_PROTO_VERSION;
361  socket->WriteStringList(retlist);
362  HandleDone(socket);
363  return;
364  }
365 
366  QString token = slist[2];
367  if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
368  {
369  LOG(VB_GENERAL, LOG_ERR, LOC +
370  QString("Client sent incorrect protocol token \"%1\" for "
371  "protocol version. Refusing connection!").arg(token));
372  retlist << "REJECT" << MYTH_PROTO_VERSION;
373  socket->WriteStringList(retlist);
374  HandleDone(socket);
375  return;
376  }
377 
378  LOG(VB_SOCKET, LOG_DEBUG, LOC + "Client validated");
379  retlist << "ACCEPT" << MYTH_PROTO_VERSION;
380  socket->WriteStringList(retlist);
381  socket->m_isValidated = true;
382 }
383 
385 {
386  sock->DisconnectFromHost();
387 }
388 
ReferenceLocker
This decrements the reference on destruction.
Definition: referencecounter.h:67
MythSocketManager::readyRead
void readyRead(MythSocket *socket) override
Definition: mythsocketmanager.cpp:156
MythSocketManager::~MythSocketManager
~MythSocketManager() override
Definition: mythsocketmanager.cpp:63
MythSocketManager::HandleDone
static void HandleDone(MythSocket *socket)
Definition: mythsocketmanager.cpp:384
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:125
MYTH_PROTO_TOKEN
#define MYTH_PROTO_TOKEN
Definition: mythversion.h:48
SocketRequestHandler::GetHandlerName
virtual QString GetHandlerName(void)
Definition: socketrequesthandler.h:25
ServerPool::close
void close(void)
Definition: serverpool.cpp:367
ServerPool
Manages a collection of sockets listening on different ports.
Definition: serverpool.h:60
MythSocket::IsAnnounced
bool IsAnnounced(void) const
Definition: mythsocket.h:47
MythServer::newTcpConnection
void newTcpConnection(qt_socket_fd_t socket) override
Definition: mythsocketmanager.cpp:53
mythsocketmanager.h
arg
arg(title).arg(filename).arg(doDelete))
MythServer::MythServer
MythServer(QObject *parent=nullptr)
Definition: mythsocketmanager.cpp:49
LOC
#define LOC
Definition: mythsocketmanager.cpp:25
MythSocket::IsValidated
bool IsValidated(void) const
Definition: mythsocket.h:42
MythSocket::DisconnectFromHost
void DisconnectFromHost(void)
Definition: mythsocket.cpp:514
ProcessRequestRunnable::run
void run(void) override
Definition: mythsocketmanager.cpp:38
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
socketrequesthandler.h
MThreadPool::Stop
void Stop(void)
Definition: mthreadpool.cpp:266
ProcessRequestRunnable::ProcessRequestRunnable
ProcessRequestRunnable(MythSocketManager &parent, MythSocket *sock)
Definition: mythsocketmanager.cpp:32
PRT_TIMEOUT
#define PRT_TIMEOUT
Definition: mythsocketmanager.cpp:27
MythSocketManager
Definition: mythsocketmanager.h:35
MythSocketManager::m_server
MythServer * m_server
Definition: mythsocketmanager.h:72
ProcessRequestRunnable::m_sock
MythSocket * m_sock
Definition: mythsocketmanager.cpp:46
mythversion.h
MythSocketManager::m_socketMap
QMap< MythSocket *, SocketHandler * > m_socketMap
Definition: mythsocketmanager.h:66
MythSocket
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:27
MythSocketManager::RegisterHandler
void RegisterHandler(SocketRequestHandler *handler)
Definition: mythsocketmanager.cpp:115
mythlogging.h
MythSocket::WriteStringList
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:317
MythServer
Definition: mythsocketmanager.h:22
SocketHandler::GetSocket
MythSocket * GetSocket(void)
Definition: sockethandler.h:29
MythSocketManager::AddSocketHandler
void AddSocketHandler(SocketHandler *socket)
Definition: mythsocketmanager.cpp:135
sockethandler.h
SocketRequestHandler::SetParent
virtual void SetParent(MythSocketManager *parent)
Definition: socketrequesthandler.h:29
MythSocketManager::MythSocketManager
MythSocketManager()
Definition: mythsocketmanager.cpp:58
MythSocketManager::m_threadPool
MThreadPool m_threadPool
Definition: mythsocketmanager.h:73
token
return token
Definition: musicutils.cpp:74
MythSocketManager::m_socketList
QSet< MythSocket * > m_socketList
Definition: mythsocketmanager.h:76
referencecounter.h
MythSocketManager::m_socketLock
QReadWriteLock m_socketLock
Definition: mythsocketmanager.h:67
MYTH_PROTO_VERSION
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:47
MythServer::newConnection
void newConnection(qt_socket_fd_t socket)
mythcorecontext.h
MythSocketManager::ProcessRequestWork
void ProcessRequestWork(MythSocket *socket)
Definition: mythsocketmanager.cpp:197
ProcessRequestRunnable
Definition: mythsocketmanager.cpp:30
MythSocketManager::m_handlerMap
QMap< QString, SocketRequestHandler * > m_handlerMap
Definition: mythsocketmanager.h:69
MythSocketManager::GetConnectionBySocket
SocketHandler * GetConnectionBySocket(MythSocket *socket)
Definition: mythsocketmanager.cpp:145
serverpool.h
MythSocket::IsDataAvailable
bool IsDataAvailable(void)
Definition: mythsocket.cpp:573
mthread.h
SocketHandler
Definition: sockethandler.h:17
MythSocketManager::m_handlerLock
QReadWriteLock m_handlerLock
Definition: mythsocketmanager.h:70
SocketRequestHandler
Definition: socketrequesthandler.h:13
MythSocket::m_isValidated
bool m_isValidated
Definition: mythsocket.h:112
ServerPool::listen
bool listen(QList< QHostAddress > addrs, quint16 port, bool requireall=true, PoolServerType type=kTCPServer)
Definition: serverpool.cpp:388
MythSocketManager::HandleVersion
static void HandleVersion(MythSocket *socket, const QStringList &slist)
Definition: mythsocketmanager.cpp:340
MythSocketManager::ProcessRequest
void ProcessRequest(MythSocket *socket)
Definition: mythsocketmanager.cpp:186
ProcessRequestRunnable::m_parent
MythSocketManager & m_parent
Definition: mythsocketmanager.cpp:45
MythSocketManager::m_socketListLock
QMutex m_socketListLock
Definition: mythsocketmanager.h:75
MythSocket::ReadStringList
bool ReadStringList(QStringList &list, uint timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:329
ReferenceCounter::IncrRef
virtual int IncrRef(void)
Increments reference count.
Definition: referencecounter.cpp:101
ServerPool::setProxy
void setProxy(const QNetworkProxy &proxy)
Definition: serverpool.h:98
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:81
qt_socket_fd_t
qintptr qt_socket_fd_t
Definition: mythqtcompat.h:4
MThreadPool::startReserved
void startReserved(QRunnable *runnable, const QString &debugName, int waitForAvailMS=0)
Definition: mthreadpool.cpp:360
MythSocketManager::connectionClosed
void connectionClosed(MythSocket *socket) override
Definition: mythsocketmanager.cpp:163
MythSocketManager::newConnection
void newConnection(qt_socket_fd_t sd)
Definition: mythsocketmanager.cpp:105
MythSocketManager::Listen
bool Listen(int port)
Definition: mythsocketmanager.cpp:83