MythTV  master
mythcorecontext.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QCoreApplication>
3 #include <QUrl>
4 #include <QDir>
5 #include <QFileInfo>
6 #include <QDebug>
7 #include <QMutex>
8 #include <QRunnable>
9 #include <QWaitCondition>
10 #include <QNetworkInterface>
11 #include <QAbstractSocket>
12 #include <QHostAddress>
13 #include <QHostInfo>
14 #include <QNetworkInterface>
15 #include <QNetworkAddressEntry>
16 #include <QLocale>
17 #include <QPair>
18 #include <QDateTime>
19 #include <QRunnable>
20 
21 // Std
22 #include <algorithm>
23 #include <cmath>
24 #include <cstdarg>
25 #include <queue>
26 #include <unistd.h> // for usleep()
27 using namespace std;
28 
29 #ifdef _WIN32
30 #include <winsock2.h>
31 #else
32 #include <clocale>
33 #include <utility>
34 #endif
35 
36 // MythTV
37 #include "compat.h"
38 #include "mythconfig.h" // for CONFIG_DARWIN
39 #include "mythdownloadmanager.h"
40 #include "mythcorecontext.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 
59 class MythCoreContextPrivate : public QObject
60 {
61  public:
62  MythCoreContextPrivate(MythCoreContext *lparent, QString binversion,
63  QObject *guicontext);
64  ~MythCoreContextPrivate() override;
65 
66  bool WaitForWOL(int timeout_in_ms = INT_MAX);
67 
68  public:
70  QObject *m_guiContext;
71  QObject *m_guiObject;
73 
74  QMutex m_localHostLock;
75  QString m_localHostname;
77  QString m_masterHostname;
78  QMutex m_scopesLock;
79  QMap<QString, QString> m_scopes;
80 
81  QMutex m_sockLock;
84 
89 
90  bool m_backend;
91  bool m_frontend;
92 
93  MythDB *m_database;
94 
95  QThread *m_uiThread;
96 
98  QString m_language;
99 
101 
103 
104  QMap<QObject *, QByteArray> m_playbackClients;
108 
110 
112 
114 
115  QMap<QString, QPair<int64_t, uint64_t> > m_fileswritten;
116  QMutex m_fileslock;
117 
119 
120  QList<QHostAddress> m_approvedIps;
121  QList<QHostAddress> m_deniedIps;
122 
124 };
125 
127  QString binversion,
128  QObject *guicontext)
129  : m_parent(lparent),
130  m_guiContext(guicontext),
131  m_guiObject(nullptr),
132  m_appBinaryVersion(std::move(binversion)),
133  m_sockLock(QMutex::NonRecursive),
134  m_serverSock(nullptr),
135  m_eventSock(nullptr),
136  m_wolInProgress(false),
137  m_isWOLAllowed(true),
138  m_backend(false),
139  m_frontend(false),
140  m_database(GetMythDB()),
141  m_uiThread(QThread::currentThread()),
142  m_locale(nullptr),
143  m_scheduler(nullptr),
144  m_blockingClient(true),
145  m_inwanting(false),
146  m_intvwanting(false),
147  m_announcedProtocol(false),
148  m_pluginmanager(nullptr),
149  m_isexiting(false),
150  m_sessionManager(nullptr),
151  m_power(nullptr)
152 {
153  MThread::ThreadSetup("CoreContext");
154 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
155  srandom(MythDate::current().toTime_t() ^ QTime::currentTime().msec());
156 #elif QT_VERSION < QT_VERSION_CHECK(5,10,0)
157  srandom(MythDate::current().toSecsSinceEpoch() ^ QTime::currentTime().msec());
158 #endif
159 }
160 
161 static void delete_sock(QMutexLocker &locker, MythSocket **s)
162 {
163  if (*s)
164  {
165  MythSocket *tmp = *s;
166  *s = nullptr;
167  locker.unlock();
168  tmp->DecrRef();
169  locker.relock();
170  }
171 }
172 
174 {
175  if (m_power)
176  MythPower::AcquireRelease(this, false);
177 
179 
180  {
181  QMutexLocker locker(&m_sockLock);
182  delete_sock(locker, &m_serverSock);
183  delete_sock(locker, &m_eventSock);
184  }
185 
186  delete m_locale;
187 
188  delete m_sessionManager;
189 
191 
193 
195 
196  // This has already been run in the MythContext dtor. Do we need it here
197  // too?
198 #if 0
199  logStop(); // need to shutdown db logger before we kill db
200 #endif
201 
203 
204  GetMythDB()->GetDBManager()->CloseDatabases();
205 
206  if (m_database) {
207  DestroyMythDB();
208  m_database = nullptr;
209  }
210 
212 }
213 
217 bool MythCoreContextPrivate::WaitForWOL(int timeout_in_ms)
218 {
219  int timeout_remaining = timeout_in_ms;
220  while (m_wolInProgress && (timeout_remaining > 0))
221  {
222  LOG(VB_GENERAL, LOG_INFO, LOC + "Wake-On-LAN in progress, waiting...");
223 
224  int max_wait = min(1000, timeout_remaining);
226  &m_wolInProgressLock, max_wait);
227  timeout_remaining -= max_wait;
228  }
229 
230  return !m_wolInProgress;
231 }
232 
233 MythCoreContext::MythCoreContext(const QString &binversion,
234  QObject *guiContext)
235  : d(nullptr)
236 {
237  d = new MythCoreContextPrivate(this, binversion, guiContext);
238 }
239 
241 {
242  if (!d)
243  {
244  LOG(VB_GENERAL, LOG_EMERG, LOC + "Out-of-memory");
245  return false;
246  }
247 
249  {
250  LOG(VB_GENERAL, LOG_CRIT,
251  QString("Application binary version (%1) does not "
252  "match libraries (%2)")
254 
255  QString warning = tr("This application is not compatible with the "
256  "installed MythTV libraries. Please recompile "
257  "after a make distclean");
258  LOG(VB_GENERAL, LOG_WARNING, warning);
259 
260  return false;
261  }
262 
263 #ifndef _WIN32
264  QString lang_variables("");
265  QString lc_value = setlocale(LC_CTYPE, nullptr);
266  if (lc_value.isEmpty())
267  {
268  // try fallback to environment variables for non-glibc systems
269  // LC_ALL, then LC_CTYPE
270  lc_value = getenv("LC_ALL");
271  if (lc_value.isEmpty())
272  lc_value = getenv("LC_CTYPE");
273  }
274  if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
275  lang_variables.append("LC_ALL or LC_CTYPE");
276  lc_value = getenv("LANG");
277  if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
278  {
279  if (!lang_variables.isEmpty())
280  lang_variables.append(", and ");
281  lang_variables.append("LANG");
282  }
283  LOG(VB_GENERAL, LOG_INFO, QString("Assumed character encoding: %1")
284  .arg(lc_value));
285  if (!lang_variables.isEmpty())
286  {
287  LOG(VB_GENERAL, LOG_WARNING, QString("This application expects to "
288  "be running a locale that specifies a UTF-8 codeset, and many "
289  "features may behave improperly with your current language "
290  "settings. Please set the %1 variable(s) in the environment "
291  "in which this program is executed to include a UTF-8 codeset "
292  "(such as 'en_US.UTF-8').").arg(lang_variables));
293  }
294 #endif
295 
296  return true;
297 }
298 
300 {
301  delete d;
302  d = nullptr;
303 }
304 
306  const QString &announcement,
307  uint timeout_in_ms,
308  bool &proto_mismatch)
309 {
310  proto_mismatch = false;
311 
312 #ifndef IGNORE_PROTO_VER_MISMATCH
313  if (!CheckProtoVersion(serverSock, timeout_in_ms, true))
314  {
315  proto_mismatch = true;
316  return false;
317  }
318 #else
319  Q_UNUSED(timeout_in_ms);
320 #endif
321 
322  QStringList strlist(announcement);
323 
324  if (!serverSock->WriteStringList(strlist))
325  {
326  LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
327  "master backend, socket write failed");
328  return false;
329  }
330 
331  if (!serverSock->ReadStringList(strlist, MythSocket::kShortTimeout) ||
332  strlist.empty() || (strlist[0] == "ERROR"))
333  {
334  if (!strlist.empty())
335  {
336  LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
337  "server socket to master backend");
338  }
339  else
340  {
341  LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
342  "server socket to master backend");
343  }
344  return false;
345  }
346 
347  return true;
348 }
349 
350 // Connects to master server safely (i.e. by taking m_sockLock)
352  bool openEventSocket)
353 {
354  QMutexLocker locker(&d->m_sockLock);
355  bool success = ConnectToMasterServer(blockingClient, openEventSocket);
356 
357  return success;
358 }
359 
360 // Assumes that either m_sockLock is held, or the app is still single
361 // threaded (i.e. during startup).
362 bool MythCoreContext::ConnectToMasterServer(bool blockingClient,
363  bool openEventSocket)
364 {
365  if (IsMasterBackend())
366  {
367  // Should never get here unless there is a bug in the code somewhere.
368  // If this happens, it can cause endless event loops.
369  LOG(VB_GENERAL, LOG_ERR, LOC + "ERROR: Master backend tried to connect back "
370  "to itself!");
371  return false;
372  }
373  if (IsExiting())
374  return false;
375 
376  QString server = GetMasterServerIP();
377  if (server.isEmpty())
378  return false;
379 
380  int port = GetMasterServerPort();
381  bool proto_mismatch = false;
382 
383  if (d->m_serverSock && !d->m_serverSock->IsConnected())
384  {
385  d->m_serverSock->DecrRef();
386  d->m_serverSock = nullptr;
387  }
388 
389  if (!d->m_serverSock)
390  {
391  QString type = IsFrontend() ? "Frontend" : (blockingClient ? "Playback" : "Monitor");
392  QString ann = QString("ANN %1 %2 %3")
393  .arg(type)
394  .arg(d->m_localHostname).arg(false);
396  server, port, ann, &proto_mismatch);
397  }
398 
399  if (!d->m_serverSock)
400  return false;
401 
402  d->m_blockingClient = blockingClient;
403 
404  if (!openEventSocket)
405  return true;
406 
407 
408  if (!IsBackend())
409  {
410  if (d->m_eventSock && !d->m_eventSock->IsConnected())
411  {
412  d->m_eventSock->DecrRef();
413  d->m_eventSock = nullptr;
414  }
415  if (!d->m_eventSock)
416  d->m_eventSock = ConnectEventSocket(server, port);
417 
418  if (!d->m_eventSock)
419  {
420  d->m_serverSock->DecrRef();
421  d->m_serverSock = nullptr;
422 
423  QCoreApplication::postEvent(
424  d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
425 
426  return false;
427  }
428  }
429 
430  return true;
431 }
432 
434  const QString &hostname, int port, const QString &announce,
435  bool *p_proto_mismatch, int maxConnTry, int setup_timeout)
436 {
437  MythSocket *serverSock = nullptr;
438 
439  {
440  QMutexLocker locker(&d->m_wolInProgressLock);
441  d->WaitForWOL();
442  }
443 
444  QString WOLcmd;
445  if (IsWOLAllowed())
446  WOLcmd = GetSetting("WOLbackendCommand", "");
447 
448  if (maxConnTry < 1)
449  maxConnTry = max(GetNumSetting("BackendConnectRetry", 1), 1);
450 
451  int WOLsleepTime = 0;
452  int WOLmaxConnTry = 0;
453  if (!WOLcmd.isEmpty())
454  {
455  WOLsleepTime = GetNumSetting("WOLbackendReconnectWaitTime", 0);
456  WOLmaxConnTry = max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
457  maxConnTry = max(maxConnTry, WOLmaxConnTry);
458  }
459 
460  bool we_attempted_wol = false;
461 
462  if (setup_timeout <= 0)
463  setup_timeout = MythSocket::kShortTimeout;
464 
465  bool proto_mismatch = false;
466  for (int cnt = 1; cnt <= maxConnTry; cnt++)
467  {
468  LOG(VB_GENERAL, LOG_INFO, LOC +
469  QString("Connecting to backend server: %1:%2 (try %3 of %4)")
470  .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
471 
472  serverSock = new MythSocket();
473 
474  int sleepms = 0;
475  if (serverSock->ConnectToHost(hostname, port))
476  {
477  if (SetupCommandSocket(
478  serverSock, announce, setup_timeout, proto_mismatch))
479  {
480  break;
481  }
482 
483  if (proto_mismatch)
484  {
485  if (p_proto_mismatch)
486  *p_proto_mismatch = true;
487 
488  serverSock->DecrRef();
489  serverSock = nullptr;
490  break;
491  }
492 
493  setup_timeout = (int)(setup_timeout * 1.5F);
494  }
495  else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
496  {
497  if (!we_attempted_wol)
498  {
499  QMutexLocker locker(&d->m_wolInProgressLock);
500  if (d->m_wolInProgress)
501  {
502  d->WaitForWOL();
503  continue;
504  }
505 
506  d->m_wolInProgress = we_attempted_wol = true;
507  }
508 
511  sleepms = WOLsleepTime * 1000;
512  }
513 
514  serverSock->DecrRef();
515  serverSock = nullptr;
516 
517  if (cnt == 1)
518  {
519  QCoreApplication::postEvent(
520  d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
521  }
522 
523  if (sleepms)
524  usleep(sleepms * 1000);
525  }
526 
527  if (we_attempted_wol)
528  {
529  QMutexLocker locker(&d->m_wolInProgressLock);
530  d->m_wolInProgress = false;
531  d->m_wolInProgressWaitCondition.wakeAll();
532  }
533 
534  if (!serverSock && !proto_mismatch)
535  {
536  LOG(VB_GENERAL, LOG_ERR,
537  "Connection to master server timed out.\n\t\t\t"
538  "Either the server is down or the master server settings"
539  "\n\t\t\t"
540  "in mythtv-settings does not contain the proper IP address\n");
541  }
542  else
543  {
544  QCoreApplication::postEvent(
545  d->m_guiContext, new MythEvent("CONNECTION_RESTABLISHED"));
546  }
547 
548  return serverSock;
549 }
550 
552  int port)
553 {
554  auto *eventSock = new MythSocket(-1, this);
555 
556  // Assume that since we _just_ connected the command socket,
557  // this one won't need multiple retries to work...
558  if (!eventSock->ConnectToHost(hostname, port))
559  {
560  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
561  "socket to master backend");
562  eventSock->DecrRef();
563  return nullptr;
564  }
565 
566  QString str = QString("ANN Monitor %1 %2")
567  .arg(d->m_localHostname).arg(true);
568  QStringList strlist(str);
569  eventSock->WriteStringList(strlist);
570  bool ok = true;
571  if (!eventSock->ReadStringList(strlist) || strlist.empty() ||
572  (strlist[0] == "ERROR"))
573  {
574  if (!strlist.empty())
575  {
576  LOG(VB_GENERAL, LOG_ERR, LOC +
577  "Problem connecting event socket to master backend");
578  }
579  else
580  {
581  LOG(VB_GENERAL, LOG_ERR, LOC +
582  "Timeout connecting event socket to master backend");
583  }
584  ok = false;
585  }
586 
587  if (!ok)
588  {
589  eventSock->DecrRef();
590  eventSock = nullptr;
591  }
592 
593  return eventSock;
594 }
595 
597 {
598  QMutexLocker locker(&d->m_sockLock);
599  return d->m_serverSock;
600 }
601 
603 {
604  QStringList strlist;
605 
606  QMutexLocker locker(&d->m_sockLock);
607  if (d->m_serverSock == nullptr)
608  return;
609 
610  strlist << "BLOCK_SHUTDOWN";
612 
613  d->m_blockingClient = true;
614 }
615 
617 {
618  QStringList strlist;
619 
620  QMutexLocker locker(&d->m_sockLock);
621  if (d->m_serverSock == nullptr)
622  return;
623 
624  strlist << "ALLOW_SHUTDOWN";
626 
627  d->m_blockingClient = false;
628 }
629 
631 {
632  return d->m_blockingClient;
633 }
634 
636 {
637  d->m_isWOLAllowed = allow;
638 }
639 
641 {
642  return d->m_isWOLAllowed;
643 }
644 
646 {
647  d->m_backend = backend;
648 }
649 
651 {
652  return d->m_backend;
653 }
654 
656 {
657  d->m_frontend = frontend;
658 }
659 
661 {
662  return d->m_frontend;
663 }
664 
666 {
667  QString host = GetHostName();
668  return IsMasterHost(host);
669 }
670 
671 bool MythCoreContext::IsMasterHost(const QString &host)
672 {
673  // Temporary code here only to facilitate the upgrade
674  // from 1346 or earlier. The way of determining master host is
675  // changing, and the new way of determning master host
676  // will not work with earlier databases.
677  // This code can be removed when updating from prior to
678  // 1347 is no longer allowed.
679  // Note that we are deprecating some settings including
680  // MasterServerIP, and can remove them at a future time.
681  if (GetNumSetting("DBSchemaVer") < 1347)
682  {
683  // Temporary copy of code from old version of
684  // IsThisHost(Qstring&,QString&)
685  QString addr(resolveSettingAddress("MasterServerIP"));
686  if (addr.toLower() == host.toLower())
687  return true;
688  QHostAddress addrfix(addr);
689  addrfix.setScopeId(QString());
690  QString addrstr = addrfix.toString();
691  if (addrfix.isNull())
692  addrstr = resolveAddress(addr);
693  QString thisip = GetBackendServerIP4(host);
694  QString thisip6 = GetBackendServerIP6(host);
695  return !addrstr.isEmpty()
696  && ((addrstr == thisip) || (addrstr == thisip6));
697  }
698  return GetSetting("MasterServerName") == host;
699 }
700 
702 {
703  return (IsBackend() && IsMasterHost());
704 }
705 
707 {
708 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
709  const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
710 #elif defined _WIN32
711  const char *command = "%systemroot%\\system32\\tasklist.exe "
712  " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
713 #else
714  const char *command = "ps ch -C mythbackend -o pid > /dev/null";
715 #endif
716  uint res = myth_system(command, kMSDontBlockInputDevs |
719  return (res == GENERIC_EXIT_OK);
720 }
721 
722 bool MythCoreContext::IsThisBackend(const QString &addr)
723 {
724  return IsBackend() && IsThisHost(addr);
725 }
726 
727 bool MythCoreContext::IsThisHost(const QString &addr)
728 {
729  return IsThisHost(addr, GetHostName());
730 }
731 
732 bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
733 {
734  if (addr.toLower() == host.toLower())
735  return true;
736 
737  QHostAddress addrfix(addr);
738  addrfix.setScopeId(QString());
739  QString addrstr = addrfix.toString();
740 
741  if (addrfix.isNull())
742  {
743  addrstr = resolveAddress(addr);
744  }
745 
746  QString thisip = GetBackendServerIP(host);
747 
748  return !addrstr.isEmpty() && ((addrstr == thisip));
749 }
750 
752 {
753  // find out if a backend runs on this host...
754  bool backendOnLocalhost = false;
755 
756  QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
757  strlist << GetHostName();
758 
759  SendReceiveStringList(strlist);
760 
761  backendOnLocalhost = strlist[0] != "FALSE";
762 
763  return !backendOnLocalhost;
764 }
765 
766 QString MythCoreContext::GenMythURL(const QString& host, int port, QString path, const QString& storageGroup)
767 {
768  QUrl ret;
769 
770  QString m_host;
771 
772  QHostAddress addr(host);
773  if (!addr.isNull())
774  {
775  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("(%1/%2): Given "
776  "IP address instead of hostname "
777  "(ID). This is invalid.").arg(host).arg(path));
778  }
779 
780  m_host = host;
781 
782  // Basically if it appears to be an IPv6 IP surround the IP with [] otherwise don't bother
783  if (!addr.isNull() && addr.protocol() == QAbstractSocket::IPv6Protocol)
784  m_host = "[" + addr.toString().toLower() + "]";
785 
786  ret.setScheme("myth");
787  if (!storageGroup.isEmpty())
788  ret.setUserName(storageGroup);
789  ret.setHost(m_host);
790  if (port > 0 && port != 6543)
791  ret.setPort(port);
792  if (!path.startsWith("/"))
793  path = QString("/") + path;
794  ret.setPath(path);
795 
796 #if 0
797  LOG(VB_GENERAL, LOG_DEBUG, LOC +
798  QString("GenMythURL returning %1").arg(ret.toString()));
799 #endif
800 
801  return ret.toString();
802 }
803 
804 QString MythCoreContext::GetMasterHostPrefix(const QString &storageGroup,
805  const QString &path)
806 {
807  return GenMythURL(GetMasterHostName(),
809  path,
810  storageGroup);
811 }
812 
814 {
815  QMutexLocker locker(&d->m_masterHostLock);
816 
817  if (d->m_masterHostname.isEmpty())
818  {
819 
820  if (IsMasterBackend())
822  else
823  {
824  QStringList strlist("QUERY_HOSTNAME");
825 
826  if (SendReceiveStringList(strlist))
827  d->m_masterHostname = strlist[0];
828  }
829  }
830 
831  return d->m_masterHostname;
832 }
833 
834 void MythCoreContext::ClearSettingsCache(const QString &myKey)
835 {
836  d->m_database->ClearSettingsCache(myKey);
837 }
838 
840 {
841  d->m_database->ActivateSettingsCache(activate);
842 }
843 
845 {
846  QMutexLocker locker(&d->m_localHostLock);
847  return d->m_localHostname;
848 }
849 
851 {
852  return GetSetting("RecordFilePrefix");
853 }
854 
856  int &width, int &height,
857  double &forced_aspect,
858  double &refresh_rate,
859  int index)
860 {
861  d->m_database->GetResolutionSetting(type, width, height, forced_aspect,
862  refresh_rate, index);
863 }
864 
865 void MythCoreContext::GetResolutionSetting(const QString &t, int &w,
866  int &h, int i)
867 {
868  d->m_database->GetResolutionSetting(t, w, h, i);
869 }
870 
872 {
873  return d->m_database->GetDBManager();
874 }
875 
883 {
884  return d->m_database->IsDatabaseIgnored();
885 }
886 
887 void MythCoreContext::SaveSetting(const QString &key, int newValue)
888 {
889  d->m_database->SaveSetting(key, newValue);
890 }
891 
892 void MythCoreContext::SaveSetting(const QString &key, const QString &newValue)
893 {
894  d->m_database->SaveSetting(key, newValue);
895 }
896 
897 bool MythCoreContext::SaveSettingOnHost(const QString &key,
898  const QString &newValue,
899  const QString &host)
900 {
901  return d->m_database->SaveSettingOnHost(key, newValue, host);
902 }
903 
904 QString MythCoreContext::GetSetting(const QString &key,
905  const QString &defaultval)
906 {
907  return d->m_database->GetSetting(key, defaultval);
908 }
909 
910 bool MythCoreContext::GetBoolSetting(const QString &key, bool defaultval)
911 {
912  int result = GetNumSetting(key, static_cast<int>(defaultval));
913  return result > 0;
914 }
915 
916 int MythCoreContext::GetNumSetting(const QString &key, int defaultval)
917 {
918  return d->m_database->GetNumSetting(key, defaultval);
919 }
920 
921 double MythCoreContext::GetFloatSetting(const QString &key, double defaultval)
922 {
923  return d->m_database->GetFloatSetting(key, defaultval);
924 }
925 
926 QString MythCoreContext::GetSettingOnHost(const QString &key,
927  const QString &host,
928  const QString &defaultval)
929 {
930  return d->m_database->GetSettingOnHost(key, host, defaultval);
931 }
932 
934  const QString &host,
935  bool defaultval)
936 {
937  int result = GetNumSettingOnHost(key, host, static_cast<int>(defaultval));
938  return result > 0;
939 }
940 
942  const QString &host,
943  int defaultval)
944 {
945  return d->m_database->GetNumSettingOnHost(key, host, defaultval);
946 }
947 
948 double MythCoreContext::GetFloatSettingOnHost(const QString &key,
949  const QString &host,
950  double defaultval)
951 {
952  return d->m_database->GetFloatSettingOnHost(key, host, defaultval);
953 }
954 
961 {
962  QString masterserver = gCoreContext->GetSetting("MasterServerName");
963  QString masterip = resolveSettingAddress("BackendServerAddr",masterserver);
964  // Even if empty, return it here if we were to assume that localhost
965  // should be used it just causes a lot of unnecessary error messages.
966  return masterip;
967 }
968 
975 {
976  QString masterserver = gCoreContext->GetSetting
977  ("MasterServerName");
979  ("BackendServerPort", masterserver, 6543);
980 }
981 
988 {
989  QString masterhost = GetMasterHostName();
990 
991  return GetBackendStatusPort(masterhost);
992 }
993 
999 {
1001 }
1002 
1012 QString MythCoreContext::GetBackendServerIP(const QString &host)
1013 {
1014  return resolveSettingAddress("BackendServerAddr",host);
1015 }
1016 
1022 {
1024 }
1025 
1031 QString MythCoreContext::GetBackendServerIP4(const QString &host)
1032 {
1033  return resolveSettingAddress("BackendServerIP", host, ResolveIPv4);
1034 }
1035 
1041 {
1043 }
1044 
1050 QString MythCoreContext::GetBackendServerIP6(const QString &host)
1051 {
1052  return resolveSettingAddress("BackendServerIP6", host, ResolveIPv6);
1053 }
1054 
1059 {
1061 }
1062 
1067 {
1068  return GetNumSettingOnHost("BackendServerPort", host, 6543);
1069 }
1070 
1075 {
1077 }
1078 
1083 {
1084  return GetNumSettingOnHost("BackendStatusPort", host, 6544);
1085 }
1086 
1091 bool MythCoreContext::GetScopeForAddress(QHostAddress &addr) const
1092 {
1093  QHostAddress addr1 = addr;
1094  addr1.setScopeId(QString());
1095  QString addrstr = addr1.toString();
1096  QMutexLocker lock(&d->m_scopesLock);
1097 
1098  if (!d->m_scopes.contains(addrstr))
1099  return false;
1100 
1101  addr.setScopeId(d->m_scopes[addrstr]);
1102  return true;
1103 }
1104 
1111 void MythCoreContext::SetScopeForAddress(const QHostAddress &addr)
1112 {
1113  QHostAddress addr1 = addr;
1114  addr1.setScopeId(QString());
1115  QMutexLocker lock(&d->m_scopesLock);
1116 
1117  d->m_scopes.insert(addr1.toString(), addr.scopeId());
1118 }
1119 
1125 void MythCoreContext::SetScopeForAddress(const QHostAddress &addr, int scope)
1126 {
1127  QHostAddress addr1 = addr;
1128  addr1.setScopeId(QString());
1129  QMutexLocker lock(&d->m_scopesLock);
1130 
1131  d->m_scopes.insert(addr1.toString(), QString::number(scope));
1132 }
1133 
1145 QString MythCoreContext::resolveSettingAddress(const QString &name,
1146  const QString &host,
1147  ResolveType type, bool keepscope)
1148 {
1149  QString value;
1150 
1151  if (host.isEmpty())
1152  {
1153  value = GetSetting(name);
1154  }
1155  else
1156  {
1157  value = GetSettingOnHost(name, host);
1158  }
1159 
1160  return resolveAddress(value, type, keepscope);
1161 }
1162 
1174 QString MythCoreContext::resolveAddress(const QString &host, ResolveType type,
1175  bool keepscope)
1176 {
1177  QHostAddress addr(host);
1178 
1179  if (!host.isEmpty() && addr.isNull())
1180  {
1181  // address is likely a hostname, attempts to resolve it
1182  QHostInfo info = QHostInfo::fromName(host);
1183  QList<QHostAddress> list = info.addresses();
1184 
1185  if (list.isEmpty())
1186  {
1187  LOG(VB_GENERAL, LOG_WARNING, LOC +
1188  QString("Can't resolve hostname:'%1', using localhost").arg(host));
1189  return type == ResolveIPv4 ? "127.0.0.1" : "::1";
1190  }
1191  QHostAddress v4;
1192  QHostAddress v6;
1193 
1194  // Return the first address fitting the type critera
1195  foreach (const auto & item, list)
1196  {
1197  addr = item;
1198  QAbstractSocket::NetworkLayerProtocol prot = addr.protocol();
1199 
1200  if (prot == QAbstractSocket::IPv4Protocol)
1201  {
1202  v4 = addr;
1203  if (type == 0)
1204  break;
1205  }
1206  else if (prot == QAbstractSocket::IPv6Protocol)
1207  {
1208  v6 = addr;
1209  if (type != 0)
1210  break;
1211  }
1212  }
1213  switch (type)
1214  {
1215  case ResolveIPv4:
1216  addr = v4.isNull() ? QHostAddress::LocalHost : v4;
1217  break;
1218  case ResolveIPv6:
1219  addr = v6.isNull() ? QHostAddress::LocalHostIPv6 : v6;
1220  break;
1221  default:
1222  addr = v6.isNull() ?
1223  (v4.isNull() ? QHostAddress::LocalHostIPv6 : v4) : v6;
1224  break;
1225  }
1226  }
1227  else if (host.isEmpty())
1228  {
1229  return QString();
1230  }
1231 
1232  if (!keepscope)
1233  {
1234  addr.setScopeId(QString());
1235  }
1236  return addr.toString();
1237 }
1238 
1250 bool MythCoreContext::CheckSubnet(const QAbstractSocket *socket)
1251 {
1252  QHostAddress peer = socket->peerAddress();
1253  return CheckSubnet(peer);
1254 }
1255 
1267 bool MythCoreContext::CheckSubnet(const QHostAddress &peer)
1268 {
1269  static const QHostAddress kLinkLocal("fe80::");
1270  if (GetBoolSetting("AllowConnFromAll",false))
1271  return true;
1272  if (d->m_approvedIps.contains(peer))
1273  return true;
1274  if (d->m_deniedIps.contains(peer))
1275  {
1276  LOG(VB_GENERAL, LOG_WARNING, LOC +
1277  QString("Repeat denied connection from ip address: %1")
1278  .arg(peer.toString()));
1279  return false;
1280  }
1281 
1282  // allow all link-local
1283  if (peer.isInSubnet(kLinkLocal,10))
1284  {
1285  d->m_approvedIps.append(peer);
1286  return true;
1287  }
1288 
1289  // loop through all available interfaces
1290  QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
1291  QList<QNetworkInterface>::const_iterator qni;
1292  for (qni = IFs.begin(); qni != IFs.end(); ++qni)
1293  {
1294  if ((qni->flags() & QNetworkInterface::IsRunning) == 0)
1295  continue;
1296 
1297  QList<QNetworkAddressEntry> IPs = qni->addressEntries();
1298  QList<QNetworkAddressEntry>::iterator qnai;
1299  for (qnai = IPs.begin(); qnai != IPs.end(); ++qnai)
1300  {
1301  int pfxlen = qnai->prefixLength();
1302  // Set this to test rejection without having an extra
1303  // network.
1304  if (GetBoolSetting("DebugSubnet"))
1305  pfxlen += 4;
1306  if (peer.isInSubnet(qnai->ip(),pfxlen))
1307  {
1308  d->m_approvedIps.append(peer);
1309  return true;
1310  }
1311  }
1312  }
1313  d->m_deniedIps.append(peer);
1314  LOG(VB_GENERAL, LOG_WARNING, LOC +
1315  QString("Denied connection from ip address: %1")
1316  .arg(peer.toString()));
1317  return false;
1318 }
1319 
1320 
1322  const QString &value)
1323 {
1324  d->m_database->OverrideSettingForSession(key, value);
1325 }
1326 
1328 {
1329  d->m_database->ClearOverrideSettingForSession(key);
1330 }
1331 
1333 {
1334  return is_current_thread(d->m_uiThread);
1335 }
1336 
1356  QStringList &strlist, bool quickTimeout, bool block)
1357 {
1358  QString msg;
1359  if (HasGUI() && IsUIThread())
1360  {
1361  msg = "SendReceiveStringList(";
1362  for (uint i=0; i<(uint)strlist.size() && i<2; i++)
1363  msg += (i?",":"") + strlist[i];
1364  msg += (strlist.size() > 2) ? "...)" : ")";
1365  LOG(VB_GENERAL, LOG_DEBUG, LOC + msg + " called from UI thread");
1366  }
1367 
1368  QString query_type = "UNKNOWN";
1369 
1370  if (!strlist.isEmpty())
1371  query_type = strlist[0];
1372 
1373  QMutexLocker locker(&d->m_sockLock);
1374  if (!d->m_serverSock)
1375  {
1376  bool blockingClient = d->m_blockingClient &&
1377  (GetNumSetting("idleTimeoutSecs",0) > 0);
1378  ConnectToMasterServer(blockingClient);
1379  }
1380 
1381  bool ok = false;
1382 
1383  if (d->m_serverSock)
1384  {
1385  QStringList sendstrlist = strlist;
1386  uint timeout = quickTimeout ?
1388  ok = d->m_serverSock->SendReceiveStringList(strlist, 0, timeout);
1389 
1390  if (!ok)
1391  {
1392  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1393  QString("Connection to backend server lost"));
1394  d->m_serverSock->DecrRef();
1395  d->m_serverSock = nullptr;
1396 
1397  if (d->m_eventSock)
1398  {
1399  d->m_eventSock->DecrRef();
1400  d->m_eventSock = nullptr;
1401  }
1402 
1403  if (block)
1404  {
1406 
1407  if (d->m_serverSock)
1408  {
1410  strlist, 0, timeout);
1411  }
1412  }
1413  }
1414 
1415  // this should not happen
1416  while (ok && strlist[0] == "BACKEND_MESSAGE")
1417  {
1418  // oops, not for us
1419  LOG(VB_GENERAL, LOG_EMERG, LOC + "SRSL you shouldn't see this!!");
1420  QString message = strlist[1];
1421  strlist.pop_front(); strlist.pop_front();
1422 
1423  MythEvent me(message, strlist);
1424  dispatch(me);
1425 
1426  ok = d->m_serverSock->ReadStringList(strlist, timeout);
1427  }
1428 
1429  if (!ok)
1430  {
1431  if (d->m_serverSock)
1432  {
1433  d->m_serverSock->DecrRef();
1434  d->m_serverSock = nullptr;
1435  }
1436 
1437  LOG(VB_GENERAL, LOG_CRIT, LOC +
1438  QString("Reconnection to backend server failed"));
1439 
1440  QCoreApplication::postEvent(d->m_guiContext,
1441  new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
1442  }
1443  }
1444 
1445  if (ok)
1446  {
1447  if (strlist.isEmpty())
1448  ok = false;
1449  else if (strlist[0] == "ERROR")
1450  {
1451  if (strlist.size() == 2)
1452  {
1453  LOG(VB_GENERAL, LOG_INFO, LOC +
1454  QString("Protocol query '%1' responded with the error '%2'")
1455  .arg(query_type).arg(strlist[1]));
1456  }
1457  else
1458  {
1459  LOG(VB_GENERAL, LOG_INFO, LOC +
1460  QString("Protocol query '%1' responded with an error, but "
1461  "no error message.") .arg(query_type));
1462  }
1463 
1464  ok = false;
1465  }
1466  else if (strlist[0] == "UNKNOWN_COMMAND")
1467  {
1468  LOG(VB_GENERAL, LOG_ERR, LOC +
1469  QString("Protocol query '%1' responded with the error 'UNKNOWN_COMMAND'")
1470  .arg(query_type));
1471 
1472  ok = false;
1473  }
1474  }
1475 
1476  return ok;
1477 }
1478 
1479 class SendAsyncMessage : public QRunnable
1480 {
1481  public:
1482  SendAsyncMessage(QString msg, QStringList extra) :
1483  m_message(std::move(msg)), m_extraData(std::move(extra))
1484  {
1485  }
1486 
1487  explicit SendAsyncMessage(QString msg) : m_message(std::move(msg)) { }
1488 
1489  void run(void) override // QRunnable
1490  {
1491  QStringList strlist("MESSAGE");
1492  strlist << m_message;
1493  strlist << m_extraData;
1495  }
1496 
1497  private:
1498  QString m_message;
1499  QStringList m_extraData;
1500 };
1501 
1502 void MythCoreContext::SendMessage(const QString &message)
1503 {
1504  if (IsBackend())
1505  {
1506  dispatch(MythEvent(message));
1507  }
1508  else
1509  {
1511  new SendAsyncMessage(message), "SendMessage");
1512  }
1513 }
1514 
1516 {
1517  if (IsBackend())
1518  {
1519  dispatch(event);
1520  }
1521  else
1522  {
1524  new SendAsyncMessage(event.Message(), event.ExtraDataList()),
1525  "SendEvent");
1526  }
1527 }
1528 
1529 void MythCoreContext::SendSystemEvent(const QString &msg)
1530 {
1531  if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1532  return;
1533 
1534  SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
1535  .arg(msg).arg(GetHostName()));
1536 }
1537 
1539  const QString &hostname,
1540  const QString &args)
1541 {
1542  SendSystemEvent(QString("%1 HOST %2 %3").arg(msg).arg(hostname).arg(args));
1543 }
1544 
1545 
1547 {
1548  do
1549  {
1550  QStringList strlist;
1551  if (!sock->ReadStringList(strlist))
1552  continue;
1553 
1554  if (strlist.size() < 2)
1555  continue;
1556 
1557  QString prefix = strlist[0];
1558  QString message = strlist[1];
1559  QStringList tokens = message.split(" ", QString::SkipEmptyParts);
1560 
1561  if (prefix == "OK")
1562  {
1563  }
1564  else if (prefix != "BACKEND_MESSAGE")
1565  {
1566  LOG(VB_NETWORK, LOG_ERR, LOC +
1567  QString("Received a: %1 message from the backend "
1568  "but I don't know what to do with it.")
1569  .arg(prefix));
1570  }
1571  else if (message == "CLEAR_SETTINGS_CACHE")
1572  {
1573  // No need to dispatch this message to ourself, so handle it
1574  LOG(VB_NETWORK, LOG_INFO, LOC + "Received remote 'Clear Cache' request");
1576  }
1577  else if (message.startsWith("FILE_WRITTEN"))
1578  {
1579  QString file;
1580  uint64_t size = 0;
1581  int NUMTOKENS = 3; // Number of tokens expected
1582 
1583  if (tokens.size() == NUMTOKENS)
1584  {
1585  file = tokens[1];
1586  size = tokens[2].toULongLong();
1587  }
1588  else
1589  {
1590  LOG(VB_NETWORK, LOG_ERR, LOC +
1591  QString("FILE_WRITTEN event received "
1592  "with invalid number of arguments, "
1593  "%1 expected, %2 actual")
1594  .arg(NUMTOKENS-1)
1595  .arg(tokens.size()-1));
1596  return;
1597  }
1598  // No need to dispatch this message to ourself, so handle it
1599  LOG(VB_NETWORK, LOG_INFO, LOC +
1600  QString("Received remote 'FILE_WRITTEN %1' request").arg(file));
1601  RegisterFileForWrite(file, size);
1602  }
1603  else if (message.startsWith("FILE_CLOSED"))
1604  {
1605  QString file;
1606  int NUMTOKENS = 2; // Number of tokens expected
1607 
1608  if (tokens.size() == NUMTOKENS)
1609  {
1610  file = tokens[1];
1611  }
1612  else
1613  {
1614  LOG(VB_NETWORK, LOG_ERR, LOC +
1615  QString("FILE_CLOSED event received "
1616  "with invalid number of arguments, "
1617  "%1 expected, %2 actual")
1618  .arg(NUMTOKENS-1)
1619  .arg(tokens.size()-1));
1620  return;
1621  }
1622  // No need to dispatch this message to ourself, so handle it
1623  LOG(VB_NETWORK, LOG_INFO, LOC +
1624  QString("Received remote 'FILE_CLOSED %1' request").arg(file));
1626  }
1627  else
1628  {
1629  strlist.pop_front();
1630  strlist.pop_front();
1631  MythEvent me(message, strlist);
1632  dispatch(me);
1633  }
1634  }
1635  while (sock->IsDataAvailable());
1636 }
1637 
1639 {
1640  (void)sock;
1641 
1642  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1643  "Event socket closed. No connection to the backend.");
1644 
1645  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1646 }
1647 
1649  bool error_dialog_desired)
1650 {
1651  if (!socket)
1652  return false;
1653 
1654  QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
1655  .arg(MYTH_PROTO_VERSION)
1656  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1657  socket->WriteStringList(strlist);
1658 
1659  if (!socket->ReadStringList(strlist, timeout_ms) || strlist.empty())
1660  {
1661  LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
1662  "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
1663  "This happens when the backend is too busy to respond,\n\t\t\t"
1664  "or has deadlocked due to bugs or hardware failure.");
1665 
1666  return false;
1667  }
1668  if (strlist[0] == "REJECT" && strlist.size() >= 2)
1669  {
1670  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Protocol version or token mismatch "
1671  "(frontend=%1/%2,backend=%3/\?\?)\n")
1672  .arg(MYTH_PROTO_VERSION)
1673  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN))
1674  .arg(strlist[1]));
1675 
1676  if (error_dialog_desired && d->m_guiContext)
1677  {
1678  QStringList list(strlist[1]);
1679  QCoreApplication::postEvent(
1680  d->m_guiContext, new MythEvent("VERSION_MISMATCH", list));
1681  }
1682 
1683  return false;
1684  }
1685  if (strlist[0] == "ACCEPT")
1686  {
1687  if (!d->m_announcedProtocol)
1688  {
1689  d->m_announcedProtocol = true;
1690  LOG(VB_GENERAL, LOG_INFO, LOC +
1691  QString("Using protocol version %1 %2")
1692  .arg(MYTH_PROTO_VERSION)
1693  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1694  }
1695 
1696  return true;
1697  }
1698 
1699  LOG(VB_GENERAL, LOG_ERR, LOC +
1700  QString("Unexpected response to MYTH_PROTO_VERSION: %1")
1701  .arg(strlist[0]));
1702  return false;
1703 }
1704 
1706 {
1707  LOG(VB_NETWORK, LOG_INFO, LOC + QString("MythEvent: %1").arg(event.Message()));
1708 
1709  MythObservable::dispatch(event);
1710 }
1711 
1713 {
1714  QMutexLocker locker(&d->m_localHostLock);
1716  d->m_database->SetLocalHostname(hostname);
1717 }
1718 
1720 {
1721  d->m_guiObject = gui;
1722 }
1723 
1724 bool MythCoreContext::HasGUI(void) const
1725 {
1726  return d->m_guiObject;
1727 }
1728 
1730 {
1731  return d->m_guiObject;
1732 }
1733 
1735 {
1736  return d->m_guiContext;
1737 }
1738 
1740 {
1741  return d->m_database;
1742 }
1743 
1745 {
1746  return d->m_locale;
1747 }
1748 
1754 {
1755  return GetLanguageAndVariant().left(2);
1756 }
1757 
1766 {
1767  if (d->m_language.isEmpty())
1768  d->m_language = GetSetting("Language", "en_US").toLower();
1769 
1770  return d->m_language;
1771 }
1772 
1774 {
1775  d->m_language.clear();
1776 }
1777 
1779 {
1780  QMutexLocker locker(&d->m_sockLock);
1781  LOG(VB_GENERAL, LOG_INFO, "Restarting Backend Connections");
1782  if (d->m_serverSock)
1784  if (d->m_eventSock)
1786  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1787 }
1788 
1790 {
1791  if (Create && !d->m_power)
1792  {
1794  }
1795  else if (!Create && d->m_power)
1796  {
1797  MythPower::AcquireRelease(d, false);
1798  d->m_power = nullptr;
1799  }
1800 }
1801 
1803 {
1804  if (!d->m_locale)
1805  d->m_locale = new MythLocale();
1806 
1807  QString localeCode = d->m_locale->GetLocaleCode();
1808  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1809  .arg(localeCode));
1810  QLocale::setDefault(d->m_locale->ToQLocale());
1811 }
1812 
1814 {
1815  if (!d->m_locale)
1816  d->m_locale = new MythLocale();
1817  else
1818  d->m_locale->ReInit();
1819 
1820  QString localeCode = d->m_locale->GetLocaleCode();
1821  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1822  .arg(localeCode));
1823  QLocale::setDefault(d->m_locale->ToQLocale());
1824 }
1825 
1827 {
1828  if (!d->m_locale)
1829  InitLocale();
1830 
1831  return d->m_locale->ToQLocale();
1832 }
1833 
1835 {
1836  if (!d->m_locale)
1837  InitLocale();
1838 
1839  if (!d->m_locale->GetLocaleCode().isEmpty())
1840  {
1841  LOG(VB_GENERAL, LOG_INFO,
1842  QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
1843 
1845  return;
1846  }
1847 
1848  LOG(VB_GENERAL, LOG_ERR, LOC +
1849  "No locale defined! We weren't able to set locale defaults.");
1850 }
1851 
1853 {
1854  d->m_scheduler = sched;
1855 }
1856 
1858 {
1859  return d->m_scheduler;
1860 }
1861 
1867 void MythCoreContext::WaitUntilSignals(const char *signal1, ...)
1868 {
1869  if (!signal1)
1870  return;
1871 
1872  QEventLoop eventLoop;
1873  va_list vl;
1874 
1875  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1876  QString("Waiting for signal %1")
1877  .arg(signal1));
1878  connect(this, signal1, &eventLoop, SLOT(quit()));
1879 
1880  va_start(vl, signal1);
1881  const char *s = va_arg(vl, const char *);
1882  while (s)
1883  {
1884  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1885  QString("Waiting for signal %1")
1886  .arg(s));
1887  connect(this, s, &eventLoop, SLOT(quit()));
1888  s = va_arg(vl, const char *);
1889  }
1890  va_end(vl);
1891 
1892  eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1893 }
1894 
1901 void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method)
1902 {
1903  if (!sender || !method)
1904  return;
1905 
1906  QMutexLocker lock(&d->m_playbackLock);
1907 
1908  if (!d->m_playbackClients.contains(sender))
1909  {
1910  d->m_playbackClients.insert(sender, QByteArray(method));
1911  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1912  sender, method,
1913  Qt::BlockingQueuedConnection);
1914  }
1915 }
1916 
1923 {
1924  QMutexLocker lock(&d->m_playbackLock);
1925 
1926  if (d->m_playbackClients.contains(sender))
1927  {
1928  QByteArray ba = d->m_playbackClients.value(sender);
1929  const char *method = ba.constData();
1930  disconnect(this, SIGNAL(TVPlaybackAboutToStart()),
1931  sender, method);
1932  d->m_playbackClients.remove(sender);
1933  }
1934 }
1935 
1943 {
1944  QMutexLocker lock(&d->m_playbackLock);
1945  QByteArray ba;
1946  const char *method = nullptr;
1947  d->m_inwanting = true;
1948 
1949  // If any registered client are in the same thread, they will deadlock, so rebuild
1950  // connections for any clients in the same thread as non-blocking connection
1951  QThread *currentThread = QThread::currentThread();
1952 
1953  QMap<QObject *, QByteArray>::iterator it = d->m_playbackClients.begin();
1954  for (; it != d->m_playbackClients.end(); ++it)
1955  {
1956  if (it.key() == sender)
1957  continue; // will be done separately, no need to do it again
1958 
1959  QThread *thread = it.key()->thread();
1960 
1961  if (thread != currentThread)
1962  continue;
1963 
1964  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1965  connect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1966  }
1967 
1968  // disconnect sender so it won't receive the message
1969  if (d->m_playbackClients.contains(sender))
1970  {
1971  ba = d->m_playbackClients.value(sender);
1972  method = ba.constData();
1973  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method);
1974  }
1975 
1976  // emit signal
1977  emit TVPlaybackAboutToStart();
1978 
1979  // reconnect sender
1980  if (method)
1981  {
1982  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1983  sender, method,
1984  Qt::BlockingQueuedConnection);
1985  }
1986  // Restore blocking connections
1987  for (; it != d->m_playbackClients.end(); ++it)
1988  {
1989  if (it.key() == sender)
1990  continue; // already done above, no need to do it again
1991 
1992  QThread *thread = it.key()->thread();
1993 
1994  if (thread != currentThread)
1995  continue;
1996 
1997  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1998  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1999  it.key(), it.value(), Qt::BlockingQueuedConnection);
2000  }
2001  d->m_inwanting = false;
2002 }
2003 
2010 {
2011  // when called, it will be while the m_playbackLock is held
2012  // following a call to WantingPlayback
2013  d->m_intvwanting = b;
2014 }
2015 
2022 {
2023  bool locked = d->m_playbackLock.tryLock();
2024  bool intvplayback = d->m_intvwanting;
2025 
2026  if (!locked && d->m_inwanting)
2027  return true; // we're in the middle of WantingPlayback
2028 
2029  if (!locked)
2030  return false;
2031 
2032  d->m_playbackLock.unlock();
2033 
2034  return intvplayback;
2035 }
2036 
2038 {
2039  if (!d->m_sessionManager)
2041 
2042  return d->m_sessionManager;
2043 }
2044 
2045 bool MythCoreContext::TestPluginVersion(const QString &name,
2046  const QString &libversion,
2047  const QString &pluginversion)
2048 {
2049  if (libversion == pluginversion)
2050  return true;
2051 
2052  LOG(VB_GENERAL, LOG_EMERG, LOC +
2053  QString("Plugin %1 (%2) binary version does not "
2054  "match libraries (%3)")
2055  .arg(name).arg(pluginversion).arg(libversion));
2056  return false;
2057 }
2058 
2060 {
2061  if (d->m_pluginmanager == pmanager)
2062  return;
2063 
2064  if (d->m_pluginmanager)
2065  {
2066  delete d->m_pluginmanager;
2067  d->m_pluginmanager = nullptr;
2068  }
2069 
2070  d->m_pluginmanager = pmanager;
2071 }
2072 
2074 {
2075  return d->m_pluginmanager;
2076 }
2077 
2079 {
2080  d->m_isexiting = exiting;
2081 }
2082 
2084 {
2085  return d->m_isexiting;
2086 }
2087 
2088 void MythCoreContext::RegisterFileForWrite(const QString& file, uint64_t size)
2089 {
2090  QMutexLocker lock(&d->m_fileslock);
2091 
2092  QPair<int64_t, uint64_t> pair(QDateTime::currentMSecsSinceEpoch(), size);
2093  d->m_fileswritten.insert(file, pair);
2094 
2095  if (IsBackend())
2096  {
2097  QString message = QString("FILE_WRITTEN %1 %2").arg(file).arg(size);
2098  MythEvent me(message);
2099  dispatch(me);
2100  }
2101 
2102  LOG(VB_FILE, LOG_DEBUG, LOC +
2103  QString("%1").arg(file));
2104 }
2105 
2107 {
2108  QMutexLocker lock(&d->m_fileslock);
2109 
2110  d->m_fileswritten.remove(file);
2111 
2112  if (IsBackend())
2113  {
2114  QString message = QString("FILE_CLOSED %1").arg(file);
2115  MythEvent me(message);
2116  dispatch(me);
2117  }
2118 
2119  LOG(VB_FILE, LOG_DEBUG, LOC +
2120  QString("%1").arg(file));
2121 }
2122 
2124 {
2125  QMutexLocker lock(&d->m_fileslock);
2126 
2127  return d->m_fileswritten.contains(file);
2128 }
2129 
2130 /* 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
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
QLocale ToQLocale() const
Definition: mythlocale.h:30
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
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:227
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)
static 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
bool IsBlockingClient(void) const
is this client blocking shutdown
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
bool IsDataAvailable(void)
Definition: mythsocket.cpp:567
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
static void ShutdownAllPools(void)
~MythCoreContext() override
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)
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
SendAsyncMessage(QString msg)
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)
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)
static 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
#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.
bool GetBoolSettingOnHost(const QString &key, const QString &host, bool defaultval=false)
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
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.
QString m_masterHostname
master backend hostname
void SetExiting(bool exiting=true)
static const uint16_t * d
void OverrideSettingForSession(const QString &key, const QString &value)
static 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="")
static bool BackendIsRunning(void)
a backend process is running on this host
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)
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.
string hostname
Definition: caa.py:17
unsigned int uint
Definition: compat.h:140
void InitPower(bool Create=true)
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:784
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)
void dispatch(const MythEvent &event)
void UnregisterFileForWrite(const QString &file)
bool IsBackend(void) const
is this process a backend process
QWaitCondition m_wolInProgressWaitCondition
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)
process events while waiting
Definition: mythsystem.h:37
#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)
avoid disabling UI drawing
Definition: mythsystem.h:35
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:750
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(""))
SendAsyncMessage(QString msg, QStringList extra)
static QPair< QHostAddress, int > kLinkLocal
Definition: serverpool.cpp:24
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)
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
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:65
void ActivateSettingsCache(bool activate=true)
static MythPower * AcquireRelease(void *Reference, bool Acquire, uint MinimumDelay=0)
Definition: mythpower.cpp:70
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
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
void SendSystemEvent(const QString &msg)
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.
void ResetLanguage(void)
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.