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 connect(
m_tcpSocket, &QAbstractSocket::errorOccurred,
105 Qt::DirectConnection);
108 connect(
m_tcpSocket, &QAbstractSocket::disconnected,
110 Qt::DirectConnection);
113 Qt::DirectConnection);
117 Qt::QueuedConnection);
119 if (!use_shared_thread)
142 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"MythSocket dtor : cb 0x%2")
178 QMutexLocker locker(&
m_lock);
185 m_tcpSocket->setSocketOption(QAbstractSocket::LowDelayOption, QVariant(1));
186 m_tcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
188 int reuse_addr_val = 1;
189 #if defined(Q_OS_WIN)
190 int ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
191 SO_REUSEADDR, (
char*) &reuse_addr_val,
192 sizeof(reuse_addr_val));
194 int ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
195 SO_REUSEADDR, &reuse_addr_val,
196 sizeof(reuse_addr_val));
200 LOG(VB_SOCKET, LOG_INFO,
LOC() +
"Failed to set SO_REUSEADDR" +
ENO);
204 #if defined(Q_OS_WIN)
205 ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
206 SO_RCVBUF, (
char*) &rcv_buf_val,
207 sizeof(rcv_buf_val));
209 ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
210 SO_RCVBUF, &rcv_buf_val,
211 sizeof(rcv_buf_val));
215 LOG(VB_SOCKET, LOG_INFO,
LOC() +
"Failed to set SO_RCVBUF" +
ENO);
220 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
221 "calling m_callback->connected()");
230 if (err == QAbstractSocket::SocketTimeoutError)
235 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
236 "calling m_callback->error() err: " +
m_tcpSocket->errorString());
244 QMutexLocker locker(&
m_lock);
253 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
254 "calling m_callback->connectionClosed()");
261 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
"AboutToClose");
281 LOG(VB_SOCKET, LOG_DEBUG,
LOC() +
282 "calling m_callback->readyRead()");
288 const QHostAddress &address, quint16 port)
291 QMetaObject::invokeMethod(
292 this,
"ConnectToHostReal",
294 Qt::BlockingQueuedConnection : Qt::DirectConnection,
295 Q_ARG(QHostAddress, address),
296 Q_ARG(quint16, port),
304 QMetaObject::invokeMethod(
305 this,
"WriteStringListReal",
307 Qt::BlockingQueuedConnection : Qt::DirectConnection,
308 Q_ARG(
const QStringList*, &list),
316 QMetaObject::invokeMethod(
317 this,
"ReadStringListReal",
319 Qt::BlockingQueuedConnection : Qt::DirectConnection,
320 Q_ARG(QStringList*, &list),
321 Q_ARG(std::chrono::milliseconds, timeoutMS),
327 QStringList &strlist,
uint min_reply_length, std::chrono::milliseconds timeoutMS)
335 LOG(VB_GENERAL, LOG_EMERG, QString(
"Programmer Error! "
336 "SendReceiveStringList(%1) used on "
337 "socket with callbacks enabled.")
338 .arg(strlist.isEmpty() ?
"empty" : strlist[0]));
343 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"Failed to send command.");
349 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"No response.");
353 if (min_reply_length && ((
uint)strlist.size() < min_reply_length))
355 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"Response too short.");
360 if (!strlist.empty() && strlist[0] ==
"BACKEND_MESSAGE")
362 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"Got MythEvent on non-event socket");
379 if (!hadr.setAddress(host))
386 QHostInfo
info = QHostInfo::fromName(host);
387 if (!
info.addresses().isEmpty())
389 hadr =
info.addresses().constFirst();
393 LOG(VB_GENERAL, LOG_ERR,
LOC() + QString(
"Unable to lookup: %1")
408 QStringList strlist(QString(
"MYTH_PROTO_VERSION %1 %2")
409 .arg(MYTH_PROTO_VERSION,
410 QString::fromUtf8(MYTH_PROTO_TOKEN)));
416 LOG(VB_GENERAL, LOG_ERR,
"Protocol version check failure.\n\t\t\t"
417 "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
418 "This happens when the backend is too busy to respond,\n\t\t\t"
419 "or has deadlocked due to bugs or hardware failure.");
423 if (strlist[0] ==
"REJECT" && (strlist.size() >= 2))
425 LOG(VB_GENERAL, LOG_ERR,
426 QString(
"Protocol version or token mismatch "
427 "(frontend=%1/%2,backend=%3/\?\?)\n")
428 .arg(MYTH_PROTO_VERSION,
429 QString::fromUtf8(MYTH_PROTO_TOKEN),
433 if (error_dialog_desired && GUIcontext)
435 QStringList list(strlist[1]);
436 QCoreApplication::postEvent(
437 GUIcontext,
new MythEvent(
"VERSION_MISMATCH", list));
440 else if (strlist[0] ==
"ACCEPT")
442 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Using protocol version %1 %2")
443 .arg(MYTH_PROTO_VERSION, QString::fromUtf8(MYTH_PROTO_TOKEN)));
448 LOG(VB_GENERAL, LOG_ERR,
449 QString(
"Unexpected response to MYTH_PROTO_VERSION: %1")
460 LOG(VB_GENERAL, LOG_ERR,
LOC() +
461 "refusing to announce unvalidated socket");
467 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"refusing to re-announce socket");
476 LOG(VB_GENERAL, LOG_ERR,
LOC() +
477 QString(
"\n\t\t\tCould not read string list from server %1:%2")
503 LOG(VB_GENERAL, LOG_ERR,
LOC() +
504 QString(
"Programmer error, QEventLoop isn't running and deleting "
505 "MythSocket(0x%1)").arg(
reinterpret_cast<intptr_t
>(
this),0,16));
508 QMetaObject::invokeMethod(
509 this,
"DisconnectFromHostReal",
511 Qt::BlockingQueuedConnection : Qt::DirectConnection);
517 QMetaObject::invokeMethod(
520 Qt::BlockingQueuedConnection : Qt::DirectConnection,
521 Q_ARG(
const char*, data),
530 QMetaObject::invokeMethod(
533 Qt::BlockingQueuedConnection : Qt::DirectConnection,
536 Q_ARG(std::chrono::milliseconds, max_wait),
543 QMetaObject::invokeMethod(
546 Qt::BlockingQueuedConnection : Qt::DirectConnection);
553 QMutexLocker locker(&
m_lock);
567 QMetaObject::invokeMethod(
568 this,
"IsDataAvailableReal",
569 Qt::BlockingQueuedConnection,
577 QMutexLocker locker(&
m_lock);
583 QMutexLocker locker(&
m_lock);
589 QMutexLocker locker(&
m_lock);
603 if (
m_tcpSocket->state() == QAbstractSocket::ConnectedState)
605 LOG(VB_SOCKET, LOG_ERR,
LOC() +
606 "connect() called with already open socket, closing");
610 QHostAddress addr = _addr;
611 addr.setScopeId(QString());
623 QList<QHostAddress> localIPs = QNetworkInterface::allAddresses();
624 for (
int i = 0; i < localIPs.count() && !usingLoopback; ++i)
626 QHostAddress local = localIPs[i];
627 local.setScopeId(QString());
631 QHostAddress::SpecialAddress loopback = QHostAddress::LocalHost;
632 if (addr.protocol() == QAbstractSocket::IPv6Protocol)
633 loopback = QHostAddress::LocalHostIPv6;
637 addr = QHostAddress(loopback);
638 usingLoopback =
true;
645 LOG(VB_SOCKET, LOG_INFO,
LOC() +
646 "IP is local, using loopback address instead");
649 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"attempting connect() to (%1:%2)")
650 .arg(addr.toString()).arg(port));
657 QString host = addr.toString();
659 addr.setAddress(host);
664 m_tcpSocket->connectToHost(addr, port, QAbstractSocket::ReadWrite);
670 LOG(VB_SOCKET, LOG_INFO,
LOC() + QString(
"Connected to (%1:%2)")
671 .arg(addr.toString()).arg(port));
675 LOG(VB_GENERAL, LOG_ERR,
LOC() +
676 QString(
"Failed to connect to (%1:%2) %3")
677 .arg(addr.toString()).arg(port)
693 LOG(VB_GENERAL, LOG_ERR,
LOC() +
694 "WriteStringList: Error, invalid string list.");
699 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
701 LOG(VB_GENERAL, LOG_ERR,
LOC() +
702 "WriteStringList: Error, called with unconnected socket.");
707 QString str = list->join(
"[]:[]");
710 LOG(VB_GENERAL, LOG_ERR,
LOC() +
711 "WriteStringList: Error, joined null string.");
716 QByteArray utf8 = str.toUtf8();
717 int size = utf8.length();
719 int written_since_timer_restart = 0;
722 payload = payload.setNum(size);
726 size = payload.length();
730 QString msg = QString(
"write -> %1 %2")
731 .arg(
m_tcpSocket->socketDescriptor(), 2).arg(payload.data());
733 if (
logLevel < LOG_DEBUG && msg.length() > 128)
738 LOG(VB_NETWORK, LOG_INFO,
LOC() + msg);
742 unsigned int errorcount = 0;
745 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
747 LOG(VB_GENERAL, LOG_ERR,
LOC() +
748 "WriteStringList: Error, socket went unconnected." +
749 QString(
"\n\t\t\tWe wrote %1 of %2 bytes with %3 errors")
750 .arg(written).arg(written+size).arg(errorcount) +
751 QString(
"\n\t\t\tstarts with: %1").arg(
to_sample(payload)));
756 int temp =
m_tcpSocket->write(payload.data() + written, size);
760 written_since_timer_restart += temp;
762 if ((timer.
elapsed() > 500ms) && written_since_timer_restart != 0)
765 written_since_timer_restart = 0;
773 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"WriteStringList: Error, " +
774 QString(
"No data written on write (%1 errors)")
776 QString(
"\n\t\t\tstarts with: %1")
791 QStringList *list, std::chrono::milliseconds timeoutMS,
bool *ret)
798 std::chrono::milliseconds elapsed { 0ms };
803 if (elapsed >= timeoutMS)
805 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"ReadStringList: " +
806 QString(
"Error, timed out after %1 ms.").arg(timeoutMS.count()));
812 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
814 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"ReadStringList: Connection died.");
822 QByteArray sizestr(8,
'\0');
825 LOG(VB_GENERAL, LOG_ERR,
LOC() +
826 QString(
"ReadStringList: Error, read return error (%1)")
833 QString sizes = sizestr;
835 int btr = sizes.trimmed().toInt(&ok);
840 LOG(VB_GENERAL, LOG_ERR,
LOC() +
841 QString(
"Protocol error: %1'%2' is not a valid size "
842 "prefix. %3 bytes pending.")
843 .arg(ok ?
"" :
"(parse failed) ",
844 sizestr.data(), QString::number(pending)));
849 QByteArray utf8(btr + 1, 0);
851 qint64 readoffset = 0;
852 std::chrono::milliseconds errmsgtime { 0ms };
859 if (
m_tcpSocket->state() == QAbstractSocket::ConnectedState)
865 LOG(VB_GENERAL, LOG_ERR,
LOC() +
866 "ReadStringList: Connection died.");
872 qint64 sret =
m_tcpSocket->read(utf8.data() + readoffset, btr);
884 LOG(VB_GENERAL, LOG_ERR,
LOC() +
"ReadStringList: Error, read");
891 LOG(VB_GENERAL, LOG_ERR,
LOC() +
892 "ReadStringList: Error, socket went unconnected");
902 if ((elapsed - errmsgtime) > 10s)
904 errmsgtime = elapsed;
905 LOG(VB_GENERAL, LOG_ERR,
LOC() +
906 QString(
"ReadStringList: Waiting for data: %1 %2")
907 .arg(readoffset).arg(btr));
913 LOG(VB_GENERAL, LOG_ERR,
LOC() +
914 "Error, ReadStringList timeout (readBlock)");
921 QString str = QString::fromUtf8(utf8.data());
926 payload = payload.setNum(str.length());
929 payload += utf8.data();
931 QString msg = QString(
"read <- %1 %2")
933 .arg(payload.data());
935 if (
logLevel < LOG_DEBUG && msg.length() > 128)
940 LOG(VB_NETWORK, LOG_INFO,
LOC() + msg);
943 *list = str.split(
"[]:[]");
959 while ((
m_tcpSocket->state() == QAbstractSocket::ConnectedState) &&
961 (
t.elapsed() < max_wait_ms))
963 m_tcpSocket->waitForReadyRead(max(2ms, max_wait_ms -
t.elapsed()).count());
967 if (
t.elapsed() > 50ms)
969 LOG(VB_NETWORK, LOG_INFO,
970 QString(
"ReadReal(?, %1, %2) -> %3 took %4 ms")
971 .arg(size).arg(max_wait_ms.count()).arg(*ret)
972 .arg(
t.elapsed().count()));
981 std::vector<char> trash;
989 trash.resize(std::max((
uint)trash.size(),avail));
993 LOG(VB_NETWORK, LOG_INFO,
LOC() +
"Reset() " +
994 QString(
"%1 bytes available").arg(avail));