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