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  {
276  LOG(VB_GENERAL, LOG_WARNING, QString("This application expects to "
277  "be running a locale that specifies a UTF-8 codeset, and many "
278  "features may behave improperly with your current language "
279  "settings. Please set the %1 variable(s) in the environment "
280  "in which this program is executed to include a UTF-8 codeset "
281  "(such as 'en_US.UTF-8').").arg(lang_variables));
282  }
283 #endif
284 
285  return true;
286 }
287 
289 {
290  delete d;
291  d = nullptr;
292 }
293 
295  const QString &announcement,
296  uint timeout_in_ms,
297  bool &proto_mismatch)
298 {
299  proto_mismatch = false;
300 
301 #ifndef IGNORE_PROTO_VER_MISMATCH
302  if (!CheckProtoVersion(serverSock, timeout_in_ms, true))
303  {
304  proto_mismatch = true;
305  return false;
306  }
307 #else
308  Q_UNUSED(timeout_in_ms);
309 #endif
310 
311  QStringList strlist(announcement);
312 
313  if (!serverSock->WriteStringList(strlist))
314  {
315  LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
316  "master backend, socket write failed");
317  return false;
318  }
319 
320  if (!serverSock->ReadStringList(strlist, MythSocket::kShortTimeout) ||
321  strlist.empty() || (strlist[0] == "ERROR"))
322  {
323  if (!strlist.empty())
324  {
325  LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
326  "server socket to master backend");
327  }
328  else
329  {
330  LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
331  "server socket to master backend");
332  }
333  return false;
334  }
335 
336  return true;
337 }
338 
339 // Connects to master server safely (i.e. by taking m_sockLock)
341  bool openEventSocket)
342 {
343  QMutexLocker locker(&d->m_sockLock);
344  bool success = ConnectToMasterServer(blockingClient, openEventSocket);
345 
346  return success;
347 }
348 
349 // Assumes that either m_sockLock is held, or the app is still single
350 // threaded (i.e. during startup).
351 bool MythCoreContext::ConnectToMasterServer(bool blockingClient,
352  bool openEventSocket)
353 {
354  if (IsMasterBackend())
355  {
356  // Should never get here unless there is a bug in the code somewhere.
357  // If this happens, it can cause endless event loops.
358  LOG(VB_GENERAL, LOG_ERR, LOC + "ERROR: Master backend tried to connect back "
359  "to itself!");
360  return false;
361  }
362  if (IsExiting())
363  return false;
364 
365  QString server = GetMasterServerIP();
366  if (server.isEmpty())
367  return false;
368 
369  int port = GetMasterServerPort();
370  bool proto_mismatch = false;
371 
372  if (d->m_serverSock && !d->m_serverSock->IsConnected())
373  {
374  d->m_serverSock->DecrRef();
375  d->m_serverSock = nullptr;
376  }
377 
378  if (!d->m_serverSock)
379  {
380  QString type = IsFrontend() ? "Frontend" : (blockingClient ? "Playback" : "Monitor");
381  QString ann = QString("ANN %1 %2 %3")
382  .arg(type)
383  .arg(d->m_localHostname).arg(false);
385  server, port, ann, &proto_mismatch);
386  }
387 
388  if (!d->m_serverSock)
389  return false;
390 
391  d->m_blockingClient = blockingClient;
392 
393  if (!openEventSocket)
394  return true;
395 
396 
397  if (!IsBackend())
398  {
399  if (d->m_eventSock && !d->m_eventSock->IsConnected())
400  {
401  d->m_eventSock->DecrRef();
402  d->m_eventSock = nullptr;
403  }
404  if (!d->m_eventSock)
405  d->m_eventSock = ConnectEventSocket(server, port);
406 
407  if (!d->m_eventSock)
408  {
409  d->m_serverSock->DecrRef();
410  d->m_serverSock = nullptr;
411 
412  QCoreApplication::postEvent(
413  d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
414 
415  return false;
416  }
417  }
418 
419  return true;
420 }
421 
423  const QString &hostname, int port, const QString &announce,
424  bool *p_proto_mismatch, int maxConnTry, int setup_timeout)
425 {
426  MythSocket *serverSock = nullptr;
427 
428  {
429  QMutexLocker locker(&d->m_wolInProgressLock);
430  d->WaitForWOL();
431  }
432 
433  QString WOLcmd;
434  if (IsWOLAllowed())
435  WOLcmd = GetSetting("WOLbackendCommand", "");
436 
437  if (maxConnTry < 1)
438  maxConnTry = max(GetNumSetting("BackendConnectRetry", 1), 1);
439 
440  int WOLsleepTime = 0;
441  int WOLmaxConnTry = 0;
442  if (!WOLcmd.isEmpty())
443  {
444  WOLsleepTime = GetNumSetting("WOLbackendReconnectWaitTime", 0);
445  WOLmaxConnTry = max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
446  maxConnTry = max(maxConnTry, WOLmaxConnTry);
447  }
448 
449  bool we_attempted_wol = false;
450 
451  if (setup_timeout <= 0)
452  setup_timeout = MythSocket::kShortTimeout;
453 
454  bool proto_mismatch = false;
455  for (int cnt = 1; cnt <= maxConnTry; cnt++)
456  {
457  LOG(VB_GENERAL, LOG_INFO, LOC +
458  QString("Connecting to backend server: %1:%2 (try %3 of %4)")
459  .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
460 
461  serverSock = new MythSocket();
462 
463  int sleepms = 0;
464  if (serverSock->ConnectToHost(hostname, port))
465  {
466  if (SetupCommandSocket(
467  serverSock, announce, setup_timeout, proto_mismatch))
468  {
469  break;
470  }
471 
472  if (proto_mismatch)
473  {
474  if (p_proto_mismatch)
475  *p_proto_mismatch = true;
476 
477  serverSock->DecrRef();
478  serverSock = nullptr;
479  break;
480  }
481 
482  setup_timeout = (int)(setup_timeout * 1.5F);
483  }
484  else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
485  {
486  if (!we_attempted_wol)
487  {
488  QMutexLocker locker(&d->m_wolInProgressLock);
489  if (d->m_wolInProgress)
490  {
491  d->WaitForWOL();
492  continue;
493  }
494 
495  d->m_wolInProgress = we_attempted_wol = true;
496  }
497 
500  sleepms = WOLsleepTime * 1000;
501  }
502 
503  serverSock->DecrRef();
504  serverSock = nullptr;
505 
506  if (cnt == 1)
507  {
508  QCoreApplication::postEvent(
509  d->m_guiContext, new MythEvent("CONNECTION_FAILURE"));
510  }
511 
512  if (sleepms)
513  usleep(sleepms * 1000);
514  }
515 
516  if (we_attempted_wol)
517  {
518  QMutexLocker locker(&d->m_wolInProgressLock);
519  d->m_wolInProgress = false;
520  d->m_wolInProgressWaitCondition.wakeAll();
521  }
522 
523  if (!serverSock && !proto_mismatch)
524  {
525  LOG(VB_GENERAL, LOG_ERR,
526  "Connection to master server timed out.\n\t\t\t"
527  "Either the server is down or the master server settings"
528  "\n\t\t\t"
529  "in mythtv-settings does not contain the proper IP address\n");
530  }
531  else
532  {
533  QCoreApplication::postEvent(
534  d->m_guiContext, new MythEvent("CONNECTION_RESTABLISHED"));
535  }
536 
537  return serverSock;
538 }
539 
541  int port)
542 {
543  auto *eventSock = new MythSocket(-1, this);
544 
545  // Assume that since we _just_ connected the command socket,
546  // this one won't need multiple retries to work...
547  if (!eventSock->ConnectToHost(hostname, port))
548  {
549  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
550  "socket to master backend");
551  eventSock->DecrRef();
552  return nullptr;
553  }
554 
555  QString str = QString("ANN Monitor %1 %2")
556  .arg(d->m_localHostname).arg(true);
557  QStringList strlist(str);
558  eventSock->WriteStringList(strlist);
559  bool ok = true;
560  if (!eventSock->ReadStringList(strlist) || strlist.empty() ||
561  (strlist[0] == "ERROR"))
562  {
563  if (!strlist.empty())
564  {
565  LOG(VB_GENERAL, LOG_ERR, LOC +
566  "Problem connecting event socket to master backend");
567  }
568  else
569  {
570  LOG(VB_GENERAL, LOG_ERR, LOC +
571  "Timeout connecting event socket to master backend");
572  }
573  ok = false;
574  }
575 
576  if (!ok)
577  {
578  eventSock->DecrRef();
579  eventSock = nullptr;
580  }
581 
582  return eventSock;
583 }
584 
586 {
587  QMutexLocker locker(&d->m_sockLock);
588  return d->m_serverSock;
589 }
590 
592 {
593  QStringList strlist;
594 
595  QMutexLocker locker(&d->m_sockLock);
596  if (d->m_serverSock == nullptr)
597  return;
598 
599  strlist << "BLOCK_SHUTDOWN";
601 
602  d->m_blockingClient = true;
603 }
604 
606 {
607  QStringList strlist;
608 
609  QMutexLocker locker(&d->m_sockLock);
610  if (d->m_serverSock == nullptr)
611  return;
612 
613  strlist << "ALLOW_SHUTDOWN";
615 
616  d->m_blockingClient = false;
617 }
618 
620 {
621  return d->m_blockingClient;
622 }
623 
625 {
626  d->m_isWOLAllowed = allow;
627 }
628 
630 {
631  return d->m_isWOLAllowed;
632 }
633 
635 {
636  d->m_backend = backend;
637 }
638 
640 {
641  return d->m_backend;
642 }
643 
645 {
646  d->m_frontend = frontend;
647 }
648 
650 {
651  return d->m_frontend;
652 }
653 
655 {
656  QString host = GetHostName();
657  return IsMasterHost(host);
658 }
659 
660 bool MythCoreContext::IsMasterHost(const QString &host)
661 {
662  // Temporary code here only to facilitate the upgrade
663  // from 1346 or earlier. The way of determining master host is
664  // changing, and the new way of determning master host
665  // will not work with earlier databases.
666  // This code can be removed when updating from prior to
667  // 1347 is no longer allowed.
668  // Note that we are deprecating some settings including
669  // MasterServerIP, and can remove them at a future time.
670  if (GetNumSetting("DBSchemaVer") < 1347)
671  {
672  // Temporary copy of code from old version of
673  // IsThisHost(Qstring&,QString&)
674  QString addr(resolveSettingAddress("MasterServerIP"));
675  if (addr.toLower() == host.toLower())
676  return true;
677  QHostAddress addrfix(addr);
678  addrfix.setScopeId(QString());
679  QString addrstr = addrfix.toString();
680  if (addrfix.isNull())
681  addrstr = resolveAddress(addr);
682  QString thisip = GetBackendServerIP4(host);
683  QString thisip6 = GetBackendServerIP6(host);
684  return !addrstr.isEmpty()
685  && ((addrstr == thisip) || (addrstr == thisip6));
686  }
687  return GetSetting("MasterServerName") == host;
688 }
689 
691 {
692  return (IsBackend() && IsMasterHost());
693 }
694 
696 {
697 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
698  const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
699 #elif defined _WIN32
700  const char *command = "%systemroot%\\system32\\tasklist.exe "
701  " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
702 #else
703  const char *command = "ps ch -C mythbackend -o pid > /dev/null";
704 #endif
705  uint res = myth_system(command, kMSDontBlockInputDevs |
708  return (res == GENERIC_EXIT_OK);
709 }
710 
711 bool MythCoreContext::IsThisBackend(const QString &addr)
712 {
713  return IsBackend() && IsThisHost(addr);
714 }
715 
716 bool MythCoreContext::IsThisHost(const QString &addr)
717 {
718  return IsThisHost(addr, GetHostName());
719 }
720 
721 bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
722 {
723  if (addr.toLower() == host.toLower())
724  return true;
725 
726  QHostAddress addrfix(addr);
727  addrfix.setScopeId(QString());
728  QString addrstr = addrfix.toString();
729 
730  if (addrfix.isNull())
731  {
732  addrstr = resolveAddress(addr);
733  }
734 
735  QString thisip = GetBackendServerIP(host);
736 
737  return !addrstr.isEmpty() && ((addrstr == thisip));
738 }
739 
741 {
742  // find out if a backend runs on this host...
743  bool backendOnLocalhost = false;
744 
745  QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
746  strlist << GetHostName();
747 
748  SendReceiveStringList(strlist);
749 
750  backendOnLocalhost = strlist[0] != "FALSE";
751 
752  return !backendOnLocalhost;
753 }
754 
755 QString MythCoreContext::GenMythURL(const QString& host, int port, QString path, const QString& storageGroup)
756 {
757  QUrl ret;
758 
759  QString m_host;
760 
761  QHostAddress addr(host);
762  if (!addr.isNull())
763  {
764  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("(%1/%2): Given "
765  "IP address instead of hostname "
766  "(ID). This is invalid.").arg(host).arg(path));
767  }
768 
769  m_host = host;
770 
771  // Basically if it appears to be an IPv6 IP surround the IP with [] otherwise don't bother
772  if (!addr.isNull() && addr.protocol() == QAbstractSocket::IPv6Protocol)
773  m_host = "[" + addr.toString().toLower() + "]";
774 
775  ret.setScheme("myth");
776  if (!storageGroup.isEmpty())
777  ret.setUserName(storageGroup);
778  ret.setHost(m_host);
779  if (port > 0 && port != 6543)
780  ret.setPort(port);
781  if (!path.startsWith("/"))
782  path = QString("/") + path;
783  ret.setPath(path);
784 
785 #if 0
786  LOG(VB_GENERAL, LOG_DEBUG, LOC +
787  QString("GenMythURL returning %1").arg(ret.toString()));
788 #endif
789 
790  return ret.toString();
791 }
792 
793 QString MythCoreContext::GetMasterHostPrefix(const QString &storageGroup,
794  const QString &path)
795 {
796  return GenMythURL(GetMasterHostName(),
798  path,
799  storageGroup);
800 }
801 
803 {
804  QMutexLocker locker(&d->m_masterHostLock);
805 
806  if (d->m_masterHostname.isEmpty())
807  {
808 
809  if (IsMasterBackend())
811  else
812  {
813  QStringList strlist("QUERY_HOSTNAME");
814 
815  if (SendReceiveStringList(strlist))
816  d->m_masterHostname = strlist[0];
817  }
818  }
819 
820  return d->m_masterHostname;
821 }
822 
823 void MythCoreContext::ClearSettingsCache(const QString &myKey)
824 {
825  d->m_database->ClearSettingsCache(myKey);
826 }
827 
829 {
830  d->m_database->ActivateSettingsCache(activate);
831 }
832 
834 {
835  QMutexLocker locker(&d->m_localHostLock);
836  return d->m_localHostname;
837 }
838 
840 {
841  return GetSetting("RecordFilePrefix");
842 }
843 
845  int &width, int &height,
846  double &forced_aspect,
847  double &refresh_rate,
848  int index)
849 {
850  d->m_database->GetResolutionSetting(type, width, height, forced_aspect,
851  refresh_rate, index);
852 }
853 
854 void MythCoreContext::GetResolutionSetting(const QString &t, int &w,
855  int &h, int i)
856 {
857  d->m_database->GetResolutionSetting(t, w, h, i);
858 }
859 
861 {
862  return d->m_database->GetDBManager();
863 }
864 
872 {
873  return d->m_database->IsDatabaseIgnored();
874 }
875 
876 void MythCoreContext::SaveSetting(const QString &key, int newValue)
877 {
878  d->m_database->SaveSetting(key, newValue);
879 }
880 
881 void MythCoreContext::SaveSetting(const QString &key, const QString &newValue)
882 {
883  d->m_database->SaveSetting(key, newValue);
884 }
885 
886 bool MythCoreContext::SaveSettingOnHost(const QString &key,
887  const QString &newValue,
888  const QString &host)
889 {
890  return d->m_database->SaveSettingOnHost(key, newValue, host);
891 }
892 
893 QString MythCoreContext::GetSetting(const QString &key,
894  const QString &defaultval)
895 {
896  return d->m_database->GetSetting(key, defaultval);
897 }
898 
899 bool MythCoreContext::GetBoolSetting(const QString &key, bool defaultval)
900 {
901  int result = GetNumSetting(key, static_cast<int>(defaultval));
902  return result > 0;
903 }
904 
905 int MythCoreContext::GetNumSetting(const QString &key, int defaultval)
906 {
907  return d->m_database->GetNumSetting(key, defaultval);
908 }
909 
910 double MythCoreContext::GetFloatSetting(const QString &key, double defaultval)
911 {
912  return d->m_database->GetFloatSetting(key, defaultval);
913 }
914 
915 QString MythCoreContext::GetSettingOnHost(const QString &key,
916  const QString &host,
917  const QString &defaultval)
918 {
919  return d->m_database->GetSettingOnHost(key, host, defaultval);
920 }
921 
923  const QString &host,
924  bool defaultval)
925 {
926  int result = GetNumSettingOnHost(key, host, static_cast<int>(defaultval));
927  return result > 0;
928 }
929 
931  const QString &host,
932  int defaultval)
933 {
934  return d->m_database->GetNumSettingOnHost(key, host, defaultval);
935 }
936 
937 double MythCoreContext::GetFloatSettingOnHost(const QString &key,
938  const QString &host,
939  double defaultval)
940 {
941  return d->m_database->GetFloatSettingOnHost(key, host, defaultval);
942 }
943 
950 {
951  QString masterserver = gCoreContext->GetSetting("MasterServerName");
952  QString masterip = resolveSettingAddress("BackendServerAddr",masterserver);
953  // Even if empty, return it here if we were to assume that localhost
954  // should be used it just causes a lot of unnecessary error messages.
955  return masterip;
956 }
957 
964 {
965  QString masterserver = gCoreContext->GetSetting
966  ("MasterServerName");
968  ("BackendServerPort", masterserver, 6543);
969 }
970 
977 {
978  QString masterhost = GetMasterHostName();
979 
980  return GetBackendStatusPort(masterhost);
981 }
982 
988 {
990 }
991 
1001 QString MythCoreContext::GetBackendServerIP(const QString &host)
1002 {
1003  return resolveSettingAddress("BackendServerAddr",host);
1004 }
1005 
1011 {
1013 }
1014 
1020 QString MythCoreContext::GetBackendServerIP4(const QString &host)
1021 {
1022  return resolveSettingAddress("BackendServerIP", host, ResolveIPv4);
1023 }
1024 
1030 {
1032 }
1033 
1039 QString MythCoreContext::GetBackendServerIP6(const QString &host)
1040 {
1041  return resolveSettingAddress("BackendServerIP6", host, ResolveIPv6);
1042 }
1043 
1048 {
1050 }
1051 
1056 {
1057  return GetNumSettingOnHost("BackendServerPort", host, 6543);
1058 }
1059 
1064 {
1066 }
1067 
1072 {
1073  return GetNumSettingOnHost("BackendStatusPort", host, 6544);
1074 }
1075 
1080 bool MythCoreContext::GetScopeForAddress(QHostAddress &addr) const
1081 {
1082  QHostAddress addr1 = addr;
1083  addr1.setScopeId(QString());
1084  QString addrstr = addr1.toString();
1085  QMutexLocker lock(&d->m_scopesLock);
1086 
1087  if (!d->m_scopes.contains(addrstr))
1088  return false;
1089 
1090  addr.setScopeId(d->m_scopes[addrstr]);
1091  return true;
1092 }
1093 
1100 void MythCoreContext::SetScopeForAddress(const QHostAddress &addr)
1101 {
1102  QHostAddress addr1 = addr;
1103  addr1.setScopeId(QString());
1104  QMutexLocker lock(&d->m_scopesLock);
1105 
1106  d->m_scopes.insert(addr1.toString(), addr.scopeId());
1107 }
1108 
1114 void MythCoreContext::SetScopeForAddress(const QHostAddress &addr, int scope)
1115 {
1116  QHostAddress addr1 = addr;
1117  addr1.setScopeId(QString());
1118  QMutexLocker lock(&d->m_scopesLock);
1119 
1120  d->m_scopes.insert(addr1.toString(), QString::number(scope));
1121 }
1122 
1134 QString MythCoreContext::resolveSettingAddress(const QString &name,
1135  const QString &host,
1136  ResolveType type, bool keepscope)
1137 {
1138  QString value;
1139 
1140  if (host.isEmpty())
1141  {
1142  value = GetSetting(name);
1143  }
1144  else
1145  {
1146  value = GetSettingOnHost(name, host);
1147  }
1148 
1149  return resolveAddress(value, type, keepscope);
1150 }
1151 
1163 QString MythCoreContext::resolveAddress(const QString &host, ResolveType type,
1164  bool keepscope)
1165 {
1166  QHostAddress addr(host);
1167 
1168  if (!host.isEmpty() && addr.isNull())
1169  {
1170  // address is likely a hostname, attempts to resolve it
1171  QHostInfo info = QHostInfo::fromName(host);
1172  QList<QHostAddress> list = info.addresses();
1173 
1174  if (list.isEmpty())
1175  {
1176  LOG(VB_GENERAL, LOG_WARNING, LOC +
1177  QString("Can't resolve hostname:'%1', using localhost").arg(host));
1178  return type == ResolveIPv4 ? "127.0.0.1" : "::1";
1179  }
1180  QHostAddress v4;
1181  QHostAddress v6;
1182 
1183  // Return the first address fitting the type critera
1184  foreach (const auto & item, list)
1185  {
1186  addr = item;
1187  QAbstractSocket::NetworkLayerProtocol prot = addr.protocol();
1188 
1189  if (prot == QAbstractSocket::IPv4Protocol)
1190  {
1191  v4 = addr;
1192  if (type == 0)
1193  break;
1194  }
1195  else if (prot == QAbstractSocket::IPv6Protocol)
1196  {
1197  v6 = addr;
1198  if (type != 0)
1199  break;
1200  }
1201  }
1202  switch (type)
1203  {
1204  case ResolveIPv4:
1205  addr = v4.isNull() ? QHostAddress::LocalHost : v4;
1206  break;
1207  case ResolveIPv6:
1208  addr = v6.isNull() ? QHostAddress::LocalHostIPv6 : v6;
1209  break;
1210  default:
1211  addr = v6.isNull() ?
1212  (v4.isNull() ? QHostAddress::LocalHostIPv6 : v4) : v6;
1213  break;
1214  }
1215  }
1216  else if (host.isEmpty())
1217  {
1218  return QString();
1219  }
1220 
1221  if (!keepscope)
1222  {
1223  addr.setScopeId(QString());
1224  }
1225  return addr.toString();
1226 }
1227 
1239 bool MythCoreContext::CheckSubnet(const QAbstractSocket *socket)
1240 {
1241  QHostAddress peer = socket->peerAddress();
1242  return CheckSubnet(peer);
1243 }
1244 
1256 bool MythCoreContext::CheckSubnet(const QHostAddress &peer)
1257 {
1258  static const QHostAddress kLinkLocal("fe80::");
1259  if (GetBoolSetting("AllowConnFromAll",false))
1260  return true;
1261  if (d->m_approvedIps.contains(peer))
1262  return true;
1263  if (d->m_deniedIps.contains(peer))
1264  {
1265  LOG(VB_GENERAL, LOG_WARNING, LOC +
1266  QString("Repeat denied connection from ip address: %1")
1267  .arg(peer.toString()));
1268  return false;
1269  }
1270 
1271  // allow all link-local
1272  if (peer.isInSubnet(kLinkLocal,10))
1273  {
1274  d->m_approvedIps.append(peer);
1275  return true;
1276  }
1277 
1278  // loop through all available interfaces
1279  QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
1280  QList<QNetworkInterface>::const_iterator qni;
1281  for (qni = IFs.begin(); qni != IFs.end(); ++qni)
1282  {
1283  if ((qni->flags() & QNetworkInterface::IsRunning) == 0)
1284  continue;
1285 
1286  QList<QNetworkAddressEntry> IPs = qni->addressEntries();
1287  QList<QNetworkAddressEntry>::iterator qnai;
1288  for (qnai = IPs.begin(); qnai != IPs.end(); ++qnai)
1289  {
1290  int pfxlen = qnai->prefixLength();
1291  // Set this to test rejection without having an extra
1292  // network.
1293  if (GetBoolSetting("DebugSubnet"))
1294  pfxlen += 4;
1295  if (peer.isInSubnet(qnai->ip(),pfxlen))
1296  {
1297  d->m_approvedIps.append(peer);
1298  return true;
1299  }
1300  }
1301  }
1302  d->m_deniedIps.append(peer);
1303  LOG(VB_GENERAL, LOG_WARNING, LOC +
1304  QString("Denied connection from ip address: %1")
1305  .arg(peer.toString()));
1306  return false;
1307 }
1308 
1309 
1311  const QString &value)
1312 {
1313  d->m_database->OverrideSettingForSession(key, value);
1314 }
1315 
1317 {
1318  d->m_database->ClearOverrideSettingForSession(key);
1319 }
1320 
1322 {
1323  return is_current_thread(d->m_uiThread);
1324 }
1325 
1345  QStringList &strlist, bool quickTimeout, bool block)
1346 {
1347  QString msg;
1348  if (HasGUI() && IsUIThread())
1349  {
1350  msg = "SendReceiveStringList(";
1351  for (uint i=0; i<(uint)strlist.size() && i<2; i++)
1352  msg += (i?",":"") + strlist[i];
1353  msg += (strlist.size() > 2) ? "...)" : ")";
1354  LOG(VB_GENERAL, LOG_DEBUG, LOC + msg + " called from UI thread");
1355  }
1356 
1357  QString query_type = "UNKNOWN";
1358 
1359  if (!strlist.isEmpty())
1360  query_type = strlist[0];
1361 
1362  QMutexLocker locker(&d->m_sockLock);
1363  if (!d->m_serverSock)
1364  {
1365  bool blockingClient = d->m_blockingClient &&
1366  (GetNumSetting("idleTimeoutSecs",0) > 0);
1367  ConnectToMasterServer(blockingClient);
1368  }
1369 
1370  bool ok = false;
1371 
1372  if (d->m_serverSock)
1373  {
1374  QStringList sendstrlist = strlist;
1375  uint timeout = quickTimeout ?
1377  ok = d->m_serverSock->SendReceiveStringList(strlist, 0, timeout);
1378 
1379  if (!ok)
1380  {
1381  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1382  QString("Connection to backend server lost"));
1383  d->m_serverSock->DecrRef();
1384  d->m_serverSock = nullptr;
1385 
1386  if (d->m_eventSock)
1387  {
1388  d->m_eventSock->DecrRef();
1389  d->m_eventSock = nullptr;
1390  }
1391 
1392  if (block)
1393  {
1395 
1396  if (d->m_serverSock)
1397  {
1399  strlist, 0, timeout);
1400  }
1401  }
1402  }
1403 
1404  // this should not happen
1405  while (ok && strlist[0] == "BACKEND_MESSAGE")
1406  {
1407  // oops, not for us
1408  LOG(VB_GENERAL, LOG_EMERG, LOC + "SRSL you shouldn't see this!!");
1409  QString message = strlist[1];
1410  strlist.pop_front(); strlist.pop_front();
1411 
1412  MythEvent me(message, strlist);
1413  dispatch(me);
1414 
1415  ok = d->m_serverSock->ReadStringList(strlist, timeout);
1416  }
1417 
1418  if (!ok)
1419  {
1420  if (d->m_serverSock)
1421  {
1422  d->m_serverSock->DecrRef();
1423  d->m_serverSock = nullptr;
1424  }
1425 
1426  LOG(VB_GENERAL, LOG_CRIT, LOC +
1427  QString("Reconnection to backend server failed"));
1428 
1429  QCoreApplication::postEvent(d->m_guiContext,
1430  new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
1431  }
1432  }
1433 
1434  if (ok)
1435  {
1436  if (strlist.isEmpty())
1437  ok = false;
1438  else if (strlist[0] == "ERROR")
1439  {
1440  if (strlist.size() == 2)
1441  {
1442  LOG(VB_GENERAL, LOG_INFO, LOC +
1443  QString("Protocol query '%1' responded with the error '%2'")
1444  .arg(query_type).arg(strlist[1]));
1445  }
1446  else
1447  {
1448  LOG(VB_GENERAL, LOG_INFO, LOC +
1449  QString("Protocol query '%1' responded with an error, but "
1450  "no error message.") .arg(query_type));
1451  }
1452 
1453  ok = false;
1454  }
1455  else if (strlist[0] == "UNKNOWN_COMMAND")
1456  {
1457  LOG(VB_GENERAL, LOG_ERR, LOC +
1458  QString("Protocol query '%1' responded with the error 'UNKNOWN_COMMAND'")
1459  .arg(query_type));
1460 
1461  ok = false;
1462  }
1463  }
1464 
1465  return ok;
1466 }
1467 
1468 class SendAsyncMessage : public QRunnable
1469 {
1470  public:
1471  SendAsyncMessage(QString msg, QStringList extra) :
1472  m_message(std::move(msg)), m_extraData(std::move(extra))
1473  {
1474  }
1475 
1476  explicit SendAsyncMessage(QString msg) : m_message(std::move(msg)) { }
1477 
1478  void run(void) override // QRunnable
1479  {
1480  QStringList strlist("MESSAGE");
1481  strlist << m_message;
1482  strlist << m_extraData;
1484  }
1485 
1486  private:
1487  QString m_message;
1488  QStringList m_extraData;
1489 };
1490 
1491 void MythCoreContext::SendMessage(const QString &message)
1492 {
1493  if (IsBackend())
1494  {
1495  dispatch(MythEvent(message));
1496  }
1497  else
1498  {
1500  new SendAsyncMessage(message), "SendMessage");
1501  }
1502 }
1503 
1505 {
1506  if (IsBackend())
1507  {
1508  dispatch(event);
1509  }
1510  else
1511  {
1513  new SendAsyncMessage(event.Message(), event.ExtraDataList()),
1514  "SendEvent");
1515  }
1516 }
1517 
1518 void MythCoreContext::SendSystemEvent(const QString &msg)
1519 {
1520  if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1521  return;
1522 
1523  SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
1524  .arg(msg).arg(GetHostName()));
1525 }
1526 
1528  const QString &hostname,
1529  const QString &args)
1530 {
1531  SendSystemEvent(QString("%1 HOST %2 %3").arg(msg).arg(hostname).arg(args));
1532 }
1533 
1534 
1536 {
1537  do
1538  {
1539  QStringList strlist;
1540  if (!sock->ReadStringList(strlist))
1541  continue;
1542 
1543  if (strlist.size() < 2)
1544  continue;
1545 
1546  QString prefix = strlist[0];
1547  QString message = strlist[1];
1548  QStringList tokens = message.split(" ", QString::SkipEmptyParts);
1549 
1550  if (prefix == "OK")
1551  {
1552  }
1553  else if (prefix != "BACKEND_MESSAGE")
1554  {
1555  LOG(VB_NETWORK, LOG_ERR, LOC +
1556  QString("Received a: %1 message from the backend "
1557  "but I don't know what to do with it.")
1558  .arg(prefix));
1559  }
1560  else if (message == "CLEAR_SETTINGS_CACHE")
1561  {
1562  // No need to dispatch this message to ourself, so handle it
1563  LOG(VB_NETWORK, LOG_INFO, LOC + "Received remote 'Clear Cache' request");
1565  }
1566  else if (message.startsWith("FILE_WRITTEN"))
1567  {
1568  QString file;
1569  uint64_t size = 0;
1570  int NUMTOKENS = 3; // Number of tokens expected
1571 
1572  if (tokens.size() == NUMTOKENS)
1573  {
1574  file = tokens[1];
1575  size = tokens[2].toULongLong();
1576  }
1577  else
1578  {
1579  LOG(VB_NETWORK, LOG_ERR, LOC +
1580  QString("FILE_WRITTEN event received "
1581  "with invalid number of arguments, "
1582  "%1 expected, %2 actual")
1583  .arg(NUMTOKENS-1)
1584  .arg(tokens.size()-1));
1585  return;
1586  }
1587  // No need to dispatch this message to ourself, so handle it
1588  LOG(VB_NETWORK, LOG_INFO, LOC +
1589  QString("Received remote 'FILE_WRITTEN %1' request").arg(file));
1590  RegisterFileForWrite(file, size);
1591  }
1592  else if (message.startsWith("FILE_CLOSED"))
1593  {
1594  QString file;
1595  int NUMTOKENS = 2; // Number of tokens expected
1596 
1597  if (tokens.size() == NUMTOKENS)
1598  {
1599  file = tokens[1];
1600  }
1601  else
1602  {
1603  LOG(VB_NETWORK, LOG_ERR, LOC +
1604  QString("FILE_CLOSED event received "
1605  "with invalid number of arguments, "
1606  "%1 expected, %2 actual")
1607  .arg(NUMTOKENS-1)
1608  .arg(tokens.size()-1));
1609  return;
1610  }
1611  // No need to dispatch this message to ourself, so handle it
1612  LOG(VB_NETWORK, LOG_INFO, LOC +
1613  QString("Received remote 'FILE_CLOSED %1' request").arg(file));
1615  }
1616  else
1617  {
1618  strlist.pop_front();
1619  strlist.pop_front();
1620  MythEvent me(message, strlist);
1621  dispatch(me);
1622  }
1623  }
1624  while (sock->IsDataAvailable());
1625 }
1626 
1628 {
1629  (void)sock;
1630 
1631  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1632  "Event socket closed. No connection to the backend.");
1633 
1634  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1635 }
1636 
1638  bool error_dialog_desired)
1639 {
1640  if (!socket)
1641  return false;
1642 
1643  QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
1644  .arg(MYTH_PROTO_VERSION)
1645  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1646  socket->WriteStringList(strlist);
1647 
1648  if (!socket->ReadStringList(strlist, timeout_ms) || strlist.empty())
1649  {
1650  LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
1651  "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
1652  "This happens when the backend is too busy to respond,\n\t\t\t"
1653  "or has deadlocked due to bugs or hardware failure.");
1654 
1655  return false;
1656  }
1657  if (strlist[0] == "REJECT" && strlist.size() >= 2)
1658  {
1659  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Protocol version or token mismatch "
1660  "(frontend=%1/%2,backend=%3/\?\?)\n")
1661  .arg(MYTH_PROTO_VERSION)
1662  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN))
1663  .arg(strlist[1]));
1664 
1665  if (error_dialog_desired && d->m_guiContext)
1666  {
1667  QStringList list(strlist[1]);
1668  QCoreApplication::postEvent(
1669  d->m_guiContext, new MythEvent("VERSION_MISMATCH", list));
1670  }
1671 
1672  return false;
1673  }
1674  if (strlist[0] == "ACCEPT")
1675  {
1676  if (!d->m_announcedProtocol)
1677  {
1678  d->m_announcedProtocol = true;
1679  LOG(VB_GENERAL, LOG_INFO, LOC +
1680  QString("Using protocol version %1 %2")
1681  .arg(MYTH_PROTO_VERSION)
1682  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1683  }
1684 
1685  return true;
1686  }
1687 
1688  LOG(VB_GENERAL, LOG_ERR, LOC +
1689  QString("Unexpected response to MYTH_PROTO_VERSION: %1")
1690  .arg(strlist[0]));
1691  return false;
1692 }
1693 
1695 {
1696  LOG(VB_NETWORK, LOG_INFO, LOC + QString("MythEvent: %1").arg(event.Message()));
1697 
1698  MythObservable::dispatch(event);
1699 }
1700 
1702 {
1703  QMutexLocker locker(&d->m_localHostLock);
1705  d->m_database->SetLocalHostname(hostname);
1706 }
1707 
1709 {
1710  d->m_guiObject = gui;
1711 }
1712 
1713 bool MythCoreContext::HasGUI(void) const
1714 {
1715  return d->m_guiObject;
1716 }
1717 
1719 {
1720  return d->m_guiObject;
1721 }
1722 
1724 {
1725  return d->m_guiContext;
1726 }
1727 
1729 {
1730  return d->m_database;
1731 }
1732 
1734 {
1735  return d->m_locale;
1736 }
1737 
1743 {
1744  return GetLanguageAndVariant().left(2);
1745 }
1746 
1755 {
1756  if (d->m_language.isEmpty())
1757  d->m_language = GetSetting("Language", "en_US").toLower();
1758 
1759  return d->m_language;
1760 }
1761 
1763 {
1764  d->m_language.clear();
1765 }
1766 
1768 {
1769  QMutexLocker locker(&d->m_sockLock);
1770  LOG(VB_GENERAL, LOG_INFO, "Restarting Backend Connections");
1771  if (d->m_serverSock)
1773  if (d->m_eventSock)
1775  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1776 }
1777 
1779 {
1780  if (!d->m_locale)
1781  d->m_locale = new MythLocale();
1782 
1783  QString localeCode = d->m_locale->GetLocaleCode();
1784  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1785  .arg(localeCode));
1786  QLocale::setDefault(d->m_locale->ToQLocale());
1787 }
1788 
1790 {
1791  if (!d->m_locale)
1792  d->m_locale = new MythLocale();
1793  else
1794  d->m_locale->ReInit();
1795 
1796  QString localeCode = d->m_locale->GetLocaleCode();
1797  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1798  .arg(localeCode));
1799  QLocale::setDefault(d->m_locale->ToQLocale());
1800 }
1801 
1803 {
1804  if (!d->m_locale)
1805  InitLocale();
1806 
1807  return d->m_locale->ToQLocale();
1808 }
1809 
1811 {
1812  if (!d->m_locale)
1813  InitLocale();
1814 
1815  if (!d->m_locale->GetLocaleCode().isEmpty())
1816  {
1817  LOG(VB_GENERAL, LOG_INFO,
1818  QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
1819 
1821  return;
1822  }
1823 
1824  LOG(VB_GENERAL, LOG_ERR, LOC +
1825  "No locale defined! We weren't able to set locale defaults.");
1826 }
1827 
1829 {
1830  d->m_scheduler = sched;
1831 }
1832 
1834 {
1835  return d->m_scheduler;
1836 }
1837 
1843 void MythCoreContext::WaitUntilSignals(const char *signal1, ...)
1844 {
1845  if (!signal1)
1846  return;
1847 
1848  QEventLoop eventLoop;
1849  va_list vl;
1850 
1851  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1852  QString("Waiting for signal %1")
1853  .arg(signal1));
1854  connect(this, signal1, &eventLoop, SLOT(quit()));
1855 
1856  va_start(vl, signal1);
1857  const char *s = va_arg(vl, const char *);
1858  while (s)
1859  {
1860  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1861  QString("Waiting for signal %1")
1862  .arg(s));
1863  connect(this, s, &eventLoop, SLOT(quit()));
1864  s = va_arg(vl, const char *);
1865  }
1866  va_end(vl);
1867 
1868  eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1869 }
1870 
1877 void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method)
1878 {
1879  if (!sender || !method)
1880  return;
1881 
1882  QMutexLocker lock(&d->m_playbackLock);
1883 
1884  if (!d->m_playbackClients.contains(sender))
1885  {
1886  d->m_playbackClients.insert(sender, QByteArray(method));
1887  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1888  sender, method,
1889  Qt::BlockingQueuedConnection);
1890  }
1891 }
1892 
1899 {
1900  QMutexLocker lock(&d->m_playbackLock);
1901 
1902  if (d->m_playbackClients.contains(sender))
1903  {
1904  QByteArray ba = d->m_playbackClients.value(sender);
1905  const char *method = ba.constData();
1906  disconnect(this, SIGNAL(TVPlaybackAboutToStart()),
1907  sender, method);
1908  d->m_playbackClients.remove(sender);
1909  }
1910 }
1911 
1919 {
1920  QMutexLocker lock(&d->m_playbackLock);
1921  QByteArray ba;
1922  const char *method = nullptr;
1923  d->m_inwanting = true;
1924 
1925  // If any registered client are in the same thread, they will deadlock, so rebuild
1926  // connections for any clients in the same thread as non-blocking connection
1927  QThread *currentThread = QThread::currentThread();
1928 
1929  QMap<QObject *, QByteArray>::iterator it = d->m_playbackClients.begin();
1930  for (; it != d->m_playbackClients.end(); ++it)
1931  {
1932  if (it.key() == sender)
1933  continue; // will be done separately, no need to do it again
1934 
1935  QThread *thread = it.key()->thread();
1936 
1937  if (thread != currentThread)
1938  continue;
1939 
1940  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1941  connect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1942  }
1943 
1944  // disconnect sender so it won't receive the message
1945  if (d->m_playbackClients.contains(sender))
1946  {
1947  ba = d->m_playbackClients.value(sender);
1948  method = ba.constData();
1949  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method);
1950  }
1951 
1952  // emit signal
1953  emit TVPlaybackAboutToStart();
1954 
1955  // reconnect sender
1956  if (method)
1957  {
1958  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1959  sender, method,
1960  Qt::BlockingQueuedConnection);
1961  }
1962  // Restore blocking connections
1963  for (; it != d->m_playbackClients.end(); ++it)
1964  {
1965  if (it.key() == sender)
1966  continue; // already done above, no need to do it again
1967 
1968  QThread *thread = it.key()->thread();
1969 
1970  if (thread != currentThread)
1971  continue;
1972 
1973  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1974  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1975  it.key(), it.value(), Qt::BlockingQueuedConnection);
1976  }
1977  d->m_inwanting = false;
1978 }
1979 
1986 {
1987  // when called, it will be while the m_playbackLock is held
1988  // following a call to WantingPlayback
1989  d->m_intvwanting = b;
1990 }
1991 
1998 {
1999  bool locked = d->m_playbackLock.tryLock();
2000  bool intvplayback = d->m_intvwanting;
2001 
2002  if (!locked && d->m_inwanting)
2003  return true; // we're in the middle of WantingPlayback
2004 
2005  if (!locked)
2006  return false;
2007 
2008  d->m_playbackLock.unlock();
2009 
2010  return intvplayback;
2011 }
2012 
2014 {
2015  if (!d->m_sessionManager)
2017 
2018  return d->m_sessionManager;
2019 }
2020 
2021 bool MythCoreContext::TestPluginVersion(const QString &name,
2022  const QString &libversion,
2023  const QString &pluginversion)
2024 {
2025  if (libversion == pluginversion)
2026  return true;
2027 
2028  LOG(VB_GENERAL, LOG_EMERG, LOC +
2029  QString("Plugin %1 (%2) binary version does not "
2030  "match libraries (%3)")
2031  .arg(name).arg(pluginversion).arg(libversion));
2032  return false;
2033 }
2034 
2036 {
2037  if (d->m_pluginmanager == pmanager)
2038  return;
2039 
2040  if (d->m_pluginmanager)
2041  {
2042  delete d->m_pluginmanager;
2043  d->m_pluginmanager = nullptr;
2044  }
2045 
2046  d->m_pluginmanager = pmanager;
2047 }
2048 
2050 {
2051  return d->m_pluginmanager;
2052 }
2053 
2055 {
2056  d->m_isexiting = exiting;
2057 }
2058 
2060 {
2061  return d->m_isexiting;
2062 }
2063 
2064 void MythCoreContext::RegisterFileForWrite(const QString& file, uint64_t size)
2065 {
2066  QMutexLocker lock(&d->m_fileslock);
2067 
2068  QPair<int64_t, uint64_t> pair(QDateTime::currentMSecsSinceEpoch(), size);
2069  d->m_fileswritten.insert(file, pair);
2070 
2071  if (IsBackend())
2072  {
2073  QString message = QString("FILE_WRITTEN %1 %2").arg(file).arg(size);
2074  MythEvent me(message);
2075  dispatch(me);
2076  }
2077 
2078  LOG(VB_FILE, LOG_DEBUG, LOC +
2079  QString("%1").arg(file));
2080 }
2081 
2083 {
2084  QMutexLocker lock(&d->m_fileslock);
2085 
2086  d->m_fileswritten.remove(file);
2087 
2088  if (IsBackend())
2089  {
2090  QString message = QString("FILE_CLOSED %1").arg(file);
2091  MythEvent me(message);
2092  dispatch(me);
2093  }
2094 
2095  LOG(VB_FILE, LOG_DEBUG, LOC +
2096  QString("%1").arg(file));
2097 }
2098 
2100 {
2101  QMutexLocker lock(&d->m_fileslock);
2102 
2103  return d->m_fileswritten.contains(file);
2104 }
2105 
2106 /* 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 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)
QMutex * avcodeclock
This global variable is used to makes certain calls to avlib threadsafe.
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)
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.