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 #endif
32 
33 #include "compat.h"
34 #include "mythconfig.h" // for CONFIG_DARWIN
35 #include "mythdownloadmanager.h"
36 #include "mythcorecontext.h"
37 #include "mythsocket.h"
38 #include "mythsystemlegacy.h"
39 #include "mthreadpool.h"
40 #include "exitcodes.h"
41 #include "mythlogging.h"
42 #include "mythversion.h"
43 #include "logging.h"
44 #include "mthread.h"
45 #include "serverpool.h"
46 #include "mythdate.h"
47 #include "mythplugin.h"
48 #include "mythmiscutil.h"
49 
50 #define LOC QString("MythCoreContext::%1(): ").arg(__func__)
51 
53 QMutex *avcodeclock = new QMutex(QMutex::Recursive);
54 
55 class MythCoreContextPrivate : public QObject
56 {
57  public:
58  MythCoreContextPrivate(MythCoreContext *lparent, QString binversion,
59  QObject *guicontext);
61 
62  bool WaitForWOL(int timeout_ms = INT_MAX);
63 
64  public:
66  QObject *m_GUIcontext;
67  QObject *m_GUIobject;
69 
70  QMutex m_localHostLock;
71  QString m_localHostname;
73  QString m_masterHostname;
74  QMutex m_scopesLock;
75  QMap<QString, QString> m_scopes;
76 
77  QMutex m_sockLock;
80 
85 
86  bool m_backend;
87  bool m_frontend;
88 
89  MythDB *m_database;
90 
91  QThread *m_UIThread;
92 
94  QString language;
95 
97 
99 
100  QMap<QObject *, QByteArray> m_playbackClients;
104 
106 
108 
110 
111  QMap<QString, QPair<int64_t, uint64_t> > m_fileswritten;
112  QMutex m_fileslock;
113 
115 
116  QList<QHostAddress> m_approvedIps;
117  QList<QHostAddress> m_deniedIps;
118 };
119 
121  QString binversion,
122  QObject *guicontext)
123  : m_parent(lparent),
124  m_GUIcontext(guicontext), m_GUIobject(nullptr),
125  m_appBinaryVersion(binversion),
126  m_sockLock(QMutex::NonRecursive),
127  m_serverSock(nullptr), m_eventSock(nullptr),
128  m_WOLInProgress(false),
129  m_IsWOLAllowed(true),
130  m_backend(false),
131  m_frontend(false),
132  m_database(GetMythDB()),
133  m_UIThread(QThread::currentThread()),
134  m_locale(nullptr),
135  m_scheduler(nullptr),
136  m_blockingClient(true),
137  m_inwanting(false),
138  m_intvwanting(false),
139  m_announcedProtocol(false),
140  m_pluginmanager(nullptr),
141  m_isexiting(false),
142  m_sessionManager(nullptr)
143 {
144  MThread::ThreadSetup("CoreContext");
145 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
146  srandom(MythDate::current().toTime_t() ^ QTime::currentTime().msec());
147 #else
148  srandom(MythDate::current().toSecsSinceEpoch() ^ QTime::currentTime().msec());
149 #endif
150 }
151 
152 static void delete_sock(QMutexLocker &locker, MythSocket **s)
153 {
154  if (*s)
155  {
156  MythSocket *tmp = *s;
157  *s = nullptr;
158  locker.unlock();
159  tmp->DecrRef();
160  locker.relock();
161  }
162 }
163 
165 {
167 
168  {
169  QMutexLocker locker(&m_sockLock);
170  delete_sock(locker, &m_serverSock);
171  delete_sock(locker, &m_eventSock);
172  }
173 
174  delete m_locale;
175 
176  delete m_sessionManager;
177 
179 
181 
183 
184  // This has already been run in the MythContext dtor. Do we need it here
185  // too?
186 #if 0
187  logStop(); // need to shutdown db logger before we kill db
188 #endif
189 
191 
192  GetMythDB()->GetDBManager()->CloseDatabases();
193 
194  if (m_database) {
195  DestroyMythDB();
196  m_database = nullptr;
197  }
198 
200 }
201 
205 bool MythCoreContextPrivate::WaitForWOL(int timeout_in_ms)
206 {
207  int timeout_remaining = timeout_in_ms;
208  while (m_WOLInProgress && (timeout_remaining > 0))
209  {
210  LOG(VB_GENERAL, LOG_INFO, LOC + "Wake-On-LAN in progress, waiting...");
211 
212  int max_wait = min(1000, timeout_remaining);
214  &m_WOLInProgressLock, max_wait);
215  timeout_remaining -= max_wait;
216  }
217 
218  return !m_WOLInProgress;
219 }
220 
221 MythCoreContext::MythCoreContext(const QString &binversion,
222  QObject *guiContext)
223  : d(nullptr)
224 {
225  d = new MythCoreContextPrivate(this, binversion, guiContext);
226 }
227 
229 {
230  if (!d)
231  {
232  LOG(VB_GENERAL, LOG_EMERG, LOC + "Out-of-memory");
233  return false;
234  }
235 
237  {
238  LOG(VB_GENERAL, LOG_CRIT,
239  QString("Application binary version (%1) does not "
240  "match libraries (%2)")
242 
243  QString warning = tr("This application is not compatible with the "
244  "installed MythTV libraries. Please recompile "
245  "after a make distclean");
246  LOG(VB_GENERAL, LOG_WARNING, warning);
247 
248  return false;
249  }
250 
251 #ifndef _WIN32
252  QString lang_variables("");
253  QString lc_value = setlocale(LC_CTYPE, nullptr);
254  if (lc_value.isEmpty())
255  {
256  // try fallback to environment variables for non-glibc systems
257  // LC_ALL, then LC_CTYPE
258  lc_value = getenv("LC_ALL");
259  if (lc_value.isEmpty())
260  lc_value = getenv("LC_CTYPE");
261  }
262  if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
263  lang_variables.append("LC_ALL or LC_CTYPE");
264  lc_value = getenv("LANG");
265  if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
266  {
267  if (!lang_variables.isEmpty())
268  lang_variables.append(", and ");
269  lang_variables.append("LANG");
270  }
271  LOG(VB_GENERAL, LOG_INFO, QString("Assumed character encoding: %1")
272  .arg(lc_value));
273  if (!lang_variables.isEmpty())
274  LOG(VB_GENERAL, LOG_WARNING, QString("This application expects to "
275  "be running a locale that specifies a UTF-8 codeset, and many "
276  "features may behave improperly with your current language "
277  "settings. Please set the %1 variable(s) in the environment "
278  "in which this program is executed to include a UTF-8 codeset "
279  "(such as 'en_US.UTF-8').").arg(lang_variables));
280 #endif
281 
282  return true;
283 }
284 
286 {
287  delete d;
288  d = nullptr;
289 }
290 
292  const QString &announcement,
293  uint timeout_in_ms,
294  bool &proto_mismatch)
295 {
296  proto_mismatch = false;
297 
298 #ifndef IGNORE_PROTO_VER_MISMATCH
299  if (!CheckProtoVersion(serverSock, timeout_in_ms, true))
300  {
301  proto_mismatch = true;
302  return false;
303  }
304 #else
305  Q_UNUSED(timeout_in_ms);
306 #endif
307 
308  QStringList strlist(announcement);
309 
310  if (!serverSock->WriteStringList(strlist))
311  {
312  LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
313  "master backend, socket write failed");
314  return false;
315  }
316 
317  if (!serverSock->ReadStringList(strlist, MythSocket::kShortTimeout) ||
318  strlist.empty() || (strlist[0] == "ERROR"))
319  {
320  if (!strlist.empty())
321  LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
322  "server socket to master backend");
323  else
324  LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
325  "server socket to master backend");
326  return false;
327  }
328 
329  return true;
330 }
331 
332 // Connects to master server safely (i.e. by taking m_sockLock)
334  bool openEventSocket)
335 {
336  QMutexLocker locker(&d->m_sockLock);
337  bool success = ConnectToMasterServer(blockingClient, openEventSocket);
338 
339  return success;
340 }
341 
342 // Assumes that either m_sockLock is held, or the app is still single
343 // threaded (i.e. during startup).
344 bool MythCoreContext::ConnectToMasterServer(bool blockingClient,
345  bool openEventSocket)
346 {
347  if (IsMasterBackend())
348  {
349  // Should never get here unless there is a bug in the code somewhere.
350  // If this happens, it can cause endless event loops.
351  LOG(VB_GENERAL, LOG_ERR, LOC + "ERROR: Master backend tried to connect back "
352  "to itself!");
353  return false;
354  }
355  if (IsExiting())
356  return false;
357 
358  QString server = GetMasterServerIP();
359  if (server.isEmpty())
360  return false;
361 
362  int port = GetMasterServerPort();
363  bool proto_mismatch = false;
364 
365  if (d->m_serverSock && !d->m_serverSock->IsConnected())
366  {
367  d->m_serverSock->DecrRef();
368  d->m_serverSock = nullptr;
369  }
370 
371  if (!d->m_serverSock)
372  {
373  QString type = IsFrontend() ? "Frontend" : (blockingClient ? "Playback" : "Monitor");
374  QString ann = QString("ANN %1 %2 %3")
375  .arg(type)
376  .arg(d->m_localHostname).arg(false);
378  server, port, ann, &proto_mismatch);
379  }
380 
381  if (!d->m_serverSock)
382  return false;
383 
384  d->m_blockingClient = blockingClient;
385 
386  if (!openEventSocket)
387  return true;
388 
389 
390  if (!IsBackend())
391  {
392  if (d->m_eventSock && !d->m_eventSock->IsConnected())
393  {
394  d->m_eventSock->DecrRef();
395  d->m_eventSock = nullptr;
396  }
397  if (!d->m_eventSock)
398  d->m_eventSock = ConnectEventSocket(server, port);
399 
400  if (!d->m_eventSock)
401  {
402  d->m_serverSock->DecrRef();
403  d->m_serverSock = nullptr;
404 
405  QCoreApplication::postEvent(
406  d->m_GUIcontext, new MythEvent("CONNECTION_FAILURE"));
407 
408  return false;
409  }
410  }
411 
412  return true;
413 }
414 
416  const QString &hostname, int port, const QString &announce,
417  bool *p_proto_mismatch, int maxConnTry, int setup_timeout)
418 {
419  MythSocket *serverSock = nullptr;
420 
421  {
422  QMutexLocker locker(&d->m_WOLInProgressLock);
423  d->WaitForWOL();
424  }
425 
426  QString WOLcmd;
427  if (IsWOLAllowed())
428  WOLcmd = GetSetting("WOLbackendCommand", "");
429 
430  if (maxConnTry < 1)
431  maxConnTry = max(GetNumSetting("BackendConnectRetry", 1), 1);
432 
433  int WOLsleepTime = 0, WOLmaxConnTry = 0;
434  if (!WOLcmd.isEmpty())
435  {
436  WOLsleepTime = GetNumSetting("WOLbackendReconnectWaitTime", 0);
437  WOLmaxConnTry = max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
438  maxConnTry = max(maxConnTry, WOLmaxConnTry);
439  }
440 
441  bool we_attempted_wol = false;
442 
443  if (setup_timeout <= 0)
444  setup_timeout = MythSocket::kShortTimeout;
445 
446  bool proto_mismatch = false;
447  for (int cnt = 1; cnt <= maxConnTry; cnt++)
448  {
449  LOG(VB_GENERAL, LOG_INFO, LOC +
450  QString("Connecting to backend server: %1:%2 (try %3 of %4)")
451  .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
452 
453  serverSock = new MythSocket();
454 
455  int sleepms = 0;
456  if (serverSock->ConnectToHost(hostname, port))
457  {
458  if (SetupCommandSocket(
459  serverSock, announce, setup_timeout, proto_mismatch))
460  {
461  break;
462  }
463 
464  if (proto_mismatch)
465  {
466  if (p_proto_mismatch)
467  *p_proto_mismatch = true;
468 
469  serverSock->DecrRef();
470  serverSock = nullptr;
471  break;
472  }
473 
474  setup_timeout = (int)(setup_timeout * 1.5f);
475  }
476  else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
477  {
478  if (!we_attempted_wol)
479  {
480  QMutexLocker locker(&d->m_WOLInProgressLock);
481  if (d->m_WOLInProgress)
482  {
483  d->WaitForWOL();
484  continue;
485  }
486 
487  d->m_WOLInProgress = we_attempted_wol = true;
488  }
489 
492  sleepms = WOLsleepTime * 1000;
493  }
494 
495  serverSock->DecrRef();
496  serverSock = nullptr;
497 
498  if (cnt == 1)
499  {
500  QCoreApplication::postEvent(
501  d->m_GUIcontext, new MythEvent("CONNECTION_FAILURE"));
502  }
503 
504  if (sleepms)
505  usleep(sleepms * 1000);
506  }
507 
508  if (we_attempted_wol)
509  {
510  QMutexLocker locker(&d->m_WOLInProgressLock);
511  d->m_WOLInProgress = false;
512  d->m_WOLInProgressWaitCondition.wakeAll();
513  }
514 
515  if (!serverSock && !proto_mismatch)
516  {
517  LOG(VB_GENERAL, LOG_ERR,
518  "Connection to master server timed out.\n\t\t\t"
519  "Either the server is down or the master server settings"
520  "\n\t\t\t"
521  "in mythtv-settings does not contain the proper IP address\n");
522  }
523  else
524  {
525  QCoreApplication::postEvent(
526  d->m_GUIcontext, new MythEvent("CONNECTION_RESTABLISHED"));
527  }
528 
529  return serverSock;
530 }
531 
533  int port)
534 {
535  MythSocket *eventSock = new MythSocket(-1, this);
536 
537  // Assume that since we _just_ connected the command socket,
538  // this one won't need multiple retries to work...
539  if (!eventSock->ConnectToHost(hostname, port))
540  {
541  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
542  "socket to master backend");
543  eventSock->DecrRef();
544  return nullptr;
545  }
546 
547  QString str = QString("ANN Monitor %1 %2")
548  .arg(d->m_localHostname).arg(true);
549  QStringList strlist(str);
550  eventSock->WriteStringList(strlist);
551  bool ok = true;
552  if (!eventSock->ReadStringList(strlist) || strlist.empty() ||
553  (strlist[0] == "ERROR"))
554  {
555  if (!strlist.empty())
556  {
557  LOG(VB_GENERAL, LOG_ERR, LOC +
558  "Problem connecting event socket to master backend");
559  }
560  else
561  {
562  LOG(VB_GENERAL, LOG_ERR, LOC +
563  "Timeout connecting event socket to master backend");
564  }
565  ok = false;
566  }
567 
568  if (!ok)
569  {
570  eventSock->DecrRef();
571  eventSock = nullptr;
572  }
573 
574  return eventSock;
575 }
576 
578 {
579  QMutexLocker locker(&d->m_sockLock);
580  return d->m_serverSock;
581 }
582 
584 {
585  QStringList strlist;
586 
587  QMutexLocker locker(&d->m_sockLock);
588  if (d->m_serverSock == nullptr)
589  return;
590 
591  strlist << "BLOCK_SHUTDOWN";
593 
594  d->m_blockingClient = true;
595 }
596 
598 {
599  QStringList strlist;
600 
601  QMutexLocker locker(&d->m_sockLock);
602  if (d->m_serverSock == nullptr)
603  return;
604 
605  strlist << "ALLOW_SHUTDOWN";
607 
608  d->m_blockingClient = false;
609 }
610 
612 {
613  return d->m_blockingClient;
614 }
615 
617 {
618  d->m_IsWOLAllowed = allow;
619 }
620 
622 {
623  return d->m_IsWOLAllowed;
624 }
625 
627 {
628  d->m_backend = backend;
629 }
630 
632 {
633  return d->m_backend;
634 }
635 
637 {
638  d->m_frontend = frontend;
639 }
640 
642 {
643  return d->m_frontend;
644 }
645 
647 {
648  QString host = GetHostName();
649  return IsMasterHost(host);
650 }
651 
652 bool MythCoreContext::IsMasterHost(const QString &host)
653 {
654  // Temporary code here only to facilitate the upgrade
655  // from 1346 or earlier. The way of determining master host is
656  // changing, and the new way of determning master host
657  // will not work with earlier databases.
658  // This code can be removed when updating from prior to
659  // 1347 is no longer allowed.
660  // Note that we are deprecating some settings including
661  // MasterServerIP, and can remove them at a future time.
662  if (GetNumSetting("DBSchemaVer") < 1347)
663  {
664  // Temporary copy of code from old version of
665  // IsThisHost(Qstring&,QString&)
666  QString addr(resolveSettingAddress("MasterServerIP"));
667  if (addr.toLower() == host.toLower())
668  return true;
669  QHostAddress addrfix(addr);
670  addrfix.setScopeId(QString());
671  QString addrstr = addrfix.toString();
672  if (addrfix.isNull())
673  addrstr = resolveAddress(addr);
674  QString thisip = GetBackendServerIP4(host);
675  QString thisip6 = GetBackendServerIP6(host);
676  return !addrstr.isEmpty()
677  && ((addrstr == thisip) || (addrstr == thisip6));
678  }
679  return GetSetting("MasterServerName") == host;
680 }
681 
683 {
684  return (IsBackend() && IsMasterHost());
685 }
686 
688 {
689 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
690  const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
691 #elif defined _WIN32
692  const char *command = "%systemroot%\\system32\\tasklist.exe "
693  " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
694 #else
695  const char *command = "ps ch -C mythbackend -o pid > /dev/null";
696 #endif
697  uint res = myth_system(command, kMSDontBlockInputDevs |
700  return (res == GENERIC_EXIT_OK);
701 }
702 
703 bool MythCoreContext::IsThisBackend(const QString &addr)
704 {
705  return IsBackend() && IsThisHost(addr);
706 }
707 
708 bool MythCoreContext::IsThisHost(const QString &addr)
709 {
710  return IsThisHost(addr, GetHostName());
711 }
712 
713 bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
714 {
715  if (addr.toLower() == host.toLower())
716  return true;
717 
718  QHostAddress addrfix(addr);
719  addrfix.setScopeId(QString());
720  QString addrstr = addrfix.toString();
721 
722  if (addrfix.isNull())
723  {
724  addrstr = resolveAddress(addr);
725  }
726 
727  QString thisip = GetBackendServerIP(host);
728 
729  return !addrstr.isEmpty() && ((addrstr == thisip));
730 }
731 
733 {
734  // find out if a backend runs on this host...
735  bool backendOnLocalhost = false;
736 
737  QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
738  strlist << GetHostName();
739 
740  SendReceiveStringList(strlist);
741 
742  if (strlist[0] == "FALSE")
743  backendOnLocalhost = false;
744  else
745  backendOnLocalhost = true;
746 
747  return !backendOnLocalhost;
748 }
749 
750 QString MythCoreContext::GenMythURL(QString host, QString port, QString path, QString storageGroup)
751 {
752  return GenMythURL(host,port.toInt(),path,storageGroup);
753 }
754 
755 QString MythCoreContext::GenMythURL(QString host, int port, QString path, 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) ? true : false;
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) ? true : false;
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 
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) const
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, v6;
1181 
1182  // Return the first address fitting the type critera
1183  for (int i=0; i < list.size(); i++)
1184  {
1185  addr = list[i];
1186  QAbstractSocket::NetworkLayerProtocol prot = addr.protocol();
1187 
1188  if (prot == QAbstractSocket::IPv4Protocol)
1189  {
1190  v4 = addr;
1191  if (type == 0)
1192  break;
1193  }
1194  else if (prot == QAbstractSocket::IPv6Protocol)
1195  {
1196  v6 = addr;
1197  if (type != 0)
1198  break;
1199  }
1200  }
1201  switch (type)
1202  {
1203  case ResolveIPv4:
1204  addr = v4.isNull() ? QHostAddress::LocalHost : v4;
1205  break;
1206  case ResolveIPv6:
1207  addr = v6.isNull() ? QHostAddress::LocalHostIPv6 : v6;
1208  break;
1209  default:
1210  addr = v6.isNull() ?
1211  (v4.isNull() ? QHostAddress::LocalHostIPv6 : v4) : v6;
1212  break;
1213  }
1214  }
1215  else if (host.isEmpty())
1216  {
1217  return QString();
1218  }
1219 
1220  if (!keepscope)
1221  {
1222  addr.setScopeId(QString());
1223  }
1224  return addr.toString();
1225 }
1226 
1238 bool MythCoreContext::CheckSubnet(const QAbstractSocket *socket)
1239 {
1240  QHostAddress peer = socket->peerAddress();
1241  return CheckSubnet(peer);
1242 }
1243 
1255 bool MythCoreContext::CheckSubnet(const QHostAddress &peer)
1256 {
1257  static const QHostAddress linklocal("fe80::");
1258  if (GetBoolSetting("AllowConnFromAll",false))
1259  return true;
1260  if (d->m_approvedIps.contains(peer))
1261  return true;
1262  if (d->m_deniedIps.contains(peer))
1263  {
1264  LOG(VB_GENERAL, LOG_WARNING, LOC +
1265  QString("Repeat denied connection from ip address: %1")
1266  .arg(peer.toString()));
1267  return false;
1268  }
1269 
1270  // allow all link-local
1271  if (peer.isInSubnet(linklocal,10))
1272  {
1273  d->m_approvedIps.append(peer);
1274  return true;
1275  }
1276 
1277  // loop through all available interfaces
1278  QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
1279  QList<QNetworkInterface>::const_iterator qni;
1280  for (qni = IFs.begin(); qni != IFs.end(); ++qni)
1281  {
1282  if ((qni->flags() & QNetworkInterface::IsRunning) == 0)
1283  continue;
1284 
1285  QList<QNetworkAddressEntry> IPs = qni->addressEntries();
1286  QList<QNetworkAddressEntry>::iterator qnai;
1287  for (qnai = IPs.begin(); qnai != IPs.end(); ++qnai)
1288  {
1289  int pfxlen = qnai->prefixLength();
1290  // Set this to test rejection without having an extra
1291  // network.
1292  if (GetBoolSetting("DebugSubnet"))
1293  pfxlen += 4;
1294  if (peer.isInSubnet(qnai->ip(),pfxlen))
1295  {
1296  d->m_approvedIps.append(peer);
1297  return true;
1298  }
1299  }
1300  }
1301  d->m_deniedIps.append(peer);
1302  LOG(VB_GENERAL, LOG_WARNING, LOC +
1303  QString("Denied connection from ip address: %1")
1304  .arg(peer.toString()));
1305  return false;
1306 }
1307 
1308 
1310  const QString &value)
1311 {
1312  d->m_database->OverrideSettingForSession(key, value);
1313 }
1314 
1316 {
1317  d->m_database->ClearOverrideSettingForSession(key);
1318 }
1319 
1321 {
1322  return is_current_thread(d->m_UIThread);
1323 }
1324 
1344  QStringList &strlist, bool quickTimeout, bool block)
1345 {
1346  QString msg;
1347  if (HasGUI() && IsUIThread())
1348  {
1349  msg = "SendReceiveStringList(";
1350  for (uint i=0; i<(uint)strlist.size() && i<2; i++)
1351  msg += (i?",":"") + strlist[i];
1352  msg += (strlist.size() > 2) ? "...)" : ")";
1353  LOG(VB_GENERAL, LOG_DEBUG, LOC + msg + " called from UI thread");
1354  }
1355 
1356  QString query_type = "UNKNOWN";
1357 
1358  if (!strlist.isEmpty())
1359  query_type = strlist[0];
1360 
1361  QMutexLocker locker(&d->m_sockLock);
1362  if (!d->m_serverSock)
1363  {
1364  bool blockingClient = d->m_blockingClient &&
1365  (GetNumSetting("idleTimeoutSecs",0) > 0);
1366  ConnectToMasterServer(blockingClient);
1367  }
1368 
1369  bool ok = false;
1370 
1371  if (d->m_serverSock)
1372  {
1373  QStringList sendstrlist = strlist;
1374  uint timeout = quickTimeout ?
1376  ok = d->m_serverSock->SendReceiveStringList(strlist, 0, timeout);
1377 
1378  if (!ok)
1379  {
1380  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1381  QString("Connection to backend server lost"));
1382  d->m_serverSock->DecrRef();
1383  d->m_serverSock = nullptr;
1384 
1385  if (d->m_eventSock)
1386  {
1387  d->m_eventSock->DecrRef();
1388  d->m_eventSock = nullptr;
1389  }
1390 
1391  if (block)
1392  {
1394 
1395  if (d->m_serverSock)
1396  {
1398  strlist, 0, timeout);
1399  }
1400  }
1401  }
1402 
1403  // this should not happen
1404  while (ok && strlist[0] == "BACKEND_MESSAGE")
1405  {
1406  // oops, not for us
1407  LOG(VB_GENERAL, LOG_EMERG, LOC + "SRSL you shouldn't see this!!");
1408  QString message = strlist[1];
1409  strlist.pop_front(); strlist.pop_front();
1410 
1411  MythEvent me(message, strlist);
1412  dispatch(me);
1413 
1414  ok = d->m_serverSock->ReadStringList(strlist, timeout);
1415  }
1416 
1417  if (!ok)
1418  {
1419  if (d->m_serverSock)
1420  {
1421  d->m_serverSock->DecrRef();
1422  d->m_serverSock = nullptr;
1423  }
1424 
1425  LOG(VB_GENERAL, LOG_CRIT, LOC +
1426  QString("Reconnection to backend server failed"));
1427 
1428  QCoreApplication::postEvent(d->m_GUIcontext,
1429  new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
1430  }
1431  }
1432 
1433  if (ok)
1434  {
1435  if (strlist.isEmpty())
1436  ok = false;
1437  else if (strlist[0] == "ERROR")
1438  {
1439  if (strlist.size() == 2)
1440  LOG(VB_GENERAL, LOG_INFO, LOC +
1441  QString("Protocol query '%1' responded with the error '%2'")
1442  .arg(query_type).arg(strlist[1]));
1443  else
1444  LOG(VB_GENERAL, LOG_INFO, LOC +
1445  QString("Protocol query '%1' responded with an error, but "
1446  "no error message.") .arg(query_type));
1447 
1448  ok = false;
1449  }
1450  else if (strlist[0] == "UNKNOWN_COMMAND")
1451  {
1452  LOG(VB_GENERAL, LOG_ERR, LOC +
1453  QString("Protocol query '%1' responded with the error 'UNKNOWN_COMMAND'")
1454  .arg(query_type));
1455 
1456  ok = false;
1457  }
1458  }
1459 
1460  return ok;
1461 }
1462 
1463 class SendAsyncMessage : public QRunnable
1464 {
1465  public:
1466  SendAsyncMessage(const QString &msg, const QStringList &extra) :
1467  m_message(msg), m_extraData(extra)
1468  {
1469  }
1470 
1471  explicit SendAsyncMessage(const QString &msg) : m_message(msg) { }
1472 
1473  void run(void) override // QRunnable
1474  {
1475  QStringList strlist("MESSAGE");
1476  strlist << m_message;
1477  strlist << m_extraData;
1479  }
1480 
1481  private:
1482  QString m_message;
1483  QStringList m_extraData;
1484 };
1485 
1486 void MythCoreContext::SendMessage(const QString &message)
1487 {
1488  if (IsBackend())
1489  {
1490  dispatch(MythEvent(message));
1491  }
1492  else
1493  {
1495  new SendAsyncMessage(message), "SendMessage");
1496  }
1497 }
1498 
1500 {
1501  if (IsBackend())
1502  {
1503  dispatch(event);
1504  }
1505  else
1506  {
1508  new SendAsyncMessage(event.Message(), event.ExtraDataList()),
1509  "SendEvent");
1510  }
1511 }
1512 
1513 void MythCoreContext::SendSystemEvent(const QString &msg)
1514 {
1515  if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1516  return;
1517 
1518  SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
1519  .arg(msg).arg(GetHostName()));
1520 }
1521 
1523  const QString &hostname,
1524  const QString &args)
1525 {
1526  SendSystemEvent(QString("%1 HOST %2 %3").arg(msg).arg(hostname).arg(args));
1527 }
1528 
1529 
1531 {
1532  do
1533  {
1534  QStringList strlist;
1535  if (!sock->ReadStringList(strlist))
1536  continue;
1537 
1538  if (strlist.size() < 2)
1539  continue;
1540 
1541  QString prefix = strlist[0];
1542  QString message = strlist[1];
1543  QStringList tokens = message.split(" ", QString::SkipEmptyParts);
1544 
1545  if (prefix == "OK")
1546  {
1547  }
1548  else if (prefix != "BACKEND_MESSAGE")
1549  {
1550  LOG(VB_NETWORK, LOG_ERR, LOC +
1551  QString("Received a: %1 message from the backend "
1552  "but I don't know what to do with it.")
1553  .arg(prefix));
1554  }
1555  else if (message == "CLEAR_SETTINGS_CACHE")
1556  {
1557  // No need to dispatch this message to ourself, so handle it
1558  LOG(VB_NETWORK, LOG_INFO, LOC + "Received remote 'Clear Cache' request");
1560  }
1561  else if (message.startsWith("FILE_WRITTEN"))
1562  {
1563  QString file;
1564  uint64_t size;
1565  int NUMTOKENS = 3; // Number of tokens expected
1566 
1567  if (tokens.size() == NUMTOKENS)
1568  {
1569  file = tokens[1];
1570  size = tokens[2].toULongLong();
1571  }
1572  else
1573  {
1574  LOG(VB_NETWORK, LOG_ERR, LOC +
1575  QString("FILE_WRITTEN event received "
1576  "with invalid number of arguments, "
1577  "%1 expected, %2 actual")
1578  .arg(NUMTOKENS-1)
1579  .arg(tokens.size()-1));
1580  return;
1581  }
1582  // No need to dispatch this message to ourself, so handle it
1583  LOG(VB_NETWORK, LOG_INFO, LOC +
1584  QString("Received remote 'FILE_WRITTEN %1' request").arg(file));
1585  RegisterFileForWrite(file, size);
1586  }
1587  else if (message.startsWith("FILE_CLOSED"))
1588  {
1589  QString file;
1590  int NUMTOKENS = 2; // Number of tokens expected
1591 
1592  if (tokens.size() == NUMTOKENS)
1593  {
1594  file = tokens[1];
1595  }
1596  else
1597  {
1598  LOG(VB_NETWORK, LOG_ERR, LOC +
1599  QString("FILE_CLOSED event received "
1600  "with invalid number of arguments, "
1601  "%1 expected, %2 actual")
1602  .arg(NUMTOKENS-1)
1603  .arg(tokens.size()-1));
1604  return;
1605  }
1606  // No need to dispatch this message to ourself, so handle it
1607  LOG(VB_NETWORK, LOG_INFO, LOC +
1608  QString("Received remote 'FILE_CLOSED %1' request").arg(file));
1609  UnregisterFileForWrite(file);
1610  }
1611  else
1612  {
1613  strlist.pop_front();
1614  strlist.pop_front();
1615  MythEvent me(message, strlist);
1616  dispatch(me);
1617  }
1618  }
1619  while (sock->IsDataAvailable());
1620 }
1621 
1623 {
1624  (void)sock;
1625 
1626  LOG(VB_GENERAL, LOG_NOTICE, LOC +
1627  "Event socket closed. No connection to the backend.");
1628 
1629  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1630 }
1631 
1633  bool error_dialog_desired)
1634 {
1635  if (!socket)
1636  return false;
1637 
1638  QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
1639  .arg(MYTH_PROTO_VERSION)
1640  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1641  socket->WriteStringList(strlist);
1642 
1643  if (!socket->ReadStringList(strlist, timeout_ms) || strlist.empty())
1644  {
1645  LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
1646  "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
1647  "This happens when the backend is too busy to respond,\n\t\t\t"
1648  "or has deadlocked due to bugs or hardware failure.");
1649 
1650  return false;
1651  }
1652  else if (strlist[0] == "REJECT" && strlist.size() >= 2)
1653  {
1654  LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Protocol version or token mismatch "
1655  "(frontend=%1/%2,backend=%3/\?\?)\n")
1656  .arg(MYTH_PROTO_VERSION)
1657  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN))
1658  .arg(strlist[1]));
1659 
1660  if (error_dialog_desired && d->m_GUIcontext)
1661  {
1662  QStringList list(strlist[1]);
1663  QCoreApplication::postEvent(
1664  d->m_GUIcontext, new MythEvent("VERSION_MISMATCH", list));
1665  }
1666 
1667  return false;
1668  }
1669  else if (strlist[0] == "ACCEPT")
1670  {
1671  if (!d->m_announcedProtocol)
1672  {
1673  d->m_announcedProtocol = true;
1674  LOG(VB_GENERAL, LOG_INFO, LOC +
1675  QString("Using protocol version %1 %2")
1676  .arg(MYTH_PROTO_VERSION)
1677  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
1678  }
1679 
1680  return true;
1681  }
1682 
1683  LOG(VB_GENERAL, LOG_ERR, LOC +
1684  QString("Unexpected response to MYTH_PROTO_VERSION: %1")
1685  .arg(strlist[0]));
1686  return false;
1687 }
1688 
1690 {
1691  LOG(VB_NETWORK, LOG_INFO, LOC + QString("MythEvent: %1").arg(event.Message()));
1692 
1693  MythObservable::dispatch(event);
1694 }
1695 
1697 {
1698  QMutexLocker locker(&d->m_localHostLock);
1700  d->m_database->SetLocalHostname(hostname);
1701 }
1702 
1704 {
1705  d->m_GUIobject = gui;
1706 }
1707 
1708 bool MythCoreContext::HasGUI(void) const
1709 {
1710  return d->m_GUIobject;
1711 }
1712 
1714 {
1715  return d->m_GUIobject;
1716 }
1717 
1719 {
1720  return d->m_GUIcontext;
1721 }
1722 
1724 {
1725  return d->m_database;
1726 }
1727 
1729 {
1730  return d->m_locale;
1731 }
1732 
1738 {
1739  return GetLanguageAndVariant().left(2);
1740 }
1741 
1750 {
1751  if (d->language.isEmpty())
1752  d->language = GetSetting("Language", "en_US").toLower();
1753 
1754  return d->language;
1755 }
1756 
1758 {
1759  d->language.clear();
1760 }
1761 
1763 {
1764  QMutexLocker locker(&d->m_sockLock);
1765  LOG(VB_GENERAL, LOG_INFO, "Restarting Backend Connections");
1766  if (d->m_serverSock)
1768  if (d->m_eventSock)
1770  dispatch(MythEvent("BACKEND_SOCKETS_CLOSED"));
1771 }
1772 
1774 {
1775  if (!d->m_locale)
1776  d->m_locale = new MythLocale();
1777 
1778  QString localeCode = d->m_locale->GetLocaleCode();
1779  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1780  .arg(localeCode));
1781  QLocale::setDefault(d->m_locale->ToQLocale());
1782 }
1783 
1785 {
1786  if (!d->m_locale)
1787  d->m_locale = new MythLocale();
1788  else
1789  d->m_locale->ReInit();
1790 
1791  QString localeCode = d->m_locale->GetLocaleCode();
1792  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
1793  .arg(localeCode));
1794  QLocale::setDefault(d->m_locale->ToQLocale());
1795 }
1796 
1797 const QLocale MythCoreContext::GetQLocale(void)
1798 {
1799  if (!d->m_locale)
1800  InitLocale();
1801 
1802  return d->m_locale->ToQLocale();
1803 }
1804 
1806 {
1807  if (!d->m_locale)
1808  InitLocale();
1809 
1810  if (!d->m_locale->GetLocaleCode().isEmpty())
1811  {
1812  LOG(VB_GENERAL, LOG_INFO,
1813  QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
1814 
1816  return;
1817  }
1818 
1819  LOG(VB_GENERAL, LOG_ERR, LOC +
1820  "No locale defined! We weren't able to set locale defaults.");
1821 }
1822 
1824 {
1825  d->m_scheduler = sched;
1826 }
1827 
1829 {
1830  return d->m_scheduler;
1831 }
1832 
1838 void MythCoreContext::WaitUntilSignals(const char *signal1, ...)
1839 {
1840  if (!signal1)
1841  return;
1842 
1843  const char *s;
1844  QEventLoop eventLoop;
1845  va_list vl;
1846 
1847  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1848  QString("Waiting for signal %1")
1849  .arg(signal1));
1850  connect(this, signal1, &eventLoop, SLOT(quit()));
1851 
1852  va_start(vl, signal1);
1853  s = va_arg(vl, const char *);
1854  while (s)
1855  {
1856  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1857  QString("Waiting for signal %1")
1858  .arg(s));
1859  connect(this, s, &eventLoop, SLOT(quit()));
1860  s = va_arg(vl, const char *);
1861  }
1862  va_end(vl);
1863 
1864  eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1865 }
1866 
1873 void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method)
1874 {
1875  if (!sender || !method)
1876  return;
1877 
1878  QMutexLocker lock(&d->m_playbackLock);
1879 
1880  if (!d->m_playbackClients.contains(sender))
1881  {
1882  d->m_playbackClients.insert(sender, QByteArray(method));
1883  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1884  sender, method,
1885  Qt::BlockingQueuedConnection);
1886  }
1887 }
1888 
1895 {
1896  QMutexLocker lock(&d->m_playbackLock);
1897 
1898  if (d->m_playbackClients.contains(sender))
1899  {
1900  QByteArray ba = d->m_playbackClients.value(sender);
1901  const char *method = ba.constData();
1902  disconnect(this, SIGNAL(TVPlaybackAboutToStart()),
1903  sender, method);
1904  d->m_playbackClients.remove(sender);
1905  }
1906 }
1907 
1915 {
1916  QMutexLocker lock(&d->m_playbackLock);
1917  QByteArray ba;
1918  const char *method = nullptr;
1919  d->m_inwanting = true;
1920 
1921  // If any registered client are in the same thread, they will deadlock, so rebuild
1922  // connections for any clients in the same thread as non-blocking connection
1923  QThread *currentThread = QThread::currentThread();
1924 
1925  QMap<QObject *, QByteArray>::iterator it = d->m_playbackClients.begin();
1926  for (; it != d->m_playbackClients.end(); ++it)
1927  {
1928  if (it.key() == sender)
1929  continue; // will be done separately, no need to do it again
1930 
1931  QThread *thread = it.key()->thread();
1932 
1933  if (thread != currentThread)
1934  continue;
1935 
1936  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1937  connect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1938  }
1939 
1940  // disconnect sender so it won't receive the message
1941  if (d->m_playbackClients.contains(sender))
1942  {
1943  ba = d->m_playbackClients.value(sender);
1944  method = ba.constData();
1945  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method);
1946  }
1947 
1948  // emit signal
1949  emit TVPlaybackAboutToStart();
1950 
1951  // reconnect sender
1952  if (method)
1953  {
1954  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1955  sender, method,
1956  Qt::BlockingQueuedConnection);
1957  }
1958  // Restore blocking connections
1959  for (; it != d->m_playbackClients.end(); ++it)
1960  {
1961  if (it.key() == sender)
1962  continue; // already done above, no need to do it again
1963 
1964  QThread *thread = it.key()->thread();
1965 
1966  if (thread != currentThread)
1967  continue;
1968 
1969  disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value());
1970  connect(this, SIGNAL(TVPlaybackAboutToStart()),
1971  it.key(), it.value(), Qt::BlockingQueuedConnection);
1972  }
1973  d->m_inwanting = false;
1974 }
1975 
1982 {
1983  // when called, it will be while the m_playbackLock is held
1984  // following a call to WantingPlayback
1985  d->m_intvwanting = b;
1986 }
1987 
1994 {
1995  bool locked = d->m_playbackLock.tryLock();
1996  bool intvplayback = d->m_intvwanting;
1997 
1998  if (!locked && d->m_inwanting)
1999  return true; // we're in the middle of WantingPlayback
2000 
2001  if (!locked)
2002  return false;
2003 
2004  d->m_playbackLock.unlock();
2005 
2006  return intvplayback;
2007 }
2008 
2010 {
2011  if (!d->m_sessionManager)
2013 
2014  return d->m_sessionManager;
2015 }
2016 
2018  const QString &libversion,
2019  const QString &pluginversion)
2020 {
2021  if (libversion == pluginversion)
2022  return true;
2023 
2024  LOG(VB_GENERAL, LOG_EMERG, LOC +
2025  QString("Plugin %1 (%2) binary version does not "
2026  "match libraries (%3)")
2027  .arg(name).arg(pluginversion).arg(libversion));
2028  return false;
2029 }
2030 
2032 {
2033  if (d->m_pluginmanager == pmanager)
2034  return;
2035 
2036  if (d->m_pluginmanager)
2037  {
2038  delete d->m_pluginmanager;
2039  d->m_pluginmanager = nullptr;
2040  }
2041 
2042  d->m_pluginmanager = pmanager;
2043 }
2044 
2046 {
2047  return d->m_pluginmanager;
2048 }
2049 
2051 {
2052  d->m_isexiting = exiting;
2053 }
2054 
2056 {
2057  return d->m_isexiting;
2058 }
2059 
2060 void MythCoreContext::RegisterFileForWrite(const QString& file, uint64_t size)
2061 {
2062  QMutexLocker lock(&d->m_fileslock);
2063 
2064  QPair<int64_t, uint64_t> pair(QDateTime::currentMSecsSinceEpoch(), size);
2065  d->m_fileswritten.insert(file, pair);
2066 
2067  if (IsBackend())
2068  {
2069  QString message = QString("FILE_WRITTEN %1 %2").arg(file).arg(size);
2070  MythEvent me(message);
2071  dispatch(me);
2072  }
2073 
2074  LOG(VB_FILE, LOG_DEBUG, LOC +
2075  QString("%1").arg(file));
2076 }
2077 
2079 {
2080  QMutexLocker lock(&d->m_fileslock);
2081 
2082  d->m_fileswritten.remove(file);
2083 
2084  if (IsBackend())
2085  {
2086  QString message = QString("FILE_CLOSED %1").arg(file);
2087  MythEvent me(message);
2088  dispatch(me);
2089  }
2090 
2091  LOG(VB_FILE, LOG_DEBUG, LOC +
2092  QString("%1").arg(file));
2093 }
2094 
2096 {
2097  QMutexLocker lock(&d->m_fileslock);
2098 
2099  return d->m_fileswritten.contains(file);
2100 }
2101 
2102 /* vim: set expandtab tabstop=4 shiftwidth=4: */
QString GetLocaleCode() const
Name of language in that language.
Definition: mythlocale.h:28
QString GetLanguageAndVariant(void)
Returns the user-set language and variant.
bool SetupCommandSocket(MythSocket *serverSock, const QString &announcement, uint timeout_in_ms, bool &proto_mismatch)
void SaveLocaleDefaults(void)
MythPluginManager * m_pluginmanager
void DestroyMythDB(void)
Definition: mythdb.cpp:51
MythScheduler * GetScheduler(void)
This is an generic interface to a program scheduler.
Definition: mythscheduler.h:20
avoid disabling UI drawing
Definition: mythsystem.h:35
MythLocale * GetLocale(void) const
QMutex m_sockLock
protects both m_serverSock and m_eventSock
void run(void) override
MythCoreContext * m_parent
QString GenMythURL(QString host=QString(), QString port=QString(), QString path=QString(), QString storageGroup=QString())
void start(QRunnable *runnable, QString debugName, int priority=0)
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
MythScheduler * m_scheduler
QList< QHostAddress > m_deniedIps
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
QString m_localHostname
hostname from config.xml or gethostname()
static const uint kLongTimeout
Definition: mythsocket.h:72
static void delete_sock(QMutexLocker &locker, MythSocket **s)
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
bool HasGUI(void) const
QMutex m_localHostLock
Locking for m_localHostname.
MythSessionManager * m_sessionManager
void SaveSetting(const QString &key, int newValue)
void connectionClosed(MythSocket *sock) override
void ShutdownMythDownloadManager(void)
Deletes the running MythDownloadManager at program exit.
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
MythSessionManager * GetSessionManager(void)
int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
void ReInit()
Definition: mythlocale.cpp:54
SendAsyncMessage(const QString &msg, const QStringList &extra)
bool IsBlockingClient(void) const
is this client blocking shutdown
static void srandom(unsigned int seed)
Definition: compat.h:146
void SetScopeForAddress(const QHostAddress &addr)
Record the scope Id of the given IP address.
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
static void ShutdownAllPools(void)
const QLocale GetQLocale(void)
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval=0.0)
bool IsFrontend(void) const
is this process a frontend process
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
void SetScheduler(MythScheduler *sched)
bool MythWakeup(const QString &wakeUpCommand, uint flags, uint timeout)
void readyRead(MythSocket *sock) override
MythCoreContextPrivate(MythCoreContext *lparent, QString binversion, QObject *guicontext)
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool IsThisBackend(const QString &addr)
is this address mapped to this backend host
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
QList< QHostAddress > m_approvedIps
void SendHostSystemEvent(const QString &msg, const QString &hostname, const QString &args)
const QLocale ToQLocale() const
Definition: mythlocale.h:30
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
static guint32 * tmp
Definition: goom_core.c:35
void SetLocalHostname(const QString &hostname)
bool WaitForWOL(int timeout_ms=INT_MAX)
If another thread has already started WOL process, wait on them...
bool TestPluginVersion(const QString &name, const QString &libversion, const QString &pluginversion)
void SendMessage(const QString &message)
QMap< QString, QPair< int64_t, uint64_t > > m_fileswritten
process events while waiting
Definition: mythsystem.h:37
#define MYTH_APPNAME_MYTHTV_SETUP
bool IsThisHost(const QString &addr)
is this address mapped to this host
int GetBackendServerPort(void)
Returns the locally defined backend control port.
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
unsigned char b
Definition: ParseText.cpp:340
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:517
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:15
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refreshrate, int index=-1)
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)
QObject * GetGUIContext(void)
MythSocket * m_serverSock
socket for sending MythProto requests
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
bool BackendIsRunning(void)
a backend process is running on this host
bool IsDataAvailable(void) const
Definition: mythsocket.cpp:576
unsigned char t
Definition: ParseText.cpp:340
void SetPluginManager(MythPluginManager *pmanager)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
#define MYTH_PROTO_TOKEN
Definition: mythversion.h:49
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
QString GetMasterHostName(void)
string hostname
Definition: caa.py:17
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:793
const char * name
Definition: ParseText.cpp:339
SendAsyncMessage(const QString &msg)
uint myth_system(const QString &command, uint flags, uint timeout)
void MBASE_PUBLIC ShutdownMythSystemLegacy(void)
int GetNumSettingOnHost(const QString &key, const QString &host, int defaultval=0)
QString GetBackendServerIP6(void)
Returns the IPv6 address defined for the current host see GetBackendServerIP6(host)
bool IsMasterHost(void)
is this the same host as the master
void AllowShutdown(void)
QString resolveAddress(const QString &host, ResolveType=ResolveAny, bool keepscope=false) const
if host is an IP address, it will be returned or resolved otherwise.
void dispatch(const MythEvent &event)
void UnregisterFileForWrite(const QString &file)
bool IsBackend(void) const
is this process a backend process
bool ReadStringList(QStringList &list, uint timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:332
static MThreadPool * globalInstance(void)
int GetNumSetting(const QString &key, int defaultval=0)
int GetBackendStatusPort(void)
Returns the locally defined backend status port.
static void StopAllPools(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QMutex m_scopesLock
Locking for m_masterHostname.
DB connection pool, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:53
void SetGUIObject(QObject *gui)
QString resolveSettingAddress(const QString &name, const QString &host=QString(), ResolveType type=ResolveAny, bool keepscope=false)
Retrieve IP setting "name" for "host".
QString GetFilePrefix(void)
bool GetBoolSetting(const QString &key, bool defaultval=false)
QMutex * avcodeclock
This global variable is used to makes certain calls to avlib threadsafe.
bool ConnectToHost(const QString &hostname, quint16 port)
connect to host
Definition: mythsocket.cpp:393
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:759
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:146
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:570
void WaitUntilSignals(const char *signal1,...)
Wait until either of the provided signals have been received.
void ClearSettingsCache(const QString &myKey=QString(""))
bool GetScopeForAddress(QHostAddress &addr) const
Return the cached scope Id for the given address.
void SaveLocaleDefaults(bool overwrite=false)
Definition: mythlocale.cpp:173
#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:228
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())
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:345
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.
MythCoreContext(const QString &binversion, QObject *eventHandler)
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:320
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:57
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)
void SendSystemEvent(const QString &msg)
QWaitCondition m_WOLInProgressWaitCondition
void SendEvent(const MythEvent &event)
bool InWantingPlayback(void)
Returns true if a client has requested playback.
void SetWOLAllowed(bool allow)
This class contains the runtime context for MythTV.
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
void ResetLanguage(void)
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.