2 #include <QNetworkInterface>
3 #include <QCoreApplication>
4 #include <QWaitCondition>
5 #include <QSharedPointer>
18 #include <sys/socket.h>
28 #include "mythversion.h"
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 >();
62 for (
uint i = 0; (i<60) && (i<(
uint)payload.length()); i++)
64 sample += QChar(payload[i]).isPrint() ?
65 QChar(payload[i]) : QChar(
'?');
67 sample += (payload.length() > 60) ?
"..." :
"";
74 m_tcpSocket(new QTcpSocket()),
76 m_useSharedThread(use_shared_thread)
78 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"MythSocket(%1, 0x%2) ctor")
79 .arg(socket).arg((intptr_t)(cb),0,16));
84 socket, QAbstractSocket::ConnectedState,
85 QAbstractSocket::ReadWrite);
102 Qt::DirectConnection);
103 #if QT_VERSION < QT_VERSION_CHECK(5,15,0)
106 Qt::DirectConnection);
108 connect(
m_tcpSocket, &QAbstractSocket::errorOccurred,
110 Qt::DirectConnection);
114 connect(
m_tcpSocket, &QAbstractSocket::disconnected,
116 Qt::DirectConnection);
119 Qt::DirectConnection);
123 Qt::QueuedConnection);
125 if (!use_shared_thread)
148 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"MythSocket dtor : cb 0x%2")
184 QMutexLocker locker(&
m_lock);
191 m_tcpSocket->setSocketOption(QAbstractSocket::LowDelayOption, QVariant(1));
192 m_tcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
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));
200 int ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
201 SO_REUSEADDR, &reuse_addr_val,
202 sizeof(reuse_addr_val));
206 LOG(VB_SOCKET, LOG_INFO,
LOC() +
"Failed to set SO_REUSEADDR" +
ENO);
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));
215 ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
216 SO_RCVBUF, &rcv_buf_val,
217 sizeof(rcv_buf_val));
221 LOG(VB_SOCKET, LOG_INFO,
LOC() +
"Failed to set SO_RCVBUF" +
ENO);
226 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
227 "calling m_callback->connected()");
236 if (err == QAbstractSocket::SocketTimeoutError)
241 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
242 "calling m_callback->error() err: " +
m_tcpSocket->errorString());
250 QMutexLocker locker(&
m_lock);
259 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
260 "calling m_callback->connectionClosed()");
267 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
"AboutToClose");
287 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
288 "calling m_callback->readyRead()");
294 const QHostAddress &address, quint16 port)
297 QMetaObject::invokeMethod(
298 this,
"ConnectToHostReal",
300 Qt::BlockingQueuedConnection : Qt::DirectConnection,
301 Q_ARG(QHostAddress, address),
302 Q_ARG(quint16, port),
310 QMetaObject::invokeMethod(
311 this,
"WriteStringListReal",
313 Qt::BlockingQueuedConnection : Qt::DirectConnection,
314 Q_ARG(
const QStringList*, &list),
322 QMetaObject::invokeMethod(
323 this,
"ReadStringListReal",
325 Qt::BlockingQueuedConnection : Qt::DirectConnection,
326 Q_ARG(QStringList*, &list),
327 Q_ARG(std::chrono::milliseconds, timeoutMS),
333 QStringList &strlist,
uint min_reply_length, std::chrono::milliseconds timeoutMS)
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]));
349 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"Failed to send command.");
355 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"No response.");
359 if (min_reply_length && ((
uint)strlist.size() < min_reply_length))
361 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"Response too short.");
366 if (!strlist.empty() && strlist[0] ==
"BACKEND_MESSAGE")
368 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"Got MythEvent on non-event socket");
385 if (!hadr.setAddress(host))
392 QHostInfo info = QHostInfo::fromName(host);
393 if (!info.addresses().isEmpty())
395 hadr = info.addresses().constFirst();
399 LOG(VB_GENERAL, LOG_ERR,
LOC() + QString(
"Unable to lookup: %1")
414 QStringList strlist(QString(
"MYTH_PROTO_VERSION %1 %2")
415 .arg(MYTH_PROTO_VERSION,
416 QString::fromUtf8(MYTH_PROTO_TOKEN)));
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.");
429 if (strlist[0] ==
"REJECT" && (strlist.size() >= 2))
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),
439 if (error_dialog_desired && GUIcontext)
441 QStringList list(strlist[1]);
442 QCoreApplication::postEvent(
443 GUIcontext,
new MythEvent(
"VERSION_MISMATCH", list));
446 else if (strlist[0] ==
"ACCEPT")
448 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Using protocol version %1 %2")
449 .arg(MYTH_PROTO_VERSION, QString::fromUtf8(MYTH_PROTO_TOKEN)));
454 LOG(VB_GENERAL, LOG_ERR,
455 QString(
"Unexpected response to MYTH_PROTO_VERSION: %1")
466 LOG(VB_GENERAL, LOG_ERR,
LOC() +
467 "refusing to announce unvalidated socket");
473 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"refusing to re-announce socket");
482 LOG(VB_GENERAL, LOG_ERR,
LOC() +
483 QString(
"\n\t\t\tCould not read string list from server %1:%2")
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));
514 QMetaObject::invokeMethod(
515 this,
"DisconnectFromHostReal",
517 Qt::BlockingQueuedConnection : Qt::DirectConnection);
523 QMetaObject::invokeMethod(
526 Qt::BlockingQueuedConnection : Qt::DirectConnection,
527 Q_ARG(
const char*, data),
536 QMetaObject::invokeMethod(
539 Qt::BlockingQueuedConnection : Qt::DirectConnection,
542 Q_ARG(std::chrono::milliseconds, max_wait),
549 QMetaObject::invokeMethod(
552 Qt::BlockingQueuedConnection : Qt::DirectConnection);
559 QMutexLocker locker(&
m_lock);
573 QMetaObject::invokeMethod(
574 this,
"IsDataAvailableReal",
575 Qt::BlockingQueuedConnection,
583 QMutexLocker locker(&
m_lock);
589 QMutexLocker locker(&
m_lock);
595 QMutexLocker locker(&
m_lock);
609 if (
m_tcpSocket->state() == QAbstractSocket::ConnectedState)
611 LOG(VB_SOCKET, LOG_ERR,
LOC() +
612 "connect() called with already open socket, closing");
616 QHostAddress addr = _addr;
617 addr.setScopeId(QString());
629 QList<QHostAddress> localIPs = QNetworkInterface::allAddresses();
630 for (
int i = 0; i < localIPs.count() && !usingLoopback; ++i)
632 QHostAddress local = localIPs[i];
633 local.setScopeId(QString());
637 QHostAddress::SpecialAddress loopback = QHostAddress::LocalHost;
638 if (addr.protocol() == QAbstractSocket::IPv6Protocol)
639 loopback = QHostAddress::LocalHostIPv6;
643 addr = QHostAddress(loopback);
644 usingLoopback =
true;
651 LOG(VB_SOCKET, LOG_INFO,
LOC() +
652 "IP is local, using loopback address instead");
655 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"attempting connect() to (%1:%2)")
656 .arg(addr.toString()).arg(port));
663 QString host = addr.toString();
665 addr.setAddress(host);
670 m_tcpSocket->connectToHost(addr, port, QAbstractSocket::ReadWrite);
676 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"Connected to (%1:%2)")
677 .arg(addr.toString()).arg(port));
681 LOG(VB_GENERAL, LOG_ERR,
LOC() +
682 QString(
"Failed to connect to (%1:%2) %3")
683 .arg(addr.toString()).arg(port)
699 LOG(VB_GENERAL, LOG_ERR,
LOC() +
700 "WriteStringList: Error, invalid string list.");
705 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
707 LOG(VB_GENERAL, LOG_ERR,
LOC() +
708 "WriteStringList: Error, called with unconnected socket.");
713 QString str = list->join(
"[]:[]");
716 LOG(VB_GENERAL, LOG_ERR,
LOC() +
717 "WriteStringList: Error, joined null string.");
722 QByteArray utf8 = str.toUtf8();
723 int size = utf8.length();
725 int written_since_timer_restart = 0;
728 payload = payload.setNum(size);
732 size = payload.length();
736 QString msg = QString(
"write -> %1 %2")
737 .arg(
m_tcpSocket->socketDescriptor(), 2).arg(payload.data());
739 if (
logLevel < LOG_DEBUG && msg.length() > 128)
744 LOG(VB_NETWORK, LOG_INFO,
LOC() + msg);
748 unsigned int errorcount = 0;
751 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
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)));
762 int temp =
m_tcpSocket->write(payload.data() + written, size);
766 written_since_timer_restart += temp;
768 if ((timer.
elapsed() > 500ms) && written_since_timer_restart != 0)
771 written_since_timer_restart = 0;
779 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"WriteStringList: Error, " +
780 QString(
"No data written on write (%1 errors)")
782 QString(
"\n\t\t\tstarts with: %1")
797 QStringList *list, std::chrono::milliseconds timeoutMS,
bool *ret)
804 std::chrono::milliseconds elapsed { 0ms };
809 if (elapsed >= timeoutMS)
811 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"ReadStringList: " +
812 QString(
"Error, timed out after %1 ms.").arg(timeoutMS.count()));
818 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
820 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"ReadStringList: Connection died.");
828 QByteArray sizestr(8,
'\0');
831 LOG(VB_GENERAL, LOG_ERR,
LOC() +
832 QString(
"ReadStringList: Error, read return error (%1)")
839 QString sizes = sizestr;
841 int btr = sizes.trimmed().toInt(&ok);
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)));
855 QByteArray utf8(btr + 1, 0);
857 qint64 readoffset = 0;
858 std::chrono::milliseconds errmsgtime { 0ms };
865 if (
m_tcpSocket->state() == QAbstractSocket::ConnectedState)
871 LOG(VB_GENERAL, LOG_ERR,
LOC() +
872 "ReadStringList: Connection died.");
878 qint64 sret =
m_tcpSocket->read(utf8.data() + readoffset, btr);
890 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"ReadStringList: Error, read");
897 LOG(VB_GENERAL, LOG_ERR,
LOC() +
898 "ReadStringList: Error, socket went unconnected");
908 if ((elapsed - errmsgtime) > 10s)
910 errmsgtime = elapsed;
911 LOG(VB_GENERAL, LOG_ERR,
LOC() +
912 QString(
"ReadStringList: Waiting for data: %1 %2")
913 .arg(readoffset).arg(btr));
919 LOG(VB_GENERAL, LOG_ERR,
LOC() +
920 "Error, ReadStringList timeout (readBlock)");
927 QString str = QString::fromUtf8(utf8.data());
932 payload = payload.setNum(str.length());
935 payload += utf8.data();
937 QString msg = QString(
"read <- %1 %2")
939 .arg(payload.data());
941 if (
logLevel < LOG_DEBUG && msg.length() > 128)
946 LOG(VB_NETWORK, LOG_INFO,
LOC() + msg);
949 *list = str.split(
"[]:[]");
965 while ((
m_tcpSocket->state() == QAbstractSocket::ConnectedState) &&
967 (
t.elapsed() < max_wait_ms))
969 m_tcpSocket->waitForReadyRead(max(2ms, max_wait_ms -
t.elapsed()).count());
973 if (
t.elapsed() > 50ms)
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()));
987 std::vector<char> trash;
995 trash.resize(std::max((
uint)trash.size(),avail));
999 LOG(VB_NETWORK, LOG_INFO,
LOC() +
"Reset() " +
1000 QString(
"%1 bytes available").arg(avail));