MythTV  master
mythsocket.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QNetworkInterface> // for QNetworkInterface::allAddresses ()
3 #include <QCoreApplication>
4 #include <QWaitCondition>
5 #include <QSharedPointer>
6 #include <QByteArray>
7 #include <QTcpSocket>
8 #include <QHostInfo>
9 #include <QThread>
10 #include <QMetaType>
11 
12 // setsockopt -- has to be after Qt includes for Q_OS_WIN definition
13 #if defined(Q_OS_WIN)
14 #include <winsock2.h>
15 #include <ws2tcpip.h>
16 #include <cstdio>
17 #else
18 #include <sys/socket.h>
19 #endif
20 #include <unistd.h> // for usleep (and socket code on Q_OS_WIN)
21 #include <algorithm> // for min/max
22 using std::max;
23 #include <vector> // for vector
24 using std::vector;
25 
26 // MythTV
27 #include "mythsocket.h"
28 #include "mythtimer.h"
29 #include "mythevent.h"
30 #include "mythversion.h"
31 #include "mythlogging.h"
32 #include "mythcorecontext.h"
33 #include "portchecker.h"
34 
35 #define SLOC(a) QString("MythSocket(%1:%2): ") \
36  .arg((intptr_t)(a), 0, 16) \
37  .arg((a)->GetSocketDescriptor())
38 #define LOC SLOC(this)
39 
40 const int MythSocket::kSocketReceiveBufferSize = 128 * 1024;
41 
43 QHash<QString, QHostAddress::SpecialAddress> MythSocket::s_loopbackCache;
44 
46 MThread *MythSocket::s_thread = nullptr;
48 
49 Q_DECLARE_METATYPE ( const QStringList * );
50 Q_DECLARE_METATYPE ( QStringList * );
51 Q_DECLARE_METATYPE ( const char * );
52 Q_DECLARE_METATYPE ( char * );
53 Q_DECLARE_METATYPE ( bool * );
54 Q_DECLARE_METATYPE ( int * );
55 Q_DECLARE_METATYPE ( QHostAddress );
56 static int x0 = qRegisterMetaType< const QStringList * >();
57 static int x1 = qRegisterMetaType< QStringList * >();
58 static int x2 = qRegisterMetaType< const char * >();
59 static int x3 = qRegisterMetaType< char * >();
60 static int x4 = qRegisterMetaType< bool * >();
61 static int x5 = qRegisterMetaType< int * >();
62 static int x6 = qRegisterMetaType< QHostAddress >();
64  x0 + x1 + x2 + x3 + x4 + x5 + x6;
65 
66 static QString to_sample(const QByteArray &payload)
67 {
68  QString sample("");
69  for (uint i = 0; (i<60) && (i<(uint)payload.length()); i++)
70  {
71  sample += QChar(payload[i]).isPrint() ?
72  QChar(payload[i]) : QChar('?');
73  }
74  sample += (payload.length() > 60) ? "..." : "";
75  return sample;
76 }
77 
79  qt_socket_fd_t socket, MythSocketCBs *cb, bool use_shared_thread) :
80  ReferenceCounter(QString("MythSocket(%1)").arg(socket)),
81  m_tcpSocket(new QTcpSocket()),
82  m_callback(cb),
83  m_useSharedThread(use_shared_thread)
84 {
85  LOG(VB_SOCKET, LOG_INFO, LOC + QString("MythSocket(%1, 0x%2) ctor")
86  .arg(socket).arg((intptr_t)(cb),0,16));
87 
88  if (socket != -1)
89  {
90  m_tcpSocket->setSocketDescriptor(
91  socket, QAbstractSocket::ConnectedState,
92  QAbstractSocket::ReadWrite);
94  {
95  m_tcpSocket->abort();
96  m_connected = false;
97  m_useSharedThread = false;
98  return;
99  }
100  ConnectHandler(); // already called implicitly above?
101  }
102 
103  // Use direct connections so m_tcpSocket can be used
104  // in the handlers safely since they will be running
105  // in the same thread as all other m_tcpSocket users.
106 
107  connect(m_tcpSocket, &QAbstractSocket::connected,
109  Qt::DirectConnection);
110 #if QT_VERSION < QT_VERSION_CHECK(5,15,0)
111  connect(m_tcpSocket, qOverload<QAbstractSocket::SocketError>(&QAbstractSocket::error),
113  Qt::DirectConnection);
114 #else
115  connect(m_tcpSocket, &QAbstractSocket::errorOccurred,
117  Qt::DirectConnection);
118 #endif
119  connect(m_tcpSocket, &QIODevice::aboutToClose,
121  connect(m_tcpSocket, &QAbstractSocket::disconnected,
123  Qt::DirectConnection);
124  connect(m_tcpSocket, &QIODevice::readyRead,
126  Qt::DirectConnection);
127 
128  connect(this, &MythSocket::CallReadyRead,
130  Qt::QueuedConnection);
131 
132  if (!use_shared_thread)
133  {
134  m_thread = new MThread(QString("MythSocketThread(%1)").arg(socket));
135  m_thread->start();
136  }
137  else
138  {
139  QMutexLocker locker(&s_thread_lock);
140  if (!s_thread)
141  {
142  s_thread = new MThread("SharedMythSocketThread");
143  s_thread->start();
144  }
145  m_thread = s_thread;
146  s_thread_cnt++;
147  }
148 
149  m_tcpSocket->moveToThread(m_thread->qthread());
150  moveToThread(m_thread->qthread());
151 }
152 
154 {
155  LOG(VB_SOCKET, LOG_INFO, LOC + QString("MythSocket dtor : cb 0x%2")
156  .arg((intptr_t)(m_callback),0,16));
157 
158  if (IsConnected())
160 
161  if (!m_useSharedThread)
162  {
163  if (m_thread)
164  {
165  m_thread->quit();
166  m_thread->wait();
167  delete m_thread;
168  }
169  }
170  else
171  {
172  QMutexLocker locker(&s_thread_lock);
173  s_thread_cnt--;
174  if (0 == s_thread_cnt)
175  {
176  s_thread->quit();
177  s_thread->wait();
178  delete s_thread;
179  s_thread = nullptr;
180  }
181  }
182  m_thread = nullptr;
183 
184  delete m_tcpSocket;
185  m_tcpSocket = nullptr;
186 }
187 
189 {
190  {
191  QMutexLocker locker(&m_lock);
192  m_connected = true;
193  m_socketDescriptor = m_tcpSocket->socketDescriptor();
194  m_peerAddress = m_tcpSocket->peerAddress();
195  m_peerPort = m_tcpSocket->peerPort();
196  }
197 
198  m_tcpSocket->setSocketOption(QAbstractSocket::LowDelayOption, QVariant(1));
199  m_tcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
200 
201  int reuse_addr_val = 1;
202 #if defined(Q_OS_WIN)
203  int ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
204  SO_REUSEADDR, (char*) &reuse_addr_val,
205  sizeof(reuse_addr_val));
206 #else
207  int ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
208  SO_REUSEADDR, &reuse_addr_val,
209  sizeof(reuse_addr_val));
210 #endif
211  if (ret < 0)
212  {
213  LOG(VB_SOCKET, LOG_INFO, LOC + "Failed to set SO_REUSEADDR" + ENO);
214  }
215 
216  int rcv_buf_val = kSocketReceiveBufferSize;
217 #if defined(Q_OS_WIN)
218  ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
219  SO_RCVBUF, (char*) &rcv_buf_val,
220  sizeof(rcv_buf_val));
221 #else
222  ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
223  SO_RCVBUF, &rcv_buf_val,
224  sizeof(rcv_buf_val));
225 #endif
226  if (ret < 0)
227  {
228  LOG(VB_SOCKET, LOG_INFO, LOC + "Failed to set SO_RCVBUF" + ENO);
229  }
230 
231  if (m_callback)
232  {
233  LOG(VB_SOCKET, LOG_DEBUG, LOC +
234  "calling m_callback->connected()");
235  m_callback->connected(this);
236  }
237 }
238 
239 void MythSocket::ErrorHandler(QAbstractSocket::SocketError err)
240 {
241  // Filter these out, we get them because we call waitForReadyRead with a
242  // small timeout so we can print our own debugging for long timeouts.
243  if (err == QAbstractSocket::SocketTimeoutError)
244  return;
245 
246  if (m_callback)
247  {
248  LOG(VB_SOCKET, LOG_DEBUG, LOC +
249  "calling m_callback->error() err: " + m_tcpSocket->errorString());
250  m_callback->error(this, (int)err);
251  }
252 }
253 
255 {
256  {
257  QMutexLocker locker(&m_lock);
258  m_connected = false;
259  m_socketDescriptor = -1;
260  m_peerAddress.clear();
261  m_peerPort = -1;
262  }
263 
264  if (m_callback)
265  {
266  LOG(VB_SOCKET, LOG_DEBUG, LOC +
267  "calling m_callback->connectionClosed()");
269  }
270 }
271 
273 {
274  LOG(VB_SOCKET, LOG_DEBUG, LOC + "AboutToClose");
275 }
276 
278 {
279  m_dataAvailable.fetchAndStoreOrdered(1);
280  if (m_callback && m_disableReadyReadCallback.testAndSetOrdered(0,0))
281  {
282  emit CallReadyRead();
283  }
284 }
285 
287 {
288  // Because the connection to this is a queued connection the
289  // data may have already been read by the time this is called
290  // so we check that there is still data to read before calling
291  // the callback.
292  if (IsDataAvailable())
293  {
294  LOG(VB_SOCKET, LOG_DEBUG, LOC +
295  "calling m_callback->readyRead()");
296  m_callback->readyRead(this);
297  }
298 }
299 
301  const QHostAddress &address, quint16 port)
302 {
303  bool ret = false;
304  QMetaObject::invokeMethod(
305  this, "ConnectToHostReal",
306  (QThread::currentThread() != m_thread->qthread()) ?
307  Qt::BlockingQueuedConnection : Qt::DirectConnection,
308  Q_ARG(QHostAddress, address),
309  Q_ARG(quint16, port),
310  Q_ARG(bool*, &ret));
311  return ret;
312 }
313 
314 bool MythSocket::WriteStringList(const QStringList &list)
315 {
316  bool ret = false;
317  QMetaObject::invokeMethod(
318  this, "WriteStringListReal",
319  (QThread::currentThread() != m_thread->qthread()) ?
320  Qt::BlockingQueuedConnection : Qt::DirectConnection,
321  Q_ARG(const QStringList*, &list),
322  Q_ARG(bool*, &ret));
323  return ret;
324 }
325 
326 bool MythSocket::ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS)
327 {
328  bool ret = false;
329  QMetaObject::invokeMethod(
330  this, "ReadStringListReal",
331  (QThread::currentThread() != m_thread->qthread()) ?
332  Qt::BlockingQueuedConnection : Qt::DirectConnection,
333  Q_ARG(QStringList*, &list),
334  Q_ARG(std::chrono::milliseconds, timeoutMS),
335  Q_ARG(bool*, &ret));
336  return ret;
337 }
338 
340  QStringList &strlist, uint min_reply_length, std::chrono::milliseconds timeoutMS)
341 {
342  if (m_callback && m_disableReadyReadCallback.testAndSetOrdered(0,0))
343  {
344  // If callbacks are enabled then SendReceiveStringList() will conflict
345  // causing failed reads and socket disconnections - see #11777
346  // SendReceiveStringList() should NOT be used with an event socket, only
347  // the control socket
348  LOG(VB_GENERAL, LOG_EMERG, QString("Programmer Error! "
349  "SendReceiveStringList(%1) used on "
350  "socket with callbacks enabled.")
351  .arg(strlist.isEmpty() ? "empty" : strlist[0]));
352  }
353 
354  if (!WriteStringList(strlist))
355  {
356  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send command.");
357  return false;
358  }
359 
360  if (!ReadStringList(strlist, timeoutMS))
361  {
362  LOG(VB_GENERAL, LOG_ERR, LOC + "No response.");
363  return false;
364  }
365 
366  if (min_reply_length && ((uint)strlist.size() < min_reply_length))
367  {
368  LOG(VB_GENERAL, LOG_ERR, LOC + "Response too short.");
369  return false;
370  }
371 
372 #if 0
373  if (!strlist.empty() && strlist[0] == "BACKEND_MESSAGE")
374  {
375  LOG(VB_GENERAL, LOG_ERR, LOC + "Got MythEvent on non-event socket");
376  return false;
377  }
378 #endif
379 
380  return true;
381 }
382 
387 bool MythSocket::ConnectToHost(const QString &host, quint16 port)
388 {
389  QHostAddress hadr;
390 
391  // attempt direct assignment
392  if (!hadr.setAddress(host))
393  {
394  // attempt internal lookup through MythCoreContext
395  if (!gCoreContext ||
396  !hadr.setAddress(gCoreContext->GetBackendServerIP(host)))
397  {
398  // attempt external lookup from hosts/DNS
399  QHostInfo info = QHostInfo::fromName(host);
400  if (!info.addresses().isEmpty())
401  {
402  hadr = info.addresses().constFirst();
403  }
404  else
405  {
406  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to lookup: %1")
407  .arg(host));
408  return false;
409  }
410  }
411  }
412 
413  return MythSocket::ConnectToHost(hadr, port);
414 }
415 
416 bool MythSocket::Validate(std::chrono::milliseconds timeout, bool error_dialog_desired)
417 {
418  if (m_isValidated)
419  return true;
420 
421  QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
422  .arg(MYTH_PROTO_VERSION)
423  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
424 
425  WriteStringList(strlist);
426 
427  if (!ReadStringList(strlist, timeout) || strlist.empty())
428  {
429  LOG(VB_GENERAL, LOG_ERR, "Protocol version check failure.\n\t\t\t"
430  "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
431  "This happens when the backend is too busy to respond,\n\t\t\t"
432  "or has deadlocked due to bugs or hardware failure.");
433  return m_isValidated;
434  }
435 
436  if (strlist[0] == "REJECT" && (strlist.size() >= 2))
437  {
438  LOG(VB_GENERAL, LOG_ERR,
439  QString("Protocol version or token mismatch "
440  "(frontend=%1/%2,backend=%3/\?\?)\n")
441  .arg(MYTH_PROTO_VERSION)
442  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN))
443  .arg(strlist[1]));
444 
445  QObject *GUIcontext = gCoreContext->GetGUIContext();
446  if (error_dialog_desired && GUIcontext)
447  {
448  QStringList list(strlist[1]);
449  QCoreApplication::postEvent(
450  GUIcontext, new MythEvent("VERSION_MISMATCH", list));
451  }
452  }
453  else if (strlist[0] == "ACCEPT")
454  {
455  LOG(VB_GENERAL, LOG_NOTICE, QString("Using protocol version %1 %2")
456  .arg(MYTH_PROTO_VERSION).arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
457  m_isValidated = true;
458  }
459  else
460  {
461  LOG(VB_GENERAL, LOG_ERR,
462  QString("Unexpected response to MYTH_PROTO_VERSION: %1")
463  .arg(strlist[0]));
464  }
465 
466  return m_isValidated;
467 }
468 
469 bool MythSocket::Announce(const QStringList &new_announce)
470 {
471  if (!m_isValidated)
472  {
473  LOG(VB_GENERAL, LOG_ERR, LOC +
474  "refusing to announce unvalidated socket");
475  return false;
476  }
477 
478  if (m_isAnnounced)
479  {
480  LOG(VB_GENERAL, LOG_ERR, LOC + "refusing to re-announce socket");
481  return false;
482  }
483 
484  WriteStringList(new_announce);
485 
486  QStringList tmplist;
488  {
489  LOG(VB_GENERAL, LOG_ERR, LOC +
490  QString("\n\t\t\tCould not read string list from server %1:%2")
491  .arg(m_tcpSocket->peerAddress().toString())
492  .arg(m_tcpSocket->peerPort()));
493  m_announce.clear();
494  m_isAnnounced = false;
495  }
496  else
497  {
498  m_announce = new_announce;
499  m_isAnnounced = true;
500  }
501 
502  return m_isAnnounced;
503 }
504 
505 void MythSocket::SetAnnounce(const QStringList &new_announce)
506 {
507  m_announce = new_announce;
508  m_isAnnounced = true;
509 }
510 
512 {
513  if (QThread::currentThread() != m_thread->qthread() &&
515  {
516  LOG(VB_GENERAL, LOG_ERR, LOC +
517  QString("Programmer error, QEventLoop isn't running and deleting "
518  "MythSocket(0x%1)").arg(reinterpret_cast<intptr_t>(this),0,16));
519  return;
520  }
521  QMetaObject::invokeMethod(
522  this, "DisconnectFromHostReal",
523  (QThread::currentThread() != m_thread->qthread()) ?
524  Qt::BlockingQueuedConnection : Qt::DirectConnection);
525 }
526 
527 int MythSocket::Write(const char *data, int size)
528 {
529  int ret = -1;
530  QMetaObject::invokeMethod(
531  this, "WriteReal",
532  (QThread::currentThread() != m_thread->qthread()) ?
533  Qt::BlockingQueuedConnection : Qt::DirectConnection,
534  Q_ARG(const char*, data),
535  Q_ARG(int, size),
536  Q_ARG(int*, &ret));
537  return ret;
538 }
539 
540 int MythSocket::Read(char *data, int size, std::chrono::milliseconds max_wait)
541 {
542  int ret = -1;
543  QMetaObject::invokeMethod(
544  this, "ReadReal",
545  (QThread::currentThread() != m_thread->qthread()) ?
546  Qt::BlockingQueuedConnection : Qt::DirectConnection,
547  Q_ARG(char*, data),
548  Q_ARG(int, size),
549  Q_ARG(std::chrono::milliseconds, max_wait),
550  Q_ARG(int*, &ret));
551  return ret;
552 }
553 
555 {
556  QMetaObject::invokeMethod(
557  this, "ResetReal",
558  (QThread::currentThread() != m_thread->qthread()) ?
559  Qt::BlockingQueuedConnection : Qt::DirectConnection);
560 }
561 
563 
564 bool MythSocket::IsConnected(void) const
565 {
566  QMutexLocker locker(&m_lock);
567  return m_connected;
568 }
569 
571 {
572  if (QThread::currentThread() == m_thread->qthread())
573  return m_tcpSocket->bytesAvailable() > 0;
574 
575  if (m_dataAvailable.testAndSetOrdered(0,0))
576  return false;
577 
578  bool ret = false;
579 
580  QMetaObject::invokeMethod(
581  this, "IsDataAvailableReal",
582  Qt::BlockingQueuedConnection,
583  Q_ARG(bool*, &ret));
584 
585  return ret;
586 }
587 
589 {
590  QMutexLocker locker(&m_lock);
591  return m_socketDescriptor;
592 }
593 
594 QHostAddress MythSocket::GetPeerAddress(void) const
595 {
596  QMutexLocker locker(&m_lock);
597  return m_peerAddress;
598 }
599 
600 int MythSocket::GetPeerPort(void) const
601 {
602  QMutexLocker locker(&m_lock);
603  return m_peerPort;
604 }
605 
607 
608 void MythSocket::IsDataAvailableReal(bool *ret) const
609 {
610  *ret = (m_tcpSocket->bytesAvailable() > 0);
611  m_dataAvailable.fetchAndStoreOrdered((*ret) ? 1 : 0);
612 }
613 
614 void MythSocket::ConnectToHostReal(const QHostAddress& _addr, quint16 port, bool *ret)
615 {
616  if (m_tcpSocket->state() == QAbstractSocket::ConnectedState)
617  {
618  LOG(VB_SOCKET, LOG_ERR, LOC +
619  "connect() called with already open socket, closing");
620  m_tcpSocket->close();
621  }
622 
623  QHostAddress addr = _addr;
624  addr.setScopeId(QString());
625 
626  s_loopbackCacheLock.lock();
627  bool usingLoopback = s_loopbackCache.contains(addr.toString());
628  s_loopbackCacheLock.unlock();
629 
630  if (usingLoopback)
631  {
632  addr = QHostAddress(s_loopbackCache.value(addr.toString()));
633  }
634  else
635  {
636  QList<QHostAddress> localIPs = QNetworkInterface::allAddresses();
637  for (int i = 0; i < localIPs.count() && !usingLoopback; ++i)
638  {
639  QHostAddress local = localIPs[i];
640  local.setScopeId(QString());
641 
642  if (addr == local)
643  {
644  QHostAddress::SpecialAddress loopback = QHostAddress::LocalHost;
645  if (addr.protocol() == QAbstractSocket::IPv6Protocol)
646  loopback = QHostAddress::LocalHostIPv6;
647 
648  QMutexLocker locker(&s_loopbackCacheLock);
649  s_loopbackCache[addr.toString()] = loopback;
650  addr = QHostAddress(loopback);
651  usingLoopback = true;
652  }
653  }
654  }
655 
656  if (usingLoopback)
657  {
658  LOG(VB_SOCKET, LOG_INFO, LOC +
659  "IP is local, using loopback address instead");
660  }
661 
662  LOG(VB_SOCKET, LOG_INFO, LOC + QString("attempting connect() to (%1:%2)")
663  .arg(addr.toString()).arg(port));
664 
665  bool ok = true;
666 
667  // Sort out link-local address scope if applicable
668  if (!usingLoopback)
669  {
670  QString host = addr.toString();
671  if (PortChecker::resolveLinkLocal(host, port))
672  addr.setAddress(host);
673  }
674 
675  if (ok)
676  {
677  m_tcpSocket->connectToHost(addr, port, QAbstractSocket::ReadWrite);
678  ok = m_tcpSocket->waitForConnected(5000);
679  }
680 
681  if (ok)
682  {
683  LOG(VB_SOCKET, LOG_INFO, LOC + QString("Connected to (%1:%2)")
684  .arg(addr.toString()).arg(port));
685  }
686  else
687  {
688  LOG(VB_GENERAL, LOG_ERR, LOC +
689  QString("Failed to connect to (%1:%2) %3")
690  .arg(addr.toString()).arg(port)
691  .arg(m_tcpSocket->errorString()));
692  }
693 
694  *ret = ok;
695 }
696 
698 {
699  m_tcpSocket->disconnectFromHost();
700 }
701 
702 void MythSocket::WriteStringListReal(const QStringList *list, bool *ret)
703 {
704  if (list->empty())
705  {
706  LOG(VB_GENERAL, LOG_ERR, LOC +
707  "WriteStringList: Error, invalid string list.");
708  *ret = false;
709  return;
710  }
711 
712  if (m_tcpSocket->state() != QAbstractSocket::ConnectedState)
713  {
714  LOG(VB_GENERAL, LOG_ERR, LOC +
715  "WriteStringList: Error, called with unconnected socket.");
716  *ret = false;
717  return;
718  }
719 
720  QString str = list->join("[]:[]");
721  if (str.isEmpty())
722  {
723  LOG(VB_GENERAL, LOG_ERR, LOC +
724  "WriteStringList: Error, joined null string.");
725  *ret = false;
726  return;
727  }
728 
729  QByteArray utf8 = str.toUtf8();
730  int size = utf8.length();
731  int written = 0;
732  int written_since_timer_restart = 0;
733 
734  QByteArray payload;
735  payload = payload.setNum(size);
736  payload += " ";
737  payload.truncate(8);
738  payload += utf8;
739  size = payload.length();
740 
741  if (VERBOSE_LEVEL_CHECK(VB_NETWORK, LOG_INFO))
742  {
743  QString msg = QString("write -> %1 %2")
744  .arg(m_tcpSocket->socketDescriptor(), 2).arg(payload.data());
745 
746  if (logLevel < LOG_DEBUG && msg.length() > 128)
747  {
748  msg.truncate(127);
749  msg += "…";
750  }
751  LOG(VB_NETWORK, LOG_INFO, LOC + msg);
752  }
753 
754  MythTimer timer; timer.start();
755  unsigned int errorcount = 0;
756  while (size > 0)
757  {
758  if (m_tcpSocket->state() != QAbstractSocket::ConnectedState)
759  {
760  LOG(VB_GENERAL, LOG_ERR, LOC +
761  "WriteStringList: Error, socket went unconnected." +
762  QString("\n\t\t\tWe wrote %1 of %2 bytes with %3 errors")
763  .arg(written).arg(written+size).arg(errorcount) +
764  QString("\n\t\t\tstarts with: %1").arg(to_sample(payload)));
765  *ret = false;
766  return;
767  }
768 
769  int temp = m_tcpSocket->write(payload.data() + written, size);
770  if (temp > 0)
771  {
772  written += temp;
773  written_since_timer_restart += temp;
774  size -= temp;
775  if ((timer.elapsed() > 500ms) && written_since_timer_restart != 0)
776  {
777  timer.restart();
778  written_since_timer_restart = 0;
779  }
780  }
781  else
782  {
783  errorcount++;
784  if (timer.elapsed() > 1s)
785  {
786  LOG(VB_GENERAL, LOG_ERR, LOC + "WriteStringList: Error, " +
787  QString("No data written on write (%1 errors)")
788  .arg(errorcount) +
789  QString("\n\t\t\tstarts with: %1")
790  .arg(to_sample(payload)));
791  *ret = false;
792  return;
793  }
794  usleep(1000);
795  }
796  }
797 
798  m_tcpSocket->flush();
799 
800  *ret = true;
801 }
802 
804  QStringList *list, std::chrono::milliseconds timeoutMS, bool *ret)
805 {
806  list->clear();
807  *ret = false;
808 
809  MythTimer timer;
810  timer.start();
811  std::chrono::milliseconds elapsed { 0ms };
812 
813  while (m_tcpSocket->bytesAvailable() < 8)
814  {
815  elapsed = timer.elapsed();
816  if (elapsed >= timeoutMS)
817  {
818  LOG(VB_GENERAL, LOG_ERR, LOC + "ReadStringList: " +
819  QString("Error, timed out after %1 ms.").arg(timeoutMS.count()));
820  m_tcpSocket->close();
821  m_dataAvailable.fetchAndStoreOrdered(0);
822  return;
823  }
824 
825  if (m_tcpSocket->state() != QAbstractSocket::ConnectedState)
826  {
827  LOG(VB_GENERAL, LOG_ERR, LOC + "ReadStringList: Connection died.");
828  m_dataAvailable.fetchAndStoreOrdered(0);
829  return;
830  }
831 
832  m_tcpSocket->waitForReadyRead(50);
833  }
834 
835  QByteArray sizestr(8, '\0');
836  if (m_tcpSocket->read(sizestr.data(), 8) < 0)
837  {
838  LOG(VB_GENERAL, LOG_ERR, LOC +
839  QString("ReadStringList: Error, read return error (%1)")
840  .arg(m_tcpSocket->errorString()));
841  m_tcpSocket->close();
842  m_dataAvailable.fetchAndStoreOrdered(0);
843  return;
844  }
845 
846  QString sizes = sizestr;
847  bool ok { false };
848  int btr = sizes.trimmed().toInt(&ok);
849 
850  if (btr < 1)
851  {
852  int pending = m_tcpSocket->bytesAvailable();
853  LOG(VB_GENERAL, LOG_ERR, LOC +
854  QString("Protocol error: %1'%2' is not a valid size "
855  "prefix. %3 bytes pending.")
856  .arg(ok ? "" : "(parse failed) ")
857  .arg(sizestr.data()).arg(pending));
858  ResetReal();
859  return;
860  }
861 
862  QByteArray utf8(btr + 1, 0);
863 
864  qint64 readoffset = 0;
865  std::chrono::milliseconds errmsgtime { 0ms };
866  timer.start();
867 
868  while (btr > 0)
869  {
870  if (m_tcpSocket->bytesAvailable() < 1)
871  {
872  if (m_tcpSocket->state() == QAbstractSocket::ConnectedState)
873  {
874  m_tcpSocket->waitForReadyRead(50);
875  }
876  else
877  {
878  LOG(VB_GENERAL, LOG_ERR, LOC +
879  "ReadStringList: Connection died.");
880  m_dataAvailable.fetchAndStoreOrdered(0);
881  return;
882  }
883  }
884 
885  qint64 sret = m_tcpSocket->read(utf8.data() + readoffset, btr);
886  if (sret > 0)
887  {
888  readoffset += sret;
889  btr -= sret;
890  if (btr > 0)
891  {
892  timer.start();
893  }
894  }
895  else if (sret < 0)
896  {
897  LOG(VB_GENERAL, LOG_ERR, LOC + "ReadStringList: Error, read");
898  m_tcpSocket->close();
899  m_dataAvailable.fetchAndStoreOrdered(0);
900  return;
901  }
902  else if (!m_tcpSocket->isValid())
903  {
904  LOG(VB_GENERAL, LOG_ERR, LOC +
905  "ReadStringList: Error, socket went unconnected");
906  m_tcpSocket->close();
907  m_dataAvailable.fetchAndStoreOrdered(0);
908  return;
909  }
910  else
911  {
912  elapsed = timer.elapsed();
913  if (elapsed > 10s)
914  {
915  if ((elapsed - errmsgtime) > 10s)
916  {
917  errmsgtime = elapsed;
918  LOG(VB_GENERAL, LOG_ERR, LOC +
919  QString("ReadStringList: Waiting for data: %1 %2")
920  .arg(readoffset).arg(btr));
921  }
922  }
923 
924  if (elapsed > 100s)
925  {
926  LOG(VB_GENERAL, LOG_ERR, LOC +
927  "Error, ReadStringList timeout (readBlock)");
928  m_dataAvailable.fetchAndStoreOrdered(0);
929  return;
930  }
931  }
932  }
933 
934  QString str = QString::fromUtf8(utf8.data());
935 
936  if (VERBOSE_LEVEL_CHECK(VB_NETWORK, LOG_INFO))
937  {
938  QByteArray payload;
939  payload = payload.setNum(str.length());
940  payload += " ";
941  payload.truncate(8);
942  payload += utf8.data();
943 
944  QString msg = QString("read <- %1 %2")
945  .arg(m_tcpSocket->socketDescriptor(), 2)
946  .arg(payload.data());
947 
948  if (logLevel < LOG_DEBUG && msg.length() > 128)
949  {
950  msg.truncate(127);
951  msg += "…";
952  }
953  LOG(VB_NETWORK, LOG_INFO, LOC + msg);
954  }
955 
956  *list = str.split("[]:[]");
957 
958  m_dataAvailable.fetchAndStoreOrdered(
959  (m_tcpSocket->bytesAvailable() > 0) ? 1 : 0);
960 
961  *ret = true;
962 }
963 
964 void MythSocket::WriteReal(const char *data, int size, int *ret)
965 {
966  *ret = m_tcpSocket->write(data, size);
967 }
968 
969 void MythSocket::ReadReal(char *data, int size, std::chrono::milliseconds max_wait_ms, int *ret)
970 {
971  MythTimer t; t.start();
972  while ((m_tcpSocket->state() == QAbstractSocket::ConnectedState) &&
973  (m_tcpSocket->bytesAvailable() < size) &&
974  (t.elapsed() < max_wait_ms))
975  {
976  m_tcpSocket->waitForReadyRead(max(2ms, max_wait_ms - t.elapsed()).count());
977  }
978  *ret = m_tcpSocket->read(data, size);
979 
980  if (t.elapsed() > 50ms)
981  {
982  LOG(VB_NETWORK, LOG_INFO,
983  QString("ReadReal(?, %1, %2) -> %3 took %4 ms")
984  .arg(size).arg(max_wait_ms.count()).arg(*ret)
985  .arg(t.elapsed().count()));
986  }
987 
988  m_dataAvailable.fetchAndStoreOrdered(
989  (m_tcpSocket->bytesAvailable() > 0) ? 1 : 0);
990 }
991 
993 {
994  vector<char> trash;
995 
996  m_tcpSocket->waitForReadyRead(30);
997  do
998  {
999  uint avail = m_tcpSocket->bytesAvailable();
1000  if (avail)
1001  {
1002  trash.resize(max((uint)trash.size(),avail));
1003  m_tcpSocket->read(trash.data(), avail);
1004  }
1005 
1006  LOG(VB_NETWORK, LOG_INFO, LOC + "Reset() " +
1007  QString("%1 bytes available").arg(avail));
1008 
1009  m_tcpSocket->waitForReadyRead(30);
1010  }
1011  while (m_tcpSocket->bytesAvailable() > 0);
1012 
1013  m_dataAvailable.fetchAndStoreOrdered(0);
1014 }
MythSocket::ConnectToHostReal
void ConnectToHostReal(const QHostAddress &addr, quint16 port, bool *ret)
Definition: mythsocket.cpp:614
MythSocket::m_connected
bool m_connected
Definition: mythsocket.h:108
MythTimer::elapsed
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
mythevent.h
MythSocket::m_dataAvailable
QAtomicInt m_dataAvailable
This is used internally as a hint that there might be data available for reading.
Definition: mythsocket.h:111
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:286
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:72
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:103
MThread::quit
void quit(void)
calls exit(0)
Definition: mthread.cpp:298
error
static void error(const char *str,...)
Definition: vbi.cpp:42
MYTH_PROTO_TOKEN
#define MYTH_PROTO_TOKEN
Definition: mythversion.h:48
MythSocket::m_disableReadyReadCallback
QAtomicInt m_disableReadyReadCallback
Definition: mythsocket.h:107
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
MythSocket::m_tcpSocket
QTcpSocket * m_tcpSocket
Definition: mythsocket.h:99
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:303
MythSocket::ReadReal
void ReadReal(char *data, int size, std::chrono::milliseconds max_wait_ms, int *ret)
Definition: mythsocket.cpp:969
m_callback
static void(* m_callback)(void *, QString &)
Definition: mythplugins/mytharchive/mytharchive/main.cpp:244
logLevel
LogLevel_t logLevel
Definition: logging.cpp:88
x2
static int x2
Definition: mythsocket.cpp:58
MythSocket::s_loopbackCacheLock
static QMutex s_loopbackCacheLock
Definition: mythsocket.h:118
MythSocket::~MythSocket
~MythSocket() override
Definition: mythsocket.cpp:153
MythSocket::Reset
void Reset(void)
Definition: mythsocket.cpp:554
MythSocket::m_peerPort
int m_peerPort
Definition: mythsocket.h:104
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
x0
static int x0
Definition: mythsocket.cpp:56
MythSocket::ConnectHandler
void ConnectHandler(void)
Definition: mythsocket.cpp:188
MythSocket::DisconnectFromHost
void DisconnectFromHost(void)
Definition: mythsocket.cpp:511
x3
static int x3
Definition: mythsocket.cpp:59
MythSocket::DisconnectHandler
void DisconnectHandler(void)
Definition: mythsocket.cpp:254
MythSocket::kSocketReceiveBufferSize
static const int kSocketReceiveBufferSize
Definition: mythsocket.h:116
MythTimer::start
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythSocket::Announce
bool Announce(const QStringList &new_announce)
Definition: mythsocket.cpp:469
MythSocket::s_thread
static MThread * s_thread
Definition: mythsocket.h:122
MythSocket::ConnectToHost
bool ConnectToHost(const QString &hostname, quint16 port)
connect to host
Definition: mythsocket.cpp:387
MythSocket::m_useSharedThread
bool m_useSharedThread
Definition: mythsocket.h:106
MythSocket::IsConnected
bool IsConnected(void) const
Definition: mythsocket.cpp:564
s_dummy_meta_variable_to_suppress_gcc_warning
int s_dummy_meta_variable_to_suppress_gcc_warning
Definition: mythsocket.cpp:63
mythversion.h
x6
static int x6
Definition: mythsocket.cpp:62
x4
static int x4
Definition: mythsocket.cpp:60
MythSocket::s_loopbackCache
static QHash< QString, QHostAddress::SpecialAddress > s_loopbackCache
Definition: mythsocket.h:119
MythSocket::m_thread
MThread * m_thread
Definition: mythsocket.h:100
MythSocketCBs::readyRead
virtual void readyRead(MythSocket *)=0
mythlogging.h
MythSocket::DisconnectFromHostReal
void DisconnectFromHostReal(void)
Definition: mythsocket.cpp:697
MythCoreContext::GetGUIContext
QObject * GetGUIContext(void)
Definition: mythcorecontext.cpp:1768
MythSocket::WriteStringList
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:314
MythSocket::s_thread_lock
static QMutex s_thread_lock
Definition: mythsocket.h:121
hardwareprofile.i18n.t
t
Definition: i18n.py:36
MythCoreContext::GetBackendServerIP
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
Definition: mythcorecontext.cpp:1029
MythSocket::m_callback
MythSocketCBs * m_callback
Definition: mythsocket.h:105
MythSocket::s_thread_cnt
static int s_thread_cnt
Definition: mythsocket.h:123
x1
static int x1
Definition: mythsocket.cpp:57
MythSocket::CallReadyRead
void CallReadyRead(void)
MythTimer::restart
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
MThread::qthread
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:236
MythSocket::CallReadyReadHandler
void CallReadyReadHandler(void)
Definition: mythsocket.cpp:286
MythSocketCBs
Definition: mythsocket_cb.h:13
portchecker.h
MythSocket::kShortTimeout
static constexpr std::chrono::milliseconds kShortTimeout
Definition: mythsocket.h:71
PortChecker::resolveLinkLocal
static bool resolveLinkLocal(QString &host, int port, std::chrono::milliseconds timeLimit=30s)
Convenience method to resolve link-local address.
Definition: portchecker.cpp:228
MythSocket::ReadyReadHandler
void ReadyReadHandler(void)
Definition: mythsocket.cpp:277
uint
unsigned int uint
Definition: compat.h:140
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
MythSocket::Read
int Read(char *data, int size, std::chrono::milliseconds max_wait)
Definition: mythsocket.cpp:540
x5
static int x5
Definition: mythsocket.cpp:61
to_sample
static QString to_sample(const QByteArray &payload)
Definition: mythsocket.cpp:66
MythSocket::WriteStringListReal
void WriteStringListReal(const QStringList *list, bool *ret)
Definition: mythsocket.cpp:702
MythSocket::m_announce
QStringList m_announce
Definition: mythsocket.h:114
MYTH_PROTO_VERSION
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:47
MythSocket::Write
int Write(const char *data, int size)
Definition: mythsocket.cpp:527
MythSocketCBs::connected
virtual void connected(MythSocket *)=0
VERBOSE_LEVEL_CHECK
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:14
MythSocket::ResetReal
void ResetReal(void)
Definition: mythsocket.cpp:992
MythSocket::MythSocket
MythSocket(qt_socket_fd_t socket=-1, MythSocketCBs *cb=nullptr, bool use_shared_thread=false)
Definition: mythsocket.cpp:78
MythSocket::AboutToCloseHandler
void AboutToCloseHandler(void)
Definition: mythsocket.cpp:272
mythcorecontext.h
MythSocketCBs::connectionClosed
virtual void connectionClosed(MythSocket *)=0
MythSocket::IsDataAvailableReal
void IsDataAvailableReal(bool *ret) const
Definition: mythsocket.cpp:608
MythSocket::Validate
bool Validate(std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
Definition: mythsocket.cpp:416
MythSocket::IsDataAvailable
bool IsDataAvailable(void)
Definition: mythsocket.cpp:570
MythSocketCBs::error
virtual void error(MythSocket *, int)
Definition: mythsocket_cb.h:18
MythSocket::GetPeerAddress
QHostAddress GetPeerAddress(void) const
Definition: mythsocket.cpp:594
MythSocket::GetSocketDescriptor
int GetSocketDescriptor(void) const
Definition: mythsocket.cpp:588
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
Q_DECLARE_METATYPE
Q_DECLARE_METATYPE(const QStringList *)
MythSocket::SetAnnounce
void SetAnnounce(const QStringList &new_announce)
Definition: mythsocket.cpp:505
MythSocket::m_peerAddress
QHostAddress m_peerAddress
Definition: mythsocket.h:103
MythSocket::GetPeerPort
int GetPeerPort(void) const
Definition: mythsocket.cpp:600
mythtimer.h
MythSocket::m_isValidated
bool m_isValidated
Definition: mythsocket.h:112
MythSocket::ReadStringListReal
void ReadStringListReal(QStringList *list, std::chrono::milliseconds timeoutMS, bool *ret)
Definition: mythsocket.cpp:803
MythCoreContext::IsExiting
bool IsExiting(void)
Definition: mythcorecontext.cpp:2103
MythSocket::m_socketDescriptor
qt_socket_fd_t m_socketDescriptor
Definition: mythsocket.h:102
MythSocket::m_lock
QMutex m_lock
Definition: mythsocket.h:101
MythCoreContext::CheckSubnet
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.
Definition: mythcorecontext.cpp:1281
mythsocket.h
LOC
#define LOC
Definition: mythsocket.cpp:38
qt_socket_fd_t
qintptr qt_socket_fd_t
Definition: mythqtcompat.h:4
MythSocket::ReadStringList
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:326
MythSocket::WriteReal
void WriteReal(const char *data, int size, int *ret)
Definition: mythsocket.cpp:964
MythSocket::ErrorHandler
void ErrorHandler(QAbstractSocket::SocketError err)
Definition: mythsocket.cpp:239
MythSocket::m_isAnnounced
bool m_isAnnounced
Definition: mythsocket.h:113
ReferenceCounter
General purpose reference counter.
Definition: referencecounter.h:26
MythSocket::SendReceiveStringList
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, std::chrono::milliseconds timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:339