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