MythTV master
mythcorecontext.cpp
Go to the documentation of this file.
1#include "mythcorecontext.h"
2
3// Qt
4#include <QtGlobal>
5#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
6#include <QtSystemDetection>
7#endif
8#include <QCoreApplication>
9#include <QUrl>
10#include <QDir>
11#include <QFileInfo>
12#include <QDebug>
13#include <QMutex>
14#include <QRunnable>
15#include <QWaitCondition>
16#include <QAbstractSocket>
17#include <QHostAddress>
18#include <QHostInfo>
19#include <QNetworkInterface>
20#include <QNetworkAddressEntry>
21#include <QLocale>
22#include <QPair>
23#include <QRegularExpression>
24#include <QDateTime>
25
26// Std
27#include <algorithm>
28#include <cmath>
29#include <cstdarg>
30#include <queue>
31#include <thread>
32
33#ifdef Q_OS_WINDOWS
34#include <winsock2.h>
35#else
36#include <clocale>
37#include <utility>
38#endif
39
40// MythTV
41#include "compat.h"
42#include "mythappname.h"
43#include "mythdownloadmanager.h"
44#include "mythsocket.h"
45#include "mythsystemlegacy.h"
46#include "mthreadpool.h"
47#include "exitcodes.h"
48#include "mythlogging.h"
49#include "mythversion.h"
50#include "logging.h"
51#include "mthread.h"
52#include "serverpool.h"
53#include "mythdate.h"
54#include "mythplugin.h"
55#include "mythmiscutil.h"
56#include "mythpower.h"
57
58#define LOC QString("MythCoreContext::%1(): ").arg(__func__)
59
61
62class MythCoreContextPrivate : public QObject
63{
64 public:
65 MythCoreContextPrivate(MythCoreContext *lparent, QString binversion,
66 QObject *guicontext);
67 ~MythCoreContextPrivate() override;
68
69 bool WaitForWOL(std::chrono::milliseconds timeout = std::chrono::milliseconds::max());
70
71 public:
73 QObject *m_guiContext { nullptr };
74 QObject *m_guiObject { nullptr };
76
81 QMutex m_scopesLock;
82 QMap<QString, QString> m_scopes;
83
84 QMutex m_sockLock;
85 MythSocket *m_serverSock { nullptr };
86 MythSocket *m_eventSock { nullptr };
87
90 bool m_wolInProgress { false };
91 bool m_isWOLAllowed { true };
92
93 bool m_backend { false };
94 bool m_frontend { false };
95
96 MythDB *m_database { nullptr };
97
98 QThread *m_uiThread { nullptr };
99
100 MythLocale *m_locale { nullptr };
101 QString m_language;
103
105
106 bool m_blockingClient { true };
107
108 QMap<QObject *, MythCoreContext::PlaybackStartCb> m_playbackClients;
110 bool m_inwanting { false };
111 bool m_intvwanting { false };
112
113 bool m_announcedProtocol { false };
114
116
117 bool m_isexiting { false };
118
119 QMap<QString, QPair<int64_t, uint64_t> > m_fileswritten;
121
123
124 QList<QHostAddress> m_approvedIps;
125 QList<QHostAddress> m_deniedIps;
127
128 MythPower *m_power { nullptr };
129};
130
132 QString binversion,
133 QObject *guicontext)
134 : m_parent(lparent),
135 m_guiContext(guicontext),
136 m_appBinaryVersion(std::move(binversion)),
137 m_database(GetMythDB()),
138 m_uiThread(QThread::currentThread())
139{
140 MThread::ThreadSetup("CoreContext");
141}
142
143static void delete_sock(
144#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
145 QMutexLocker &locker,
146#else
147 QMutexLocker<QMutex> &locker,
148#endif
149 MythSocket **s)
150{
151 if (*s)
152 {
153 MythSocket *tmp = *s;
154 *s = nullptr;
155 locker.unlock();
156 tmp->DecrRef();
157 locker.relock();
158 }
159}
160
162{
163 if (m_power)
164 MythPower::AcquireRelease(this, false);
165
167
168 {
169 QMutexLocker locker(&m_sockLock);
170 delete_sock(locker, &m_serverSock);
171 delete_sock(locker, &m_eventSock);
172 }
173
174 delete m_locale;
175
176 delete m_sessionManager;
177
179
181
183
184 // This has already been run in the MythContext dtor. Do we need it here
185 // too?
186#if 0
187 logStop(); // need to shutdown db logger before we kill db
188#endif
189
191
192 GetMythDB()->GetDBManager()->CloseDatabases();
193
194 if (m_database) {
196 m_database = nullptr;
197 }
198
200}
201
205bool MythCoreContextPrivate::WaitForWOL(std::chrono::milliseconds timeout)
206{
207 std::chrono::milliseconds timeout_remaining = timeout;
208 while (m_wolInProgress && (timeout_remaining > 0ms))
209 {
210 LOG(VB_GENERAL, LOG_INFO, LOC + "Wake-On-LAN in progress, waiting...");
211
212 std::chrono::milliseconds max_wait = std::min(1000ms, timeout_remaining);
213 m_wolInProgressWaitCondition.wait(&m_wolInProgressLock, max_wait.count());
214 timeout_remaining -= max_wait;
215 }
216
217 return !m_wolInProgress;
218}
219
220MythCoreContext::MythCoreContext(const QString &binversion,
221 QObject *guiContext)
222{
223 d = new MythCoreContextPrivate(this, binversion, guiContext);
224}
225
227{
228 if (!d)
229 {
230 LOG(VB_GENERAL, LOG_EMERG, LOC + "Out-of-memory");
231 return false;
232 }
233
234 if (d->m_appBinaryVersion != MYTH_BINARY_VERSION)
235 {
236 LOG(VB_GENERAL, LOG_CRIT,
237 QString("Application binary version (%1) does not "
238 "match libraries (%2)")
239 .arg(d->m_appBinaryVersion, MYTH_BINARY_VERSION));
240
241 QString warning = tr("This application is not compatible with the "
242 "installed MythTV libraries. Please recompile "
243 "after a make distclean");
244 LOG(VB_GENERAL, LOG_WARNING, warning);
245
246 return false;
247 }
248
249#ifndef Q_OS_WINDOWS
250 static const QRegularExpression utf8
251 { "utf-?8", QRegularExpression::CaseInsensitiveOption };
252 QString lang_variables("");
253 QString lc_value = setlocale(LC_CTYPE, nullptr);
254 if (lc_value.isEmpty())
255 {
256 // try fallback to environment variables for non-glibc systems
257 // LC_ALL, then LC_CTYPE
258 lc_value = qEnvironmentVariable("LC_ALL");
259 if (lc_value.isEmpty())
260 lc_value = qEnvironmentVariable("LC_CTYPE");
261 }
262 if (!lc_value.contains(utf8))
263 lang_variables.append("LC_ALL or LC_CTYPE");
264 lc_value = qEnvironmentVariable("LANG");
265 if (!lc_value.contains(utf8))
266 {
267 if (!lang_variables.isEmpty())
268 lang_variables.append(", and ");
269 lang_variables.append("LANG");
270 }
271 LOG(VB_GENERAL, LOG_INFO, QString("Assumed character encoding: %1")
272 .arg(lc_value));
273 if (!lang_variables.isEmpty())
274 {
275 LOG(VB_GENERAL, LOG_WARNING, QString("This application expects to "
276 "be running a locale that specifies a UTF-8 codeset, and many "
277 "features may behave improperly with your current language "
278 "settings. Please set the %1 variable(s) in the environment "
279 "in which this program is executed to include a UTF-8 codeset "
280 "(such as 'en_US.UTF-8').").arg(lang_variables));
281 }
282#endif
283
284 return true;
285}
286
288{
289 delete d;
290 d = nullptr;
291}
292
293void MythCoreContext::setTestIntSettings(QMap<QString,int> &overrides)
294{
295 m_testOverrideInts = std::move(overrides);
296}
297
298void MythCoreContext::setTestFloatSettings(QMap<QString,double> &overrides)
299{
300 m_testOverrideFloats = std::move(overrides);
301}
302
303void MythCoreContext::setTestStringSettings(QMap<QString,QString> &overrides)
304{
305 m_testOverrideStrings = std::move(overrides);
306}
307
309 const QString &announcement,
310 [[maybe_unused]] std::chrono::milliseconds timeout,
311 bool &proto_mismatch)
312{
313 proto_mismatch = false;
314
315#ifndef IGNORE_PROTO_VER_MISMATCH
316 if (!CheckProtoVersion(serverSock, timeout, true))
317 {
318 proto_mismatch = true;
319 return false;
320 }
321#endif
322
323 QStringList strlist(announcement);
324
325 if (!serverSock->WriteStringList(strlist))
326 {
327 LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
328 "master backend, socket write failed");
329 return false;
330 }
331
332 if (!serverSock->ReadStringList(strlist, MythSocket::kShortTimeout) ||
333 strlist.empty() || (strlist[0] == "ERROR"))
334 {
335 if (!strlist.empty())
336 {
337 LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
338 "server socket to master backend");
339 }
340 else
341 {
342 LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
343 "server socket to master backend");
344 }
345 return false;
346 }
347
348 return true;
349}
350
351// Connects to master server safely (i.e. by taking m_sockLock)
353 bool openEventSocket)
354{
355 QMutexLocker locker(&d->m_sockLock);
356 bool success = ConnectToMasterServer(blockingClient, openEventSocket);
357
358 return success;
359}
360
361// Assumes that either m_sockLock is held, or the app is still single
362// threaded (i.e. during startup).
364 bool openEventSocket)
365{
366 if (IsMasterBackend())
367 {
368 // Should never get here unless there is a bug in the code somewhere.
369 // If this happens, it can cause endless event loops.
370 LOG(VB_GENERAL, LOG_ERR, LOC + "ERROR: Master backend tried to connect back "
371 "to itself!");
372 return false;
373 }
374 if (IsExiting())
375 return false;
376
377 QString server = GetMasterServerIP();
378 if (server.isEmpty())
379 return false;
380
381 int port = GetMasterServerPort();
382 bool proto_mismatch = false;
383
385 {
387 d->m_serverSock = nullptr;
388 }
389
390 if (!d->m_serverSock)
391 {
392 QString type { "Monitor" };
393 if (IsFrontend())
394 type = "Frontend";
395 else if (blockingClient)
396 type = "Playback";
397 QString ann = QString("ANN %1 %2 %3")
398 .arg(type, d->m_localHostname, QString::number(static_cast<int>(false)));
400 server, port, ann, &proto_mismatch);
401 }
402
403 if (!d->m_serverSock)
404 return false;
405
406 d->m_blockingClient = blockingClient;
407
408 if (!openEventSocket)
409 return true;
410
411
412 if (!IsBackend())
413 {
415 {
417 d->m_eventSock = nullptr;
418 }
419 if (!d->m_eventSock)
420 d->m_eventSock = ConnectEventSocket(server, port);
421
422 if (!d->m_eventSock)
423 {
425 d->m_serverSock = nullptr;
426
427 QCoreApplication::postEvent(
428 d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
429
430 return false;
431 }
432 }
433
434 return true;
435}
436
438 const QString &hostname, int port, const QString &announce,
439 bool *p_proto_mismatch, int maxConnTry, std::chrono::milliseconds setup_timeout)
440{
441 MythSocket *serverSock = nullptr;
442
443 {
444 QMutexLocker locker(&d->m_wolInProgressLock);
445 d->WaitForWOL();
446 }
447
448 QString WOLcmd;
449 if (IsWOLAllowed())
450 WOLcmd = GetSetting("WOLbackendCommand", "");
451
452 if (maxConnTry < 1)
453 maxConnTry = std::max(GetNumSetting("BackendConnectRetry", 1), 1);
454
455 std::chrono::seconds WOLsleepTime = 0s;
456 int WOLmaxConnTry = 0;
457 if (!WOLcmd.isEmpty())
458 {
459 WOLsleepTime = GetDurSetting<std::chrono::seconds>("WOLbackendReconnectWaitTime", 0s);
460 WOLmaxConnTry = std::max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
461 maxConnTry = std::max(maxConnTry, WOLmaxConnTry);
462 }
463
464 bool we_attempted_wol = false;
465
466 if (setup_timeout <= 0ms)
467 setup_timeout = MythSocket::kShortTimeout;
468
469 bool proto_mismatch = false;
470 for (int cnt = 1; cnt <= maxConnTry; cnt++)
471 {
472 LOG(VB_GENERAL, LOG_INFO, LOC +
473 QString("Connecting to backend server: %1:%2 (try %3 of %4)")
474 .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
475
476 serverSock = new MythSocket();
477
478 std::chrono::microseconds sleepus = 0us;
479 if (serverSock->ConnectToHost(hostname, port))
480 {
482 serverSock, announce, setup_timeout, proto_mismatch))
483 {
484 break;
485 }
486
487 if (proto_mismatch)
488 {
489 if (p_proto_mismatch)
490 *p_proto_mismatch = true;
491
492 serverSock->DecrRef();
493 serverSock = nullptr;
494 break;
495 }
496
497 setup_timeout += setup_timeout / 2;
498 }
499 else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
500 {
501 if (!we_attempted_wol)
502 {
503 QMutexLocker locker(&d->m_wolInProgressLock);
504 if (d->m_wolInProgress)
505 {
506 d->WaitForWOL();
507 continue;
508 }
509
510 d->m_wolInProgress = we_attempted_wol = true;
511 }
512
515 sleepus = WOLsleepTime;
516 }
517
518 serverSock->DecrRef();
519 serverSock = nullptr;
520
521 if (cnt == 1)
522 {
523 QCoreApplication::postEvent(
524 d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
525 }
526
527 if (sleepus != 0us)
528 std::this_thread::sleep_for(sleepus);
529 }
530
531 if (we_attempted_wol)
532 {
533 QMutexLocker locker(&d->m_wolInProgressLock);
534 d->m_wolInProgress = false;
536 }
537
538 if (!serverSock && !proto_mismatch)
539 {
540 LOG(VB_GENERAL, LOG_ERR,
541 "Connection to master server timed out.\n\t\t\t"
542 "Either the server is down or the master server settings"
543 "\n\t\t\t"
544 "in mythtv-settings does not contain the proper IP address\n");
545 }
546 else
547 {
548 QCoreApplication::postEvent(
549 d->m_guiContext, new MythEvent("CONNECTION_RESTABLISHED"));
550 }
551
552 return serverSock;
553}
554
556 int port)
557{
558 auto *eventSock = new MythSocket(-1, this);
559
560 // Assume that since we _just_ connected the command socket,
561 // this one won't need multiple retries to work...
562 if (!eventSock->ConnectToHost(hostname, port))
563 {
564 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
565 "socket to master backend");
566 eventSock->DecrRef();
567 return nullptr;
568 }
569
570 QString str = QString("ANN Monitor %1 %2")
571 .arg(d->m_localHostname).arg(static_cast<int>(true));
572 QStringList strlist(str);
573 eventSock->WriteStringList(strlist);
574 bool ok = true;
575 if (!eventSock->ReadStringList(strlist) || strlist.empty() ||
576 (strlist[0] == "ERROR"))
577 {
578 if (!strlist.empty())
579 {
580 LOG(VB_GENERAL, LOG_ERR, LOC +
581 "Problem connecting event socket to master backend");
582 }
583 else
584 {
585 LOG(VB_GENERAL, LOG_ERR, LOC +
586 "Timeout connecting event socket to master backend");
587 }
588 ok = false;
589 }
590
591 if (!ok)
592 {
593 eventSock->DecrRef();
594 eventSock = nullptr;
595 }
596
597 return eventSock;
598}
599
601{
602 QMutexLocker locker(&d->m_sockLock);
603 return d->m_serverSock;
604}
605
607{
608 QStringList strlist;
609
610 QMutexLocker locker(&d->m_sockLock);
611 if (d->m_serverSock == nullptr)
612 return;
613
614 strlist << "BLOCK_SHUTDOWN";
616
617 d->m_blockingClient = true;
618}
619
621{
622 QStringList strlist;
623
624 QMutexLocker locker(&d->m_sockLock);
625 if (d->m_serverSock == nullptr)
626 return;
627
628 strlist << "ALLOW_SHUTDOWN";
630
631 d->m_blockingClient = false;
632}
633
635{
636 return d->m_blockingClient;
637}
638
640{
641 d->m_isWOLAllowed = allow;
642}
643
645{
646 return d->m_isWOLAllowed;
647}
648
650{
651 d->m_backend = backend;
652}
653
655{
656 return d->m_backend;
657}
658
660{
661 d->m_frontend = frontend;
662}
663
665{
666 return d->m_frontend;
667}
668
670{
671 QString host = GetHostName();
672 return IsMasterHost(host);
673}
674
675bool MythCoreContext::IsMasterHost(const QString &host)
676{
677 // Temporary code here only to facilitate the upgrade
678 // from 1346 or earlier. The way of determining master host is
679 // changing, and the new way of determning master host
680 // will not work with earlier databases.
681 // This code can be removed when updating from prior to
682 // 1347 is no longer allowed.
683 // Note that we are deprecating some settings including
684 // MasterServerIP, and can remove them at a future time.
685 if (GetNumSetting("DBSchemaVer") < 1347)
686 {
687 // Temporary copy of code from old version of
688 // IsThisHost(Qstring&,QString&)
689 QString addr(resolveSettingAddress("MasterServerIP"));
690 if (addr.toLower() == host.toLower())
691 return true;
692 QHostAddress addrfix(addr);
693 addrfix.setScopeId(QString());
694 QString addrstr = addrfix.toString();
695 if (addrfix.isNull())
696 addrstr = resolveAddress(addr);
697 QString thisip = GetBackendServerIP4(host);
698 QString thisip6 = GetBackendServerIP6(host);
699 return !addrstr.isEmpty()
700 && ((addrstr == thisip) || (addrstr == thisip6));
701 }
702 return GetSetting("MasterServerName") == host;
703}
704
706{
707 return (IsBackend() && IsMasterHost());
708}
709
711{
712#ifdef Q_OS_BSD4
713 const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
714#elif defined(Q_OS_WINDOWS)
715 const char *command = "%systemroot%\\system32\\tasklist.exe "
716 " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
717#else
718 const char *command = "ps ch -C mythbackend -o pid > /dev/null";
719#endif
720 uint res = myth_system(command, kMSDontBlockInputDevs |
723 return (res == GENERIC_EXIT_OK);
724}
725
726bool MythCoreContext::IsThisBackend(const QString &addr)
727{
728 return IsBackend() && IsThisHost(addr);
729}
730
731bool MythCoreContext::IsThisHost(const QString &addr)
732{
733 return IsThisHost(addr, GetHostName());
734}
735
736bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
737{
738 if (addr.toLower() == host.toLower())
739 return true;
740
741 QHostAddress addrfix(addr);
742 addrfix.setScopeId(QString());
743 QString addrstr = addrfix.toString();
744
745 if (addrfix.isNull())
746 {
747 addrstr = resolveAddress(addr);
748 }
749
750 QString thisip = GetBackendServerIP(host);
751
752 return !addrstr.isEmpty() && (addrstr == thisip);
753}
754
756{
757 // find out if a backend runs on this host...
758 bool backendOnLocalhost = false;
759
760 QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
761 strlist << GetHostName();
762
763 SendReceiveStringList(strlist);
764
765 backendOnLocalhost = strlist[0] != "FALSE";
766
767 return !backendOnLocalhost;
768}
769
770QString MythCoreContext::GenMythURL(const QString& host, int port, QString path, const QString& storageGroup)
771{
772 QUrl ret;
773
774 QString m_host;
775
776 QHostAddress addr(host);
777 if (!addr.isNull())
778 {
779 LOG(VB_GENERAL, LOG_CRIT, LOC + QString("(%1/%2): Given "
780 "IP address instead of hostname "
781 "(ID). This is invalid.").arg(host, path));
782 }
783
784 m_host = host;
785
786 // Basically if it appears to be an IPv6 IP surround the IP with [] otherwise don't bother
787 if (!addr.isNull() && addr.protocol() == QAbstractSocket::IPv6Protocol)
788 m_host = "[" + addr.toString().toLower() + "]";
789
790 ret.setScheme("myth");
791 if (!storageGroup.isEmpty())
792 ret.setUserName(storageGroup);
793 ret.setHost(m_host);
794 if (port > 0 && port != 6543)
795 ret.setPort(port);
796 if (!path.startsWith("/"))
797 path = QString("/") + path;
798 ret.setPath(path);
799
800#if 0
801 LOG(VB_GENERAL, LOG_DEBUG, LOC +
802 QString("GenMythURL returning %1").arg(ret.toString()));
803#endif
804
805 return ret.toString();
806}
807
808QString MythCoreContext::GetMasterHostPrefix(const QString &storageGroup,
809 const QString &path)
810{
813 path,
814 storageGroup);
815}
816
818{
819 QMutexLocker locker(&d->m_masterHostLock);
820
821 if (d->m_masterHostname.isEmpty())
822 {
823
824 if (IsMasterBackend())
825 {
827 }
828 else
829 {
830 QStringList strlist("QUERY_HOSTNAME");
831
832 if (SendReceiveStringList(strlist))
833 d->m_masterHostname = strlist[0];
834 }
835 }
836
837 return d->m_masterHostname;
838}
839
840void MythCoreContext::ClearSettingsCache(const QString &myKey)
841{
842 d->m_database->ClearSettingsCache(myKey);
843}
844
846{
847 d->m_database->ActivateSettingsCache(activate);
848}
849
851{
852 QMutexLocker locker(&d->m_localHostLock);
853 return d->m_localHostname;
854}
855
857{
858 return GetSetting("RecordFilePrefix");
859}
860
862 int &width, int &height,
863 double &forced_aspect,
864 double &refresh_rate,
865 int index)
866{
867 d->m_database->GetResolutionSetting(type, width, height, forced_aspect,
868 refresh_rate, index);
869}
870
871void MythCoreContext::GetResolutionSetting(const QString &t, int &w,
872 int &h, int i)
873{
874 d->m_database->GetResolutionSetting(t, w, h, i);
875}
876
878{
879 return d->m_database->GetDBManager();
880}
881
889{
890 return d->m_database->IsDatabaseIgnored();
891}
892
893void MythCoreContext::SaveSetting(const QString &key, int newValue)
894{
895 d->m_database->SaveSetting(key, newValue);
896}
897
898void MythCoreContext::SaveSetting(const QString &key, const QString &newValue)
899{
900 d->m_database->SaveSetting(key, newValue);
901}
902
904 const QString &newValue,
905 const QString &host)
906{
907 return d->m_database->SaveSettingOnHost(key, newValue, host);
908}
909
910QString MythCoreContext::GetSetting(const QString &key,
911 const QString &defaultval)
912{
913 if (!m_testOverrideStrings.empty())
914 return m_testOverrideStrings[key];
915 return d->m_database->GetSetting(key, defaultval);
916}
917
918bool MythCoreContext::GetBoolSetting(const QString &key, bool defaultval)
919{
920 int result = GetNumSetting(key, static_cast<int>(defaultval));
921 return result > 0;
922}
923
924int MythCoreContext::GetNumSetting(const QString &key, int defaultval)
925{
926 if (!m_testOverrideInts.empty())
927 return m_testOverrideInts[key];
928 return d->m_database->GetNumSetting(key, defaultval);
929}
930
931double MythCoreContext::GetFloatSetting(const QString &key, double defaultval)
932{
933 if (!m_testOverrideFloats.empty())
934 return m_testOverrideFloats[key];
935 return d->m_database->GetFloatSetting(key, defaultval);
936}
937
938QString MythCoreContext::GetSettingOnHost(const QString &key,
939 const QString &host,
940 const QString &defaultval)
941{
942 if (!m_testOverrideStrings.empty())
943 return m_testOverrideStrings[key];
944 return d->m_database->GetSettingOnHost(key, host, defaultval);
945}
946
948 const QString &host,
949 bool defaultval)
950{
951 int result = GetNumSettingOnHost(key, host, static_cast<int>(defaultval));
952 return result > 0;
953}
954
956 const QString &host,
957 int defaultval)
958{
959 if (!m_testOverrideInts.empty())
960 return m_testOverrideInts[key];
961 return d->m_database->GetNumSettingOnHost(key, host, defaultval);
962}
963
965 const QString &host,
966 double defaultval)
967{
968 if (!m_testOverrideFloats.empty())
969 return m_testOverrideFloats[key];
970 return d->m_database->GetFloatSettingOnHost(key, host, defaultval);
971}
972
979{
980 QString masterserver = gCoreContext->GetSetting("MasterServerName");
981 QString masterip = resolveSettingAddress("BackendServerAddr",masterserver);
982 // Even if empty, return it here if we were to assume that localhost
983 // should be used it just causes a lot of unnecessary error messages.
984 return masterip;
985}
986
993{
994 QString masterserver = gCoreContext->GetSetting
995 ("MasterServerName");
997 ("BackendServerPort", masterserver, 6543);
998}
999
1006{
1007 QString masterhost = GetMasterHostName();
1008
1009 return GetBackendStatusPort(masterhost);
1010}
1011
1017{
1019}
1020
1030QString MythCoreContext::GetBackendServerIP(const QString &host)
1031{
1032 return resolveSettingAddress("BackendServerAddr",host);
1033}
1034
1040{
1042}
1043
1049QString MythCoreContext::GetBackendServerIP4(const QString &host)
1050{
1051 return resolveSettingAddress("BackendServerIP", host, ResolveIPv4);
1052}
1053
1059{
1061}
1062
1068QString MythCoreContext::GetBackendServerIP6(const QString &host)
1069{
1070 return resolveSettingAddress("BackendServerIP6", host, ResolveIPv6);
1071}
1072
1077{
1079}
1080
1081QHash<QString,int> MythCoreContext::s_serverPortCache;
1082
1084{
1085 s_serverPortCache.clear();
1086}
1087
1092{
1093 int port = s_serverPortCache.value(host, -1);
1094 if (port != -1)
1095 return port;
1096 port = GetNumSettingOnHost("BackendServerPort", host, 6543);
1097 s_serverPortCache[host] = port;
1098 return port;
1099}
1100
1105{
1107}
1108
1113{
1114 return GetNumSettingOnHost("BackendStatusPort", host, 6544);
1115}
1116
1121bool MythCoreContext::GetScopeForAddress(QHostAddress &addr) const
1122{
1123 QHostAddress addr1 = addr;
1124 addr1.setScopeId(QString());
1125 QString addrstr = addr1.toString();
1126 QMutexLocker lock(&d->m_scopesLock);
1127
1128 if (!d->m_scopes.contains(addrstr))
1129 return false;
1130
1131 addr.setScopeId(d->m_scopes[addrstr]);
1132 return true;
1133}
1134
1141void MythCoreContext::SetScopeForAddress(const QHostAddress &addr)
1142{
1143 QHostAddress addr1 = addr;
1144 addr1.setScopeId(QString());
1145 QMutexLocker lock(&d->m_scopesLock);
1146
1147 d->m_scopes.insert(addr1.toString(), addr.scopeId());
1148}
1149
1155void MythCoreContext::SetScopeForAddress(const QHostAddress &addr, int scope)
1156{
1157 QHostAddress addr1 = addr;
1158 addr1.setScopeId(QString());
1159 QMutexLocker lock(&d->m_scopesLock);
1160
1161 d->m_scopes.insert(addr1.toString(), QString::number(scope));
1162}
1163
1175QString MythCoreContext::resolveSettingAddress(const QString &name,
1176 const QString &host,
1177 ResolveType type, bool keepscope)
1178{
1179 QString value;
1180
1181 if (host.isEmpty())
1182 {
1183 value = GetSetting(name);
1184 }
1185 else
1186 {
1187 value = GetSettingOnHost(name, host);
1188 }
1189
1190 return resolveAddress(value, type, keepscope);
1191}
1192
1205 bool keepscope)
1206{
1207 QHostAddress addr(host);
1208
1209 if (!host.isEmpty() && addr.isNull())
1210 {
1211 // address is likely a hostname, attempts to resolve it
1212 QHostInfo info = QHostInfo::fromName(host);
1213 QList<QHostAddress> list = info.addresses();
1214
1215 if (list.isEmpty())
1216 {
1217 LOG(VB_GENERAL, LOG_WARNING, LOC +
1218 QString("Can't resolve hostname:'%1', using localhost").arg(host));
1219 return type == ResolveIPv4 ? "127.0.0.1" : "::1";
1220 }
1221 QHostAddress v4;
1222 QHostAddress v6;
1223
1224 // Return the first address fitting the type critera
1225 for (const auto& item : std::as_const(list))
1226 {
1227 addr = item;
1228 QAbstractSocket::NetworkLayerProtocol prot = addr.protocol();
1229
1230 if (prot == QAbstractSocket::IPv4Protocol)
1231 {
1232 v4 = addr;
1233 if (type == 0)
1234 break;
1235 }
1236 else if (prot == QAbstractSocket::IPv6Protocol)
1237 {
1238 v6 = addr;
1239 if (type != 0)
1240 break;
1241 }
1242 }
1243 switch (type)
1244 {
1245 case ResolveIPv4:
1246 addr = v4.isNull() ? QHostAddress::LocalHost : v4;
1247 break;
1248 case ResolveIPv6:
1249 addr = v6.isNull() ? QHostAddress::LocalHostIPv6 : v6;
1250 break;
1251 default:
1252 if (!v6.isNull())
1253 addr = v6;
1254 else if (!v4.isNull())
1255 addr = v4;
1256 else
1257 addr = QHostAddress::LocalHostIPv6;
1258 break;
1259 }
1260 }
1261 else if (host.isEmpty())
1262 {
1263 return {};
1264 }
1265
1266 if (!keepscope)
1267 {
1268 addr.setScopeId(QString());
1269 }
1270 return addr.toString();
1271}
1272
1284bool MythCoreContext::CheckSubnet(const QAbstractSocket *socket)
1285{
1286 QHostAddress peer = socket->peerAddress();
1287 return CheckSubnet(peer);
1288}
1289
1301bool MythCoreContext::CheckSubnet(const QHostAddress &peer)
1302{
1303 if (GetBoolSetting("AllowConnFromAll",false))
1304 return true;
1305 return IsLocalSubnet(peer, true);
1306}
1307
1316bool MythCoreContext::IsLocalSubnet(const QHostAddress &peer, bool log)
1317{
1318 static const QHostAddress kLinkLocal("fe80::");
1319 // Ensure m_approvedIps and m_deniedIps are single threaded
1320 QMutexLocker lock(&d->m_listMutex);
1321 if (d->m_approvedIps.contains(peer))
1322 return true;
1323 if (d->m_deniedIps.contains(peer))
1324 {
1325 if (log)
1326 LOG(VB_GENERAL, LOG_WARNING, LOC +
1327 QString("Repeat denied connection from ip address: %1")
1328 .arg(peer.toString()));
1329 return false;
1330 }
1331
1332 // allow all link-local
1333 if (peer.isInSubnet(kLinkLocal,10))
1334 {
1335 d->m_approvedIps.append(peer);
1336 return true;
1337 }
1338
1339 // loop through all available interfaces
1340 QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
1341 for (const auto & qni : std::as_const(IFs))
1342 {
1343 if ((qni.flags() & QNetworkInterface::IsRunning) == 0)
1344 continue;
1345
1346 QList<QNetworkAddressEntry> IPs = qni.addressEntries();
1347 for (const auto & qnai : std::as_const(IPs))
1348 {
1349 int pfxlen = qnai.prefixLength();
1350 // Set this to test rejection without having an extra
1351 // network.
1352 if (GetBoolSetting("DebugSubnet"))
1353 pfxlen += 4;
1354 if (peer.isInSubnet(qnai.ip(),pfxlen))
1355 {
1356 d->m_approvedIps.append(peer);
1357 return true;
1358 }
1359 }
1360 }
1361 d->m_deniedIps.append(peer);
1362 if (log)
1363 LOG(VB_GENERAL, LOG_WARNING, LOC +
1364 QString("Denied connection from ip address: %1")
1365 .arg(peer.toString()));
1366 return false;
1367}
1368
1369
1371 const QString &value)
1372{
1373 d->m_database->OverrideSettingForSession(key, value);
1374}
1375
1377{
1378 d->m_database->ClearOverrideSettingForSession(key);
1379}
1380
1382{
1384}
1385
1405 QStringList &strlist, bool quickTimeout, bool block)
1406{
1407 QString msg;
1408 if (HasGUI() && IsUIThread())
1409 {
1410 msg = "SendReceiveStringList(";
1411 for (uint i=0; i<(uint)strlist.size() && i<2; i++)
1412 msg += (i?",":"") + strlist[i];
1413 msg += (strlist.size() > 2) ? "...)" : ")";
1414 LOG(VB_GENERAL, LOG_DEBUG, LOC + msg + " called from UI thread");
1415 }
1416
1417 QString query_type = "UNKNOWN";
1418
1419 if (!strlist.isEmpty())
1420 query_type = strlist[0];
1421
1422 QMutexLocker locker(&d->m_sockLock);
1423 if (!d->m_serverSock)
1424 {
1425 bool blockingClient = d->m_blockingClient &&
1426 (GetNumSetting("idleTimeoutSecs",0) > 0);
1427 ConnectToMasterServer(blockingClient);
1428 }
1429
1430 bool ok = false;
1431
1432 if (d->m_serverSock)
1433 {
1434 std::chrono::milliseconds timeout = quickTimeout ?
1436 ok = d->m_serverSock->SendReceiveStringList(strlist, 0, timeout);
1437
1438 if (!ok)
1439 {
1440 LOG(VB_GENERAL, LOG_NOTICE, LOC +
1441 QString("Connection to backend server lost"));
1443 d->m_serverSock = nullptr;
1444
1445 if (d->m_eventSock)
1446 {
1447 d->m_eventSock->DecrRef();
1448 d->m_eventSock = nullptr;
1449 }
1450
1451 if (block)
1452 {
1454
1455 if (d->m_serverSock)
1456 {
1458 strlist, 0, timeout);
1459 }
1460 }
1461 }
1462
1463 // this should not happen
1464 while (ok && strlist[0] == "BACKEND_MESSAGE")
1465 {
1466 // oops, not for us
1467 LOG(VB_GENERAL, LOG_EMERG, LOC + "SRSL you shouldn't see this!!");
1468 QString message = strlist[1];
1469 strlist.pop_front(); strlist.pop_front();
1470
1471 MythEvent me(message, strlist);
1472 dispatch(me);
1473
1474 ok = d->m_serverSock->ReadStringList(strlist, timeout);
1475 }
1476
1477 if (!ok)
1478 {
1479 if (d->m_serverSock)
1480 {
1482 d->m_serverSock = nullptr;
1483 }
1484
1485 LOG(VB_GENERAL, LOG_CRIT, LOC +
1486 QString("Reconnection to backend server failed"));
1487
1488 QCoreApplication::postEvent(d->m_guiContext,
1489 new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
1490 }
1491 }
1492
1493 if (ok)
1494 {
1495 if (strlist.isEmpty())
1496 {
1497 ok = false;
1498 }
1499 else if (strlist[0] == "ERROR")
1500 {
1501 if (strlist.size() == 2)
1502 {
1503 LOG(VB_GENERAL, LOG_INFO, LOC +
1504 QString("Protocol query '%1' responded with the error '%2'")
1505 .arg(query_type, strlist[1]));
1506 }
1507 else
1508 {
1509 LOG(VB_GENERAL, LOG_INFO, LOC +
1510 QString("Protocol query '%1' responded with an error, but "
1511 "no error message.") .arg(query_type));
1512 }
1513
1514 ok = false;
1515 }
1516 else if (strlist[0] == "UNKNOWN_COMMAND")
1517 {
1518 LOG(VB_GENERAL, LOG_ERR, LOC +
1519 QString("Protocol query '%1' responded with the error 'UNKNOWN_COMMAND'")
1520 .arg(query_type));
1521
1522 ok = false;
1523 }
1524 }
1525
1526 return ok;
1527}
1528
1529class SendAsyncMessage : public QRunnable
1530{
1531 public:
1532 SendAsyncMessage(QString msg, QStringList extra) :
1533 m_message(std::move(msg)), m_extraData(std::move(extra))
1534 {
1535 }
1536
1537 explicit SendAsyncMessage(QString msg) : m_message(std::move(msg)) { }
1538
1539 void run(void) override // QRunnable
1540 {
1541 QStringList strlist("MESSAGE");
1542 strlist << m_message;
1543 strlist << m_extraData;
1545 }
1546
1547 private:
1548 QString m_message;
1549 QStringList m_extraData;
1550};
1551
1552void MythCoreContext::SendMessage(const QString &message)
1553{
1554 if (IsBackend())
1555 {
1556 dispatch(MythEvent(message));
1557 }
1558 else
1559 {
1561 new SendAsyncMessage(message), "SendMessage");
1562 }
1563}
1564
1566{
1567 if (IsBackend())
1568 {
1569 dispatch(event);
1570 }
1571 else
1572 {
1574 new SendAsyncMessage(event.Message(), event.ExtraDataList()),
1575 "SendEvent");
1576 }
1577}
1578
1579void MythCoreContext::SendSystemEvent(const QString &msg)
1580{
1581 if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1582 return;
1583
1584 SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
1585 .arg(msg, GetHostName()));
1586}
1587
1589 const QString &hostname,
1590 const QString &args)
1591{
1592 SendSystemEvent(QString("%1 HOST %2 %3").arg(msg, hostname, args));
1593}
1594
1595
1597{
1598 while (sock->IsDataAvailable())
1599 {
1600 QStringList strlist;
1601 if (!sock->ReadStringList(strlist))
1602 continue;
1603
1604 if (strlist.size() < 2)
1605 continue;
1606
1607 QString prefix = strlist[0];
1608 QString message = strlist[1];
1609 QStringList tokens = message.split(" ", Qt::SkipEmptyParts);
1610
1611 if (prefix == "OK")
1612 {
1613 }
1614 else if (prefix != "BACKEND_MESSAGE")
1615 {
1616 LOG(VB_NETWORK, LOG_ERR, LOC +
1617 QString("Received a: %1 message from the backend "
1618 "but I don't know what to do with it.")
1619 .arg(prefix));
1620 }
1621 else if (message == "CLEAR_SETTINGS_CACHE")
1622 {
1623 // No need to dispatch this message to ourself, so handle it
1624 LOG(VB_NETWORK, LOG_INFO, LOC + "Received remote 'Clear Cache' request");
1626 }
1627 else if (message.startsWith("FILE_WRITTEN"))
1628 {
1629 QString file;
1630 uint64_t size = 0;
1631 int NUMTOKENS = 3; // Number of tokens expected
1632
1633 if (tokens.size() == NUMTOKENS)
1634 {
1635 file = tokens[1];
1636 size = tokens[2].toULongLong();
1637 }
1638 else
1639 {
1640 LOG(VB_NETWORK, LOG_ERR, LOC +
1641 QString("FILE_WRITTEN event received "
1642 "with invalid number of arguments, "
1643 "%1 expected, %2 actual")
1644 .arg(NUMTOKENS-1)
1645 .arg(tokens.size()-1));
1646 return;
1647 }
1648 // No need to dispatch this message to ourself, so handle it
1649 LOG(VB_NETWORK, LOG_INFO, LOC +
1650 QString("Received remote 'FILE_WRITTEN %1' request").arg(file));
1652 }
1653 else if (message.startsWith("FILE_CLOSED"))
1654 {
1655 QString file;
1656 int NUMTOKENS = 2; // Number of tokens expected
1657
1658 if (tokens.size() == NUMTOKENS)
1659 {
1660 file = tokens[1];
1661 }
1662 else
1663 {
1664 LOG(VB_NETWORK, LOG_ERR, LOC +
1665 QString("FILE_CLOSED event received "
1666 "with invalid number of arguments, "
1667 "%1 expected, %2 actual")
1668 .arg(NUMTOKENS-1)
1669 .arg(tokens.size()-1));
1670 return;
1671 }
1672 // No need to dispatch this message to ourself, so handle it
1673 LOG(VB_NETWORK, LOG_INFO, LOC +
1674 QString("Received remote 'FILE_CLOSED %1' request").arg(file));
1676 }
1677 else
1678 {
1679 strlist.pop_front();
1680 strlist.pop_front();
1681 MythEvent me(message, strlist);
1682 dispatch(me);
1683 }
1684 }
1685}
1686
1688{
1689 LOG(VB_GENERAL, LOG_NOTICE, LOC +
1690 "Event socket closed. No connection to the backend.");
1691
1692 dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1693}
1694
1696 std::chrono::milliseconds timeout,
1697 bool error_dialog_desired)
1698{
1699 if (!socket)
1700 return false;
1701
1702 QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
1703 .arg(MYTH_PROTO_VERSION,
1704 QString::fromUtf8(MYTH_PROTO_TOKEN)));
1705 socket->WriteStringList(strlist);
1706
1707 if (!socket->ReadStringList(strlist, timeout) || strlist.empty())
1708 {
1709 LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
1710 "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
1711 "This happens when the backend is too busy to respond,\n\t\t\t"
1712 "or has deadlocked due to bugs or hardware failure.");
1713
1714 return false;
1715 }
1716 if (strlist[0] == "REJECT" && strlist.size() >= 2)
1717 {
1718 LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Protocol version or token mismatch "
1719 "(frontend=%1/%2,backend=%3/\?\?)\n")
1720 .arg(MYTH_PROTO_VERSION,
1721 QString::fromUtf8(MYTH_PROTO_TOKEN),
1722 strlist[1]));
1723
1724 if (error_dialog_desired && d->m_guiContext)
1725 {
1726 QStringList list(strlist[1]);
1727 QCoreApplication::postEvent(
1728 d->m_guiContext, new MythEvent("VERSION_MISMATCH", list));
1729 }
1730
1731 return false;
1732 }
1733 if (strlist[0] == "ACCEPT")
1734 {
1735 if (!d->m_announcedProtocol)
1736 {
1737 d->m_announcedProtocol = true;
1738 LOG(VB_GENERAL, LOG_INFO, LOC +
1739 QString("Using protocol version %1 %2")
1740 .arg(MYTH_PROTO_VERSION,
1741 QString::fromUtf8(MYTH_PROTO_TOKEN)));
1742 }
1743
1744 return true;
1745 }
1746
1747 LOG(VB_GENERAL, LOG_ERR, LOC +
1748 QString("Unexpected response to MYTH_PROTO_VERSION: %1")
1749 .arg(strlist[0]));
1750 return false;
1751}
1752
1754{
1755 LOG(VB_NETWORK, LOG_INFO, LOC + QString("MythEvent: %1").arg(event.Message()));
1756
1758}
1759
1761{
1762 QMutexLocker locker(&d->m_localHostLock);
1764 d->m_database->SetLocalHostname(hostname);
1765}
1766
1768{
1769 d->m_guiObject = gui;
1770}
1771
1773{
1774 return d->m_guiObject;
1775}
1776
1778{
1779 return d->m_guiObject;
1780}
1781
1783{
1784 return d->m_guiContext;
1785}
1786
1788{
1789 return d->m_database;
1790}
1791
1793{
1794 return d->m_locale;
1795}
1796
1802{
1803 return GetLanguageAndVariant().left(2);
1804}
1805
1814{
1815 if (d->m_language.isEmpty())
1816 d->m_language = GetSetting("Language", "en_US").toLower();
1817
1818 return d->m_language;
1819}
1820
1822{
1823 d->m_language.clear();
1824}
1825
1831{
1832 return GetAudioLanguageAndVariant().left(2);
1833}
1834
1843{
1844 if (d->m_audioLanguage.isEmpty())
1845 {
1846 auto menuLanguage = GetLanguageAndVariant();
1847 d->m_audioLanguage = GetSetting("AudioLanguage", menuLanguage).toLower();
1848
1849 LOG(VB_AUDIO, LOG_DEBUG, LOC + QString("audio language:%1 menu language:%2")
1850 .arg(d->m_audioLanguage, menuLanguage));
1851 }
1852 return d->m_audioLanguage;
1853}
1854
1856{
1857 d->m_audioLanguage.clear();
1858}
1859
1861{
1862 QMutexLocker locker(&d->m_sockLock);
1863 LOG(VB_GENERAL, LOG_INFO, "Restarting Backend Connections");
1864 if (d->m_serverSock)
1866 if (d->m_eventSock)
1868 dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1869}
1870
1872{
1873 if (Create && !d->m_power)
1874 {
1876 }
1877 else if (!Create && d->m_power)
1878 {
1880 d->m_power = nullptr;
1881 }
1882}
1883
1885{
1886 if (!d->m_locale)
1887 d->m_locale = new MythLocale();
1888
1889 QString localeCode = d->m_locale->GetLocaleCode();
1890 LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1891 .arg(localeCode));
1892 QLocale::setDefault(d->m_locale->ToQLocale());
1893}
1894
1896{
1897 if (!d->m_locale)
1898 d->m_locale = new MythLocale();
1899 else
1900 d->m_locale->ReInit();
1901
1902 QString localeCode = d->m_locale->GetLocaleCode();
1903 LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1904 .arg(localeCode));
1905 QLocale::setDefault(d->m_locale->ToQLocale());
1906}
1907
1909{
1910 if (!d->m_locale)
1911 InitLocale();
1912
1913 return d->m_locale->ToQLocale();
1914}
1915
1917{
1918 if (!d->m_locale)
1919 InitLocale();
1920
1921 if (!d->m_locale->GetLocaleCode().isEmpty())
1922 {
1923 LOG(VB_GENERAL, LOG_INFO,
1924 QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
1925
1927 return;
1928 }
1929
1930 LOG(VB_GENERAL, LOG_ERR, LOC +
1931 "No locale defined! We weren't able to set locale defaults.");
1932}
1933
1935{
1936 d->m_scheduler = sched;
1937}
1938
1940{
1941 return d->m_scheduler;
1942}
1943
1948void MythCoreContext::WaitUntilSignals(std::vector<CoreWaitInfo> & sigs) const
1949{
1950 if (sigs.empty())
1951 return;
1952
1953 QEventLoop eventLoop;
1954 for (const auto& s : sigs)
1955 {
1956 LOG(VB_GENERAL, LOG_DEBUG, LOC +
1957 QString("Waiting for signal %1")
1958 .arg(s.name));
1959 connect(this, s.fn, &eventLoop, &QEventLoop::quit);// clazy:exclude=connect-non-signal
1960 }
1961
1962 eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1963}
1964
1972{
1973 if (!sender || !method)
1974 return;
1975
1976 QMutexLocker lock(&d->m_playbackLock);
1977
1978 if (!d->m_playbackClients.contains(sender))
1979 {
1980 d->m_playbackClients.insert(sender, method);
1982 sender, method,
1983 Qt::BlockingQueuedConnection);
1984 }
1985}
1986
1993{
1994 QMutexLocker lock(&d->m_playbackLock);
1995
1996 if (d->m_playbackClients.contains(sender))
1997 {
1998 PlaybackStartCb method = d->m_playbackClients.value(sender);
2000 sender, method);
2001 d->m_playbackClients.remove(sender);
2002 }
2003}
2004
2012{
2013 QMutexLocker lock(&d->m_playbackLock);
2014 PlaybackStartCb method { nullptr };
2015 d->m_inwanting = true;
2016
2017 // If any registered client are in the same thread, they will deadlock, so rebuild
2018 // connections for any clients in the same thread as non-blocking connection
2019 QThread *currentThread = QThread::currentThread();
2020
2021 QMap<QObject *, PlaybackStartCb>::iterator it = d->m_playbackClients.begin();
2022 for (; it != d->m_playbackClients.end(); ++it)
2023 {
2024 if (it.key() == sender)
2025 continue; // will be done separately, no need to do it again
2026
2027 QThread *thread = it.key()->thread();
2028
2029 if (thread != currentThread)
2030 continue;
2031
2032 disconnect(this, &MythCoreContext::TVPlaybackAboutToStart, it.key(), it.value());
2033 connect(this, &MythCoreContext::TVPlaybackAboutToStart, it.key(), it.value());
2034 }
2035
2036 // disconnect sender so it won't receive the message
2037 if (d->m_playbackClients.contains(sender))
2038 {
2039 method = d->m_playbackClients.value(sender);
2040 disconnect(this, &MythCoreContext::TVPlaybackAboutToStart, sender, method);
2041 }
2042
2043 // emit signal
2045
2046 // reconnect sender
2047 if (method)
2048 {
2050 sender, method,
2051 Qt::BlockingQueuedConnection);
2052 }
2053 // Restore blocking connections
2054 it = d->m_playbackClients.begin();
2055 for (; it != d->m_playbackClients.end(); ++it)
2056 {
2057 if (it.key() == sender)
2058 continue; // already done above, no need to do it again
2059
2060 QThread *thread = it.key()->thread();
2061
2062 if (thread != currentThread)
2063 continue;
2064
2066 it.key(), it.value());
2068 it.key(), it.value(), Qt::BlockingQueuedConnection);
2069 }
2070 d->m_inwanting = false;
2071}
2072
2079{
2080 // when called, it will be while the m_playbackLock is held
2081 // following a call to WantingPlayback
2082 d->m_intvwanting = b;
2083}
2084
2091{
2092 bool locked = d->m_playbackLock.tryLock();
2093 bool intvplayback = d->m_intvwanting;
2094
2095 if (!locked && d->m_inwanting)
2096 return true; // we're in the middle of WantingPlayback
2097
2098 if (!locked)
2099 return false;
2100
2101 d->m_playbackLock.unlock();
2102
2103 return intvplayback;
2104}
2105
2107{
2108 if (!d->m_sessionManager)
2109 {
2111 if (!d->m_sessionManager)
2114 }
2115 return d->m_sessionManager;
2116}
2117
2118bool MythCoreContext::TestPluginVersion(const QString &name,
2119 const QString &libversion,
2120 const QString &pluginversion)
2121{
2122 if (libversion == pluginversion)
2123 return true;
2124
2125 LOG(VB_GENERAL, LOG_EMERG, LOC +
2126 QString("Plugin %1 (%2) binary version does not "
2127 "match libraries (%3)")
2128 .arg(name, pluginversion, libversion));
2129 return false;
2130}
2131
2133{
2134 if (d->m_pluginmanager == pmanager)
2135 return;
2136
2137 if (d->m_pluginmanager)
2138 {
2139 delete d->m_pluginmanager;
2140 d->m_pluginmanager = nullptr;
2141 }
2142
2143 d->m_pluginmanager = pmanager;
2144}
2145
2147{
2148 return d->m_pluginmanager;
2149}
2150
2152{
2153 d->m_isexiting = exiting;
2154}
2155
2157{
2158 return d->m_isexiting;
2159}
2160
2161void MythCoreContext::RegisterFileForWrite(const QString& file, uint64_t size)
2162{
2163 QMutexLocker lock(&d->m_fileslock);
2164
2165 QPair<int64_t, uint64_t> pair(QDateTime::currentMSecsSinceEpoch(), size);
2166 d->m_fileswritten.insert(file, pair);
2167
2168 if (IsBackend())
2169 {
2170 QString message = QString("FILE_WRITTEN %1 %2").arg(file).arg(size);
2171 MythEvent me(message);
2172 dispatch(me);
2173 }
2174
2175 LOG(VB_FILE, LOG_DEBUG, LOC +
2176 QString("%1").arg(file));
2177}
2178
2180{
2181 QMutexLocker lock(&d->m_fileslock);
2182
2183 d->m_fileswritten.remove(file);
2184
2185 if (IsBackend())
2186 {
2187 QString message = QString("FILE_CLOSED %1").arg(file);
2188 MythEvent me(message);
2189 dispatch(me);
2190 }
2191
2192 LOG(VB_FILE, LOG_DEBUG, LOC +
2193 QString("%1").arg(file));
2194}
2195
2197{
2198 QMutexLocker lock(&d->m_fileslock);
2199
2200 return d->m_fileswritten.contains(file);
2201}
2202
2203#include "moc_mythcorecontext.cpp"
DB connection pool, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:55
static void StopAllPools(void)
static MThreadPool * globalInstance(void)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
static void ShutdownAllPools(void)
static void Cleanup(void)
This will print out all the running threads, call exit(1) on each and then wait up to 5 seconds total...
Definition: mthread.cpp:126
static void ThreadSetup(const QString &name)
This is to be called on startup in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:205
MythSocket * m_eventSock
socket events arrive on
MythCoreContextPrivate(MythCoreContext *lparent, QString binversion, QObject *guicontext)
MythCoreContext * m_parent
QMutex m_sockLock
protects both m_serverSock and m_eventSock
MythSocket * m_serverSock
socket for sending MythProto requests
QMap< QObject *, MythCoreContext::PlaybackStartCb > m_playbackClients
QString m_masterHostname
master backend hostname
QString m_localHostname
hostname from config.xml or gethostname()
QWaitCondition m_wolInProgressWaitCondition
MythSessionManager * m_sessionManager
MythPluginManager * m_pluginmanager
bool WaitForWOL(std::chrono::milliseconds timeout=std::chrono::milliseconds::max())
If another thread has already started WOL process, wait on them...
QMutex m_masterHostLock
Locking for m_masterHostname.
MythScheduler * m_scheduler
QMutex m_scopesLock
Locking for m_masterHostname.
QMutex m_localHostLock
Locking for m_localHostname.
QMap< QString, QPair< int64_t, uint64_t > > m_fileswritten
QList< QHostAddress > m_deniedIps
QMap< QString, QString > m_scopes
Scope Id cache for Link-Local addresses.
QList< QHostAddress > m_approvedIps
This class contains the runtime context for MythTV.
void TVPlaybackAboutToStart(void)
bool IsFrontend(void) const
is this process a frontend process
void ResetAudioLanguage(void)
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
bool IsConnectedToMaster(void)
MythDB * GetDB(void)
void setTestIntSettings(QMap< QString, int > &overrides)
void ClearSettingsCache(const QString &myKey=QString(""))
bool SafeConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
QString resolveSettingAddress(const QString &name, const QString &host=QString(), ResolveType type=ResolveAny, bool keepscope=false)
Retrieve IP setting "name" for "host".
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
int GetNumSettingOnHost(const QString &key, const QString &host, int defaultval=0)
QMap< QString, QString > m_testOverrideStrings
void readyRead(MythSocket *sock) override
QMap< QString, double > m_testOverrideFloats
void ActivateSettingsCache(bool activate=true)
bool IsLocalSubnet(const QHostAddress &peer, bool log)
Check if peer is on local subnet.
void SetWOLAllowed(bool allow)
void SendHostSystemEvent(const QString &msg, const QString &hostname, const QString &args)
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
QString GetHostName(void)
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
MythCoreContextPrivate * d
static bool BackendIsRunning(void)
a backend process is running on this host
bool GetScopeForAddress(QHostAddress &addr) const
Return the cached scope Id for the given address.
void ResetLanguage(void)
void ClearOverrideSettingForSession(const QString &key)
static QHash< QString, int > s_serverPortCache
void TVInWantingPlayback(bool b)
Let the TV class tell us if we was interrupted following a call to WantingPlayback().
void connectionClosed(MythSocket *sock) override
QString GetBackendServerIP4(void)
Returns the IPv4 address defined for the current host see GetBackendServerIP4(host)
void SaveSetting(const QString &key, int newValue)
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, std::chrono::milliseconds setup_timeout=-1ms)
void setTestFloatSettings(QMap< QString, double > &overrides)
void SetLocalHostname(const QString &hostname)
void SetExiting(bool exiting=true)
static QString resolveAddress(const QString &host, ResolveType type=ResolveAny, bool keepscope=false)
if host is an IP address, it will be returned or resolved otherwise.
void SetAsBackend(bool backend)
QLocale GetQLocale(void)
void RegisterForPlayback(QObject *sender, PlaybackStartCb method)
Register sender for TVPlaybackAboutToStart signal.
bool IsThisHost(const QString &addr)
is this address mapped to this host
MythScheduler * GetScheduler(void)
MythCoreContext(const QString &binversion, QObject *guiContext)
QString GetMasterHostPrefix(const QString &storageGroup=QString(), const QString &path=QString())
MythSessionManager * GetSessionManager(void)
QString GetSetting(const QString &key, const QString &defaultval="")
void SendSystemEvent(const QString &msg)
QString GetAudioLanguageAndVariant(void)
Returns the user-set audio language and variant.
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
bool IsRegisteredFileForWrite(const QString &file)
bool CheckProtoVersion(MythSocket *socket, std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
QString GetAudioLanguage(void)
Returns two character ISO-639 language descriptor for audio language.
QObject * GetGUIContext(void)
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.
void setTestStringSettings(QMap< QString, QString > &overrides)
MythPluginManager * GetPluginManager(void)
bool InWantingPlayback(void)
Returns true if a client has requested playback.
int GetBackendServerPort(void)
Returns the locally defined backend control port.
void OverrideSettingForSession(const QString &key, const QString &value)
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
QObject * GetGUIObject(void)
bool IsFrontendOnly(void)
is there a frontend, but no backend, running on this host
void BlockShutdown(void)
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
int GetBackendStatusPort(void)
Returns the locally defined backend status port.
bool IsBackend(void) const
is this process a backend process
double GetFloatSetting(const QString &key, double defaultval=0.0)
bool IsThisBackend(const QString &addr)
is this address mapped to this backend host
int GetMasterServerStatusPort(void)
Returns the Master Backend status port If no master server status port has been defined in the databa...
~MythCoreContext() override
QString GetBackendServerIP6(void)
Returns the IPv6 address defined for the current host see GetBackendServerIP6(host)
void SetScheduler(MythScheduler *sched)
QString GetFilePrefix(void)
void dispatch(const MythEvent &event)
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
void SendMessage(const QString &message)
bool SetupCommandSocket(MythSocket *serverSock, const QString &announcement, std::chrono::milliseconds timeout, bool &proto_mismatch)
bool IsMasterHost(void)
is this the same host as the master
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetLanguage(void)
Returns two character ISO-639 language descriptor for UI language.
void SetPluginManager(MythPluginManager *pmanager)
bool IsWOLAllowed() const
void SetGUIObject(QObject *gui)
void SaveLocaleDefaults(void)
int GetNumSetting(const QString &key, int defaultval=0)
static bool TestPluginVersion(const QString &name, const QString &libversion, const QString &pluginversion)
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
void(QObject::*)(void) PlaybackStartCb
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval=0.0)
void SetAsFrontend(bool frontend)
QString GetMasterHostName(void)
QMap< QString, int > m_testOverrideInts
bool IsMasterBackend(void)
is this the actual MBE process
MythSocket * ConnectEventSocket(const QString &hostname, int port)
static void ClearBackendServerPortCache()
void UnregisterFileForWrite(const QString &file)
bool IsBlockingClient(void) const
is this client blocking shutdown
MDBManager * GetDBManager(void)
void AllowShutdown(void)
void InitPower(bool Create=true)
bool GetBoolSettingOnHost(const QString &key, const QString &host, bool defaultval=false)
void WaitUntilSignals(std::vector< CoreWaitInfo > &sigs) const
Wait until any of the provided signals have been received.
void SendEvent(const MythEvent &event)
QString GetLanguageAndVariant(void)
Returns the user-set language and variant.
bool GetBoolSetting(const QString &key, bool defaultval=false)
MythLocale * GetLocale(void) const
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
bool HasGUI(void) const
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
const QStringList & ExtraDataList() const
Definition: mythevent.h:67
void ReInit()
Definition: mythlocale.cpp:54
QLocale ToQLocale() const
Definition: mythlocale.h:29
QString GetLocaleCode() const
Name of language in that language.
Definition: mythlocale.h:27
void SaveLocaleDefaults(bool overwrite=false)
Definition: mythlocale.cpp:173
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
static MythPower * AcquireRelease(void *Reference, bool Acquire, std::chrono::seconds MinimumDelay=0s)
Definition: mythpower.cpp:77
This is an generic interface to a program scheduler.
Definition: mythscheduler.h:18
We use digest authentication because it protects the password over unprotected networks.
Definition: mythsession.h:106
static void UnlockSessions()
static void LockSessions()
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
static constexpr std::chrono::milliseconds kLongTimeout
Definition: mythsocket.h:71
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, std::chrono::milliseconds timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:330
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:317
bool IsConnected(void) const
Definition: mythsocket.cpp:555
bool IsDataAvailable(void)
Definition: mythsocket.cpp:561
static constexpr std::chrono::milliseconds kShortTimeout
Definition: mythsocket.h:70
void DisconnectFromHost(void)
Definition: mythsocket.cpp:502
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:305
bool ConnectToHost(const QString &hostname, quint16 port)
connect to host
Definition: mythsocket.cpp:378
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
SendAsyncMessage(QString msg)
void run(void) override
SendAsyncMessage(QString msg, QStringList extra)
unsigned int uint
Definition: compat.h:60
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ quit
Definition: lirc_client.h:34
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:721
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:683
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:40
static constexpr const char * MYTH_APPNAME_MYTHTV_SETUP
Definition: mythappname.h:7
static void delete_sock(QMutexLocker< QMutex > &locker, MythSocket **s)
#define LOC
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythDB(void)
Definition: mythdb.cpp:50
void DestroyMythDB(void)
Definition: mythdb.cpp:55
void ShutdownMythDownloadManager(void)
Deletes the running MythDownloadManager at program exit.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool MythWakeup(const QString &wakeUpCommand, uint flags, std::chrono::seconds timeout)
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:36
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:39
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
void MBASE_PUBLIC ShutdownMythSystemLegacy(void)
dictionary info
Definition: azlyrics.py:7
string hostname
Definition: caa.py:17
None log(str msg, int level=LOGDEBUG)
Definition: xbmc.py:9
static QPair< QHostAddress, int > kLinkLocal
Definition: serverpool.cpp:27
Scheduler * sched