MythTV  master
mythcorecontext.cpp
Go to the documentation of this file.
1 #include <QCoreApplication>
2 #include <QUrl>
3 #include <QDir>
4 #include <QFileInfo>
5 #include <QDebug>
6 #include <QMutex>
7 #include <QRunnable>
8 #include <QWaitCondition>
9 #include <QNetworkInterface>
10 #include <QAbstractSocket>
11 #include <QHostAddress>
12 #include <QHostInfo>
13 #include <QNetworkInterface>
14 #include <QNetworkAddressEntry>
15 #include <QLocale>
16 #include <QPair>
17 #include <QDateTime>
18 #include <QRunnable>
19 
20 #include <algorithm>
21 #include <cmath>
22 #include <cstdarg>
23 #include <queue>
24 #include <unistd.h> // for usleep()
25 using namespace std;
26 
27 #ifdef _WIN32
28 #include <winsock2.h>
29 #else
30 #include <clocale>
31 #include <utility>
32 #endif
33 
34 #include "compat.h"
35 #include "mythconfig.h" // for CONFIG_DARWIN
36 #include "mythdownloadmanager.h"
37 #include "mythcorecontext.h"
38 #include "mythsocket.h"
39 #include "mythsystemlegacy.h"
40 #include "mthreadpool.h"
41 #include "exitcodes.h"
42 #include "mythlogging.h"
43 #include "mythversion.h"
44 #include "logging.h"
45 #include "mthread.h"
46 #include "serverpool.h"
47 #include "mythdate.h"
48 #include "mythplugin.h"
49 #include "mythmiscutil.h"
50 
51 #define LOC QString("MythCoreContext::%1(): ").arg(__func__)
52 
54 QMutex *avcodeclock = new QMutex(QMutex::Recursive);
55 
56 class MythCoreContextPrivate : public QObject
57 {
58  public:
59  MythCoreContextPrivate(MythCoreContext *lparent, QString binversion,
60  QObject *guicontext);
61  ~MythCoreContextPrivate() override;
62 
63  bool WaitForWOL(int timeout_in_ms = INT_MAX);
64 
65  public:
67  QObject *m_GUIcontext;
68  QObject *m_GUIobject;
70 
71  QMutex m_localHostLock;
72  QString m_localHostname;
74  QString m_masterHostname;
75  QMutex m_scopesLock;
76  QMap<QString, QString> m_scopes;
77 
78  QMutex m_sockLock;
81 
86 
87  bool m_backend;
88  bool m_frontend;
89 
90  MythDB *m_database;
91 
92  QThread *m_UIThread;
93 
95  QString m_language;
96 
98 
100 
101  QMap<QObject *, QByteArray> m_playbackClients;
105 
107 
109 
111 
112  QMap<QString, QPair<int64_t, uint64_t> > m_fileswritten;
113  QMutex m_fileslock;
114 
116 
117  QList<QHostAddress> m_approvedIps;
118  QList<QHostAddress> m_deniedIps;
119 };
120 
122  QString binversion,
123  QObject *guicontext)
124  : m_parent(lparent),
125  m_GUIcontext(guicontext), m_GUIobject(nullptr),
126  m_appBinaryVersion(std::move(binversion)),
127  m_sockLock(QMutex::NonRecursive),
128  m_serverSock(nullptr), m_eventSock(nullptr),
129  m_WOLInProgress(false),
130  m_IsWOLAllowed(true),
131  m_backend(false),
132  m_frontend(false),
133  m_database(GetMythDB()),
134  m_UIThread(QThread::currentThread()),
135  m_locale(nullptr),
136  m_scheduler(nullptr),
137  m_blockingClient(true),
138  m_inwanting(false),
139  m_intvwanting(false),
140  m_announcedProtocol(false),
141  m_pluginmanager(nullptr),
142  m_isexiting(false),
143  m_sessionManager(nullptr)
144 {
145  MThread::ThreadSetup("CoreContext");
146 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
147  srandom(MythDate::current().toTime_t() ^ QTime::currentTime().msec());
148 #elif QT_VERSION < QT_VERSION_CHECK(5,10,0)
149  srandom(MythDate::current().toSecsSinceEpoch() ^ QTime::currentTime().msec());
150 #endif
151 }
152 
153 static void delete_sock(QMutexLocker &locker, MythSocket **s)
154 {
155  if (*s)
156  {
157  MythSocket *tmp = *s;
158  *s = nullptr;
159  locker.unlock();
160  tmp->DecrRef();
161  locker.relock();
162  }
163 }
164 
166 {
168 
169  {
170  QMutexLocker locker(&m_sockLock);
171  delete_sock(locker, &m_serverSock);
172  delete_sock(locker, &m_eventSock);
173  }
174 
175  delete m_locale;
176 
177  delete m_sessionManager;
178 
180 
182 
184 
185  // This has already been run in the MythContext dtor. Do we need it here
186  // too?
187 #if 0
188  logStop(); // need to shutdown db logger before we kill db
189 #endif
190 
192 
193  GetMythDB()->GetDBManager()->CloseDatabases();
194 
195  if (m_database) {
196  DestroyMythDB();
197  m_database = nullptr;
198  }
199 
201 }
202 
206 bool MythCoreContextPrivate::WaitForWOL(int timeout_in_ms)
207 {
208  int timeout_remaining = timeout_in_ms;
209  while (m_WOLInProgress && (timeout_remaining > 0))
210  {
211  LOG(VB_GENERAL, LOG_INFO, LOC + "Wake-On-LAN in progress, waiting...");
212 
213  int max_wait = min(1000, timeout_remaining);
215  &m_WOLInProgressLock, max_wait);
216  timeout_remaining -= max_wait;
217  }
218 
219  return !m_WOLInProgress;
220 }
221 
222 MythCoreContext::MythCoreContext(const QString &binversion,
223  QObject *guiContext)
224  : d(nullptr)
225 {
226  d = new MythCoreContextPrivate(this, binversion, guiContext);
227 }
228 
230 {
231  if (!d)
232  {
233  LOG(VB_GENERAL, LOG_EMERG, LOC + "Out-of-memory");
234  return false;
235  }
236 
238  {
239  LOG(VB_GENERAL, LOG_CRIT,
240  QString("Application binary version (%1) does not "
241  "match libraries (%2)")
243 
244  QString warning = tr("This application is not compatible with the "
245  "installed MythTV libraries. Please recompile "
246  "after a make distclean");
247  LOG(VB_GENERAL, LOG_WARNING, warning);
248 
249  return false;
250  }
251 
252 #ifndef _WIN32
253  QString lang_variables("");
254  QString lc_value = setlocale(LC_CTYPE, nullptr);
255  if (lc_value.isEmpty())
256  {
257  // try fallback to environment variables for non-glibc systems
258  // LC_ALL, then LC_CTYPE
259  lc_value = getenv("LC_ALL");
260  if (lc_value.isEmpty())
261  lc_value = getenv("LC_CTYPE");
262  }
263  if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
264  lang_variables.append("LC_ALL or LC_CTYPE");
265  lc_value = getenv("LANG");
266  if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
267  {
268  if (!lang_variables.isEmpty())
269  lang_variables.append(", and ");
270  lang_variables.append("LANG");
271  }
272  LOG(VB_GENERAL, LOG_INFO, QString("Assumed character encoding: %1")
273  .arg(lc_value));
274  if (!lang_variables.isEmpty())
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 #endif
282 
283  return true;
284 }
285 
287 {
288  delete d;
289  d = nullptr;
290 }
291 
293  const QString &announcement,
294  uint timeout_in_ms,
295  bool &proto_mismatch)
296 {
297  proto_mismatch = false;
298 
299 #ifndef IGNORE_PROTO_VER_MISMATCH
300  if (!CheckProtoVersion(serverSock, timeout_in_ms, true))
301  {
302  proto_mismatch = true;
303  return false;
304  }
305 #else
306  Q_UNUSED(timeout_in_ms);
307 #endif
308 
309  QStringList strlist(announcement);
310 
311  if (!serverSock->WriteStringList(strlist))
312  {
313  LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
314  "master backend, socket write failed");
315  return false;
316  }
317 
318  if (!serverSock->ReadStringList(strlist, MythSocket::kShortTimeout) ||
319  strlist.empty() || (strlist[0] == "ERROR"))
320  {
321  if (!strlist.empty())
322  LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
323  "server socket to master backend");
324  else
325  LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
326  "server socket to master backend");
327  return false;
328  }
329 
330  return true;
331 }
332 
333 // Connects to master server safely (i.e. by taking m_sockLock)
335  bool openEventSocket)
336 {
337  QMutexLocker locker(&d->m_sockLock);
338  bool success = ConnectToMasterServer(blockingClient, openEventSocket);
339 
340  return success;
341 }
342 
343 // Assumes that either m_sockLock is held, or the app is still single
344 // threaded (i.e. during startup).
345 bool MythCoreContext::ConnectToMasterServer(bool blockingClient,
346  bool openEventSocket)
347 {
348  if (IsMasterBackend())
349  {
350  // Should never get here unless there is a bug in the code somewhere.
351  // If this happens, it can cause endless event loops.
352  LOG(VB_GENERAL, LOG_ERR, LOC + "ERROR: Master backend tried to connect back "
353  "to itself!");
354  return false;
355  }
356  if (IsExiting())
357  return false;
358 
359  QString server = GetMasterServerIP();
360  if (server.isEmpty())
361  return false;
362 
363  int port = GetMasterServerPort();
364  bool proto_mismatch = false;
365 
366  if (d->m_serverSock && !d->m_serverSock->IsConnected())
367  {
368  d->m_serverSock->DecrRef();
369  d->m_serverSock = nullptr;
370  }
371 
372  if (!d->m_serverSock)
373  {
374  QString type = IsFrontend() ? "Frontend" : (blockingClient ? "Playback" : "Monitor");
375  QString ann = QString("ANN %1 %2 %3")
376  .arg(type)
377  .arg(d->m_localHostname).arg(false);
379  server, port, ann, &proto_mismatch);
380  }
381 
382  if (!d->m_serverSock)
383  return false;
384 
385  d->m_blockingClient = blockingClient;
386 
387  if (!openEventSocket)
388  return true;
389 
390 
391  if (!IsBackend())
392  {
393  if (d->m_eventSock && !d->m_eventSock->IsConnected())
394  {
395  d->m_eventSock->DecrRef();
396  d->m_eventSock = nullptr;
397  }
398  if (!d->m_eventSock)
399  d->m_eventSock = ConnectEventSocket(server, port);
400 
401  if (!d->m_eventSock)
402  {
403  d->m_serverSock->DecrRef();
404  d->m_serverSock = nullptr;
405 
406  QCoreApplication::postEvent(
407  d->m_GUIcontext, new MythEvent("CONNECTION_FAILURE"));
408 
409  return false;
410  }
411  }
412 
413  return true;
414 }
415 
417  const QString &hostname, int port, const QString &announce,
418  bool *p_proto_mismatch, int maxConnTry, int setup_timeout)
419 {
420  MythSocket *serverSock = nullptr;
421 
422  {
423  QMutexLocker locker(&d->m_WOLInProgressLock);
424  d->WaitForWOL();
425  }
426 
427  QString WOLcmd;
428  if (IsWOLAllowed())
429  WOLcmd = GetSetting("WOLbackendCommand", "");
430 
431  if (maxConnTry < 1)
432  maxConnTry = max(GetNumSetting("BackendConnectRetry", 1), 1);
433 
434  int WOLsleepTime = 0, WOLmaxConnTry = 0;
435  if (!WOLcmd.isEmpty())
436  {
437  WOLsleepTime = GetNumSetting("WOLbackendReconnectWaitTime", 0);
438  WOLmaxConnTry = max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
439  maxConnTry = max(maxConnTry, WOLmaxConnTry);
440  }
441 
442  bool we_attempted_wol = false;
443 
444  if (setup_timeout <= 0)
445  setup_timeout = MythSocket::kShortTimeout;
446 
447  bool proto_mismatch = false;
448  for (int cnt = 1; cnt <= maxConnTry; cnt++)
449  {
450  LOG(VB_GENERAL, LOG_INFO, LOC +
451  QString("Connecting to backend server: %1:%2 (try %3 of %4)")
452  .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
453 
454  serverSock = new MythSocket();
455 
456  int sleepms = 0;
457  if (serverSock->ConnectToHost(hostname, port))
458  {
459  if (SetupCommandSocket(
460  serverSock, announce, setup_timeout, proto_mismatch))
461  {
462  break;
463  }
464 
465  if (proto_mismatch)
466  {
467  if (p_proto_mismatch)
468  *p_proto_mismatch = true;
469 
470  serverSock->DecrRef();
471  serverSock = nullptr;
472  break;
473  }
474 
475  setup_timeout = (int)(setup_timeout * 1.5F);
476  }
477  else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
478  {
479  if (!we_attempted_wol)
480  {
481  QMutexLocker locker(&d->m_WOLInProgressLock);
482  if (d->m_WOLInProgress)
483  {
484  d->WaitForWOL();
485  continue;
486  }
487 
488  d->m_WOLInProgress = we_attempted_wol = true;
489  }
490 
493  sleepms = WOLsleepTime * 1000;
494  }
495 
496  serverSock->DecrRef();
497  serverSock = nullptr;
498 
499  if (cnt == 1)
500  {
501  QCoreApplication::postEvent(
502  d->m_GUIcontext, new MythEvent("CONNECTION_FAILURE"));
503  }
504 
505  if (sleepms)
506  usleep(sleepms * 1000);
507  }
508 
509  if (we_attempted_wol)
510  {
511  QMutexLocker locker(&d->m_WOLInProgressLock);
512  d->m_WOLInProgress = false;
513  d->m_WOLInProgressWaitCondition.wakeAll();
514  }
515 
516  if (!serverSock && !proto_mismatch)
517  {
518  LOG(VB_GENERAL, LOG_ERR,
519  "Connection to master server timed out.\n\t\t\t"
520  "Either the server is down or the master server settings"
521  "\n\t\t\t"
522  "in mythtv-settings does not contain the proper IP address\n");
523  }
524  else
525  {
526  QCoreApplication::postEvent(
527  d->m_GUIcontext, new MythEvent("CONNECTION_RESTABLISHED"));
528  }
529 
530  return serverSock;
531 }
532 
534  int port)
535 {
536  MythSocket *eventSock = new MythSocket(-1, this);
537 
538  // Assume that since we _just_ connected the command socket,
539  // this one won't need multiple retries to work...
540  if (!eventSock->ConnectToHost(hostname, port))
541  {
542  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
543  "socket to master backend");
544  eventSock->DecrRef();
545  return nullptr;
546  }
547 
548  QString str = QString("ANN Monitor %1 %2")
549  .arg(d->m_localHostname).arg(true);
550  QStringList strlist(str);
551  eventSock->WriteStringList(strlist);
552  bool ok = true;
553  if (!eventSock->ReadStringList(strlist) || strlist.empty() ||
554  (strlist[0] == "ERROR"))
555  {
556  if (!strlist.empty())
557  {
558  LOG(VB_GENERAL, LOG_ERR, LOC +
559  "Problem connecting event socket to master backend");
560  }
561  else
562  {
563  LOG(VB_GENERAL, LOG_ERR, LOC +
564  "Timeout connecting event socket to master backend");
565  }
566  ok = false;
567  }
568 
569  if (!ok)
570  {
571  eventSock->DecrRef();
572  eventSock = nullptr;
573  }
574 
575  return eventSock;
576 }
577 
579 {
580  QMutexLocker locker(&d->m_sockLock);
581  return d->m_serverSock;
582 }
583 
585 {
586  QStringList strlist;
587 
588  QMutexLocker locker(&d->m_sockLock);
589  if (d->m_serverSock == nullptr)
590  return;
591 
592  strlist << "BLOCK_SHUTDOWN";
594 
595  d->m_blockingClient = true;
596 }
597 
599 {
600  QStringList strlist;
601 
602  QMutexLocker locker(&d->m_sockLock);
603  if (d->m_serverSock == nullptr)
604  return;
605 
606  strlist << "ALLOW_SHUTDOWN";
608 
609  d->m_blockingClient = false;
610 }
611 
613 {
614  return d->m_blockingClient;
615 }
616 
618 {
619  d->m_IsWOLAllowed = allow;
620 }
621 
623 {
624  return d->m_IsWOLAllowed;
625 }
626 
628 {
629  d->m_backend = backend;
630 }
631 
633 {
634  return d->m_backend;
635 }
636 
638 {
639  d->m_frontend = frontend;
640 }
641 
643 {
644  return d->m_frontend;
645 }
646 
648 {
649  QString host = GetHostName();
650  return IsMasterHost(host);
651 }
652 
653 bool MythCoreContext::IsMasterHost(const QString &host)
654 {
655  // Temporary code here only to facilitate the upgrade
656  // from 1346 or earlier. The way of determining master host is
657  // changing, and the new way of determning master host
658  // will not work with earlier databases.
659  // This code can be removed when updating from prior to
660  // 1347 is no longer allowed.
661  // Note that we are deprecating some settings including
662  // MasterServerIP, and can remove them at a future time.
663  if (GetNumSetting("DBSchemaVer") < 1347)
664  {
665  // Temporary copy of code from old version of
666  // IsThisHost(Qstring&,QString&)
667  QString addr(resolveSettingAddress("MasterServerIP"));
668  if (addr.toLower() == host.toLower())
669  return true;
670  QHostAddress addrfix(addr);
671  addrfix.setScopeId(QString());
672  QString addrstr = addrfix.toString();
673  if (addrfix.isNull())
674  addrstr = resolveAddress(addr);
675  QString thisip = GetBackendServerIP4(host);
676  QString thisip6 = GetBackendServerIP6(host);
677  return !addrstr.isEmpty()
678  && ((addrstr == thisip) || (addrstr == thisip6));
679  }
680  return GetSetting("MasterServerName") == host;
681 }
682 
684 {
685  return (IsBackend() && IsMasterHost());
686 }
687 
689 {
690 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
691  const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
692 #elif defined _WIN32
693  const char *command = "%systemroot%\\system32\\tasklist.exe "
694  " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
695 #else
696  const char *command = "ps ch -C mythbackend -o pid > /dev/null";
697 #endif
698  uint res = myth_system(command, kMSDontBlockInputDevs |
701  return (res == GENERIC_EXIT_OK);
702 }
703 
704 bool MythCoreContext::IsThisBackend(const QString &addr)
705 {
706  return IsBackend() && IsThisHost(addr);
707 }
708 
709 bool MythCoreContext::IsThisHost(const QString &addr)
710 {
711  return IsThisHost(addr, GetHostName());
712 }
713 
714 bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
715 {
716  if (addr.toLower() == host.toLower())
717  return true;
718 
719  QHostAddress addrfix(addr);
720  addrfix.setScopeId(QString());
721  QString addrstr = addrfix.toString();
722 
723  if (addrfix.isNull())
724  {
725  addrstr = resolveAddress(addr);
726  }
727 
728  QString thisip = GetBackendServerIP(host);
729 
730  return !addrstr.isEmpty() && ((addrstr == thisip));
731 }
732 
734 {
735  // find out if a backend runs on this host...
736  bool backendOnLocalhost = false;
737 
738  QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
739  strlist << GetHostName();
740 
741  SendReceiveStringList(strlist);
742 
743  backendOnLocalhost = strlist[0] != "FALSE";
744 
745  return !backendOnLocalhost;
746 }
747 
748 QString MythCoreContext::GenMythURL(const QString& host, int port, QString path, const QString& storageGroup)
749 {
750  QUrl ret;
751 
752  QString m_host;
753 
754  QHostAddress addr(host);
755  if (!addr.isNull())
756  {
757  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("(%1/%2): Given "
758  "IP address instead of hostname "
759  "(ID). This is invalid.").arg(host).arg(path));
760  }
761 
762  m_host = host;
763 
764  // Basically if it appears to be an IPv6 IP surround the IP with [] otherwise don't bother
765  if (!addr.isNull() && addr.protocol() == QAbstractSocket::IPv6Protocol)
766  m_host = "[" + addr.toString().toLower() + "]";
767 
768  ret.setScheme("myth");
769  if (!storageGroup.isEmpty())
770  ret.setUserName(storageGroup);
771  ret.setHost(m_host);
772  if (port > 0 && port != 6543)
773  ret.setPort(port);
774  if (!path.startsWith("/"))
775  path = QString("/") + path;
776  ret.setPath(path);
777 
778 #if 0
779  LOG(VB_GENERAL, LOG_DEBUG, LOC +
780  QString("GenMythURL returning %1").arg(ret.toString()));
781 #endif
782 
783  return ret.toString();
784 }
785 
786 QString MythCoreContext::GetMasterHostPrefix(const QString &storageGroup,
787  const QString &path)
788 {
789  return GenMythURL(GetMasterHostName(),
791  path,
792  storageGroup);
793 }
794 
796 {
797  QMutexLocker locker(&d->m_masterHostLock);
798 
799  if (d->m_masterHostname.isEmpty())
800  {
801 
802  if (IsMasterBackend())
804  else
805  {
806  QStringList strlist("QUERY_HOSTNAME");
807 
808  if (SendReceiveStringList(strlist))
809  d->m_masterHostname = strlist[0];
810  }
811  }
812 
813  return d->m_masterHostname;
814 }
815 
816 void MythCoreContext::ClearSettingsCache(const QString &myKey)
817 {
818  d->m_database->ClearSettingsCache(myKey);
819 }
820 
822 {
823  d->m_database->ActivateSettingsCache(activate);
824 }
825 
827 {
828  QMutexLocker locker(&d->m_localHostLock);
829  return d->m_localHostname;
830 }
831 
833 {
834  return GetSetting("RecordFilePrefix");
835 }
836 
838  int &width, int &height,
839  double &forced_aspect,
840  double &refresh_rate,
841  int index)
842 {
843  d->m_database->GetResolutionSetting(type, width, height, forced_aspect,
844  refresh_rate, index);
845 }
846 
847 void MythCoreContext::GetResolutionSetting(const QString &t, int &w,
848  int &h, int i)
849 {
850  d->m_database->GetResolutionSetting(t, w, h, i);
851 }
852 
854 {
855  return d->m_database->GetDBManager();
856 }
857 
865 {
866  return d->m_database->IsDatabaseIgnored();
867 }
868 
869 void MythCoreContext::SaveSetting(const QString &key, int newValue)
870 {
871  d->m_database->SaveSetting(key, newValue);
872 }
873 
874 void MythCoreContext::SaveSetting(const QString &key, const QString &newValue)
875 {
876  d->m_database->SaveSetting(key, newValue);
877 }
878 
879 bool MythCoreContext::SaveSettingOnHost(const QString &key,
880  const QString &newValue,
881  const QString &host)
882 {
883  return d->m_database->SaveSettingOnHost(key, newValue, host);
884 }
885 
886 QString MythCoreContext::GetSetting(const QString &key,
887  const QString &defaultval)
888 {
889  return d->m_database->GetSetting(key, defaultval);
890 }
891 
892 bool MythCoreContext::GetBoolSetting(const QString &key, bool defaultval)
893 {
894  int result = GetNumSetting(key, static_cast<int>(defaultval));
895  return result > 0;
896 }
897 
898 int MythCoreContext::GetNumSetting(const QString &key, int defaultval)
899 {
900  return d->m_database->GetNumSetting(key, defaultval);
901 }
902 
903 double MythCoreContext::GetFloatSetting(const QString &key, double defaultval)
904 {
905  return d->m_database->GetFloatSetting(key, defaultval);
906 }
907 
908 QString MythCoreContext::GetSettingOnHost(const QString &key,
909  const QString &host,
910  const QString &defaultval)
911 {
912  return d->m_database->GetSettingOnHost(key, host, defaultval);
913 }
914 
916  const QString &host,
917  bool defaultval)
918 {
919  int result = GetNumSettingOnHost(key, host, static_cast<int>(defaultval));
920  return result > 0;
921 }
922 
924  const QString &host,
925  int defaultval)
926 {
927  return d->m_database->GetNumSettingOnHost(key, host, defaultval);
928 }
929 
930 double MythCoreContext::GetFloatSettingOnHost(const QString &key,
931  const QString &host,
932  double defaultval)
933 {
934  return d->m_database->GetFloatSettingOnHost(key, host, defaultval);
935 }
936 
943 {
944  QString masterserver = gCoreContext->GetSetting("MasterServerName");
945  QString masterip = resolveSettingAddress("BackendServerAddr",masterserver);
946  // Even if empty, return it here if we were to assume that localhost
947  // should be used it just causes a lot of unnecessary error messages.
948  return masterip;
949 }
950 
957 {
958  QString masterserver = gCoreContext->GetSetting
959  ("MasterServerName");
961  ("BackendServerPort", masterserver, 6543);
962 }
963 
970 {
971  QString masterhost = GetMasterHostName();
972 
973  return GetBackendStatusPort(masterhost);
974 }
975 
981 {
983 }
984 
994 QString MythCoreContext::GetBackendServerIP(const QString &host)
995 {
996  return resolveSettingAddress("BackendServerAddr",host);
997 }
998 
1004 {
1006 }
1007 
1013 QString MythCoreContext::GetBackendServerIP4(const QString &host)
1014 {
1015  return resolveSettingAddress("BackendServerIP", host, ResolveIPv4);
1016 }
1017 
1023 {
1025 }
1026 
1032 QString MythCoreContext::GetBackendServerIP6(const QString &host)
1033 {
1034  return resolveSettingAddress("BackendServerIP6", host, ResolveIPv6);
1035 }
1036 
1041 {
1043 }
1044 
1049 {
1050  return GetNumSettingOnHost("BackendServerPort", host, 6543);
1051 }
1052 
1057 {
1059 }
1060 
1065 {
1066  return GetNumSettingOnHost("BackendStatusPort", host, 6544);
1067 }
1068 
1073 bool MythCoreContext::GetScopeForAddress(QHostAddress &addr) const
1074 {
1075  QHostAddress addr1 = addr;
1076  addr1.setScopeId(QString());
1077  QString addrstr = addr1.toString();
1078  QMutexLocker lock(&d->m_scopesLock);
1079 
1080  if (!d->m_scopes.contains(addrstr))
1081  return false;
1082 
1083  addr.setScopeId(d->m_scopes[addrstr]);
1084  return true;
1085 }
1086 
1093 void MythCoreContext::SetScopeForAddress(const QHostAddress &addr)
1094 {
1095  QHostAddress addr1 = addr;
1096  addr1.setScopeId(QString());
1097  QMutexLocker lock(&d->m_scopesLock);
1098 
1099  d->m_scopes.insert(addr1.toString(), addr.scopeId());
1100 }
1101 
1107 void MythCoreContext::SetScopeForAddress(const QHostAddress &addr, int scope)
1108 {
1109  QHostAddress addr1 = addr;
1110  addr1.setScopeId(QString());
1111  QMutexLocker lock(&d->m_scopesLock);
1112 
1113  d->m_scopes.insert(addr1.toString(), QString::number(scope));
1114 }
1115 
1128  const QString &host,
1129  ResolveType type, bool keepscope)
1130 {
1131  QString value;
1132 
1133  if (host.isEmpty())
1134  {
1135  value = GetSetting(name);
1136  }
1137  else
1138  {
1139  value = GetSettingOnHost(name, host);
1140  }
1141 
1142  return resolveAddress(value, type, keepscope);
1143 }
1144 
1156 QString MythCoreContext::resolveAddress(const QString &host, ResolveType type,
1157  bool keepscope) const
1158 {
1159  QHostAddress addr(host);
1160 
1161  if (!host.isEmpty() && addr.isNull())
1162  {
1163  // address is likely a hostname, attempts to resolve it
1164  QHostInfo info = QHostInfo::fromName(host);
1165  QList<QHostAddress> list = info.addresses();
1166 
1167  if (list.isEmpty())
1168  {
1169  LOG(VB_GENERAL, LOG_WARNING, LOC +
1170  QString("Can't resolve hostname:'%1', using localhost").arg(host));
1171  return type == ResolveIPv4 ? "127.0.0.1" : "::1";
1172  }
1173  QHostAddress v4, v6;
1174 
1175  // Return the first address fitting the type critera
1176  for (int i=0; i < list.size(); i++)
1177  {
1178  addr = list[i];
1179  QAbstractSocket::NetworkLayerProtocol prot = addr.protocol();
1180 
1181  if (prot == QAbstractSocket::IPv4Protocol)
1182  {
1183  v4 = addr;
1184  if (type == 0)
1185  break;
1186  }
1187  else if (prot == QAbstractSocket::IPv6Protocol)
1188  {
1189  v6 = addr;
1190  if (type != 0)
1191  break;
1192  }
1193  }
1194  switch (type)
1195  {
1196  case ResolveIPv4:
1197  addr = v4.isNull() ? QHostAddress::LocalHost : v4;
1198  break;
1199  case ResolveIPv6:
1200  addr = v6.isNull() ? QHostAddress::LocalHostIPv6 : v6;
1201  break;
1202  default:
1203  addr = v6.isNull() ?
1204  (v4.isNull() ? QHostAddress::LocalHostIPv6 : v4) : v6;
1205  break;
1206  }
1207  }
1208  else if (host.isEmpty())
1209  {
1210  return QString();
1211  }
1212 
1213  if (!keepscope)
1214  {
1215  addr.setScopeId(QString());
1216  }
1217  return addr.toString();
1218 }
1219 
1231 bool MythCoreContext::CheckSubnet(const QAbstractSocket *socket)
1232 {
1233  QHostAddress peer = socket->peerAddress();
1234  return CheckSubnet(peer);
1235 }
1236 
1248 bool MythCoreContext::CheckSubnet(const QHostAddress &peer)
1249 {
1250  static const QHostAddress linklocal("fe80::");
1251  if (GetBoolSetting("AllowConnFromAll",false))
1252  return true;
1253  if (d->m_approvedIps.contains(peer))
1254  return true;
1255  if (d->m_deniedIps.contains(peer))
1256  {
1257  LOG(VB_GENERAL, LOG_WARNING, LOC +
1258  QString("Repeat denied connection from ip address: %1")
1259  .arg(peer.toString()));
1260  return false;
1261  }
1262 
1263  // allow all link-local
1264  if (peer.isInSubnet(linklocal,10))
1265  {
1266  d->m_approvedIps.append(peer);
1267  return true;
1268  }
1269 
1270  // loop through all available interfaces
1271  QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
1272  QList<QNetworkInterface>::const_iterator qni;
1273  for (qni = IFs.begin(); qni != IFs.end(); ++qni)
1274  {
1275  if ((qni->flags() & QNetworkInterface::IsRunning) == 0)
1276  continue;
1277 
1278  QList<QNetworkAddressEntry> IPs = qni->addressEntries();
1279  QList<QNetworkAddressEntry>::iterator qnai;
1280  for (qnai = IPs.begin(); qnai != IPs.end(); ++qnai)
1281  {
1282  int pfxlen = qnai->prefixLength();
1283  // Set this to test rejection without having an extra
1284  // network.
1285  if (GetBoolSetting("DebugSubnet"))
1286  pfxlen += 4;
1287  if (peer.isInSubnet(qnai->ip(),pfxlen))
1288  {
1289  d->m_approvedIps.append(peer);
1290  return true;
1291  }
1292  }
1293  }
1294  d->m_deniedIps.append(peer);
1295  LOG(VB_GENERAL, LOG_WARNING, LOC +
1296  QString("Denied connection from ip address: %1")
1297  .arg(peer.toString()));
1298  return false;
1299 }
1300 
1301 
1303  const QString &value)
1304 {
1305  d->m_database->OverrideSettingForSession(key, value);
1306 }
1307 
1309 {
1310  d->m_database->ClearOverrideSettingForSession(key);
1311 }
1312 
1314 {
1315  return is_current_thread(d->m_UIThread);
1316 }
1317 
1337  QStringList &strlist, bool quickTimeout, bool block)
1338 {
1339  QString msg;
1340  if (HasGUI() && IsUIThread())
1341  {
1342  msg = "SendReceiveStringList(";
1343  for (uint i=0; i<(uint)strlist.size() && i<2; i++)
1344  msg += (i?",":"") + strlist[i];
1345  msg += (strlist.size() > 2) ? "...)" : ")";
1346  LOG(VB_GENERAL, LOG_DEBUG, LOC + msg + " called from UI thread");
1347  }
1348 
1349  QString query_type = "UNKNOWN";
1350 
1351  if (!strlist.isEmpty())
1352  query_type = strlist[0];
1353 
1354  QMutexLocker locker(&d->m_sockLock);
1355  if (!d->m_serverSock)
1356  {
1357  bool blockingClient = d->m_blockingClient &&
1358  (GetNumSetting("idleTimeoutSecs",0) > 0);
1359  ConnectToMasterServer(blockingClient);
1360  }
1361 
1362  bool ok = false;
1363 
1364  if (d->m_serverSock)
1365  {
1366  QStringList sendstrlist = strlist;
1367  uint timeout = quickTimeout ?
1369  ok = d->m_serverSock->SendReceiveStringList(strlist, 0, timeout);
1370 
1371  if (!ok)
1372  {
1373  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1374  QString("Connection to backend server lost"));
1375  d->m_serverSock->DecrRef();
1376  d->m_serverSock = nullptr;
1377 
1378  if (d->m_eventSock)
1379  {
1380  d->m_eventSock->DecrRef();
1381  d->m_eventSock = nullptr;
1382  }
1383 
1384  if (block)
1385  {
1387 
1388  if (d->m_serverSock)
1389  {
1391  strlist, 0, timeout);
1392  }
1393  }
1394  }
1395 
1396  // this should not happen
1397  while (ok && strlist[0] == "BACKEND_MESSAGE")
1398  {
1399  // oops, not for us
1400  LOG(VB_GENERAL, LOG_EMERG, LOC + "SRSL you shouldn't see this!!");
1401  QString message = strlist[1];
1402  strlist.pop_front(); strlist.pop_front();
1403 
1404  MythEvent me(message, strlist);
1405  dispatch(me);
1406 
1407  ok = d->m_serverSock->ReadStringList(strlist, timeout);
1408  }
1409 
1410  if (!ok)
1411  {
1412  if (d->m_serverSock)
1413  {
1414  d->m_serverSock->DecrRef();
1415  d->m_serverSock = nullptr;
1416  }
1417 
1418  LOG(VB_GENERAL, LOG_CRIT, LOC +
1419  QString("Reconnection to backend server failed"));
1420 
1421  QCoreApplication::postEvent(d->m_GUIcontext,
1422  new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
1423  }
1424  }
1425 
1426  if (ok)
1427  {
1428  if (strlist.isEmpty())
1429  ok = false;
1430  else if (strlist[0] == "ERROR")
1431  {
1432  if (strlist.size() == 2)
1433  LOG(VB_GENERAL, LOG_INFO, LOC +
1434  QString("Protocol query '%1' responded with the error '%2'")
1435  .arg(query_type).arg(strlist[1]));
1436  else
1437  LOG(VB_GENERAL, LOG_INFO, LOC +
1438  QString("Protocol query '%1' responded with an error, but "
1439  "no error message.") .arg(query_type));
1440 
1441  ok = false;
1442  }
1443  else if (strlist[0] == "UNKNOWN_COMMAND")
1444  {
1445  LOG(VB_GENERAL, LOG_ERR, LOC +
1446  QString("Protocol query '%1' responded with the error 'UNKNOWN_COMMAND'")
1447  .arg(query_type));
1448 
1449  ok = false;
1450  }
1451  }
1452 
1453  return ok;
1454 }
1455 
1456 class SendAsyncMessage : public QRunnable
1457 {
1458  public:
1459  SendAsyncMessage(const QString &msg, const QStringList &extra) :
1460  m_message(msg), m_extraData(extra)
1461  {
1462  }
1463 
1464  explicit SendAsyncMessage(const QString &msg) : m_message(msg) { }
1465 
1466  void run(void) override // QRunnable
1467  {
1468  QStringList strlist("MESSAGE");
1469  strlist << m_message;
1470  strlist << m_extraData;
1472  }
1473 
1474  private:
1475  QString m_message;
1476  QStringList m_extraData;
1477 };
1478 
1479 void MythCoreContext::SendMessage(const QString &message)
1480 {
1481  if (IsBackend())
1482  {
1483  dispatch(MythEvent(message));
1484  }
1485  else
1486  {
1488  new SendAsyncMessage(message), "SendMessage");
1489  }
1490 }
1491 
1493 {
1494  if (IsBackend())
1495  {
1496  dispatch(event);
1497  }
1498  else
1499  {
1501  new SendAsyncMessage(event.Message(), event.ExtraDataList()),
1502  "SendEvent");
1503  }
1504 }
1505 
1506 void MythCoreContext::SendSystemEvent(const QString &msg)
1507 {
1508  if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1509  return;
1510 
1511  SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
1512  .arg(msg).arg(GetHostName()));
1513 }
1514 
1516  const QString &hostname,
1517  const QString &args)
1518 {
1519  SendSystemEvent(QString("%1 HOST %2 %3").arg(msg).arg(hostname).arg(args));
1520 }
1521 
1522 
1524 {
1525  do
1526  {
1527  QStringList strlist;
1528  if (!sock->ReadStringList(strlist))
1529  continue;
1530 
1531  if (strlist.size() < 2)
1532  continue;
1533 
1534  QString prefix = strlist[0];
1535  QString message = strlist[1];
1536  QStringList tokens = message.split(" ", QString::SkipEmptyParts);
1537 
1538  if (prefix == "OK")
1539  {
1540  }
1541  else if (prefix != "BACKEND_MESSAGE")
1542  {
1543  LOG(VB_NETWORK, LOG_ERR, LOC +
1544  QString("Received a: %1 message from the backend "
1545  "but I don't know what to do with it.")
1546  .arg(prefix));
1547  }
1548  else if (message == "CLEAR_SETTINGS_CACHE")
1549  {
1550  // No need to dispatch this message to ourself, so handle it
1551  LOG(VB_NETWORK, LOG_INFO, LOC + "Received remote 'Clear Cache' request");
1553  }
1554  else if (message.startsWith("FILE_WRITTEN"))
1555  {
1556  QString file;
1557  uint64_t size;
1558  int NUMTOKENS = 3; // Number of tokens expected
1559 
1560  if (tokens.size() == NUMTOKENS)
1561  {
1562  file = tokens[1];
1563  size = tokens[2].toULongLong();
1564  }
1565  else
1566  {
1567  LOG(VB_NETWORK, LOG_ERR, LOC +
1568  QString("FILE_WRITTEN event received "
1569  "with invalid number of arguments, "
1570  "%1 expected, %2 actual")
1571  .arg(NUMTOKENS-1)
1572  .arg(tokens.size()-1));
1573  return;
1574  }
1575  // No need to dispatch this message to ourself, so handle it
1576  LOG(VB_NETWORK, LOG_INFO, LOC +
1577  QString("Received remote 'FILE_WRITTEN %1' request").arg(file));
1578  RegisterFileForWrite(file, size);
1579  }
1580  else if (message.startsWith("FILE_CLOSED"))
1581  {
1582  QString file;
1583  int NUMTOKENS = 2; // Number of tokens expected
1584 
1585  if (tokens.size() == NUMTOKENS)
1586  {
1587  file = tokens[1];
1588  }
1589  else
1590  {
1591  LOG(VB_NETWORK, LOG_ERR, LOC +
1592  QString("FILE_CLOSED event received "
1593  "with invalid number of arguments, "
1594  "%1 expected, %2 actual")
1595  .arg(NUMTOKENS-1)
1596  .arg(tokens.size()-1));
1597  return;
1598  }
1599  // No need to dispatch this message to ourself, so handle it
1600  LOG(VB_NETWORK, LOG_INFO, LOC +
1601  QString("Received remote 'FILE_CLOSED %1' request").arg(file));
1602  UnregisterFileForWrite(file);
1603  }
1604  else
1605  {
1606  strlist.pop_front();
1607  strlist.pop_front();
1608  MythEvent me(message, strlist);
1609  dispatch(me);
1610  }
1611  }
1612  while (sock->IsDataAvailable());
1613 }
1614 
1616 {
1617  (void)sock;
1618 
1619  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1620  "Event socket closed. No connection to the backend.");
1621 
1622  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1623 }
1624 
1626  bool error_dialog_desired)
1627 {
1628  if (!socket)
1629  return false;
1630 
1631  QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
1632  .arg(MYTH_PROTO_VERSION)
1633  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1634  socket->WriteStringList(strlist);
1635 
1636  if (!socket->ReadStringList(strlist, timeout_ms) || strlist.empty())
1637  {
1638  LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
1639  "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
1640  "This happens when the backend is too busy to respond,\n\t\t\t"
1641  "or has deadlocked due to bugs or hardware failure.");
1642 
1643  return false;
1644  }
1645  if (strlist[0] == "REJECT" && strlist.size() >= 2)
1646  {
1647  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Protocol version or token mismatch "
1648  "(frontend=%1/%2,backend=%3/\?\?)\n")
1649  .arg(MYTH_PROTO_VERSION)
1650  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN))
1651  .arg(strlist[1]));
1652 
1653  if (error_dialog_desired && d->m_GUIcontext)
1654  {
1655  QStringList list(strlist[1]);
1656  QCoreApplication::postEvent(
1657  d->m_GUIcontext, new MythEvent("VERSION_MISMATCH", list));
1658  }
1659 
1660  return false;
1661  }
1662  if (strlist[0] == "ACCEPT")
1663  {
1664  if (!d->m_announcedProtocol)
1665  {
1666  d->m_announcedProtocol = true;
1667  LOG(VB_GENERAL, LOG_INFO, LOC +
1668  QString("Using protocol version %1 %2")
1669  .arg(MYTH_PROTO_VERSION)
1670  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1671  }
1672 
1673  return true;
1674  }
1675 
1676  LOG(VB_GENERAL, LOG_ERR, LOC +
1677  QString("Unexpected response to MYTH_PROTO_VERSION: %1")
1678  .arg(strlist[0]));
1679  return false;
1680 }
1681 
1683 {
1684  LOG(VB_NETWORK, LOG_INFO, LOC + QString("MythEvent: %1").arg(event.Message()));
1685 
1686  MythObservable::dispatch(event);
1687 }
1688 
1690 {
1691  QMutexLocker locker(&d->m_localHostLock);
1693  d->m_database->SetLocalHostname(hostname);
1694 }
1695 
1697 {
1698  d->m_GUIobject = gui;
1699 }
1700 
1701 bool MythCoreContext::HasGUI(void) const
1702 {
1703  return d->m_GUIobject;
1704 }
1705 
1707 {
1708  return d->m_GUIobject;
1709 }
1710 
1712 {
1713  return d->m_GUIcontext;
1714 }
1715 
1717 {
1718  return d->m_database;
1719 }
1720 
1722 {
1723  return d->m_locale;
1724 }
1725 
1731 {
1732  return GetLanguageAndVariant().left(2);
1733 }
1734 
1743 {
1744  if (d->m_language.isEmpty())
1745  d->m_language = GetSetting("Language", "en_US").toLower();
1746 
1747  return d->m_language;
1748 }
1749 
1751 {
1752  d->m_language.clear();
1753 }
1754 
1756 {
1757  QMutexLocker locker(&d->m_sockLock);
1758  LOG(VB_GENERAL, LOG_INFO, "Restarting Backend Connections");
1759  if (d->m_serverSock)
1761  if (d->m_eventSock)
1763  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1764 }
1765 
1767 {
1768  if (!d->m_locale)
1769  d->m_locale = new MythLocale();
1770 
1771  QString localeCode = d->m_locale->GetLocaleCode();
1772  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1773  .arg(localeCode));
1774  QLocale::setDefault(d->m_locale->ToQLocale());
1775 }
1776 
1778 {
1779  if (!d->m_locale)
1780  d->m_locale = new MythLocale();
1781  else
1782  d->m_locale->ReInit();
1783 
1784  QString localeCode = d->m_locale->GetLocaleCode();
1785  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1786  .arg(localeCode));
1787  QLocale::setDefault(d->m_locale->ToQLocale());
1788 }
1789 
1791 {
1792  if (!d->m_locale)
1793  InitLocale();
1794 
1795  return d->m_locale->ToQLocale();
1796 }
1797 
1799 {
1800  if (!d->m_locale)
1801  InitLocale();
1802 
1803  if (!d->m_locale->GetLocaleCode().isEmpty())
1804  {
1805  LOG(VB_GENERAL, LOG_INFO,
1806  QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
1807 
1809  return;
1810  }
1811 
1812  LOG(VB_GENERAL, LOG_ERR, LOC +
1813  "No locale defined! We weren't able to set locale defaults.");
1814 }
1815 
1817 {
1818  d->m_scheduler = sched;
1819 }
1820 
1822 {
1823  return d->m_scheduler;
1824 }
1825 
1831 void MythCoreContext::WaitUntilSignals(const char *signal1, ...)
1832 {
1833  if (!signal1)
1834  return;
1835 
1836  const char *s;
1837  QEventLoop eventLoop;
1838  va_list vl;
1839 
1840  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1841  QString("Waiting for signal %1")
1842  .arg(signal1));
1843  connect(this, signal1, &eventLoop, SLOT(quit()));
1844 
1845  va_start(vl, signal1);
1846  s = va_arg(vl, const char *);
1847  while (s)
1848  {
1849  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1850  QString("Waiting for signal %1")
1851  .arg(s));
1852  connect(this, s, &eventLoop, SLOT(quit()));
1853  s = va_arg(vl, const char *);
1854  }
1855  va_end(vl);
1856 
1857  eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1858 }
1859 
1866 void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method)
1867 {
1868  if (!sender || !method)
1869  return;
1870 
1871  QMutexLocker lock(&d->m_playbackLock);
1872 
1873  if (!d->m_playbackClients.contains(sender))
1874  {
1875  d->m_playbackClients.insert(sender, QByteArray(method));
1876  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1877  sender, method,
1878  Qt::BlockingQueuedConnection);
1879  }
1880 }
1881 
1888 {
1889  QMutexLocker lock(&d->m_playbackLock);
1890 
1891  if (d->m_playbackClients.contains(sender))
1892  {
1893  QByteArray ba = d->m_playbackClients.value(sender);
1894  const char *method = ba.constData();
1895  disconnect(this, SIGNAL(TVPlaybackAboutToStart()),
1896  sender, method);
1897  d->m_playbackClients.remove(sender);
1898  }
1899 }
1900 
1908 {
1909  QMutexLocker lock(&d->m_playbackLock);
1910  QByteArray ba;
1911  const char *method = nullptr;
1912  d->m_inwanting = true;
1913 
1914  // If any registered client are in the same thread, they will deadlock, so rebuild
1915  // connections for any clients in the same thread as non-blocking connection
1916  QThread *currentThread = QThread::currentThread();
1917 
1918  QMap<QObject *, QByteArray>::iterator it = d->m_playbackClients.begin();
1919  for (; it != d->m_playbackClients.end(); ++it)
1920  {
1921  if (it.key() == sender)
1922  continue; // will be done separately, no need to do it again
1923 
1924  QThread *thread = it.key()->thread();
1925 
1926  if (thread != currentThread)
1927  continue;
1928 
1929  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1930  connect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1931  }
1932 
1933  // disconnect sender so it won't receive the message
1934  if (d->m_playbackClients.contains(sender))
1935  {
1936  ba = d->m_playbackClients.value(sender);
1937  method = ba.constData();
1938  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method);
1939  }
1940 
1941  // emit signal
1942  emit TVPlaybackAboutToStart();
1943 
1944  // reconnect sender
1945  if (method)
1946  {
1947  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1948  sender, method,
1949  Qt::BlockingQueuedConnection);
1950  }
1951  // Restore blocking connections
1952  for (; it != d->m_playbackClients.end(); ++it)
1953  {
1954  if (it.key() == sender)
1955  continue; // already done above, no need to do it again
1956 
1957  QThread *thread = it.key()->thread();
1958 
1959  if (thread != currentThread)
1960  continue;
1961 
1962  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1963  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1964  it.key(), it.value(), Qt::BlockingQueuedConnection);
1965  }
1966  d->m_inwanting = false;
1967 }
1968 
1975 {
1976  // when called, it will be while the m_playbackLock is held
1977  // following a call to WantingPlayback
1978  d->m_intvwanting = b;
1979 }
1980 
1987 {
1988  bool locked = d->m_playbackLock.tryLock();
1989  bool intvplayback = d->m_intvwanting;
1990 
1991  if (!locked && d->m_inwanting)
1992  return true; // we're in the middle of WantingPlayback
1993 
1994  if (!locked)
1995  return false;
1996 
1997  d->m_playbackLock.unlock();
1998 
1999  return intvplayback;
2000 }
2001 
2003 {
2004  if (!d->m_sessionManager)
2006 
2007  return d->m_sessionManager;
2008 }
2009 
2011  const QString &libversion,
2012  const QString &pluginversion)
2013 {
2014  if (libversion == pluginversion)
2015  return true;
2016 
2017  LOG(VB_GENERAL, LOG_EMERG, LOC +
2018  QString("Plugin %1 (%2) binary version does not "
2019  "match libraries (%3)")
2020  .arg(name).arg(pluginversion).arg(libversion));
2021  return false;
2022 }
2023 
2025 {
2026  if (d->m_pluginmanager == pmanager)
2027  return;
2028 
2029  if (d->m_pluginmanager)
2030  {
2031  delete d->m_pluginmanager;
2032  d->m_pluginmanager = nullptr;
2033  }
2034 
2035  d->m_pluginmanager = pmanager;
2036 }
2037 
2039 {
2040  return d->m_pluginmanager;
2041 }
2042 
2044 {
2045  d->m_isexiting = exiting;
2046 }
2047 
2049 {
2050  return d->m_isexiting;
2051 }
2052 
2053 void MythCoreContext::RegisterFileForWrite(const QString& file, uint64_t size)
2054 {
2055  QMutexLocker lock(&d->m_fileslock);
2056 
2057  QPair<int64_t, uint64_t> pair(QDateTime::currentMSecsSinceEpoch(), size);
2058  d->m_fileswritten.insert(file, pair);
2059 
2060  if (IsBackend())
2061  {
2062  QString message = QString("FILE_WRITTEN %1 %2").arg(file).arg(size);
2063  MythEvent me(message);
2064  dispatch(me);
2065  }
2066 
2067  LOG(VB_FILE, LOG_DEBUG, LOC +
2068  QString("%1").arg(file));
2069 }
2070 
2072 {
2073  QMutexLocker lock(&d->m_fileslock);
2074 
2075  d->m_fileswritten.remove(file);
2076 
2077  if (IsBackend())
2078  {
2079  QString message = QString("FILE_CLOSED %1").arg(file);
2080  MythEvent me(message);
2081  dispatch(me);
2082  }
2083 
2084  LOG(VB_FILE, LOG_DEBUG, LOC +
2085  QString("%1").arg(file));
2086 }
2087 
2089 {
2090  QMutexLocker lock(&d->m_fileslock);
2091 
2092  return d->m_fileswritten.contains(file);
2093 }
2094 
2095 /* vim: set expandtab tabstop=4 shiftwidth=4: */
QString GetLocaleCode() const
Name of language in that language.
Definition: mythlocale.h:28
QString GetLanguageAndVariant(void)
Returns the user-set language and variant.
bool SetupCommandSocket(MythSocket *serverSock, const QString &announcement, uint timeout_in_ms, bool &proto_mismatch)
void SaveLocaleDefaults(void)
MythPluginManager * m_pluginmanager
void DestroyMythDB(void)
Definition: mythdb.cpp:51
MythScheduler * GetScheduler(void)
This is an generic interface to a program scheduler.
Definition: mythscheduler.h:20
avoid disabling UI drawing
Definition: mythsystem.h:35
MythCoreContext(const QString &binversion, QObject *guiContext)
MythLocale * GetLocale(void) const
QMutex m_sockLock
protects both m_serverSock and m_eventSock
void run(void) override
MythCoreContext * m_parent
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
MythScheduler * m_scheduler
QList< QHostAddress > m_deniedIps
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
QString m_localHostname
hostname from config.xml or gethostname()
static const uint kLongTimeout
Definition: mythsocket.h:72
static void delete_sock(QMutexLocker &locker, MythSocket **s)
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
bool HasGUI(void) const
QMutex m_localHostLock
Locking for m_localHostname.
MythSessionManager * m_sessionManager
void SaveSetting(const QString &key, int newValue)
void connectionClosed(MythSocket *sock) override
void ShutdownMythDownloadManager(void)
Deletes the running MythDownloadManager at program exit.
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
MythSessionManager * GetSessionManager(void)
int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
void ReInit()
Definition: mythlocale.cpp:53
SendAsyncMessage(const QString &msg, const QStringList &extra)
bool IsBlockingClient(void) const
is this client blocking shutdown
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
static void ShutdownAllPools(void)
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval=0.0)
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
bool IsFrontend(void) const
is this process a frontend process
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
void SetScheduler(MythScheduler *sched)
bool MythWakeup(const QString &wakeUpCommand, uint flags, uint timeout)
void readyRead(MythSocket *sock) override
MythCoreContextPrivate(MythCoreContext *lparent, QString binversion, QObject *guicontext)
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool IsThisBackend(const QString &addr)
is this address mapped to this backend host
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
QList< QHostAddress > m_approvedIps
void SendHostSystemEvent(const QString &msg, const QString &hostname, const QString &args)
const QLocale ToQLocale() const
Definition: mythlocale.h:30
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
static guint32 * tmp
Definition: goom_core.c:35
void SetLocalHostname(const QString &hostname)
bool TestPluginVersion(const QString &name, const QString &libversion, const QString &pluginversion)
void SendMessage(const QString &message)
QMap< QString, QPair< int64_t, uint64_t > > m_fileswritten
process events while waiting
Definition: mythsystem.h:37
#define MYTH_APPNAME_MYTHTV_SETUP
bool IsThisHost(const QString &addr)
is this address mapped to this host
int GetBackendServerPort(void)
Returns the locally defined backend control port.
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
unsigned char b
Definition: ParseText.cpp:329
QMap< QString, QString > m_scopes
Scope Id cache for Link-Local addresses.
void BlockShutdown(void)
double GetFloatSetting(const QString &key, double defaultval=0.0)
void DisconnectFromHost(void)
Definition: mythsocket.cpp:508
We use digest authentication because it protects the password over unprotected networks.
Definition: mythsession.h:98
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, int setup_timeout=-1)
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
QMap< QObject *, QByteArray > m_playbackClients
MythPluginManager * GetPluginManager(void)
This class is used as a container for messages.
Definition: mythevent.h:16
void ClearOverrideSettingForSession(const QString &key)
bool IsConnectedToMaster(void)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
void SetAsBackend(bool backend)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
bool GetBoolSettingOnHost(const QString &key, const QString &host, bool defaultval=0)
QString m_masterHostname
master backend hostname
void SetExiting(bool exiting=true)
static const uint16_t * d
void OverrideSettingForSession(const QString &key, const QString &value)
QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QObject * GetGUIContext(void)
MythSocket * m_serverSock
socket for sending MythProto requests
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
bool BackendIsRunning(void)
a backend process is running on this host
bool IsDataAvailable(void) const
Definition: mythsocket.cpp:567
unsigned char t
Definition: ParseText.cpp:329
void SetPluginManager(MythPluginManager *pmanager)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
#define MYTH_PROTO_TOKEN
Definition: mythversion.h:49
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
QString GetMasterHostName(void)
string hostname
Definition: caa.py:17
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:787
const char * name
Definition: ParseText.cpp:328
SendAsyncMessage(const QString &msg)
uint myth_system(const QString &command, uint flags, uint timeout)
void MBASE_PUBLIC ShutdownMythSystemLegacy(void)
int GetNumSettingOnHost(const QString &key, const QString &host, int defaultval=0)
QString GetBackendServerIP6(void)
Returns the IPv6 address defined for the current host see GetBackendServerIP6(host)
bool IsMasterHost(void)
is this the same host as the master
void AllowShutdown(void)
QString resolveAddress(const QString &host, ResolveType=ResolveAny, bool keepscope=false) const
if host is an IP address, it will be returned or resolved otherwise.
void dispatch(const MythEvent &event)
void UnregisterFileForWrite(const QString &file)
bool IsBackend(void) const
is this process a backend process
bool ReadStringList(QStringList &list, uint timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:323
static MThreadPool * globalInstance(void)
int GetNumSetting(const QString &key, int defaultval=0)
int GetBackendStatusPort(void)
Returns the locally defined backend status port.
static void StopAllPools(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QMutex m_scopesLock
Locking for m_masterHostname.
DB connection pool, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:53
void SetGUIObject(QObject *gui)
QString resolveSettingAddress(const QString &name, const QString &host=QString(), ResolveType type=ResolveAny, bool keepscope=false)
Retrieve IP setting "name" for "host".
void start(QRunnable *runnable, const QString &debugName, int priority=0)
QString GetFilePrefix(void)
QLocale GetQLocale(void)
bool GetBoolSetting(const QString &key, bool defaultval=false)
QMutex * avcodeclock
This global variable is used to makes certain calls to avlib threadsafe.
bool ConnectToHost(const QString &hostname, quint16 port)
connect to host
Definition: mythsocket.cpp:384
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:753
MythCoreContextPrivate * d
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:145
bool CheckProtoVersion(MythSocket *socket, uint timeout_ms=kMythSocketLongTimeout, bool error_dialog_desired=false)
static const uint kShortTimeout
Definition: mythsocket.h:71
MythSocket * m_eventSock
socket events arrive on
bool IsConnected(void) const
Definition: mythsocket.cpp:561
void WaitUntilSignals(const char *signal1,...)
Wait until either of the provided signals have been received.
void ClearSettingsCache(const QString &myKey=QString(""))
bool GetScopeForAddress(QHostAddress &addr) const
Return the cached scope Id for the given address.
void SaveLocaleDefaults(bool overwrite=false)
Definition: mythlocale.cpp:172
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
bool SafeConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
static void ThreadSetup(const QString &)
This is to be called on startup in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:227
bool IsWOLAllowed() const
QString GetBackendServerIP4(void)
Returns the IPv4 address defined for the current host see GetBackendServerIP4(host)
MythDB * GetDB(void)
QString GetMasterHostPrefix(const QString &storageGroup=QString(), const QString &path=QString())
bool WaitForWOL(int timeout_in_ms=INT_MAX)
If another thread has already started WOL process, wait on them...
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
int GetMasterServerStatusPort(void)
Returns the Master Backend status port If no master server status port has been defined in the databa...
void TVPlaybackAboutToStart(void)
Scheduler * sched
MDBManager * GetDBManager(void)
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, uint timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:336
virtual ~MythCoreContext()
bool IsFrontendOnly(void)
is there a frontend, but no backend, running on this host
void RegisterForPlayback(QObject *sender, const char *method)
Register sender for TVPlaybackAboutToStart signal.
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:41
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:311
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
QMutex m_masterHostLock
Locking for m_masterHostname.
bool IsRegisteredFileForWrite(const QString &file)
void TVInWantingPlayback(bool b)
Let the TV class tell us if we was interrupted following a call to WantingPlayback().
bool IsMasterBackend(void)
is this the actual MBE process
#define LOC
const QString & Message() const
Definition: mythevent.h:58
void ActivateSettingsCache(bool activate=true)
QObject * GetGUIObject(void)
MythSocket * ConnectEventSocket(const QString &hostname, int port)
QString GetLanguage(void)
Returns two character ISO-639 language descriptor for UI language.
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
QString GetHostName(void)
void SetAsFrontend(bool frontend)
static void srandom(unsigned int)
Definition: compat.h:148
void SendSystemEvent(const QString &msg)
QWaitCondition m_WOLInProgressWaitCondition
void SendEvent(const MythEvent &event)
bool InWantingPlayback(void)
Returns true if a client has requested playback.
void SetWOLAllowed(bool allow)
This class contains the runtime context for MythTV.
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
void ResetLanguage(void)
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.