MythTV master
mythcontext.cpp
Go to the documentation of this file.
1#include "mythcontext.h"
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <iostream>
7#include <queue>
8#include <thread>
9#include <unistd.h> // for usleep(), gethostname
10#include <vector>
11
12#include <QCoreApplication>
13#include <QDateTime>
14#include <QDebug>
15#include <QDir>
16#include <QEventLoop>
17#include <QFileInfo>
18#include <QHostInfo>
19#include <QMutex>
20#include <QTcpSocket>
21#ifdef Q_OS_ANDROID
22#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
23#include <QtAndroidExtras>
24#else
25#include <QJniEnvironment>
26#include <QJniObject>
27#define QAndroidJniEnvironment QJniEnvironment
28#define QAndroidJniObject QJniObject
29#endif
30#endif
31
32#ifdef _WIN32
33#include "libmythbase/compat.h"
34#endif
36#include "libmythbase/dbutil.h"
41#include "libmythbase/mythdb.h"
49#include "libmythbase/mythversion.h"
58#include "libmythui/mythimage.h"
62#include "libmythupnp/ssdp.h"
65#include "libmythupnp/upnp.h"
66
67#include "backendselect.h"
68#include "guistartup.h"
69
70#define LOC QString("MythContext: ")
71static const QString sLocation = "MythContext";
72
73namespace
74{
76{
77 public:
78 GUISettingsCache() = default;
79 GUISettingsCache(const QString& cache_filename, QString cache_path)
80 : m_cachePath(std::move(cache_path))
81 {
82 m_cacheFilename = m_cachePath + '/' + cache_filename;
83 if (m_cachePath.isEmpty() || cache_filename.isEmpty())
84 {
85 m_cacheFilename = m_cachePath = QString();
86 }
87 }
88
89 bool save();
90 void loadOverrides() const;
91 static void clearOverrides();
92
93 private:
94 QString m_cacheFilename {"cache/contextcache.xml"};
95 QString m_cachePath {"cache"};
96
97 static const std::array<QString, 13> kSettings;
98};
99
100} // anonymous namespace
101
102class MythContext::Impl : public QObject
103{
104 Q_OBJECT;
105
106 public:
107 explicit Impl();
108 ~Impl() override;
109
110 bool Init (bool gui,
111 bool promptForBackend,
112 bool disableAutoDiscovery,
113 bool ignoreDB);
114 bool FindDatabase(bool prompt, bool noAutodetect);
115 bool FindDatabaseChoose(bool loaded, bool manualSelect, bool autoSelect);
116
117 void TempMainWindow();
118 void EndTempWindow();
119 static void LanguagePrompt();
120
121 static bool LoadDatabaseSettings();
122
123 static QString setLocalHostName(QString hostname);
124
125 bool PromptForDatabaseParams(const QString &error);
126 QString TestDBconnection(bool prompt=true);
127 void SilenceDBerrors();
128 void EnableDBerrors() const;
129 static void ResetDatabase(const DatabaseParams& dbParams);
130
132 ChooseBackend(const QString &error);
133 static int UPnPautoconf(std::chrono::milliseconds milliSeconds = 2s);
134 static bool DefaultUPnP(QString& Error);
135 static bool UPnPconnect(const DeviceLocation *backend, const QString &PIN);
136 void ShowGuiStartup();
137 bool checkPort(QString &host, int port, std::chrono::seconds timeLimit) const;
138 static void processEvents();
139
140 protected:
141 bool event(QEvent* /*e*/) override; // QObject
142
143 void ShowConnectionFailurePopup(bool persistent);
145
146 void ShowVersionMismatchPopup(uint remote_version);
147
148 public slots:
149 void OnCloseDialog() const;
151
152 public:
154 bool m_gui {false};
155
157
158 QString m_dbHostCp;
159
161
162 MythUIHelper *m_ui {nullptr};
164 QEventLoop *m_loop {nullptr};
165 bool m_needsBackend {false};
166
167 GUISettingsCache m_GUISettingsCache;
168
169 private:
172 QDateTime m_lastCheck;
173};
174
175static void exec_program_cb(const QString &cmd)
176{
177 myth_system(cmd);
178}
179
180static void exec_program_tv_cb(const QString &cmd)
181{
182 QString s = cmd;
183 QStringList tokens = cmd.simplified().split(" ");
184 QStringList strlist;
185
186 bool cardidok = false;
187 int wantcardid = tokens[0].toInt(&cardidok, 10);
188
189 if (cardidok && wantcardid > 0)
190 {
191 strlist << QString("LOCK_TUNER %1").arg(wantcardid);
192 s = s.replace(0, tokens[0].length() + 1, "");
193 }
194 else
195 {
196 strlist << "LOCK_TUNER";
197 }
198
200 int cardid = strlist[0].toInt();
201
202 if (cardid >= 0)
203 {
204 s = s.arg(qPrintable(strlist[1]),
205 qPrintable(strlist[2]),
206 qPrintable(strlist[3]));
207
208 myth_system(s);
209
210 strlist = QStringList(QString("FREE_TUNER %1").arg(cardid));
212 }
213 else
214 {
215 QString label;
216
217 if (cardidok)
218 {
219 if (cardid == -1)
220 {
221 label = QObject::tr("Could not find specified tuner (%1).")
222 .arg(wantcardid);
223 }
224 else
225 {
226 label = QObject::tr("Specified tuner (%1) is already in use.")
227 .arg(wantcardid);
228 }
229 }
230 else
231 {
232 label = QObject::tr("All tuners are currently in use. If you want "
233 "to watch TV, you can cancel one of the "
234 "in-progress recordings from the delete menu");
235 }
236
237 LOG(VB_GENERAL, LOG_ALERT, QString("exec_program_tv: ") + label);
238
239 ShowOkPopup(label);
240 }
241}
242
243static void configplugin_cb(const QString &cmd)
244{
246 if (!pmanager)
247 return;
248
249 if (GetNotificationCenter() && pmanager->config_plugin(cmd.trimmed()))
250 {
252 QObject::tr("Failed to configure plugin"));
253 }
254}
255
256static void plugin_cb(const QString &cmd)
257{
259 if (!pmanager)
260 return;
261
262 if (GetNotificationCenter() && pmanager->run_plugin(cmd.trimmed()))
263 {
264 ShowNotificationError(QObject::tr("Plugin failure"),
265 sLocation,
266 QObject::tr("%1 failed to run for some reason").arg(cmd));
267 }
268}
269
270static void eject_cb()
271{
273}
274
276 : m_loop(new QEventLoop(this))
277{
279}
280
282{
283 if (GetNotificationCenter() && m_registration > 0)
284 {
285 GetNotificationCenter()->UnRegister(this, m_registration, true);
286 }
287
288 delete m_loop;
289
290 if (m_ui)
292}
293
305{
306 if (HasMythMainWindow())
307 return;
308
309 SilenceDBerrors();
310
311#ifdef Q_OS_DARWIN
312 // Qt 4.4 has window-focus problems
313 gCoreContext->OverrideSettingForSession("RunFrontendInWindow", "1");
314#endif
315 GetMythUI()->Init();
317 mainWindow->Init();
318}
319
321{
322 if (HasMythMainWindow())
323 {
324 if (m_guiStartup && !m_guiStartup->m_Exit)
325 {
327 if (mainStack)
328 {
329 mainStack->PopScreen(m_guiStartup, false);
330 m_guiStartup = nullptr;
331 }
332 }
333 }
334 EnableDBerrors();
335}
336
338{
339 // ask user for language settings
341 MythTranslation::load("mythfrontend");
342}
343
353bool MythContext::Impl::checkPort(QString &host, int port, std::chrono::seconds timeLimit) const
354{
355 PortChecker checker;
356 if (m_guiStartup)
357 QObject::connect(m_guiStartup, &GUIStartup::cancelPortCheck, &checker, &PortChecker::cancelPortCheck);
358 return checker.checkPort(host, port, timeLimit);
359}
360
361
362bool MythContext::Impl::Init(const bool gui,
363 const bool promptForBackend,
364 const bool disableAutoDiscovery,
365 const bool ignoreDB)
366{
367 gCoreContext->GetDB()->IgnoreDatabase(ignoreDB);
368 m_gui = gui;
369 if (gui)
370 {
371 m_GUISettingsCache.loadOverrides();
372 }
373
375 m_needsBackend = true;
376
377 // Creates screen saver control if we will have a GUI
378 if (gui)
379 m_ui = GetMythUI();
380
381 // ---- database connection stuff ----
382
383 if (!ignoreDB && !FindDatabase(promptForBackend, disableAutoDiscovery))
384 {
385 EndTempWindow();
386 return false;
387 }
388
389 // ---- keep all DB-using stuff below this line ----
390
391 // Prompt for language if this is a first time install and
392 // we didn't already do so.
393 if (m_gui && !gCoreContext->GetDB()->HaveSchema())
394 {
395 TempMainWindow();
396 LanguagePrompt();
397 }
400
401 // Close GUI Startup Window.
402 if (m_guiStartup)
403 {
405 if (mainStack)
406 mainStack->PopScreen(m_guiStartup, false);
407 m_guiStartup=nullptr;
408 }
409 EndTempWindow();
410
411 if (gui)
412 {
413 MythUIMenuCallbacks cbs {};
415 cbs.exec_program_tv = exec_program_tv_cb;
416 cbs.configplugin = configplugin_cb;
417 cbs.plugin = plugin_cb;
418 cbs.eject = eject_cb;
419
420 m_ui->Init(cbs);
421 }
422
423 return true;
424}
425
431bool MythContext::Impl::FindDatabaseChoose(bool loaded, bool manualSelect, bool autoSelect)
432{
433 QString failure;
434
435 // 2. If the user isn't forcing up the chooser UI, look for a default
436 // backend in XmlConfiguration::k_default_filename, then test DB settings we've got so far:
437 if (!manualSelect)
438 {
439 // XmlConfiguration::k_default_filename may contain a backend host UUID and PIN.
440 // If so, try to AutoDiscover UPnP server, and use its DB settings:
441
442 if (DefaultUPnP(failure)) // Probably a valid backend,
443 autoSelect = manualSelect = false; // so disable any further UPnP
444 else
445 if (!failure.isEmpty())
446 LOG(VB_GENERAL, LOG_ALERT, failure);
447
448 failure = TestDBconnection(loaded);
449 if (failure.isEmpty())
450 return true;
451 if (m_guiStartup && m_guiStartup->m_Exit)
452 return false;
453 if (m_guiStartup && m_guiStartup->m_Search)
454 autoSelect=true;
455 }
456
457 // 3. Try to automatically find the single backend:
458 if (autoSelect)
459 {
460 int count = UPnPautoconf();
461
462 if (count == 0)
463 failure = QObject::tr("No UPnP backends found", "Backend Setup");
464
465 if (count == 1)
466 {
467 failure = TestDBconnection();
468 if (failure.isEmpty())
469 return true;
470 if (m_guiStartup && m_guiStartup->m_Exit)
471 return false;
472 }
473
474 // Multiple BEs, or needs PIN.
475 manualSelect |= (count > 1 || count == -1);
476 // Search requested
477 if (m_guiStartup && m_guiStartup->m_Search)
478 manualSelect=true;
479 }
480
481 manualSelect &= m_gui; // no interactive command-line chooser yet
482
483 // Queries the user for the DB info
484 bool haveDbInfo {false};
485 while (!haveDbInfo)
486 {
487 if (manualSelect)
488 {
489 // Get the user to select a backend from a possible list:
490 switch (ChooseBackend(failure))
491 {
493 break;
495 manualSelect = false;
496 break;
498 {
499 LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
500 return false;
501 }
502 }
503 }
504
505 if (!manualSelect)
506 {
507 // If this is a backend, No longer prompt for database.
508 // Instead allow the web server to start so that the
509 // database can be set up there
511 || !PromptForDatabaseParams(failure))
512 {
513 LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
514 return false;
515 }
516 }
517 failure = TestDBconnection();
518 haveDbInfo = failure.isEmpty();
519 if (!failure.isEmpty())
520 LOG(VB_GENERAL, LOG_ALERT, failure);
521 if (m_guiStartup && m_guiStartup->m_Exit)
522 return false;
523 if (m_guiStartup && m_guiStartup->m_Search)
524 manualSelect=true;
525 if (m_guiStartup && m_guiStartup->m_Setup)
526 manualSelect=false;
527 }
528
529 return true;
530}
531
544bool MythContext::Impl::FindDatabase(bool prompt, bool noAutodetect)
545{
546 // We can only prompt if autodiscovery is enabled..
547 bool manualSelect = prompt && !noAutodetect;
548
549 // 1. Either load XmlConfiguration::k_default_filename or use sensible "localhost" defaults:
550 bool loaded = LoadDatabaseSettings();
551 const DatabaseParams dbParamsFromFile = GetMythDB()->GetDatabaseParams();
552 setLocalHostName(dbParamsFromFile.m_localHostName);
553
554 // In addition to the UI chooser, we can also try to autoSelect later,
555 // but only if we're not doing manualSelect and there was no
556 // valid XmlConfiguration::k_default_filename
557 bool autoSelect = !manualSelect && !loaded && !noAutodetect;
558
559 if (!FindDatabaseChoose(loaded, manualSelect, autoSelect))
560 return false;
561
562 LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - Success!");
563 // If we got the database from UPNP then the wakeup settings are lost.
564 // Restore them.
565 DatabaseParams dbParams = GetMythDB()->GetDatabaseParams();
566 dbParams.m_wolEnabled = dbParamsFromFile.m_wolEnabled;
567 dbParams.m_wolReconnect = dbParamsFromFile.m_wolReconnect;
568 dbParams.m_wolRetry = dbParamsFromFile.m_wolRetry;
569 dbParams.m_wolCommand = dbParamsFromFile.m_wolCommand;
570
571 GetMythDB()->SaveDatabaseParams(dbParams, !loaded || dbParamsFromFile != dbParams);
572 EnableDBerrors();
573 ResetDatabase(dbParams);
574 return true;
575}
576
581{
582 auto config = XmlConfiguration(); // read-only
583
584 DatabaseParams dbParams;
585
586 dbParams.m_localHostName = config.GetValue("LocalHostName", "");
587 dbParams.m_dbHostPing = config.GetValue(XmlConfiguration::kDefaultDB + "PingHost", true);
588 dbParams.m_dbHostName = config.GetValue(XmlConfiguration::kDefaultDB + "Host", "");
589 dbParams.m_dbUserName = config.GetValue(XmlConfiguration::kDefaultDB + "UserName", "");
590 dbParams.m_dbPassword = config.GetValue(XmlConfiguration::kDefaultDB + "Password", "");
591 dbParams.m_dbName = config.GetValue(XmlConfiguration::kDefaultDB + "DatabaseName", "");
592 dbParams.m_dbPort = config.GetValue(XmlConfiguration::kDefaultDB + "Port", 0);
593
594 dbParams.m_wolEnabled = config.GetValue(XmlConfiguration::kDefaultWOL + "Enabled", false);
595 dbParams.m_wolReconnect =
596 config.GetDuration<std::chrono::seconds>(XmlConfiguration::kDefaultWOL + "SQLReconnectWaitTime", 0s);
597 dbParams.m_wolRetry = config.GetValue(XmlConfiguration::kDefaultWOL + "SQLConnectRetry", 5);
598 dbParams.m_wolCommand = config.GetValue(XmlConfiguration::kDefaultWOL + "Command", "");
599
601
602 if (!ok)
603 dbParams = {};
604
605 dbParams.m_localEnabled = !(dbParams.m_localHostName.isEmpty() ||
606 dbParams.m_localHostName == "my-unique-identifier-goes-here");
607
608 GetMythDB()->SetDatabaseParams(dbParams);
609 return ok;
610}
611
613{
614 if (hostname.isEmpty() ||
615 hostname == "my-unique-identifier-goes-here")
616 {
617 LOG(VB_GENERAL, LOG_INFO, "Empty LocalHostName. This is typical.");
618 hostname = QHostInfo::localHostName();
619
620#ifndef Q_OS_ANDROID
621 if (hostname.isEmpty())
622 {
623 LOG(VB_GENERAL, LOG_ALERT,
624 "MCP: Error, could not determine host name." + ENO);
625 }
626#else //elif defined Q_OS_ANDROID
627#define ANDROID_EXCEPTION_CHECK \
628 if (env->ExceptionCheck()) \
629 { \
630 env->ExceptionClear(); \
631 exception=true; \
632 }
633
634 if ((hostname == "localhost") || hostname.isEmpty())
635 {
636 hostname = "android";
637 bool exception=false;
640#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
641 QAndroidJniObject activity = QtAndroid::androidActivity();
642#else
643 QJniObject activity = QNativeInterface::QAndroidApplication::context();
644#endif
646 QAndroidJniObject appctx = activity.callObjectMethod
647 ("getApplicationContext", "()Landroid/content/Context;");
649 QAndroidJniObject contentR = appctx.callObjectMethod
650 ("getContentResolver", "()Landroid/content/ContentResolver;");
652 QAndroidJniObject androidId = QAndroidJniObject::callStaticObjectMethod
653 ("android/provider/Settings$Secure", "getString",
654 "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;",
655 contentR.object<jobject>(),
656 myID.object<jstring>());
658 if (exception)
659 LOG(VB_GENERAL, LOG_ALERT,
660 "Java exception looking for android id");
661 else
662 hostname = QString("android-%1").arg(androidId.toString());
663 }
664#endif
665
666 }
667
668 LOG(VB_GENERAL, LOG_INFO, QString("Using a profile name of: '%1' (Usually the "
669 "same as this host's name.)")
670 .arg(hostname));
672
673 return hostname;
674}
675
676
678{
679 if (m_loop && m_loop->isRunning())
680 {
681 m_loop->exit();
682 }
683}
684
685
686// No longer prompt for database, instaed allow the
687// web server to start so that the datbase can be
688// set up there
689
691{
692 bool accepted = false;
693 if (m_gui)
694 {
695 TempMainWindow();
696 LanguagePrompt();
697
698 // Tell the user what went wrong:
699 if (!error.isEmpty())
701
702 // ask user for database parameters
703
704 EnableDBerrors();
706 auto *dbsetting = new DatabaseSettings();
707 auto *ssd = new StandardSettingDialog(mainStack, "databasesettings",
708 dbsetting);
709 if (ssd->Create())
710 {
711 mainStack->AddScreen(ssd);
712 connect(dbsetting, &DatabaseSettings::isClosing,
714 if (!m_loop->isRunning())
715 m_loop->exec();
716 }
717 else
718 {
719 delete ssd;
720 }
721 SilenceDBerrors();
722 EndTempWindow();
723 accepted = true;
724 }
725 else
726 {
727 DatabaseParams params = GetMythDB()->GetDatabaseParams();
728 QString response;
729 std::this_thread::sleep_for(1s);
730 // give user chance to skip config
731 std::cout << std::endl << error.toLocal8Bit().constData() << std::endl << std::endl;
732 response = getResponse("Would you like to configure the database "
733 "connection now?",
734 "no");
735 if (!response.startsWith('y', Qt::CaseInsensitive))
736 return false;
737
738 params.m_dbHostName = getResponse("Database host name:",
739 params.m_dbHostName);
740 response = getResponse("Should I test connectivity to this host "
741 "using the ping command?", "yes");
742 params.m_dbHostPing = response.startsWith('y', Qt::CaseInsensitive);
743
744 params.m_dbPort = intResponse("Database non-default port:",
745 params.m_dbPort);
746 params.m_dbName = getResponse("Database name:",
747 params.m_dbName);
748 params.m_dbUserName = getResponse("Database user name:",
749 params.m_dbUserName);
750 params.m_dbPassword = getResponse("Database password:",
751 params.m_dbPassword);
752
753 params.m_localHostName = getResponse("Unique identifier for this machine "
754 "(if empty, the local host name "
755 "will be used):",
756 params.m_localHostName);
757 params.m_localEnabled = !params.m_localHostName.isEmpty();
758
759 response = getResponse("Would you like to use Wake-On-LAN to retry "
760 "database connections?",
761 (params.m_wolEnabled ? "yes" : "no"));
762 params.m_wolEnabled = response.startsWith('y', Qt::CaseInsensitive);
763
764 if (params.m_wolEnabled)
765 {
766 params.m_wolReconnect =
767 std::chrono::seconds(intResponse("Seconds to wait for "
768 "reconnection:",
769 params.m_wolReconnect.count()));
770 params.m_wolRetry = intResponse("Number of times to retry:",
771 params.m_wolRetry);
772 params.m_wolCommand = getResponse("Command to use to wake server or server MAC address:",
773 params.m_wolCommand);
774 }
775
776 accepted = GetMythDB()->SaveDatabaseParams(params, false);
777 }
778 return accepted;
779}
780
787{
788 QString err;
789 QString host;
790
791 // Jan 20, 2017
792 // Changed to use port check instead of ping
793
794 int port = 0;
795
796 // 1 = db awake, 2 = db listening, 3 = db connects,
797 // 4 = backend awake, 5 = backend listening
798 // 9 = all ok, 10 = quit
799
800 enum startupStates : std::uint8_t {
801 st_start = 0,
802 st_dbAwake = 1,
803 st_dbStarted = 2,
804 st_dbConnects = 3,
805 st_beWOL = 4,
806 st_beAwake = 5,
807 st_success = 6
808 } startupState = st_start;
809
810 static const std::array<const QString, 7> kGuiStatuses
811 {"start", "dbAwake", "dbStarted", "dbConnects", "beWOL", "beAwake", "success"};
812
813 auto secondsStartupScreenDelay = gCoreContext->GetDurSetting<std::chrono::seconds>("StartupScreenDelay", 2s);
814 auto msStartupScreenDelay = std::chrono::duration_cast<std::chrono::milliseconds>(secondsStartupScreenDelay);
815 DatabaseParams dbParams = GetMythDB()->GetDatabaseParams();
816 do
817 {
818 QElapsedTimer timer;
819 timer.start();
820 if (dbParams.m_dbHostName.isNull() && !m_dbHostCp.isEmpty())
821 host = m_dbHostCp;
822 else
823 host = dbParams.m_dbHostName;
824 port = dbParams.m_dbPort;
825 if (port == 0)
826 port = 3306;
827 std::chrono::seconds wakeupTime = 3s;
828 int attempts = 11;
829 if (dbParams.m_wolEnabled)
830 {
831 wakeupTime = dbParams.m_wolReconnect;
832 attempts = dbParams.m_wolRetry + 1;
833 startupState = st_start;
834 }
835 else
836 {
837 startupState = st_dbAwake;
838 }
839 attempts = std::max(attempts, 6);
840 if (!prompt)
841 attempts=1;
842 if (wakeupTime < 5s)
843 wakeupTime = 5s;
844
845 std::chrono::seconds progressTotal = wakeupTime * attempts;
846
847 if (m_guiStartup && !m_guiStartup->m_Exit)
848 m_guiStartup->setTotal(progressTotal);
849
850 QString beWOLCmd = QString();
851 QString backendIP = QString();
852 int backendPort = 0;
853 QString masterserver;
854
855 for (int attempt = 0;
856 attempt < attempts && startupState != st_success;
857 ++attempt)
858 {
859 // The first time do everything with minimum timeout and
860 // no GUI, for the normal case where all is OK
861 // After that show the GUI (if this is a GUI program)
862
863 LOG(VB_GENERAL, LOG_INFO,
864 QString("Start up testing connections. DB %1, BE %2, attempt %3, status %4, Delay: %5")
865 .arg(host, backendIP, QString::number(attempt),
866 kGuiStatuses[startupState],
867 QString::number(msStartupScreenDelay.count())) );
868
869 std::chrono::seconds useTimeout = wakeupTime;
870 if (attempt == 0)
871 useTimeout=1s;
872
873 if (m_gui && !m_guiStartup)
874 {
875 if (msStartupScreenDelay==0ms || timer.hasExpired(msStartupScreenDelay.count()))
876 {
877 ShowGuiStartup();
878 if (m_guiStartup)
879 m_guiStartup->setTotal(progressTotal);
880 }
881 }
882 if (m_guiStartup && !m_guiStartup->m_Exit)
883 {
884 if (attempt > 0)
885 m_guiStartup->setStatusState(kGuiStatuses[startupState]);
886 m_guiStartup->setMessageState("empty");
887 processEvents();
888 }
889 switch (startupState)
890 {
891 case st_start:
892 if (dbParams.m_wolEnabled)
893 {
894 if (attempt > 0)
895 MythWakeup(dbParams.m_wolCommand);
896 if (!checkPort(host, port, useTimeout))
897 break;
898 }
899 startupState = st_dbAwake;
900 [[fallthrough]];
901 case st_dbAwake:
902 if (!checkPort(host, port, useTimeout))
903 break;
904 startupState = st_dbStarted;
905 [[fallthrough]];
906 case st_dbStarted:
907 // If the database is connecting with link-local
908 // address, it may have changed
909 if (dbParams.m_dbHostName != host)
910 {
911 dbParams.m_dbHostName = host;
912 GetMythDB()->SetDatabaseParams(dbParams);
913 }
914 EnableDBerrors();
915 ResetDatabase(dbParams);
917 {
918 for (std::chrono::seconds temp = 0s; temp < useTimeout * 2 ; temp++)
919 {
920 processEvents();
921 std::this_thread::sleep_for(500ms);
922 }
923 break;
924 }
925 startupState = st_dbConnects;
926 [[fallthrough]];
927 case st_dbConnects:
928 if (m_needsBackend)
929 {
930 beWOLCmd = gCoreContext->GetSetting("WOLbackendCommand", "");
931 if (!beWOLCmd.isEmpty())
932 {
933 wakeupTime += gCoreContext->GetDurSetting<std::chrono::seconds>
934 ("WOLbackendReconnectWaitTime", 0s);
935 attempts += gCoreContext->GetNumSetting
936 ("WOLbackendConnectRetry", 0);
937 useTimeout = wakeupTime;
938 if (m_gui && !m_guiStartup && attempt == 0)
939 useTimeout=1s;
940 progressTotal = wakeupTime * attempts;
941 if (m_guiStartup && !m_guiStartup->m_Exit)
942 m_guiStartup->setTotal(progressTotal);
943 startupState = st_beWOL;
944 }
945 }
946 else
947 {
948 startupState = st_success;
949 break;
950 }
951 masterserver = gCoreContext->GetSetting
952 ("MasterServerName");
953 backendIP = gCoreContext->GetSettingOnHost
954 ("BackendServerAddr", masterserver);
956 [[fallthrough]];
957 case st_beWOL:
958 if (!beWOLCmd.isEmpty())
959 {
960 if (attempt > 0)
961 MythWakeup(beWOLCmd);
962 if (!checkPort(backendIP, backendPort, useTimeout))
963 break;
964 }
965 startupState = st_beAwake;
966 [[fallthrough]];
967 case st_beAwake:
968 if (!checkPort(backendIP, backendPort, useTimeout))
969 break;
970 startupState = st_success;
971 [[fallthrough]];
972 case st_success:
973 // Quiet compiler warning.
974 break;
975 }
976 if (m_guiStartup)
977 {
978 if (m_guiStartup->m_Exit
979 || m_guiStartup->m_Setup
980 || m_guiStartup->m_Search
981 || m_guiStartup->m_Retry)
982 break;
983 }
984 processEvents();
985 }
986 if (startupState == st_success)
987 break;
988
989 QString stateMsg = kGuiStatuses[startupState];
990 stateMsg.append("Fail");
991 LOG(VB_GENERAL, LOG_INFO,
992 QString("Start up failure. host %1, status %2")
993 .arg(host, stateMsg));
994
995 if (m_gui && !m_guiStartup)
996 {
997 ShowGuiStartup();
998 if (m_guiStartup)
999 m_guiStartup->setTotal(progressTotal);
1000 }
1001
1002 if (m_guiStartup
1003 && !m_guiStartup->m_Exit
1004 && !m_guiStartup->m_Setup
1005 && !m_guiStartup->m_Search
1006 && !m_guiStartup->m_Retry)
1007 {
1008 m_guiStartup->updateProgress(true);
1009 m_guiStartup->setStatusState(stateMsg);
1010 m_guiStartup->setMessageState("makeselection");
1011 m_loop->exec();
1012 }
1013 }
1014 while (m_guiStartup && m_guiStartup->m_Retry);
1015
1016 if (startupState < st_dbAwake)
1017 {
1018 LOG(VB_GENERAL, LOG_WARNING, QString("Pinging to %1 failed, database will be unavailable").arg(host));
1019 SilenceDBerrors();
1020 err = QObject::tr(
1021 "Cannot find (ping) database host %1 on the network",
1022 "Backend Setup");
1023 return err.arg(host);
1024 }
1025
1026 if (startupState < st_dbConnects)
1027 {
1028 SilenceDBerrors();
1029 return QObject::tr("Cannot login to database", "Backend Setup");
1030 }
1031
1032 if (startupState < st_success)
1033 {
1034 return QObject::tr("Cannot connect to backend", "Backend Setup");
1035 }
1036
1037 // Current DB connection may have been silenced (invalid):
1038 EnableDBerrors();
1039 ResetDatabase(dbParams);
1040
1041 return {};
1042}
1043
1044// Show the Gui Startup window.
1045// This is called if there is a delay in startup for any reason
1046// such as the database being unavailable
1048{
1049 if (!m_gui)
1050 return;
1051 TempMainWindow();
1052 MythMainWindow *mainWindow = GetMythMainWindow();
1053 MythScreenStack *mainStack = mainWindow->GetMainStack();
1054 if (mainStack)
1055 {
1056 if (!m_guiStartup)
1057 {
1058 m_guiStartup = new GUIStartup(mainStack, m_loop);
1059 if (!m_guiStartup->Create())
1060 {
1061 delete m_guiStartup;
1062 m_guiStartup = nullptr;
1063 }
1064 if (m_guiStartup)
1065 {
1066 mainStack->AddScreen(m_guiStartup, false);
1067 processEvents();
1068 }
1069 }
1070 }
1071}
1072
1082{
1083 // This silences any DB errors from Get*Setting(),
1084 // (which is the vast majority of them)
1085 gCoreContext->GetDB()->SetSuppressDBMessages(true);
1086
1087 // Save the configured hostname, so that we can
1088 // still display it in the DatabaseSettings screens
1089 DatabaseParams dbParams = GetMythDB()->GetDatabaseParams();
1090 if (!dbParams.m_dbHostName.isEmpty())
1091 {
1092 m_dbHostCp = dbParams.m_dbHostName;
1093 dbParams.m_dbHostName.clear();
1094 GetMythDB()->SetDatabaseParams(dbParams);
1095 }
1096}
1097
1099{
1100 // Restore (possibly) blanked hostname
1101 DatabaseParams dbParams = GetMythDB()->GetDatabaseParams();
1102 if (dbParams.m_dbHostName.isNull() && !m_dbHostCp.isEmpty())
1103 {
1104 dbParams.m_dbHostName = m_dbHostCp;
1105 GetMythDB()->SetDatabaseParams(dbParams);
1106 }
1107
1108 gCoreContext->GetDB()->SetSuppressDBMessages(false);
1109}
1110
1111
1124{
1125 auto* db = GetMythDB();
1126 db->GetDBManager()->CloseDatabases();
1127 db->SetDatabaseParams(dbParams);
1128 db->ClearSettingsCache();
1129}
1130
1135{
1136 TempMainWindow();
1137 LanguagePrompt();
1138
1139 // Tell the user what went wrong:
1140 if (!error.isEmpty())
1141 {
1142 LOG(VB_GENERAL, LOG_ERR, QString("Error: %1").arg(error));
1144 }
1145
1146 LOG(VB_GENERAL, LOG_INFO, "Putting up the UPnP backend chooser");
1147
1148 DatabaseParams dbParams = GetMythDB()->GetDatabaseParams();
1151 GetMythDB()->SetDatabaseParams(dbParams);
1152
1153 EndTempWindow();
1154
1155 return ret;
1156}
1157
1164int MythContext::Impl::UPnPautoconf(const std::chrono::milliseconds milliSeconds)
1165{
1166 auto seconds = duration_cast<std::chrono::seconds>(milliSeconds);
1167 LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1168 .arg(seconds.count()));
1169
1171
1172 // Search for a total of 'milliSeconds' ms, sending new search packet
1173 // about every 250 ms until less than one second remains.
1174 MythTimer totalTime; totalTime.start();
1175 MythTimer searchTime; searchTime.start();
1176 while (totalTime.elapsed() < milliSeconds)
1177 {
1178 usleep(25000);
1179 auto ttl = milliSeconds - totalTime.elapsed();
1180 if ((searchTime.elapsed() > 249ms) && (ttl > 1s))
1181 {
1182 auto ttlSeconds = duration_cast<std::chrono::seconds>(ttl);
1183 LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1184 .arg(ttlSeconds.count()));
1186 searchTime.start();
1187 }
1188 }
1189
1191
1192 if (!backends)
1193 {
1194 LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
1195 return 0;
1196 }
1197
1198 int count = backends->Count();
1199 if (count)
1200 {
1201 LOG(VB_GENERAL, LOG_INFO,
1202 QString("Found %1 UPnP backends").arg(count));
1203 }
1204 else
1205 {
1206 LOG(VB_GENERAL, LOG_ERR,
1207 "No UPnP backends found, but SSDPCache::Instance()->Find() not NULL");
1208 }
1209
1210 if (count != 1)
1211 {
1212 backends->DecrRef();
1213 return count;
1214 }
1215
1216 // Get this backend's location:
1217 DeviceLocation *BE = backends->GetFirst();
1218 backends->DecrRef();
1219 backends = nullptr;
1220
1221 // We don't actually know the backend's access PIN, so this will
1222 // only work for ones that have PIN access disabled (i.e. 0000)
1223 int ret = (UPnPconnect(BE, QString())) ? 1 : -1;
1224
1225 BE->DecrRef();
1226
1227 return ret;
1228}
1229
1236{
1237 static const QString loc = "DefaultUPnP() - ";
1238
1239 // potentially saved in backendselect
1240 QString pin;
1241 QString usn;
1242 {
1243 auto config = XmlConfiguration(); // read-only
1244 pin = config.GetValue(XmlConfiguration::kDefaultPIN, QString(""));
1245 usn = config.GetValue(XmlConfiguration::kDefaultUSN, QString(""));
1246 }
1247
1248 if (usn.isEmpty())
1249 {
1250 LOG(VB_UPNP, LOG_INFO, loc + "No default UPnP backend");
1251 return false;
1252 }
1253
1254 LOG(VB_UPNP, LOG_INFO,
1255 loc + QString(XmlConfiguration::kDefaultFilename) +
1256 QString(" has default PIN '%1' and host USN: %2").arg(pin, usn));
1257
1258 // ----------------------------------------------------------------------
1259
1260 std::chrono::milliseconds timeout_ms {2s};
1261 auto timeout_s = duration_cast<std::chrono::seconds>(timeout_ms);
1262 LOG(VB_GENERAL, LOG_INFO, loc + QString("UPNP Search up to %1 secs")
1263 .arg(timeout_s.count()));
1265
1266 // ----------------------------------------------------------------------
1267 // We need to give the server time to respond...
1268 // ----------------------------------------------------------------------
1269
1270 DeviceLocation* devicelocation = nullptr;
1271 MythTimer totalTime;
1272 MythTimer searchTime;
1273 totalTime.start();
1274 searchTime.start();
1275 while (totalTime.elapsed() < timeout_ms)
1276 {
1277 devicelocation = SSDPCache::Instance()->Find(SSDP::kBackendURI, usn);
1278 if (devicelocation)
1279 break;
1280
1281 usleep(25000);
1282
1283 auto ttl = timeout_ms - totalTime.elapsed();
1284 if ((searchTime.elapsed() > 249ms) && (ttl > 1s))
1285 {
1286 auto ttlSeconds = duration_cast<std::chrono::seconds>(ttl);
1287 LOG(VB_GENERAL, LOG_INFO, loc + QString("UPNP Search up to %1 secs")
1288 .arg(ttlSeconds.count()));
1290 searchTime.start();
1291 }
1292 }
1293
1294 // ----------------------------------------------------------------------
1295
1296 if (!devicelocation)
1297 {
1298 Error = "Cannot find default UPnP backend";
1299 return false;
1300 }
1301
1302 if (UPnPconnect(devicelocation, pin))
1303 {
1304 devicelocation->DecrRef();
1305 return true;
1306 }
1307
1308 devicelocation->DecrRef();
1309 Error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
1310 return false;
1311}
1312
1317 const QString &PIN)
1318{
1319 QString error;
1320 QString loc = "UPnPconnect() - ";
1321 QString URL = backend->m_sLocation;
1322 MythXMLClient client(URL);
1323 DatabaseParams dbParams = GetMythDB()->GetDatabaseParams();
1324
1325 LOG(VB_UPNP, LOG_INFO, loc + QString("Trying host at %1").arg(URL));
1326 switch (client.GetConnectionInfo(PIN, &dbParams, error))
1327 {
1328 case UPnPResult_Success:
1329 GetMythDB()->SetDatabaseParams(dbParams);
1330 LOG(VB_UPNP, LOG_INFO, loc +
1331 "Got database hostname: " + dbParams.m_dbHostName);
1332 return true;
1333
1335 // The stored PIN is probably not correct.
1336 // We could prompt for the PIN and try again, but that needs a UI.
1337 // Easier to fail for now, and put up the full UI selector later
1338 LOG(VB_UPNP, LOG_ERR, loc + "Wrong PIN?");
1339 return false;
1340
1341 default:
1342 LOG(VB_UPNP, LOG_ERR, loc + error);
1343 break;
1344 }
1345
1346 // This backend may have a local DB with the default user/pass/DBname.
1347 // For whatever reason, we have failed to get anything back via UPnP,
1348 // so we might as well try the database directly as a last resort.
1349 QUrl theURL(URL);
1350 URL = theURL.host();
1351 if (URL.isEmpty())
1352 return false;
1353
1354 LOG(VB_UPNP, LOG_INFO, "Trying default DB credentials at " + URL);
1355 dbParams.m_dbHostName = URL;
1356 GetMythDB()->SetDatabaseParams(dbParams);
1357
1358 return true;
1359}
1360
1362{
1363 if (e->type() == MythEvent::kMythEventMessage)
1364 {
1365 if (m_disableeventpopup)
1366 return true;
1367
1368 if (GetNotificationCenter() && m_registration < 0)
1369 {
1370 m_registration = GetNotificationCenter()->Register(this);
1371 }
1372
1373 auto *me = dynamic_cast<MythEvent*>(e);
1374 if (me == nullptr)
1375 return true;
1376
1377 if (me->Message() == "VERSION_MISMATCH" && (1 == me->ExtraDataCount()))
1378 ShowVersionMismatchPopup(me->ExtraData(0).toUInt());
1379 else if (me->Message() == "CONNECTION_FAILURE")
1380 ShowConnectionFailurePopup(false);
1381 else if (me->Message() == "PERSISTENT_CONNECTION_FAILURE")
1382 ShowConnectionFailurePopup(true);
1383 else if (me->Message() == "CONNECTION_RESTABLISHED")
1384 HideConnectionFailurePopup();
1385 return true;
1386 }
1387
1388 return QObject::event(e);
1389}
1390
1392{
1393 QDateTime now = MythDate::current();
1394
1395 if (!GetNotificationCenter() || !m_ui || !m_ui->IsScreenSetup())
1396 return;
1397
1398 if (m_lastCheck.isValid() && now < m_lastCheck)
1399 return;
1400
1401 // When WOL is disallowed, standy mode,
1402 // we should not show connection failures.
1403 if (!gCoreContext->IsWOLAllowed())
1404 return;
1405
1406 m_lastCheck = now.addMSecs(5000); // don't refresh notification more than every 5s
1407
1408 QString description = (persistent) ?
1409 QObject::tr(
1410 "The connection to the master backend "
1411 "server has gone away for some reason. "
1412 "Is it running?") :
1413 QObject::tr(
1414 "Could not connect to the master backend server. Is "
1415 "it running? Is the IP address set for it in "
1416 "mythtv-setup correct?");
1417
1418 QString message = QObject::tr("Could not connect to master backend");
1419 MythErrorNotification n(message, sLocation, description);
1420 n.SetId(m_registration);
1421 n.SetParent(this);
1423}
1424
1426{
1427 if (!GetNotificationCenter())
1428 return;
1429
1430 if (!m_lastCheck.isValid())
1431 return;
1432
1433 MythCheckNotification n(QObject::tr("Backend is online"), sLocation);
1434 n.SetId(m_registration);
1435 n.SetParent(this);
1436 n.SetDuration(5s);
1438 m_lastCheck = QDateTime();
1439}
1440
1442{
1443 if (m_mbeVersionPopup)
1444 return;
1445
1446 QString message =
1447 QObject::tr(
1448 "The server uses network protocol version %1, "
1449 "but this client only understands version %2. "
1450 "Make sure you are running compatible versions of "
1451 "the backend and frontend.")
1452 .arg(remote_version).arg(MYTH_PROTO_VERSION);
1453
1454 if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
1455 {
1456 m_mbeVersionPopup = ShowOkPopup(
1458 }
1459 else
1460 {
1461 LOG(VB_GENERAL, LOG_ERR, LOC + message);
1462 qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1463 }
1464}
1465
1466// Process Events while waiting for connection
1467// return true if progress is 100%
1469{
1470// bool ret = false;
1471// if (m_guiStartup)
1472// ret = m_guiStartup->updateProgress();
1473 qApp->processEvents(QEventLoop::AllEvents, 250);
1474 qApp->processEvents(QEventLoop::AllEvents, 250);
1475// return ret;
1476}
1477
1478namespace
1479{
1480// cache some settings in GUISettingsCache::m_cacheFilename
1481// only call this if the database is available.
1482
1483const std::array<QString, 13> GUISettingsCache::kSettings
1484{ "Theme", "Language", "Country", "GuiHeight",
1485 "GuiOffsetX", "GuiOffsetY", "GuiWidth", "RunFrontendInWindow",
1486 "AlwaysOnTop", "HideMouseCursor", "ThemePainter", "libCECEnabled",
1487 "StartupScreenDelay" };
1488
1489
1490bool GUISettingsCache::save()
1491{
1492 QString cacheDirName = GetConfDir() + '/' + m_cachePath;
1493 QDir dir(cacheDirName);
1494 dir.mkpath(cacheDirName);
1495 XmlConfiguration config = XmlConfiguration(m_cacheFilename);
1496 bool dirty = false;
1497 for (const auto & setting : kSettings)
1498 {
1499 QString cacheValue = config.GetValue("Settings/" + setting, QString());
1501 QString value = gCoreContext->GetSetting(setting, QString());
1502 if (value != cacheValue)
1503 {
1504 config.SetValue("Settings/" + setting, value);
1505 dirty = true;
1506 }
1507 }
1508 clearOverrides();
1509
1510 if (dirty)
1511 {
1512#ifndef Q_OS_ANDROID
1514#endif
1515 return config.Save();
1516 }
1517 return true;
1518}
1519
1520void GUISettingsCache::loadOverrides() const
1521{
1522 auto config = XmlConfiguration(m_cacheFilename); // read only
1523 for (const auto & setting : kSettings)
1524 {
1525 if (!gCoreContext->GetSetting(setting, QString()).isEmpty())
1526 continue;
1527 QString value = config.GetValue("Settings/" + setting, QString());
1528 if (!value.isEmpty())
1529 gCoreContext->OverrideSettingForSession(setting, value);
1530 }
1531 // Prevent power off TV after temporary window
1532 gCoreContext->OverrideSettingForSession("PowerOffTVAllowed", nullptr);
1533
1534 MythTranslation::load("mythfrontend");
1535}
1536
1537void GUISettingsCache::clearOverrides()
1538{
1539 QString language = gCoreContext->GetSetting("Language", QString());
1540 for (const auto & setting : kSettings)
1542 // Restore power off TV setting
1543 gCoreContext->ClearOverrideSettingForSession("PowerOffTVAllowed");
1544
1545 if (language != gCoreContext->GetSetting("Language", QString()))
1546 MythTranslation::load("mythfrontend");
1547}
1548
1549} // anonymous namespace
1550
1552{
1553 m_mbeVersionPopup = nullptr;
1554 qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1555}
1556
1557MythContext::MythContext(QString binversion, bool needsBackend)
1558 : m_impl(new MythContext::Impl()),
1559 m_appBinaryVersion(std::move(binversion))
1560{
1561#ifdef _WIN32
1562 static bool WSAStarted = false;
1563 if (!WSAStarted)
1564 {
1565 WSADATA wsadata;
1566 int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
1567 LOG(VB_SOCKET, LOG_INFO,
1568 QString("WSAStartup returned %1").arg(res));
1569 }
1570#endif
1571
1573 m_impl->m_needsBackend = needsBackend;
1574
1576
1577 if (!gCoreContext || !gCoreContext->Init())
1578 {
1579 LOG(VB_GENERAL, LOG_EMERG, LOC + "Unable to allocate MythCoreContext");
1580 qApp->exit(GENERIC_EXIT_NO_MYTHCONTEXT);
1581 }
1582}
1583
1584bool MythContext::Init(const bool gui,
1585 const bool promptForBackend,
1586 const bool disableAutoDiscovery,
1587 const bool ignoreDB)
1588{
1589 if (!m_impl)
1590 {
1591 LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
1592 return false;
1593 }
1594
1595 qRegisterMetaType<std::chrono::seconds>("std::chrono::seconds");
1596 qRegisterMetaType<std::chrono::milliseconds>("std::chrono::milliseconds");
1597 qRegisterMetaType<std::chrono::microseconds>("std::chrono::microseconds");
1598
1600
1601 if (gui && QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
1602 {
1604 QString warning = QObject::tr("mythtv-setup is deprecated.\n"
1605 "To set up MythTV, start mythbackend and use:\n"
1606 "http://localhost:6544/setupwizard");
1607 WaitFor(ShowOkPopup(warning));
1608 }
1609
1610 if (m_appBinaryVersion != MYTH_BINARY_VERSION)
1611 {
1612 LOG(VB_GENERAL, LOG_EMERG,
1613 QString("Application binary version (%1) does not "
1614 "match libraries (%2)")
1615 .arg(m_appBinaryVersion, MYTH_BINARY_VERSION));
1616
1617 QString warning = QObject::tr(
1618 "This application is not compatible "
1619 "with the installed MythTV libraries.");
1620 if (gui)
1621 {
1623 WaitFor(ShowOkPopup(warning));
1624 }
1625 LOG(VB_GENERAL, LOG_WARNING, warning);
1626
1627 return false;
1628 }
1629
1630#ifdef _WIN32
1631 // HOME environment variable might not be defined
1632 // some libraries will fail without it
1633 QString home = getenv("HOME");
1634 if (home.isEmpty())
1635 {
1636 home = getenv("LOCALAPPDATA"); // Vista
1637 if (home.isEmpty())
1638 home = getenv("APPDATA"); // XP
1639 if (home.isEmpty())
1640 home = QString("."); // getenv("TEMP")?
1641
1642 _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData());
1643 }
1644#endif
1645
1646 // If HOME isn't defined, we won't be able to use default confdir of
1647 // $HOME/.mythtv nor can we rely on a MYTHCONFDIR that references $HOME
1648 QString homedir = QDir::homePath();
1649 QString confdir = qEnvironmentVariable("MYTHCONFDIR");
1650 if ((homedir.isEmpty() || homedir == "/") &&
1651 (confdir.isEmpty() || confdir.contains("$HOME")))
1652 {
1653 QString warning = "Cannot locate your home directory."
1654 " Please set the environment variable HOME";
1655 if (gui)
1656 {
1658 WaitFor(ShowOkPopup(warning));
1659 }
1660 LOG(VB_GENERAL, LOG_WARNING, warning);
1661
1662 return false;
1663 }
1664
1665 if (!m_impl->Init(gui, promptForBackend, disableAutoDiscovery, ignoreDB))
1666 {
1667 return false;
1668 }
1669
1670 SetDisableEventPopup(false);
1671
1672 if (m_impl->m_gui)
1673 {
1675 }
1676
1679
1680 return true;
1681}
1682
1684{
1685 if (m_cleanup != nullptr)
1686 {
1687 m_cleanup();
1688 }
1689
1690 if (m_impl->m_gui)
1691 {
1693 }
1694
1696 gCoreContext->InitPower(false /*destroy*/);
1697 if (MThreadPool::globalInstance()->activeThreadCount())
1698 LOG(VB_GENERAL, LOG_INFO, "Waiting for threads to exit.");
1699
1703
1704 LOG(VB_GENERAL, LOG_INFO, "Exiting");
1705
1706 logStop();
1707
1708 delete gCoreContext;
1709 gCoreContext = nullptr;
1710
1711 delete m_impl;
1712
1714}
1715
1717{
1718 m_impl->m_disableeventpopup = check;
1719}
1720
1722{
1723 /* this check is technically redundant since this is only called from
1724 MythContext::Init() and mythfrontend::main(); however, it is for safety
1725 and clarity until MythGUIContext is refactored out.
1726 */
1727 if (m_impl->m_gui)
1728 {
1729 return m_impl->m_GUISettingsCache.save();
1730 }
1731 return true;
1732}
1733
1735#include "mythcontext.moc"
1736
1737/* vim: set expandtab tabstop=4 shiftwidth=4: */
#define QAndroidJniEnvironment
#define ANDROID_EXCEPTION_CHECK
#define QAndroidJniObject
static Decision Prompt(DatabaseParams *dbParams, const QString &config_filename)
Structure containing the basic Database parameters.
Definition: mythdbparams.h:11
QString m_dbName
database name
Definition: mythdbparams.h:26
QString m_dbPassword
DB password.
Definition: mythdbparams.h:25
std::chrono::seconds m_wolReconnect
seconds to wait for reconnect
Definition: mythdbparams.h:34
QString m_localHostName
name used for loading/saving settings
Definition: mythdbparams.h:30
bool m_localEnabled
true if localHostName is not default
Definition: mythdbparams.h:29
bool IsValid(const QString &source=QString("Unknown")) const
Definition: mythdbparams.cpp:4
bool m_dbHostPing
No longer used.
Definition: mythdbparams.h:22
QString m_dbUserName
DB user name.
Definition: mythdbparams.h:24
QString m_wolCommand
command to use for wake-on-lan
Definition: mythdbparams.h:36
bool m_wolEnabled
true if wake-on-lan params are used
Definition: mythdbparams.h:33
int m_dbPort
database port
Definition: mythdbparams.h:23
int m_wolRetry
times to retry to reconnect
Definition: mythdbparams.h:35
QString m_dbHostName
database server
Definition: mythdbparams.h:21
void isClosing(void)
QString m_sLocation
Definition: upnpdevice.h:239
void cancelPortCheck(void)
static bool prompt(bool force=false)
Ask the user for the language to use.
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:876
static MThreadPool * globalInstance(void)
void waitForDone(void)
static void ejectOpticalDisc(void)
Eject a disk, unmount a drive, open a tray.
Dialog asking for user confirmation.
bool event(QEvent *) override
static bool LoadDatabaseSettings()
Load database and host settings from XmlConfiguration::k_default_filename, or set some defaults.
QString m_dbHostCp
dbHostName backup
void OnCloseDialog() const
static void processEvents()
bool m_gui
Should this context use GUI elements?
static void LanguagePrompt()
QDateTime m_lastCheck
bool checkPort(QString &host, int port, std::chrono::seconds timeLimit) const
Check if a port is open and sort out the link-local scope.
QString TestDBconnection(bool prompt=true)
Some quick sanity checks before opening a database connection.
bool PromptForDatabaseParams(const QString &error)
static bool DefaultUPnP(QString &Error)
Get the default backend from XmlConfiguration::kDefaultFilename, use UPnP to find it.
QEventLoop * m_loop
BackendSelection::Decision ChooseBackend(const QString &error)
Search for backends via UPnP, put up a UI for the user to choose one.
bool FindDatabase(bool prompt, bool noAutodetect)
Get database connection settings and test connectivity.
void ShowVersionMismatchPopup(uint remote_version)
bool Init(bool gui, bool promptForBackend, bool disableAutoDiscovery, bool ignoreDB)
static QString setLocalHostName(QString hostname)
MythUIHelper * m_ui
static bool UPnPconnect(const DeviceLocation *backend, const QString &PIN)
Query a backend via UPnP for its database connection parameters.
void EnableDBerrors() const
bool FindDatabaseChoose(bool loaded, bool manualSelect, bool autoSelect)
Helper function for getting database connection settings and test connectivity.
static void ResetDatabase(const DatabaseParams &dbParams)
Called when the user changes the DB connection settings.
void VersionMismatchPopupClosed()
static int UPnPautoconf(std::chrono::milliseconds milliSeconds=2s)
If there is only a single UPnP backend, use it.
GUIStartup * m_guiStartup
void TempMainWindow()
Setup a minimal themed main window, and prompt for user's language.
MythConfirmationDialog * m_mbeVersionPopup
GUISettingsCache m_GUISettingsCache
void ShowConnectionFailurePopup(bool persistent)
void HideConnectionFailurePopup()
void SilenceDBerrors()
Cause MSqlDatabase::OpenDatabase() and MSqlQuery to fail silently.
QString m_masterhostname
master backend hostname
Startup context for MythTV.
Definition: mythcontext.h:20
CleanupFunction m_cleanup
This is used to destroy global state before main() returns.
Definition: mythcontext.h:47
QString m_appBinaryVersion
Definition: mythcontext.h:42
MythContext(QString binversion, bool needsBackend=false)
virtual ~MythContext()
bool Init(bool gui=true, bool promptForBackend=false, bool disableAutoDiscovery=false, bool ignoreDB=false)
bool saveSettingsCache()
void SetDisableEventPopup(bool check)
Impl * m_impl
PIMPL idiom.
Definition: mythcontext.h:41
This class contains the runtime context for MythTV.
bool IsFrontend(void) const
is this process a frontend process
MythDB * GetDB(void)
void ActivateSettingsCache(bool activate=true)
void ClearOverrideSettingForSession(const QString &key)
void SetLocalHostname(const QString &hostname)
QString GetSetting(const QString &key, const QString &defaultval="")
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
MythPluginManager * GetPluginManager(void)
void OverrideSettingForSession(const QString &key, const QString &value)
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
bool IsBackend(void) const
is this process a backend process
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
bool IsWOLAllowed() const
void SaveLocaleDefaults(void)
int GetNumSetting(const QString &key, int defaultval=0)
void InitPower(bool Create=true)
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
This class is used as a container for messages.
Definition: mythevent.h:17
static const Type kMythEventMessage
Definition: mythevent.h:79
MythScreenStack * GetMainStack()
static MythMainWindow * getMainWindow(bool UseDB=true)
Return the existing main window, or create one.
void Init(bool MayReInit=true)
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
int Register(void *from)
An application can register in which case it will be assigned a reusable screen, which can be modifie...
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void SetId(int Id)
Contains the application registration id.
void SetParent(void *Parent)
Contains the parent address. Required if id is set Id provided must match the parent address as provi...
void SetDuration(std::chrono::seconds Duration)
Contains a duration during which the notification will be displayed for. The duration is informative ...
bool config_plugin(const QString &plugname)
Definition: mythplugin.cpp:180
bool run_plugin(const QString &plugname)
Definition: mythplugin.cpp:162
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
void Init(MythUIMenuCallbacks &cbs)
UPnPResultCode GetConnectionInfo(const QString &sPin, DatabaseParams *pParams, QString &sMsg)
Small class to handle TCP port checking and finding link-local context.
Definition: portchecker.h:45
bool checkPort(QString &host, int port, std::chrono::milliseconds timeLimit=30s, bool linkLocalOnly=false)
Check if a port is open and sort out the link-local scope.
Definition: portchecker.cpp:75
void cancelPortCheck(void)
Cancel the checkPort operation currently in progress.
static void PrintDebug(void)
Print out any leaks if that level of debugging is enabled.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
uint Count(void) const
Definition: ssdpcache.h:50
DeviceLocation * GetFirst(void)
Returns random entry in cache, returns nullptr when list is empty.
Definition: ssdpcache.cpp:75
static SSDPCache * Instance()
Definition: ssdpcache.cpp:285
SSDPCacheEntries * Find(const QString &sURI)
Finds the SSDPCacheEntries in the cache, returns nullptr when absent.
Definition: ssdpcache.cpp:341
static const QString kBackendURI
Definition: ssdp.h:77
static SSDP * Instance()
Definition: ssdp.cpp:57
void PerformSearch(const QString &sST, std::chrono::seconds timeout=2s)
Send a SSDP discover multicast datagram.
Definition: ssdp.cpp:154
static void Shutdown()
Definition: ssdp.cpp:67
static void Done(void)
static void Init(QObject *parent=nullptr)
static void Shutdown()
Definition: taskqueue.cpp:65
static const QString kDefaultUSN
Definition: configuration.h:63
static const QString kDefaultWOL
Definition: configuration.h:60
static const QString kDefaultDB
Definition: configuration.h:59
QString GetValue(const QString &setting)
static constexpr auto kDefaultFilename
Definition: configuration.h:57
void SetValue(const QString &setting, bool value)
static const QString kDefaultPIN
Definition: configuration.h:62
GUISettingsCache(const QString &cache_filename, QString cache_path)
Definition: mythcontext.cpp:79
static const std::array< QString, 13 > kSettings
Definition: mythcontext.cpp:97
unsigned int uint
Definition: compat.h:68
@ GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:16
@ GENERIC_EXIT_SOCKET_ERROR
Socket error.
Definition: exitcodes.h:21
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:685
static constexpr const char * MYTH_APPNAME_MYTHTV_SETUP
Definition: mythappname.h:7
#define LOC
Definition: mythcontext.cpp:70
static void plugin_cb(const QString &cmd)
static void exec_program_cb(const QString &cmd)
static void exec_program_tv_cb(const QString &cmd)
static void configplugin_cb(const QString &cmd)
static void eject_cb()
static const QString sLocation
Definition: mythcontext.cpp:71
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythDB(void)
Definition: mythdb.cpp:51
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
bool WaitFor(MythConfirmationDialog *dialog)
Blocks until confirmation dialog exits.
static QString confdir
Definition: mythdirs.cpp:22
void InitializeMythDirs(void)
Definition: mythdirs.cpp:32
QString GetConfDir(void)
Definition: mythdirs.cpp:263
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythNotificationCenter * GetNotificationCenter(void)
bool HasMythMainWindow(void)
MythMainWindow * GetMythMainWindow(void)
void DestroyMythMainWindow(void)
bool MythWakeup(const QString &wakeUpCommand, uint flags, std::chrono::seconds timeout)
int intResponse(const QString &query, int def)
In an interactive shell, prompt the user to input a number.
QString getResponse(const QString &query, const QString &def)
In an interactive shell, prompt the user to input a string.
void ShowNotificationError(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
convenience utility to display error message as notification
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
void DestroyMythUI()
MythUIHelper * GetMythUI()
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
def error(message)
Definition: smolt.py:409
string hostname
Definition: caa.py:17
STL namespace.
void(* exec_program)(const QString &cmd)
Definition: mythuihelper.h:16
@ UPnPResult_Success
Definition: upnp.h:35
@ UPnPResult_ActionNotAuthorized
Definition: upnp.h:46