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  QStringList tokens = line.split(' ', Qt::SkipEmptyParts);
198  QString command = tokens[0];
199 
200  bool handled = false;
201 
202  // DONE can be called at any time
203  if (command == "DONE")
204  {
205  HandleDone(sock);
206  return;
207  }
208 
209  if (!sock->IsValidated())
210  {
211  // all sockets must be validated against the local protocol version
212  // before any subsequent commands can be run
213  if (command == "MYTH_PROTO_VERSION")
214  {
215  HandleVersion(sock, tokens);
216  }
217  else
218  {
219  LOG(VB_SOCKET, LOG_ERR, LOC +
220  "Use of socket attempted before protocol validation.");
221  listline.clear();
222  listline << "ERROR" << "socket has not been validated";
223  sock->WriteStringList(listline);
224  }
225  return;
226  }
227 
228  if (!sock->IsAnnounced())
229  {
230  // all sockets must be announced before any subsequent commands can
231  // be run
232  if (command == "ANN")
233  {
234  QReadLocker rlock(&m_handlerLock);
235 
236  QMap<QString, SocketRequestHandler*>::const_iterator i
237  = m_handlerMap.constBegin();
238  while (!handled && (i != m_handlerMap.constEnd()))
239  {
240  LOG(VB_SOCKET, LOG_DEBUG, LOC +
241  QString("Attempting to handle announce with: %1")
242  .arg((*i)->GetHandlerName()));
243  handled = (*i)->HandleAnnounce(sock, tokens, listline);
244  ++i;
245  }
246 
247  if (handled)
248  {
249  --i;
250  LOG(VB_SOCKET, LOG_DEBUG, LOC +
251  QString("Socket announce handled by: %1")
252  .arg((*i)->GetHandlerName()));
253  for (i = m_handlerMap.constBegin();
254  i != m_handlerMap.constEnd(); ++i)
255  (*i)->connectionAnnounced(sock, tokens, listline);
256  }
257  else
258  {
259  LOG(VB_SOCKET, LOG_ERR, LOC + "Socket announce unhandled.");
260  listline.clear();
261  listline << "ERROR" << "unhandled announce";
262  sock->WriteStringList(listline);
263  }
264 
265  return;
266  }
267  LOG(VB_SOCKET, LOG_ERR, LOC +
268  "Use of socket attempted before announcement.");
269  listline.clear();
270  listline << "ERROR" << "socket has not been announced";
271  sock->WriteStringList(listline);
272  return;
273  }
274 
275  if (command == "ANN")
276  {
277  LOG(VB_SOCKET, LOG_ERR, LOC + "ANN sent out of sequence.");
278  listline.clear();
279  listline << "ERROR" << "socket has already been announced";
280  sock->WriteStringList(listline);
281  return;
282  }
283 
284  {
285  // socket is validated and announced, handle everything else
286  QReadLocker rlock(&m_handlerLock);
287  QReadLocker slock(&m_socketLock);
288 
289  if (!m_socketMap.contains(sock))
290  {
291  LOG(VB_SOCKET, LOG_ERR, LOC + "No handler found for socket.");
292  listline.clear();
293  listline << "ERROR" << "socket handler cannot be found";
294  sock->WriteStringList(listline);
295  return;
296  }
297 
298  SocketHandler *handler = GetConnectionBySocket(sock);
299  ReferenceLocker hlock(handler);
300 
301  QMap<QString, SocketRequestHandler*>::const_iterator i
302  = m_handlerMap.constBegin();
303  while (!handled && (i != m_handlerMap.constEnd()))
304  {
305  handled = (*i)->HandleQuery(handler, tokens, listline);
306  ++i;
307  }
308 
309  if (handled)
310  LOG(VB_SOCKET, LOG_DEBUG, LOC + QString("Query handled by: %1")
311  .arg((*--i)->GetHandlerName()));
312  }
313 
314  if (!handled)
315  {
316  if (command == "BACKEND_MESSAGE")
317  {
318  // never respond to these... ever, even if they are not otherwise
319  // handled by something in m_handlerMap
320  return;
321  }
322 
323  listline.clear();
324  listline << "ERROR" << "unknown command";
325  sock->WriteStringList(listline);
326  }
327 }
328 
330  const QStringList &slist)
331 {
332  QStringList retlist;
333  const QString& version = slist[1];
334  if (version != MYTH_PROTO_VERSION)
335  {
336  LOG(VB_GENERAL, LOG_ERR, LOC +
337  "Client speaks protocol version " + version +
338  " but we speak " + MYTH_PROTO_VERSION + '!');
339  retlist << "REJECT" << MYTH_PROTO_VERSION;
340  socket->WriteStringList(retlist);
341  HandleDone(socket);
342  return;
343  }
344 
345  if (slist.size() < 3)
346  {
347  LOG(VB_GENERAL, LOG_ERR, LOC + "Client did not pass protocol "
348  "token. Refusing connection!");
349  retlist << "REJECT" << MYTH_PROTO_VERSION;
350  socket->WriteStringList(retlist);
351  HandleDone(socket);
352  return;
353  }
354 
355  const QString& token = slist[2];
356  if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
357  {
358  LOG(VB_GENERAL, LOG_ERR, LOC +
359  QString("Client sent incorrect protocol token \"%1\" for "
360  "protocol version. Refusing connection!").arg(token));
361  retlist << "REJECT" << MYTH_PROTO_VERSION;
362  socket->WriteStringList(retlist);
363  HandleDone(socket);
364  return;
365  }
366 
367  LOG(VB_SOCKET, LOG_DEBUG, LOC + "Client validated");
368  retlist << "ACCEPT" << MYTH_PROTO_VERSION;
369  socket->WriteStringList(retlist);
370  socket->m_isValidated = true;
371 }
372 
374 {
375  sock->DisconnectFromHost();
376 }
377 
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:373
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:498
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:267
MThreadPool::startReserved
void startReserved(QRunnable *runnable, const QString &debugName, std::chrono::milliseconds waitForAvailMS=0ms)
Definition: mthreadpool.cpp:361
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:301
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:557
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:329
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:313
MythSocketManager::connectionClosed
void connectionClosed(MythSocket *socket) override
Definition: mythsocketmanager.cpp:156
MythSocketManager::Listen
bool Listen(int port)
Definition: mythsocketmanager.cpp:76