MythTV master
mythcorecontext.cpp
Go to the documentation of this file.
1#include "mythcorecontext.h"
2
3// Qt
4#include <QtGlobal>
5#include <QCoreApplication>
6#include <QUrl>
7#include <QDir>
8#include <QFileInfo>
9#include <QDebug>
10#include <QMutex>
11#include <QRunnable>
12#include <QWaitCondition>
13#include <QAbstractSocket>
14#include <QHostAddress>
15#include <QHostInfo>
16#include <QNetworkInterface>
17#include <QNetworkAddressEntry>
18#include <QLocale>
19#include <QPair>
20#include <QRegularExpression>
21#include <QDateTime>
22
23// Std
24#include <algorithm>
25#include <cmath>
26#include <cstdarg>
27#include <queue>
28#include <unistd.h> // for usleep()
29
30#ifdef _WIN32
31#include <winsock2.h>
32#else
33#include <clocale>
34#include <utility>
35#endif
36
37// MythTV
38#include "compat.h"
39#include "mythappname.h"
40#include "mythdownloadmanager.h"
41#include "mythsocket.h"
42#include "mythsystemlegacy.h"
43#include "mthreadpool.h"
44#include "exitcodes.h"
45#include "mythlogging.h"
46#include "mythversion.h"
47#include "logging.h"
48#include "mthread.h"
49#include "serverpool.h"
50#include "mythdate.h"
51#include "mythplugin.h"
52#include "mythmiscutil.h"
53#include "mythpower.h"
54
55#define LOC QString("MythCoreContext::%1(): ").arg(__func__)
56
58
59class MythCoreContextPrivate : public QObject
60{
61 public:
62 MythCoreContextPrivate(MythCoreContext *lparent, QString binversion,
63 QObject *guicontext);
64 ~MythCoreContextPrivate() override;
65
66 bool WaitForWOL(std::chrono::milliseconds timeout = std::chrono::milliseconds::max());
67
68 public:
70 QObject *m_guiContext { nullptr };
71 QObject *m_guiObject { nullptr };
73
78 QMutex m_scopesLock;
79 QMap<QString, QString> m_scopes;
80
81 QMutex m_sockLock;
82 MythSocket *m_serverSock { nullptr };
83 MythSocket *m_eventSock { nullptr };
84
87 bool m_wolInProgress { false };
88 bool m_isWOLAllowed { true };
89
90 bool m_backend { false };
91 bool m_frontend { false };
92
93 MythDB *m_database { nullptr };
94
95 QThread *m_uiThread { nullptr };
96
97 MythLocale *m_locale { nullptr };
98 QString m_language;
100
102
103 bool m_blockingClient { true };
104
105 QMap<QObject *, MythCoreContext::PlaybackStartCb> m_playbackClients;
107 bool m_inwanting { false };
108 bool m_intvwanting { false };
109
110 bool m_announcedProtocol { false };
111
113
114 bool m_isexiting { false };
115
116 QMap<QString, QPair<int64_t, uint64_t> > m_fileswritten;
118
120
121 QList<QHostAddress> m_approvedIps;
122 QList<QHostAddress> m_deniedIps;
124
125 MythPower *m_power { nullptr };
126};
127
129 QString binversion,
130 QObject *guicontext)
131 : m_parent(lparent),
132 m_guiContext(guicontext),
133 m_appBinaryVersion(std::move(binversion)),
134 m_database(GetMythDB()),
135 m_uiThread(QThread::currentThread())
136{
137 MThread::ThreadSetup("CoreContext");
138}
139
140static void delete_sock(
141#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
142 QMutexLocker &locker,
143#else
144 QMutexLocker<QMutex> &locker,
145#endif
146 MythSocket **s)
147{
148 if (*s)
149 {
150 MythSocket *tmp = *s;
151 *s = nullptr;
152 locker.unlock();
153 tmp->DecrRef();
154 locker.relock();
155 }
156}
157
159{
160 if (m_power)
161 MythPower::AcquireRelease(this, false);
162
164
165 {
166 QMutexLocker locker(&m_sockLock);
167 delete_sock(locker, &m_serverSock);
168 delete_sock(locker, &m_eventSock);
169 }
170
171 delete m_locale;
172
173 delete m_sessionManager;
174
176
178
180
181 // This has already been run in the MythContext dtor. Do we need it here
182 // too?
183#if 0
184 logStop(); // need to shutdown db logger before we kill db
185#endif
186
188
189 GetMythDB()->GetDBManager()->CloseDatabases();
190
191 if (m_database) {
193 m_database = nullptr;
194 }
195
197}
198
202bool MythCoreContextPrivate::WaitForWOL(std::chrono::milliseconds timeout)
203{
204 std::chrono::milliseconds timeout_remaining = timeout;
205 while (m_wolInProgress && (timeout_remaining > 0ms))
206 {
207 LOG(VB_GENERAL, LOG_INFO, LOC + "Wake-On-LAN in progress, waiting...");
208
209 std::chrono::milliseconds max_wait = std::min(1000ms, timeout_remaining);
210 m_wolInProgressWaitCondition.wait(&m_wolInProgressLock, max_wait.count());
211 timeout_remaining -= max_wait;
212 }
213
214 return !m_wolInProgress;
215}
216
217MythCoreContext::MythCoreContext(const QString &binversion,
218 QObject *guiContext)
219{
220 d = new MythCoreContextPrivate(this, binversion, guiContext);
221}
222
224{
225 if (!d)
226 {
227 LOG(VB_GENERAL, LOG_EMERG, LOC + "Out-of-memory");
228 return false;
229 }
230
231 if (d->m_appBinaryVersion != MYTH_BINARY_VERSION)
232 {
233 LOG(VB_GENERAL, LOG_CRIT,
234 QString("Application binary version (%1) does not "
235 "match libraries (%2)")
236 .arg(d->m_appBinaryVersion, MYTH_BINARY_VERSION));
237
238 QString warning = tr("This application is not compatible with the "
239 "installed MythTV libraries. Please recompile "
240 "after a make distclean");
241 LOG(VB_GENERAL, LOG_WARNING, warning);
242
243 return false;
244 }
245
246#ifndef _WIN32
247 static const QRegularExpression utf8
248 { "utf-?8", QRegularExpression::CaseInsensitiveOption };
249 QString lang_variables("");
250 QString lc_value = setlocale(LC_CTYPE, nullptr);
251 if (lc_value.isEmpty())
252 {
253 // try fallback to environment variables for non-glibc systems
254 // LC_ALL, then LC_CTYPE
255 lc_value = qEnvironmentVariable("LC_ALL");
256 if (lc_value.isEmpty())
257 lc_value = qEnvironmentVariable("LC_CTYPE");
258 }
259 if (!lc_value.contains(utf8))
260 lang_variables.append("LC_ALL or LC_CTYPE");
261 lc_value = qEnvironmentVariable("LANG");
262 if (!lc_value.contains(utf8))
263 {
264 if (!lang_variables.isEmpty())
265 lang_variables.append(", and ");
266 lang_variables.append("LANG");
267 }
268 LOG(VB_GENERAL, LOG_INFO, QString("Assumed character encoding: %1")
269 .arg(lc_value));
270 if (!lang_variables.isEmpty())
271 {
272 LOG(VB_GENERAL, LOG_WARNING, QString("This application expects to "
273 "be running a locale that specifies a UTF-8 codeset, and many "
274 "features may behave improperly with your current language "
275 "settings. Please set the %1 variable(s) in the environment "
276 "in which this program is executed to include a UTF-8 codeset "
277 "(such as 'en_US.UTF-8').").arg(lang_variables));
278 }
279#endif
280
281 return true;
282}
283
285{
286 delete d;
287 d = nullptr;
288}
289
290void MythCoreContext::setTestIntSettings(QMap<QString,int> &overrides)
291{
292 m_testOverrideInts = std::move(overrides);
293}
294
295void MythCoreContext::setTestFloatSettings(QMap<QString,double> &overrides)
296{
297 m_testOverrideFloats = std::move(overrides);
298}
299
300void MythCoreContext::setTestStringSettings(QMap<QString,QString> &overrides)
301{
302 m_testOverrideStrings = std::move(overrides);
303}
304
306 const QString &announcement,
307 [[maybe_unused]] std::chrono::milliseconds timeout,
308 bool &proto_mismatch)
309{
310 proto_mismatch = false;
311
312#ifndef IGNORE_PROTO_VER_MISMATCH
313 if (!CheckProtoVersion(serverSock, timeout, true))
314 {
315 proto_mismatch = true;
316 return false;
317 }
318#endif
319
320 QStringList strlist(announcement);
321
322 if (!serverSock->WriteStringList(strlist))
323 {
324 LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
325 "master backend, socket write failed");
326 return false;
327 }
328
329 if (!serverSock->ReadStringList(strlist, MythSocket::kShortTimeout) ||
330 strlist.empty() || (strlist[0] == "ERROR"))
331 {
332 if (!strlist.empty())
333 {
334 LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
335 "server socket to master backend");
336 }
337 else
338 {
339 LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
340 "server socket to master backend");
341 }
342 return false;
343 }
344
345 return true;
346}
347
348// Connects to master server safely (i.e. by taking m_sockLock)
350 bool openEventSocket)
351{
352 QMutexLocker locker(&d->m_sockLock);
353 bool success = ConnectToMasterServer(blockingClient, openEventSocket);
354
355 return success;
356}
357
358// Assumes that either m_sockLock is held, or the app is still single
359// threaded (i.e. during startup).
361 bool openEventSocket)
362{
363 if (IsMasterBackend())
364 {
365 // Should never get here unless there is a bug in the code somewhere.
366 // If this happens, it can cause endless event loops.
367 LOG(VB_GENERAL, LOG_ERR, LOC + "ERROR: Master backend tried to connect back "
368 "to itself!");
369 return false;
370 }
371 if (IsExiting())
372 return false;
373
374 QString server = GetMasterServerIP();
375 if (server.isEmpty())
376 return false;
377
378 int port = GetMasterServerPort();
379 bool proto_mismatch = false;
380
382 {
384 d->m_serverSock = nullptr;
385 }
386
387 if (!d->m_serverSock)
388 {
389 QString type { "Monitor" };
390 if (IsFrontend())
391 type = "Frontend";
392 else if (blockingClient)
393 type = "Playback";
394 QString ann = QString("ANN %1 %2 %3")
395 .arg(type, d->m_localHostname, QString::number(static_cast<int>(false)));
397 server, port, ann, &proto_mismatch);
398 }
399
400 if (!d->m_serverSock)
401 return false;
402
403 d->m_blockingClient = blockingClient;
404
405 if (!openEventSocket)
406 return true;
407
408
409 if (!IsBackend())
410 {
412 {
414 d->m_eventSock = nullptr;
415 }
416 if (!d->m_eventSock)
417 d->m_eventSock = ConnectEventSocket(server, port);
418
419 if (!d->m_eventSock)
420 {
422 d->m_serverSock = nullptr;
423
424 QCoreApplication::postEvent(
425 d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
426
427 return false;
428 }
429 }
430
431 return true;
432}
433
435 const QString &hostname, int port, const QString &announce,
436 bool *p_proto_mismatch, int maxConnTry, std::chrono::milliseconds setup_timeout)
437{
438 MythSocket *serverSock = nullptr;
439
440 {
441 QMutexLocker locker(&d->m_wolInProgressLock);
442 d->WaitForWOL();
443 }
444
445 QString WOLcmd;
446 if (IsWOLAllowed())
447 WOLcmd = GetSetting("WOLbackendCommand", "");
448
449 if (maxConnTry < 1)
450 maxConnTry = std::max(GetNumSetting("BackendConnectRetry", 1), 1);
451
452 std::chrono::seconds WOLsleepTime = 0s;
453 int WOLmaxConnTry = 0;
454 if (!WOLcmd.isEmpty())
455 {
456 WOLsleepTime = GetDurSetting<std::chrono::seconds>("WOLbackendReconnectWaitTime", 0s);
457 WOLmaxConnTry = std::max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
458 maxConnTry = std::max(maxConnTry, WOLmaxConnTry);
459 }
460
461 bool we_attempted_wol = false;
462
463 if (setup_timeout <= 0ms)
464 setup_timeout = MythSocket::kShortTimeout;
465
466 bool proto_mismatch = false;
467 for (int cnt = 1; cnt <= maxConnTry; cnt++)
468 {
469 LOG(VB_GENERAL, LOG_INFO, LOC +
470 QString("Connecting to backend server: %1:%2 (try %3 of %4)")
471 .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
472
473 serverSock = new MythSocket();
474
475 std::chrono::microseconds sleepus = 0us;
476 if (serverSock->ConnectToHost(hostname, port))
477 {
479 serverSock, announce, setup_timeout, proto_mismatch))
480 {
481 break;
482 }
483
484 if (proto_mismatch)
485 {
486 if (p_proto_mismatch)
487 *p_proto_mismatch = true;
488
489 serverSock->DecrRef();
490 serverSock = nullptr;
491 break;
492 }
493
494 setup_timeout += setup_timeout / 2;
495 }
496 else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
497 {
498 if (!we_attempted_wol)
499 {
500 QMutexLocker locker(&d->m_wolInProgressLock);
501 if (d->m_wolInProgress)
502 {
503 d->WaitForWOL();
504 continue;
505 }
506
507 d->m_wolInProgress = we_attempted_wol = true;
508 }
509
512 sleepus = WOLsleepTime;
513 }
514
515 serverSock->DecrRef();
516 serverSock = nullptr;
517
518 if (cnt == 1)
519 {
520 QCoreApplication::postEvent(
521 d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
522 }
523
524 if (sleepus != 0us)
525 usleep(sleepus.count());
526 }
527
528 if (we_attempted_wol)
529 {
530 QMutexLocker locker(&d->m_wolInProgressLock);
531 d->m_wolInProgress = false;
533 }
534
535 if (!serverSock && !proto_mismatch)
536 {
537 LOG(VB_GENERAL, LOG_ERR,
538 "Connection to master server timed out.\n\t\t\t"
539 "Either the server is down or the master server settings"
540 "\n\t\t\t"
541 "in mythtv-settings does not contain the proper IP address\n");
542 }
543 else
544 {
545 QCoreApplication::postEvent(
546 d->m_guiContext, new MythEvent("CONNECTION_RESTABLISHED"));
547 }
548
549 return serverSock;
550}
551
553 int port)
554{
555 auto *eventSock = new MythSocket(-1, this);
556
557 // Assume that since we _just_ connected the command socket,
558 // this one won't need multiple retries to work...
559 if (!eventSock->ConnectToHost(hostname, port))
560 {
561 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
562 "socket to master backend");
563 eventSock->DecrRef();
564 return nullptr;
565 }
566
567 QString str = QString("ANN Monitor %1 %2")
568 .arg(d->m_localHostname).arg(static_cast<int>(true));
569 QStringList strlist(str);
570 eventSock->WriteStringList(strlist);
571 bool ok = true;
572 if (!eventSock->ReadStringList(strlist) || strlist.empty() ||
573 (strlist[0] == "ERROR"))
574 {
575 if (!strlist.empty())
576 {
577 LOG(VB_GENERAL, LOG_ERR, LOC +
578 "Problem connecting event socket to master backend");
579 }
580 else
581 {
582 LOG(VB_GENERAL, LOG_ERR, LOC +
583 "Timeout connecting event socket to master backend");
584 }
585 ok = false;
586 }
587
588 if (!ok)
589 {
590 eventSock->DecrRef();
591 eventSock = nullptr;
592 }
593
594 return eventSock;
595}
596
598{
599 QMutexLocker locker(&d->m_sockLock);
600 return d->m_serverSock;
601}
602
604{
605 QStringList strlist;
606
607 QMutexLocker locker(&d->m_sockLock);
608 if (d->m_serverSock == nullptr)
609 return;
610
611 strlist << "BLOCK_SHUTDOWN";
613
614 d->m_blockingClient = true;
615}
616
618{
619 QStringList strlist;
620
621 QMutexLocker locker(&d->m_sockLock);
622 if (d->m_serverSock == nullptr)
623 return;
624
625 strlist << "ALLOW_SHUTDOWN";
627
628 d->m_blockingClient = false;
629}
630
632{
633 return d->m_blockingClient;
634}
635
637{
638 d->m_isWOLAllowed = allow;
639}
640
642{
643 return d->m_isWOLAllowed;
644}
645
647{
648 d->m_backend = backend;
649}
650
652{
653 return d->m_backend;
654}
655
657{
658 d->m_frontend = frontend;
659}
660
662{
663 return d->m_frontend;
664}
665
667{
668 QString host = GetHostName();
669 return IsMasterHost(host);
670}
671
672bool MythCoreContext::IsMasterHost(const QString &host)
673{
674 // Temporary code here only to facilitate the upgrade
675 // from 1346 or earlier. The way of determining master host is
676 // changing, and the new way of determning master host
677 // will not work with earlier databases.
678 // This code can be removed when updating from prior to
679 // 1347 is no longer allowed.
680 // Note that we are deprecating some settings including
681 // MasterServerIP, and can remove them at a future time.
682 if (GetNumSetting("DBSchemaVer") < 1347)
683 {
684 // Temporary copy of code from old version of
685 // IsThisHost(Qstring&,QString&)
686 QString addr(resolveSettingAddress("MasterServerIP"));
687 if (addr.toLower() == host.toLower())
688 return true;
689 QHostAddress addrfix(addr);
690 addrfix.setScopeId(QString());
691 QString addrstr = addrfix.toString();
692 if (addrfix.isNull())
693 addrstr = resolveAddress(addr);
694 QString thisip = GetBackendServerIP4(host);
695 QString thisip6 = GetBackendServerIP6(host);
696 return !addrstr.isEmpty()
697 && ((addrstr == thisip) || (addrstr == thisip6));
698 }
699 return GetSetting("MasterServerName") == host;
700}
701
703{
704 return (IsBackend() && IsMasterHost());
705}
706
708{
709#if defined(Q_OS_DARWIN) || defined(__FreeBSD__) || defined(__OpenBSD__)
710 const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
711#elif defined _WIN32
712 const char *command = "%systemroot%\\system32\\tasklist.exe "
713 " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
714#else
715 const char *command = "ps ch -C mythbackend -o pid > /dev/null";
716#endif
717 uint res = myth_system(command, kMSDontBlockInputDevs |
720 return (res == GENERIC_EXIT_OK);
721}
722
723bool MythCoreContext::IsThisBackend(const QString &addr)
724{
725 return IsBackend() && IsThisHost(addr);
726}
727
728bool MythCoreContext::IsThisHost(const QString &addr)
729{
730 return IsThisHost(addr, GetHostName());
731}
732
733bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
734{
735 if (addr.toLower() == host.toLower())
736 return true;
737
738 QHostAddress addrfix(addr);
739 addrfix.setScopeId(QString());
740 QString addrstr = addrfix.toString();
741
742 if (addrfix.isNull())
743 {
744 addrstr = resolveAddress(addr);
745 }
746
747 QString thisip = GetBackendServerIP(host);
748
749 return !addrstr.isEmpty() && ((addrstr == thisip));
750}
751
753{
754 // find out if a backend runs on this host...
755 bool backendOnLocalhost = false;
756
757 QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
758 strlist << GetHostName();
759
760 SendReceiveStringList(strlist);
761
762 backendOnLocalhost = strlist[0] != "FALSE";
763
764 return !backendOnLocalhost;
765}
766
767QString MythCoreContext::GenMythURL(const QString& host, int port, QString path, const QString& storageGroup)
768{
769 QUrl ret;
770
771 QString m_host;
772
773 QHostAddress addr(host);
774 if (!addr.isNull())
775 {
776 LOG(VB_GENERAL, LOG_CRIT, LOC + QString("(%1/%2): Given "
777 "IP address instead of hostname "
778 "(ID). This is invalid.").arg(host, path));
779 }
780
781 m_host = host;
782
783 // Basically if it appears to be an IPv6 IP surround the IP with [] otherwise don't bother
784 if (!addr.isNull() && addr.protocol() == QAbstractSocket::IPv6Protocol)
785 m_host = "[" + addr.toString().toLower() + "]";
786
787 ret.setScheme("myth");
788 if (!storageGroup.isEmpty())
789 ret.setUserName(storageGroup);
790 ret.setHost(m_host);
791 if (port > 0 && port != 6543)
792 ret.setPort(port);
793 if (!path.startsWith("/"))
794 path = QString("/") + path;
795 ret.setPath(path);
796
797#if 0
798 LOG(VB_GENERAL, LOG_DEBUG, LOC +
799 QString("GenMythURL returning %1").arg(ret.toString()));
800#endif
801
802 return ret.toString();
803}
804
805QString MythCoreContext::GetMasterHostPrefix(const QString &storageGroup,
806 const QString &path)
807{
810 path,
811 storageGroup);
812}
813
815{
816 QMutexLocker locker(&d->m_masterHostLock);
817
818 if (d->m_masterHostname.isEmpty())
819 {
820
821 if (IsMasterBackend())
823 else
824 {
825 QStringList strlist("QUERY_HOSTNAME");
826
827 if (SendReceiveStringList(strlist))
828 d->m_masterHostname = strlist[0];
829 }
830 }
831
832 return d->m_masterHostname;
833}
834
835void MythCoreContext::ClearSettingsCache(const QString &myKey)
836{
837 d->m_database->ClearSettingsCache(myKey);
838}
839
841{
842 d->m_database->ActivateSettingsCache(activate);
843}
844
846{
847 QMutexLocker locker(&d->m_localHostLock);
848 return d->m_localHostname;
849}
850
852{
853 return GetSetting("RecordFilePrefix");
854}
855
857 int &width, int &height,
858 double &forced_aspect,
859 double &refresh_rate,
860 int index)
861{
862 d->m_database->GetResolutionSetting(type, width, height, forced_aspect,
863 refresh_rate, index);
864}
865
866void MythCoreContext::GetResolutionSetting(const QString &t, int &w,
867 int &h, int i)
868{
869 d->m_database->GetResolutionSetting(t, w, h, i);
870}
871
873{
874 return d->m_database->GetDBManager();
875}
876
884{
885 return d->m_database->IsDatabaseIgnored();
886}
887
888void MythCoreContext::SaveSetting(const QString &key, int newValue)
889{
890 d->m_database->SaveSetting(key, newValue);
891}
892
893void MythCoreContext::SaveSetting(const QString &key, const QString &newValue)
894{
895 d->m_database->SaveSetting(key, newValue);
896}
897
899 const QString &newValue,
900 const QString &host)
901{
902 return d->m_database->SaveSettingOnHost(key, newValue, host);
903}
904
905QString MythCoreContext::GetSetting(const QString &key,
906 const QString &defaultval)
907{
908 if (!m_testOverrideStrings.empty())
909 return m_testOverrideStrings[key];
910 return d->m_database->GetSetting(key, defaultval);
911}
912
913bool MythCoreContext::GetBoolSetting(const QString &key, bool defaultval)
914{
915 int result = GetNumSetting(key, static_cast<int>(defaultval));
916 return result > 0;
917}
918
919int MythCoreContext::GetNumSetting(const QString &key, int defaultval)
920{
921 if (!m_testOverrideInts.empty())
922 return m_testOverrideInts[key];
923 return d->m_database->GetNumSetting(key, defaultval);
924}
925
926double MythCoreContext::GetFloatSetting(const QString &key, double defaultval)
927{
928 if (!m_testOverrideFloats.empty())
929 return m_testOverrideFloats[key];
930 return d->m_database->GetFloatSetting(key, defaultval);
931}
932
933QString MythCoreContext::GetSettingOnHost(const QString &key,
934 const QString &host,
935 const QString &defaultval)
936{
937 if (!m_testOverrideStrings.empty())
938 return m_testOverrideStrings[key];
939 return d->m_database->GetSettingOnHost(key, host, defaultval);
940}
941
943 const QString &host,
944 bool defaultval)
945{
946 int result = GetNumSettingOnHost(key, host, static_cast<int>(defaultval));
947 return result > 0;
948}
949
951 const QString &host,
952 int defaultval)
953{
954 if (!m_testOverrideInts.empty())
955 return m_testOverrideInts[key];
956 return d->m_database->GetNumSettingOnHost(key, host, defaultval);
957}
958
960 const QString &host,
961 double defaultval)
962{
963 if (!m_testOverrideFloats.empty())
964 return m_testOverrideFloats[key];
965 return d->m_database->GetFloatSettingOnHost(key, host, defaultval);
966}
967
974{
975 QString masterserver = gCoreContext->GetSetting("MasterServerName");
976 QString masterip = resolveSettingAddress("BackendServerAddr",masterserver);
977 // Even if empty, return it here if we were to assume that localhost
978 // should be used it just causes a lot of unnecessary error messages.
979 return masterip;
980}
981
988{
989 QString masterserver = gCoreContext->GetSetting
990 ("MasterServerName");
992 ("BackendServerPort", masterserver, 6543);
993}
994
1001{
1002 QString masterhost = GetMasterHostName();
1003
1004 return GetBackendStatusPort(masterhost);
1005}
1006
1012{
1014}
1015
1025QString MythCoreContext::GetBackendServerIP(const QString &host)
1026{
1027 return resolveSettingAddress("BackendServerAddr",host);
1028}
1029
1035{
1037}
1038
1044QString MythCoreContext::GetBackendServerIP4(const QString &host)
1045{
1046 return resolveSettingAddress("BackendServerIP", host, ResolveIPv4);
1047}
1048
1054{
1056}
1057
1063QString MythCoreContext::GetBackendServerIP6(const QString &host)
1064{
1065 return resolveSettingAddress("BackendServerIP6", host, ResolveIPv6);
1066}
1067
1072{
1074}
1075
1076QHash<QString,int> MythCoreContext::s_serverPortCache;
1077
1079{
1080 s_serverPortCache.clear();
1081}
1082
1087{
1088 int port = s_serverPortCache.value(host, -1);
1089 if (port != -1)
1090 return port;
1091 port = GetNumSettingOnHost("BackendServerPort", host, 6543);
1092 s_serverPortCache[host] = port;
1093 return port;
1094}
1095
1100{
1102}
1103
1108{
1109 return GetNumSettingOnHost("BackendStatusPort", host, 6544);
1110}
1111
1116bool MythCoreContext::GetScopeForAddress(QHostAddress &addr) const
1117{
1118 QHostAddress addr1 = addr;
1119 addr1.setScopeId(QString());
1120 QString addrstr = addr1.toString();
1121 QMutexLocker lock(&d->m_scopesLock);
1122
1123 if (!d->m_scopes.contains(addrstr))
1124 return false;
1125
1126 addr.setScopeId(d->m_scopes[addrstr]);
1127 return true;
1128}
1129
1136void MythCoreContext::SetScopeForAddress(const QHostAddress &addr)
1137{
1138 QHostAddress addr1 = addr;
1139 addr1.setScopeId(QString());
1140 QMutexLocker lock(&d->m_scopesLock);
1141
1142 d->m_scopes.insert(addr1.toString(), addr.scopeId());
1143}
1144
1150void MythCoreContext::SetScopeForAddress(const QHostAddress &addr, int scope)
1151{
1152 QHostAddress addr1 = addr;
1153 addr1.setScopeId(QString());
1154 QMutexLocker lock(&d->m_scopesLock);
1155
1156 d->m_scopes.insert(addr1.toString(), QString::number(scope));
1157}
1158
1170QString MythCoreContext::resolveSettingAddress(const QString &name,
1171 const QString &host,
1172 ResolveType type, bool keepscope)
1173{
1174 QString value;
1175
1176 if (host.isEmpty())
1177 {
1178 value = GetSetting(name);
1179 }
1180 else
1181 {
1182 value = GetSettingOnHost(name, host);
1183 }
1184
1185 return resolveAddress(value, type, keepscope);
1186}
1187
1200 bool keepscope)
1201{
1202 QHostAddress addr(host);
1203
1204 if (!host.isEmpty() && addr.isNull())
1205 {
1206 // address is likely a hostname, attempts to resolve it
1207 QHostInfo info = QHostInfo::fromName(host);
1208 QList<QHostAddress> list = info.addresses();
1209
1210 if (list.isEmpty())
1211 {
1212 LOG(VB_GENERAL, LOG_WARNING, LOC +
1213 QString("Can't resolve hostname:'%1', using localhost").arg(host));
1214 return type == ResolveIPv4 ? "127.0.0.1" : "::1";
1215 }
1216 QHostAddress v4;
1217 QHostAddress v6;
1218
1219 // Return the first address fitting the type critera
1220 for (const auto& item : std::as_const(list))
1221 {
1222 addr = item;
1223 QAbstractSocket::NetworkLayerProtocol prot = addr.protocol();
1224
1225 if (prot == QAbstractSocket::IPv4Protocol)
1226 {
1227 v4 = addr;
1228 if (type == 0)
1229 break;
1230 }
1231 else if (prot == QAbstractSocket::IPv6Protocol)
1232 {
1233 v6 = addr;
1234 if (type != 0)
1235 break;
1236 }
1237 }
1238 switch (type)
1239 {
1240 case ResolveIPv4:
1241 addr = v4.isNull() ? QHostAddress::LocalHost : v4;
1242 break;
1243 case ResolveIPv6:
1244 addr = v6.isNull() ? QHostAddress::LocalHostIPv6 : v6;
1245 break;
1246 default:
1247 if (!v6.isNull())
1248 addr = v6;
1249 else if (!v4.isNull())
1250 addr = v4;
1251 else
1252 addr = QHostAddress::LocalHostIPv6;
1253 break;
1254 }
1255 }
1256 else if (host.isEmpty())
1257 {
1258 return {};
1259 }
1260
1261 if (!keepscope)
1262 {
1263 addr.setScopeId(QString());
1264 }
1265 return addr.toString();
1266}
1267
1279bool MythCoreContext::CheckSubnet(const QAbstractSocket *socket)
1280{
1281 QHostAddress peer = socket->peerAddress();
1282 return CheckSubnet(peer);
1283}
1284
1296bool MythCoreContext::CheckSubnet(const QHostAddress &peer)
1297{
1298 if (GetBoolSetting("AllowConnFromAll",false))
1299 return true;
1300 return IsLocalSubnet(peer, true);
1301}
1302
1311bool MythCoreContext::IsLocalSubnet(const QHostAddress &peer, bool log)
1312{
1313 static const QHostAddress kLinkLocal("fe80::");
1314 // Ensure m_approvedIps and m_deniedIps are single threaded
1315 QMutexLocker lock(&d->m_listMutex);
1316 if (d->m_approvedIps.contains(peer))
1317 return true;
1318 if (d->m_deniedIps.contains(peer))
1319 {
1320 if (log)
1321 LOG(VB_GENERAL, LOG_WARNING, LOC +
1322 QString("Repeat denied connection from ip address: %1")
1323 .arg(peer.toString()));
1324 return false;
1325 }
1326
1327 // allow all link-local
1328 if (peer.isInSubnet(kLinkLocal,10))
1329 {
1330 d->m_approvedIps.append(peer);
1331 return true;
1332 }
1333
1334 // loop through all available interfaces
1335 QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
1336 for (const auto & qni : std::as_const(IFs))
1337 {
1338 if ((qni.flags() & QNetworkInterface::IsRunning) == 0)
1339 continue;
1340
1341 QList<QNetworkAddressEntry> IPs = qni.addressEntries();
1342 for (const auto & qnai : std::as_const(IPs))
1343 {
1344 int pfxlen = qnai.prefixLength();
1345 // Set this to test rejection without having an extra
1346 // network.
1347 if (GetBoolSetting("DebugSubnet"))
1348 pfxlen += 4;
1349 if (peer.isInSubnet(qnai.ip(),pfxlen))
1350 {
1351 d->m_approvedIps.append(peer);
1352 return true;
1353 }
1354 }
1355 }
1356 d->m_deniedIps.append(peer);
1357 if (log)
1358 LOG(VB_GENERAL, LOG_WARNING, LOC +
1359 QString("Denied connection from ip address: %1")
1360 .arg(peer.toString()));
1361 return false;
1362}
1363
1364
1366 const QString &value)
1367{
1368 d->m_database->OverrideSettingForSession(key, value);
1369}
1370
1372{
1373 d->m_database->ClearOverrideSettingForSession(key);
1374}
1375
1377{
1379}
1380
1400 QStringList &strlist, bool quickTimeout, bool block)
1401{
1402 QString msg;
1403 if (HasGUI() && IsUIThread())
1404 {
1405 msg = "SendReceiveStringList(";
1406 for (uint i=0; i<(uint)strlist.size() && i<2; i++)
1407 msg += (i?",":"") + strlist[i];
1408 msg += (strlist.size() > 2) ? "...)" : ")";
1409 LOG(VB_GENERAL, LOG_DEBUG, LOC + msg + " called from UI thread");
1410 }
1411
1412 QString query_type = "UNKNOWN";
1413
1414 if (!strlist.isEmpty())
1415 query_type = strlist[0];
1416
1417 QMutexLocker locker(&d->m_sockLock);
1418 if (!d->m_serverSock)
1419 {
1420 bool blockingClient = d->m_blockingClient &&
1421 (GetNumSetting("idleTimeoutSecs",0) > 0);
1422 ConnectToMasterServer(blockingClient);
1423 }
1424
1425 bool ok = false;
1426
1427 if (d->m_serverSock)
1428 {
1429 std::chrono::milliseconds timeout = quickTimeout ?
1431 ok = d->m_serverSock->SendReceiveStringList(strlist, 0, timeout);
1432
1433 if (!ok)
1434 {
1435 LOG(VB_GENERAL, LOG_NOTICE, LOC +
1436 QString("Connection to backend server lost"));
1438 d->m_serverSock = nullptr;
1439
1440 if (d->m_eventSock)
1441 {
1442 d->m_eventSock->DecrRef();
1443 d->m_eventSock = nullptr;
1444 }
1445
1446 if (block)
1447 {
1449
1450 if (d->m_serverSock)
1451 {
1453 strlist, 0, timeout);
1454 }
1455 }
1456 }
1457
1458 // this should not happen
1459 while (ok && strlist[0] == "BACKEND_MESSAGE")
1460 {
1461 // oops, not for us
1462 LOG(VB_GENERAL, LOG_EMERG, LOC + "SRSL you shouldn't see this!!");
1463 QString message = strlist[1];
1464 strlist.pop_front(); strlist.pop_front();
1465
1466 MythEvent me(message, strlist);
1467 dispatch(me);
1468
1469 ok = d->m_serverSock->ReadStringList(strlist, timeout);
1470 }
1471
1472 if (!ok)
1473 {
1474 if (d->m_serverSock)
1475 {
1477 d->m_serverSock = nullptr;
1478 }
1479
1480 LOG(VB_GENERAL, LOG_CRIT, LOC +
1481 QString("Reconnection to backend server failed"));
1482
1483 QCoreApplication::postEvent(d->m_guiContext,
1484 new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
1485 }
1486 }
1487
1488 if (ok)
1489 {
1490 if (strlist.isEmpty())
1491 ok = false;
1492 else if (strlist[0] == "ERROR")
1493 {
1494 if (strlist.size() == 2)
1495 {
1496 LOG(VB_GENERAL, LOG_INFO, LOC +
1497 QString("Protocol query '%1' responded with the error '%2'")
1498 .arg(query_type, strlist[1]));
1499 }
1500 else
1501 {
1502 LOG(VB_GENERAL, LOG_INFO, LOC +
1503 QString("Protocol query '%1' responded with an error, but "
1504 "no error message.") .arg(query_type));
1505 }
1506
1507 ok = false;
1508 }
1509 else if (strlist[0] == "UNKNOWN_COMMAND")
1510 {
1511 LOG(VB_GENERAL, LOG_ERR, LOC +
1512 QString("Protocol query '%1' responded with the error 'UNKNOWN_COMMAND'")
1513 .arg(query_type));
1514
1515 ok = false;
1516 }
1517 }
1518
1519 return ok;
1520}
1521
1522class SendAsyncMessage : public QRunnable
1523{
1524 public:
1525 SendAsyncMessage(QString msg, QStringList extra) :
1526 m_message(std::move(msg)), m_extraData(std::move(extra))
1527 {
1528 }
1529
1530 explicit SendAsyncMessage(QString msg) : m_message(std::move(msg)) { }
1531
1532 void run(void) override // QRunnable
1533 {
1534 QStringList strlist("MESSAGE");
1535 strlist << m_message;
1536 strlist << m_extraData;
1538 }
1539
1540 private:
1541 QString m_message;
1542 QStringList m_extraData;
1543};
1544
1545void MythCoreContext::SendMessage(const QString &message)
1546{
1547 if (IsBackend())
1548 {
1549 dispatch(MythEvent(message));
1550 }
1551 else
1552 {
1554 new SendAsyncMessage(message), "SendMessage");
1555 }
1556}
1557
1559{
1560 if (IsBackend())
1561 {
1562 dispatch(event);
1563 }
1564 else
1565 {
1567 new SendAsyncMessage(event.Message(), event.ExtraDataList()),
1568 "SendEvent");
1569 }
1570}
1571
1572void MythCoreContext::SendSystemEvent(const QString &msg)
1573{
1574 if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1575 return;
1576
1577 SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
1578 .arg(msg, GetHostName()));
1579}
1580
1582 const QString &hostname,
1583 const QString &args)
1584{
1585 SendSystemEvent(QString("%1 HOST %2 %3").arg(msg, hostname, args));
1586}
1587
1588
1590{
1591 while (sock->IsDataAvailable())
1592 {
1593 QStringList strlist;
1594 if (!sock->ReadStringList(strlist))
1595 continue;
1596
1597 if (strlist.size() < 2)
1598 continue;
1599
1600 QString prefix = strlist[0];
1601 QString message = strlist[1];
1602 QStringList tokens = message.split(" ", Qt::SkipEmptyParts);
1603
1604 if (prefix == "OK")
1605 {
1606 }
1607 else if (prefix != "BACKEND_MESSAGE")
1608 {
1609 LOG(VB_NETWORK, LOG_ERR, LOC +
1610 QString("Received a: %1 message from the backend "
1611 "but I don't know what to do with it.")
1612 .arg(prefix));
1613 }
1614 else if (message == "CLEAR_SETTINGS_CACHE")
1615 {
1616 // No need to dispatch this message to ourself, so handle it
1617 LOG(VB_NETWORK, LOG_INFO, LOC + "Received remote 'Clear Cache' request");
1619 }
1620 else if (message.startsWith("FILE_WRITTEN"))
1621 {
1622 QString file;
1623 uint64_t size = 0;
1624 int NUMTOKENS = 3; // Number of tokens expected
1625
1626 if (tokens.size() == NUMTOKENS)
1627 {
1628 file = tokens[1];
1629 size = tokens[2].toULongLong();
1630 }
1631 else
1632 {
1633 LOG(VB_NETWORK, LOG_ERR, LOC +
1634 QString("FILE_WRITTEN event received "
1635 "with invalid number of arguments, "
1636 "%1 expected, %2 actual")
1637 .arg(NUMTOKENS-1)
1638 .arg(tokens.size()-1));
1639 return;
1640 }
1641 // No need to dispatch this message to ourself, so handle it
1642 LOG(VB_NETWORK, LOG_INFO, LOC +
1643 QString("Received remote 'FILE_WRITTEN %1' request").arg(file));
1645 }
1646 else if (message.startsWith("FILE_CLOSED"))
1647 {
1648 QString file;
1649 int NUMTOKENS = 2; // Number of tokens expected
1650
1651 if (tokens.size() == NUMTOKENS)
1652 {
1653 file = tokens[1];
1654 }
1655 else
1656 {
1657 LOG(VB_NETWORK, LOG_ERR, LOC +
1658 QString("FILE_CLOSED event received "
1659 "with invalid number of arguments, "
1660 "%1 expected, %2 actual")
1661 .arg(NUMTOKENS-1)
1662 .arg(tokens.size()-1));
1663 return;
1664 }
1665 // No need to dispatch this message to ourself, so handle it
1666 LOG(VB_NETWORK, LOG_INFO, LOC +
1667 QString("Received remote 'FILE_CLOSED %1' request").arg(file));
1669 }
1670 else
1671 {
1672 strlist.pop_front();
1673 strlist.pop_front();
1674 MythEvent me(message, strlist);
1675 dispatch(me);
1676 }
1677 }
1678}
1679
1681{
1682 LOG(VB_GENERAL, LOG_NOTICE, LOC +
1683 "Event socket closed. No connection to the backend.");
1684
1685 dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1686}
1687
1689 std::chrono::milliseconds timeout,
1690 bool error_dialog_desired)
1691{
1692 if (!socket)
1693 return false;
1694
1695 QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
1696 .arg(MYTH_PROTO_VERSION,
1697 QString::fromUtf8(MYTH_PROTO_TOKEN)));
1698 socket->WriteStringList(strlist);
1699
1700 if (!socket->ReadStringList(strlist, timeout) || strlist.empty())
1701 {
1702 LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
1703 "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
1704 "This happens when the backend is too busy to respond,\n\t\t\t"
1705 "or has deadlocked due to bugs or hardware failure.");
1706
1707 return false;
1708 }
1709 if (strlist[0] == "REJECT" && strlist.size() >= 2)
1710 {
1711 LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Protocol version or token mismatch "
1712 "(frontend=%1/%2,backend=%3/\?\?)\n")
1713 .arg(MYTH_PROTO_VERSION,
1714 QString::fromUtf8(MYTH_PROTO_TOKEN),
1715 strlist[1]));
1716
1717 if (error_dialog_desired && d->m_guiContext)
1718 {
1719 QStringList list(strlist[1]);
1720 QCoreApplication::postEvent(
1721 d->m_guiContext, new MythEvent("VERSION_MISMATCH", list));
1722 }
1723
1724 return false;
1725 }
1726 if (strlist[0] == "ACCEPT")
1727 {
1728 if (!d->m_announcedProtocol)
1729 {
1730 d->m_announcedProtocol = true;
1731 LOG(VB_GENERAL, LOG_INFO, LOC +
1732 QString("Using protocol version %1 %2")
1733 .arg(MYTH_PROTO_VERSION,
1734 QString::fromUtf8(MYTH_PROTO_TOKEN)));
1735 }
1736
1737 return true;
1738 }
1739
1740 LOG(VB_GENERAL, LOG_ERR, LOC +
1741 QString("Unexpected response to MYTH_PROTO_VERSION: %1")
1742 .arg(strlist[0]));
1743 return false;
1744}
1745
1747{
1748 LOG(VB_NETWORK, LOG_INFO, LOC + QString("MythEvent: %1").arg(event.Message()));
1749
1751}
1752
1754{
1755 QMutexLocker locker(&d->m_localHostLock);
1757 d->m_database->SetLocalHostname(hostname);
1758}
1759
1761{
1762 d->m_guiObject = gui;
1763}
1764
1766{
1767 return d->m_guiObject;
1768}
1769
1771{
1772 return d->m_guiObject;
1773}
1774
1776{
1777 return d->m_guiContext;
1778}
1779
1781{
1782 return d->m_database;
1783}
1784
1786{
1787 return d->m_locale;
1788}
1789
1795{
1796 return GetLanguageAndVariant().left(2);
1797}
1798
1807{
1808 if (d->m_language.isEmpty())
1809 d->m_language = GetSetting("Language", "en_US").toLower();
1810
1811 return d->m_language;
1812}
1813
1815{
1816 d->m_language.clear();
1817}
1818
1824{
1825 return GetAudioLanguageAndVariant().left(2);
1826}
1827
1836{
1837 if (d->m_audioLanguage.isEmpty())
1838 {
1839 auto menuLanguage = GetLanguageAndVariant();
1840 d->m_audioLanguage = GetSetting("AudioLanguage", menuLanguage).toLower();
1841
1842 LOG(VB_AUDIO, LOG_DEBUG, LOC + QString("audio language:%1 menu language:%2")
1843 .arg(d->m_audioLanguage, menuLanguage));
1844 }
1845 return d->m_audioLanguage;
1846}
1847
1849{
1850 d->m_audioLanguage.clear();
1851}
1852
1854{
1855 QMutexLocker locker(&d->m_sockLock);
1856 LOG(VB_GENERAL, LOG_INFO, "Restarting Backend Connections");
1857 if (d->m_serverSock)
1859 if (d->m_eventSock)
1861 dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1862}
1863
1865{
1866 if (Create && !d->m_power)
1867 {
1869 }
1870 else if (!Create && d->m_power)
1871 {
1873 d->m_power = nullptr;
1874 }
1875}
1876
1878{
1879 if (!d->m_locale)
1880 d->m_locale = new MythLocale();
1881
1882 QString localeCode = d->m_locale->GetLocaleCode();
1883 LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1884 .arg(localeCode));
1885 QLocale::setDefault(d->m_locale->ToQLocale());
1886}
1887
1889{
1890 if (!d->m_locale)
1891 d->m_locale = new MythLocale();
1892 else
1893 d->m_locale->ReInit();
1894
1895 QString localeCode = d->m_locale->GetLocaleCode();
1896 LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1897 .arg(localeCode));
1898 QLocale::setDefault(d->m_locale->ToQLocale());
1899}
1900
1902{
1903 if (!d->m_locale)
1904 InitLocale();
1905
1906 return d->m_locale->ToQLocale();
1907}
1908
1910{
1911 if (!d->m_locale)
1912 InitLocale();
1913
1914 if (!d->m_locale->GetLocaleCode().isEmpty())
1915 {
1916 LOG(VB_GENERAL, LOG_INFO,
1917 QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
1918
1920 return;
1921 }
1922
1923 LOG(VB_GENERAL, LOG_ERR, LOC +
1924 "No locale defined! We weren't able to set locale defaults.");
1925}
1926
1928{
1929 d->m_scheduler = sched;
1930}
1931
1933{
1934 return d->m_scheduler;
1935}
1936
1941void MythCoreContext::WaitUntilSignals(std::vector<CoreWaitInfo> & sigs) const
1942{
1943 if (sigs.empty())
1944 return;
1945
1946 QEventLoop eventLoop;
1947 for (const auto& s : sigs)
1948 {
1949 LOG(VB_GENERAL, LOG_DEBUG, LOC +
1950 QString("Waiting for signal %1")
1951 .arg(s.name));
1952 connect(this, s.fn, &eventLoop, &QEventLoop::quit);// clazy:exclude=connect-non-signal
1953 }
1954
1955 eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1956}
1957
1965{
1966 if (!sender || !method)
1967 return;
1968
1969 QMutexLocker lock(&d->m_playbackLock);
1970
1971 if (!d->m_playbackClients.contains(sender))
1972 {
1973 d->m_playbackClients.insert(sender, method);
1975 sender, method,
1976 Qt::BlockingQueuedConnection);
1977 }
1978}
1979
1986{
1987 QMutexLocker lock(&d->m_playbackLock);
1988
1989 if (d->m_playbackClients.contains(sender))
1990 {
1991 PlaybackStartCb method = d->m_playbackClients.value(sender);
1993 sender, method);
1994 d->m_playbackClients.remove(sender);
1995 }
1996}
1997
2005{
2006 QMutexLocker lock(&d->m_playbackLock);
2007 PlaybackStartCb method { nullptr };
2008 d->m_inwanting = true;
2009
2010 // If any registered client are in the same thread, they will deadlock, so rebuild
2011 // connections for any clients in the same thread as non-blocking connection
2012 QThread *currentThread = QThread::currentThread();
2013
2014 QMap<QObject *, PlaybackStartCb>::iterator it = d->m_playbackClients.begin();
2015 for (; it != d->m_playbackClients.end(); ++it)
2016 {
2017 if (it.key() == sender)
2018 continue; // will be done separately, no need to do it again
2019
2020 QThread *thread = it.key()->thread();
2021
2022 if (thread != currentThread)
2023 continue;
2024
2025 disconnect(this, &MythCoreContext::TVPlaybackAboutToStart, it.key(), it.value());
2026 connect(this, &MythCoreContext::TVPlaybackAboutToStart, it.key(), it.value());
2027 }
2028
2029 // disconnect sender so it won't receive the message
2030 if (d->m_playbackClients.contains(sender))
2031 {
2032 method = d->m_playbackClients.value(sender);
2033 disconnect(this, &MythCoreContext::TVPlaybackAboutToStart, sender, method);
2034 }
2035
2036 // emit signal
2038
2039 // reconnect sender
2040 if (method)
2041 {
2043 sender, method,
2044 Qt::BlockingQueuedConnection);
2045 }
2046 // Restore blocking connections
2047 for (; it != d->m_playbackClients.end(); ++it)
2048 {
2049 if (it.key() == sender)
2050 continue; // already done above, no need to do it again
2051
2052 QThread *thread = it.key()->thread();
2053
2054 if (thread != currentThread)
2055 continue;
2056
2058 it.key(), it.value());
2060 it.key(), it.value(), Qt::BlockingQueuedConnection);
2061 }
2062 d->m_inwanting = false;
2063}
2064
2071{
2072 // when called, it will be while the m_playbackLock is held
2073 // following a call to WantingPlayback
2074 d->m_intvwanting = b;
2075}
2076
2083{
2084 bool locked = d->m_playbackLock.tryLock();
2085 bool intvplayback = d->m_intvwanting;
2086
2087 if (!locked && d->m_inwanting)
2088 return true; // we're in the middle of WantingPlayback
2089
2090 if (!locked)
2091 return false;
2092
2093 d->m_playbackLock.unlock();
2094
2095 return intvplayback;
2096}
2097
2099{
2100 if (!d->m_sessionManager)
2101 {
2103 if (!d->m_sessionManager)
2106 }
2107 return d->m_sessionManager;
2108}
2109
2110bool MythCoreContext::TestPluginVersion(const QString &name,
2111 const QString &libversion,
2112 const QString &pluginversion)
2113{
2114 if (libversion == pluginversion)
2115 return true;
2116
2117 LOG(VB_GENERAL, LOG_EMERG, LOC +
2118 QString("Plugin %1 (%2) binary version does not "
2119 "match libraries (%3)")
2120 .arg(name, pluginversion, libversion));
2121 return false;
2122}
2123
2125{
2126 if (d->m_pluginmanager == pmanager)
2127 return;
2128
2129 if (d->m_pluginmanager)
2130 {
2131 delete d->m_pluginmanager;
2132 d->m_pluginmanager = nullptr;
2133 }
2134
2135 d->m_pluginmanager = pmanager;
2136}
2137
2139{
2140 return d->m_pluginmanager;
2141}
2142
2144{
2145 d->m_isexiting = exiting;
2146}
2147
2149{
2150 return d->m_isexiting;
2151}
2152
2153void MythCoreContext::RegisterFileForWrite(const QString& file, uint64_t size)
2154{
2155 QMutexLocker lock(&d->m_fileslock);
2156
2157 QPair<int64_t, uint64_t> pair(QDateTime::currentMSecsSinceEpoch(), size);
2158 d->m_fileswritten.insert(file, pair);
2159
2160 if (IsBackend())
2161 {
2162 QString message = QString("FILE_WRITTEN %1 %2").arg(file).arg(size);
2163 MythEvent me(message);
2164 dispatch(me);
2165 }
2166
2167 LOG(VB_FILE, LOG_DEBUG, LOC +
2168 QString("%1").arg(file));
2169}
2170
2172{
2173 QMutexLocker lock(&d->m_fileslock);
2174
2175 d->m_fileswritten.remove(file);
2176
2177 if (IsBackend())
2178 {
2179 QString message = QString("FILE_CLOSED %1").arg(file);
2180 MythEvent me(message);
2181 dispatch(me);
2182 }
2183
2184 LOG(VB_FILE, LOG_DEBUG, LOC +
2185 QString("%1").arg(file));
2186}
2187
2189{
2190 QMutexLocker lock(&d->m_fileslock);
2191
2192 return d->m_fileswritten.contains(file);
2193}
2194
2195/* vim: set expandtab tabstop=4 shiftwidth=4: */
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:142
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:221
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:74
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:326
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:313
bool IsConnected(void) const
Definition: mythsocket.cpp:551
bool IsDataAvailable(void)
Definition: mythsocket.cpp:557
static constexpr std::chrono::milliseconds kShortTimeout
Definition: mythsocket.h:70
void DisconnectFromHost(void)
Definition: mythsocket.cpp:498
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:301
bool ConnectToHost(const QString &hostname, quint16 port)
connect to host
Definition: mythsocket.cpp:374
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
SendAsyncMessage(QString msg)
void run(void) override
SendAsyncMessage(QString msg, QStringList extra)
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
unsigned int uint
Definition: freesurround.h:24
static guint32 * tmp
Definition: goom_core.cpp:26
@ quit
Definition: lirc_client.h:30
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:723
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:685
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:51
void DestroyMythDB(void)
Definition: mythdb.cpp:56
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
STL namespace.
None log(str msg, int level=LOGDEBUG)
Definition: xbmc.py:9
static QPair< QHostAddress, int > kLinkLocal
Definition: serverpool.cpp:27
Scheduler * sched