2 #include <QNetworkInterface>
3 #include <QCoreApplication>
4 #include <QWaitCondition>
5 #include <QSharedPointer>
18 #include <sys/socket.h>
30 #define SLOC(a) QString("MythSocket(%1:%2): ") \
31 .arg((intptr_t)(a), 0, 16) \
32 .arg(a->GetSocketDescriptor())
33 #define LOC SLOC(this)
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 >();
67 for (
uint i = 0; (i<60) && (i<(
uint)payload.length()); i++)
69 sample += QChar(payload.data()[i]).isPrint() ?
70 QChar(payload.data()[i]) : QChar(
'?');
72 sample += (payload.length() > 60) ?
"..." :
"";
79 m_tcpSocket(new QTcpSocket()),
81 m_socketDescriptor(-1),
83 m_useSharedThread(use_shared_thread),
84 m_disableReadyReadCallback(
false),
90 LOG(VB_SOCKET, LOG_INFO,
LOC + QString(
"MythSocket(%1, 0x%2) ctor")
91 .arg(socket).arg((intptr_t)(cb),0,16));
99 Qt::DirectConnection);
102 Qt::DirectConnection);
107 Qt::DirectConnection);
110 Qt::DirectConnection);
114 Qt::QueuedConnection);
119 socket, QAbstractSocket::ConnectedState,
120 QAbstractSocket::ReadWrite);
125 if (!use_shared_thread)
148 LOG(VB_SOCKET, LOG_INFO,
LOC + QString(
"MythSocket dtor : cb 0x%2")
181 QMutexLocker locker(&
m_lock);
188 m_tcpSocket->setSocketOption(QAbstractSocket::LowDelayOption, QVariant(1));
189 m_tcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
191 int reuse_addr_val = 1;
192 #if defined(Q_OS_WIN)
193 int ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
194 SO_REUSEADDR, (
char*) &reuse_addr_val,
195 sizeof(reuse_addr_val));
197 int ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
198 SO_REUSEADDR, &reuse_addr_val,
199 sizeof(reuse_addr_val));
203 LOG(VB_SOCKET, LOG_INFO,
LOC +
"Failed to set SO_REUSEADDR" + ENO);
207 #if defined(Q_OS_WIN)
208 ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
209 SO_RCVBUF, (
char*) &rcv_buf_val,
210 sizeof(rcv_buf_val));
212 ret = setsockopt(
m_tcpSocket->socketDescriptor(), SOL_SOCKET,
213 SO_RCVBUF, &rcv_buf_val,
214 sizeof(rcv_buf_val));
218 LOG(VB_SOCKET, LOG_INFO,
LOC +
"Failed to set SO_RCVBUF" + ENO);
223 LOG(VB_SOCKET, LOG_DEBUG,
LOC +
224 "calling m_callback->connected()");
233 if (err == QAbstractSocket::SocketTimeoutError)
238 LOG(VB_SOCKET, LOG_DEBUG,
LOC +
239 "calling m_callback->error() err: " +
m_tcpSocket->errorString());
247 QMutexLocker locker(&
m_lock);
256 LOG(VB_SOCKET, LOG_DEBUG,
LOC +
257 "calling m_callback->connectionClosed()");
264 LOG(VB_SOCKET, LOG_DEBUG,
LOC +
"AboutToClose");
284 LOG(VB_SOCKET, LOG_DEBUG,
LOC +
285 "calling m_callback->readyRead()");
291 const QHostAddress &hadr, quint16 port)
294 QMetaObject::invokeMethod(
295 this,
"ConnectToHostReal",
297 Qt::BlockingQueuedConnection : Qt::DirectConnection,
298 Q_ARG(QHostAddress, hadr),
299 Q_ARG(quint16, port),
307 QMetaObject::invokeMethod(
308 this,
"WriteStringListReal",
310 Qt::BlockingQueuedConnection : Qt::DirectConnection,
311 Q_ARG(
const QStringList*, &list),
319 QMetaObject::invokeMethod(
320 this,
"ReadStringListReal",
322 Qt::BlockingQueuedConnection : Qt::DirectConnection,
323 Q_ARG(QStringList*, &list),
324 Q_ARG(
uint, timeoutMS),
330 QStringList &strlist,
uint min_reply_length,
uint timeoutMS)
334 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to send command.");
340 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No response.");
344 if (min_reply_length && ((
uint)strlist.size() < min_reply_length))
346 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Response too short.");
350 if (!strlist.empty() && strlist[0] ==
"BACKEND_MESSAGE")
352 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got MythEvent on non-event socket");
368 if (!hadr.setAddress(host))
375 QHostInfo info = QHostInfo::fromName(host);
376 if (!info.addresses().isEmpty())
378 hadr = info.addresses().first();
382 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Unable to lookup: %1")
397 QStringList strlist(QString(
"MYTH_PROTO_VERSION %1 %2")
398 .arg(MYTH_PROTO_VERSION).arg(MYTH_PROTO_TOKEN));
404 LOG(VB_GENERAL, LOG_ERR,
"Protocol version check failure.\n\t\t\t"
405 "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
406 "This happens when the backend is too busy to respond,\n\t\t\t"
407 "or has deadlocked due to bugs or hardware failure.");
411 if (strlist[0] ==
"REJECT" && (strlist.size() >= 2))
413 LOG(VB_GENERAL, LOG_ERR,
414 QString(
"Protocol version or token mismatch "
415 "(frontend=%1/%2,backend=%3/\?\?)\n")
416 .arg(MYTH_PROTO_VERSION).arg(MYTH_PROTO_TOKEN).arg(strlist[1]));
419 if (error_dialog_desired && GUIcontext)
421 QStringList list(strlist[1]);
422 QCoreApplication::postEvent(
423 GUIcontext,
new MythEvent(
"VERSION_MISMATCH", list));
426 else if (strlist[0] ==
"ACCEPT")
428 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Using protocol version %1")
429 .arg(MYTH_PROTO_VERSION));
434 LOG(VB_GENERAL, LOG_ERR,
435 QString(
"Unexpected response to MYTH_PROTO_VERSION: %1")
446 LOG(VB_GENERAL, LOG_ERR,
LOC +
447 "refusing to announce unvalidated socket");
453 LOG(VB_GENERAL, LOG_ERR,
LOC +
"refusing to re-announce socket");
462 LOG(VB_GENERAL, LOG_ERR,
LOC +
463 QString(
"\n\t\t\tCould not read string list from server %1:%2")
486 QMetaObject::invokeMethod(
487 this,
"DisconnectFromHostReal",
489 Qt::BlockingQueuedConnection : Qt::DirectConnection);
495 QMetaObject::invokeMethod(
498 Qt::BlockingQueuedConnection : Qt::DirectConnection,
499 Q_ARG(
const char*, data),
508 QMetaObject::invokeMethod(
511 Qt::BlockingQueuedConnection : Qt::DirectConnection,
514 Q_ARG(
int, max_wait_ms),
521 QMetaObject::invokeMethod(
524 Qt::BlockingQueuedConnection : Qt::DirectConnection);
531 QMutexLocker locker(&
m_lock);
545 QMetaObject::invokeMethod(
546 const_cast<MythSocket*>(
this),
"IsDataAvailableReal",
547 Qt::BlockingQueuedConnection,
555 QMutexLocker locker(&
m_lock);
561 QMutexLocker locker(&
m_lock);
567 QMutexLocker locker(&
m_lock);
581 if (
m_tcpSocket->state() == QAbstractSocket::ConnectedState)
583 LOG(VB_SOCKET, LOG_ERR,
LOC +
584 "connect() called with already open socket, closing");
598 QList<QHostAddress> localIPs = QNetworkInterface::allAddresses();
599 for (
int i = 0; i < localIPs.count() && !usingLoopback; ++i)
601 if (addr == localIPs[i])
603 QHostAddress::SpecialAddress loopback = QHostAddress::LocalHost;
604 if (addr.protocol() == QAbstractSocket::IPv6Protocol)
605 loopback = QHostAddress::LocalHostIPv6;
609 addr = QHostAddress(loopback);
610 usingLoopback =
true;
617 LOG(VB_SOCKET, LOG_INFO,
LOC +
618 "IP is local, using loopback address instead");
621 LOG(VB_SOCKET, LOG_INFO,
LOC + QString(
"attempting connect() to (%1:%2)")
622 .arg(addr.toString()).arg(port));
624 m_tcpSocket->connectToHost(addr, port, QAbstractSocket::ReadWrite);
630 LOG(VB_SOCKET, LOG_INFO,
LOC + QString(
"Connected to (%1:%2)")
631 .arg(addr.toString()).arg(port));
635 LOG(VB_GENERAL, LOG_ERR,
LOC +
636 QString(
"Failed to connect to (%1:%2) %3")
637 .arg(addr.toString()).arg(port)
653 LOG(VB_GENERAL, LOG_ERR,
LOC +
654 "WriteStringList: Error, invalid string list.");
659 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
661 LOG(VB_GENERAL, LOG_ERR,
LOC +
662 "WriteStringList: Error, called with unconnected socket.");
667 QString str = list->join(
"[]:[]");
670 LOG(VB_GENERAL, LOG_ERR,
LOC +
671 "WriteStringList: Error, joined null string.");
676 QByteArray
utf8 = str.toUtf8();
677 int size = utf8.length();
679 int written_since_timer_restart = 0;
682 payload = payload.setNum(size);
686 size = payload.length();
688 if (VERBOSE_LEVEL_CHECK(VB_NETWORK, LOG_INFO))
690 QString msg = QString(
"write -> %1 %2")
691 .arg(
m_tcpSocket->socketDescriptor(), 2).arg(payload.data());
693 if (
logLevel < LOG_DEBUG && msg.length() > 88)
698 LOG(VB_NETWORK, LOG_INFO,
LOC + msg);
702 unsigned int errorcount = 0;
705 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
707 LOG(VB_GENERAL, LOG_ERR,
LOC +
708 "WriteStringList: Error, socket went unconnected." +
709 QString(
"\n\t\t\tWe wrote %1 of %2 bytes with %3 errors")
710 .arg(written).arg(written+size).arg(errorcount) +
711 QString(
"\n\t\t\tstarts with: %1").arg(
to_sample(payload)));
720 written_since_timer_restart += temp;
722 if ((timer.
elapsed() > 500) && written_since_timer_restart != 0)
725 written_since_timer_restart = 0;
733 LOG(VB_GENERAL, LOG_ERR,
LOC +
"WriteStringList: Error, " +
734 QString(
"No data written on write (%1 errors)")
736 QString(
"\n\t\t\tstarts with: %1")
752 QStringList *list,
uint timeoutMS,
bool *ret)
764 if (elapsed >= (
int)timeoutMS)
766 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ReadStringList: " +
767 QString(
"Error, timed out after %1 ms.").arg(timeoutMS));
773 if (
m_tcpSocket->state() != QAbstractSocket::ConnectedState)
775 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ReadStringList: Connection died.");
783 QByteArray sizestr(8 + 1,
'\0');
786 LOG(VB_GENERAL, LOG_ERR,
LOC +
787 QString(
"ReadStringList: Error, read return error (%1)")
794 QString sizes = sizestr;
795 qint64 btr = sizes.trimmed().toInt();
800 LOG(VB_GENERAL, LOG_ERR,
LOC +
801 QString(
"Protocol error: '%1' is not a valid size "
802 "prefix. %2 bytes pending.")
803 .arg(sizestr.data()).arg(pending));
808 QByteArray
utf8(btr + 1, 0);
810 qint64 readoffset = 0;
818 if (
m_tcpSocket->state() == QAbstractSocket::ConnectedState)
824 LOG(VB_GENERAL, LOG_ERR,
LOC +
825 "ReadStringList: Connection died.");
831 qint64 sret =
m_tcpSocket->read(utf8.data() + readoffset, btr);
843 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ReadStringList: Error, read");
850 LOG(VB_GENERAL, LOG_ERR,
LOC +
851 "ReadStringList: Error, socket went unconnected");
861 if ((elapsed - errmsgtime) > 10000)
863 errmsgtime = elapsed;
864 LOG(VB_GENERAL, LOG_ERR,
LOC +
865 QString(
"ReadStringList: Waiting for data: %1 %2")
866 .arg(readoffset).arg(btr));
870 if (elapsed > 100000)
872 LOG(VB_GENERAL, LOG_ERR,
LOC +
873 "Error, ReadStringList timeout (readBlock)");
880 QString str = QString::fromUtf8(utf8.data());
883 payload = payload.setNum(str.length());
888 if (VERBOSE_LEVEL_CHECK(VB_NETWORK, LOG_INFO))
890 QString msg = QString(
"read <- %1 %2")
892 .arg(payload.data());
894 if (
logLevel < LOG_DEBUG && msg.length() > 88)
899 LOG(VB_NETWORK, LOG_INFO,
LOC + msg);
902 *list = str.split(
"[]:[]");
918 while ((
m_tcpSocket->state() == QAbstractSocket::ConnectedState) &&
928 LOG(VB_GENERAL, LOG_INFO,
929 QString(
"ReadReal(?, %1, %2) -> %3 took %4 ms")
930 .arg(size).arg(max_wait_ms).arg(*ret)
946 trash.resize(max((
uint)trash.size(),avail));
949 LOG(VB_NETWORK, LOG_INFO,
LOC +
"Reset() " +
950 QString(
"%1 bytes available").arg(avail));