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