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