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