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