MythTV  0.26-pre
mythcorecontext.cpp
Go to the documentation of this file.
00001 #include <QCoreApplication>
00002 #include <QUrl>
00003 #include <QDir>
00004 #include <QFileInfo>
00005 #include <QDebug>
00006 #include <QMutex>
00007 #include <QWaitCondition>
00008 #include <QNetworkInterface>
00009 #include <QAbstractSocket>
00010 #include <QHostAddress>
00011 #include <QNetworkInterface>
00012 #include <QNetworkAddressEntry>
00013 #include <QLocale>
00014 
00015 #include <cmath>
00016 
00017 #include <queue>
00018 #include <algorithm>
00019 using namespace std;
00020 
00021 #ifdef USING_MINGW
00022 #include <winsock2.h>
00023 #include <unistd.h>
00024 #else
00025 #include <locale.h>
00026 #endif
00027 
00028 #include "compat.h"
00029 #include "mythconfig.h"       // for CONFIG_DARWIN
00030 #include "mythdownloadmanager.h"
00031 #include "mythsocketthread.h"
00032 #include "mythcorecontext.h"
00033 #include "mythsocket.h"
00034 #include "mythsystem.h"
00035 #include "mthreadpool.h"
00036 #include "exitcodes.h"
00037 #include "mythlogging.h"
00038 #include "mythversion.h"
00039 #include "logging.h"
00040 #include "mthread.h"
00041 #include "serverpool.h"
00042 
00043 #define LOC      QString("MythCoreContext: ")
00044 
00045 MythCoreContext *gCoreContext = NULL;
00046 QMutex *avcodeclock = new QMutex(QMutex::Recursive);
00047 
00048 class MythCoreContextPrivate : public QObject
00049 {
00050   public:
00051     MythCoreContextPrivate(MythCoreContext *lparent, QString binversion,
00052                            QObject *guicontext);
00053    ~MythCoreContextPrivate();
00054 
00055     bool WaitForWOL(int timeout_ms = INT_MAX);
00056 
00057   public:
00058     MythCoreContext *m_parent;
00059     QObject         *m_GUIcontext;
00060     QObject         *m_GUIobject;
00061     QString          m_appBinaryVersion;
00062 
00063     QMutex  m_localHostLock;     
00064     QString m_localHostname;     
00065     QMutex  m_masterHostLock;    
00066     QString m_masterHostname;    
00067 
00068     QMutex      m_sockLock;      
00069     MythSocket *m_serverSock;    
00070     MythSocket *m_eventSock;     
00071 
00072     QMutex         m_WOLInProgressLock;
00073     QWaitCondition m_WOLInProgressWaitCondition;
00074     bool           m_WOLInProgress;
00075 
00076     bool m_backend;
00077 
00078     MythDB *m_database;
00079 
00080     QThread *m_UIThread;
00081 
00082     MythLocale *m_locale;
00083     QString language;
00084 
00085     MythScheduler *m_scheduler;
00086 
00087     bool m_blockingClient;
00088 };
00089 
00090 MythCoreContextPrivate::MythCoreContextPrivate(MythCoreContext *lparent,
00091                                                QString binversion,
00092                                                QObject *guicontext)
00093     : m_parent(lparent),
00094       m_GUIcontext(guicontext), m_GUIobject(NULL),
00095       m_appBinaryVersion(binversion),
00096       m_localHostname(QString::null),
00097       m_masterHostname(QString::null),
00098       m_sockLock(QMutex::NonRecursive),
00099       m_serverSock(NULL), m_eventSock(NULL),
00100       m_WOLInProgress(false),
00101       m_backend(false),
00102       m_database(GetMythDB()),
00103       m_UIThread(QThread::currentThread()),
00104       m_locale(NULL),
00105       m_scheduler(NULL),
00106       m_blockingClient(false)
00107 {
00108     MThread::ThreadSetup("CoreContext");
00109     srandom(QDateTime::currentDateTime().toTime_t() ^
00110             QTime::currentTime().msec());
00111 }
00112 
00113 MythCoreContextPrivate::~MythCoreContextPrivate()
00114 {
00115     MThreadPool::StopAllPools();
00116     ShutdownRRT();
00117 
00118     QMutexLocker locker(&m_sockLock);
00119     if (m_serverSock)
00120     {
00121         m_serverSock->DownRef();
00122         m_serverSock = NULL;
00123     }
00124     if (m_eventSock)
00125     {
00126         m_eventSock->DownRef();
00127         m_eventSock = NULL;
00128     }
00129 
00130     delete m_locale;
00131 
00132     MThreadPool::ShutdownAllPools();
00133 
00134     ShutdownMythSystem();
00135 
00136     ShutdownMythDownloadManager();
00137 
00138     // This has already been run in the MythContext dtor.  Do we need it here
00139     // too?
00140 #if 0
00141     logStop(); // need to shutdown db logger before we kill db
00142 #endif
00143 
00144     MThread::Cleanup();
00145 
00146     GetMythDB()->GetDBManager()->CloseDatabases();
00147 
00148     if (m_database) {
00149         DestroyMythDB();
00150         m_database = NULL;
00151     }
00152 
00153     loggingDeregisterThread();
00154 }
00155 
00159 bool MythCoreContextPrivate::WaitForWOL(int timeout_in_ms)
00160 {
00161     int timeout_remaining = timeout_in_ms;
00162     while (m_WOLInProgress && (timeout_remaining > 0))
00163     {
00164         LOG(VB_GENERAL, LOG_INFO, LOC + "Wake-On-LAN in progress, waiting...");
00165 
00166         int max_wait = min(1000, timeout_remaining);
00167         m_WOLInProgressWaitCondition.wait(
00168             &m_WOLInProgressLock, max_wait);
00169         timeout_remaining -= max_wait;
00170     }
00171 
00172     return !m_WOLInProgress;
00173 }
00174 
00175 MythCoreContext::MythCoreContext(const QString &binversion,
00176                                  QObject *guiContext)
00177     : d(NULL)
00178 {
00179     d = new MythCoreContextPrivate(this, binversion, guiContext);
00180 }
00181 
00182 bool MythCoreContext::Init(void)
00183 {
00184     if (!d)
00185     {
00186         LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
00187         return false;
00188     }
00189 
00190     if (d->m_appBinaryVersion != MYTH_BINARY_VERSION)
00191     {
00192         LOG(VB_GENERAL, LOG_CRIT,
00193             QString("Application binary version (%1) does not "
00194                     "match libraries (%2)")
00195                 .arg(d->m_appBinaryVersion) .arg(MYTH_BINARY_VERSION));
00196 
00197         QString warning = QObject::tr(
00198             "This application is not compatible "
00199             "with the installed MythTV libraries. "
00200             "Please recompile after a make distclean");
00201         LOG(VB_GENERAL, LOG_WARNING, warning);
00202 
00203         return false;
00204     }
00205 
00206 #ifndef _WIN32
00207     QString lang_variables("");
00208     QString lc_value = setlocale(LC_CTYPE, NULL);
00209     if (lc_value.isEmpty())
00210     {
00211         // try fallback to environment variables for non-glibc systems
00212         // LC_ALL, then LC_CTYPE
00213         lc_value = getenv("LC_ALL");
00214         if (lc_value.isEmpty())
00215             lc_value = getenv("LC_CTYPE");
00216     }
00217     if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
00218         lang_variables.append("LC_ALL or LC_CTYPE");
00219     lc_value = getenv("LANG");
00220     if (!lc_value.contains("UTF-8", Qt::CaseInsensitive))
00221     {
00222         if (!lang_variables.isEmpty())
00223             lang_variables.append(", and ");
00224         lang_variables.append("LANG");
00225     }
00226     LOG(VB_GENERAL, LOG_INFO, QString("Assumed character encoding: %1")
00227                                      .arg(lc_value));
00228     if (!lang_variables.isEmpty())
00229         LOG(VB_GENERAL, LOG_WARNING, QString("This application expects to "
00230             "be running a locale that specifies a UTF-8 codeset, and many "
00231             "features may behave improperly with your current language "
00232             "settings. Please set the %1 variable(s) in the environment "
00233             "in which this program is executed to include a UTF-8 codeset "
00234             "(such as 'en_US.UTF-8').").arg(lang_variables));
00235 #endif
00236 
00237     return true;
00238 }
00239 
00240 MythCoreContext::~MythCoreContext()
00241 {
00242     delete d;
00243     d = NULL;
00244 }
00245 
00246 bool MythCoreContext::SetupCommandSocket(MythSocket *serverSock,
00247                                          const QString &announcement,
00248                                          uint timeout_in_ms,
00249                                          bool &proto_mismatch)
00250 {
00251     proto_mismatch = false;
00252 
00253 #ifndef IGNORE_PROTO_VER_MISMATCH
00254     if (!CheckProtoVersion(serverSock, timeout_in_ms, true))
00255     {
00256         proto_mismatch = true;
00257         return false;
00258     }
00259 #endif
00260 
00261     QStringList strlist(announcement);
00262 
00263     if (!serverSock->writeStringList(strlist))
00264     {
00265         LOG(VB_GENERAL, LOG_ERR, LOC + "Connecting server socket to "
00266                                        "master backend, socket write failed");
00267         return false;
00268     }
00269 
00270     if (!serverSock->readStringList(strlist, true) || strlist.empty() ||
00271         (strlist[0] == "ERROR"))
00272     {
00273         if (!strlist.empty())
00274             LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
00275                                            "server socket to master backend");
00276         else
00277             LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
00278                                            "server socket to master backend");
00279         return false;
00280     }
00281 
00282     return true;
00283 }
00284 
00285 // Assumes that either m_sockLock is held, or the app is still single
00286 // threaded (i.e. during startup).
00287 bool MythCoreContext::ConnectToMasterServer(bool blockingClient,
00288                                             bool openEventSocket)
00289 {
00290     if (IsMasterBackend())
00291     {
00292         // Should never get here unless there is a bug in the code somewhere.
00293         // If this happens, it can cause endless event loops.
00294         LOG(VB_GENERAL, LOG_ERR, "ERROR: Master backend tried to connect back "
00295                 "to itself!");
00296         return false;
00297     }
00298 
00299     QString server = GetSetting("MasterServerIP", "localhost");
00300     int     port   = GetNumSetting("MasterServerPort", 6543);
00301     bool    proto_mismatch = false;
00302 
00303     if (!d->m_serverSock)
00304     {
00305         QString ann = QString("ANN %1 %2 %3")
00306             .arg(blockingClient ? "Playback" : "Monitor")
00307             .arg(d->m_localHostname).arg(false);
00308         d->m_serverSock = ConnectCommandSocket(
00309             server, port, ann, &proto_mismatch);
00310     }
00311 
00312     if (!d->m_serverSock)
00313         return false;
00314 
00315     d->m_blockingClient = blockingClient;
00316 
00317     if (!openEventSocket)
00318         return true;
00319 
00320     if (!IsBackend() && !d->m_eventSock)
00321         d->m_eventSock = ConnectEventSocket(server, port);
00322 
00323     if (!IsBackend() && !d->m_eventSock)
00324     {
00325         d->m_serverSock->DownRef();
00326         d->m_serverSock = NULL;
00327 
00328         QCoreApplication::postEvent(
00329             d->m_GUIcontext, new MythEvent("CONNECTION_FAILURE"));
00330 
00331         return false;
00332     }
00333 
00334     return true;
00335 }
00336 
00337 MythSocket *MythCoreContext::ConnectCommandSocket(
00338     const QString &hostname, int port, const QString &announce,
00339     bool *p_proto_mismatch, bool gui, int maxConnTry, int setup_timeout)
00340 {
00341     MythSocket *serverSock = NULL;
00342 
00343     {
00344         QMutexLocker locker(&d->m_WOLInProgressLock);
00345         d->WaitForWOL();
00346     }
00347 
00348     QString WOLcmd = GetSetting("WOLbackendCommand", "");
00349 
00350     if (maxConnTry < 1)
00351         maxConnTry = max(GetNumSetting("BackendConnectRetry", 1), 1);
00352 
00353     int WOLsleepTime = 0, WOLmaxConnTry = 0;
00354     if (!WOLcmd.isEmpty())
00355     {
00356         WOLsleepTime  = GetNumSetting("WOLbackendReconnectWaitTime", 0);
00357         WOLmaxConnTry = max(GetNumSetting("WOLbackendConnectRetry", 1), 1);
00358         maxConnTry    = max(maxConnTry, WOLmaxConnTry);
00359     }
00360 
00361     bool we_attempted_wol = false;
00362 
00363     if (setup_timeout <= 0)
00364         setup_timeout = MythSocket::kShortTimeout;
00365 
00366     bool proto_mismatch = false;
00367     for (int cnt = 1; cnt <= maxConnTry; cnt++)
00368     {
00369         LOG(VB_GENERAL, LOG_INFO, LOC +
00370             QString("Connecting to backend server: %1:%2 (try %3 of %4)")
00371                 .arg(hostname).arg(port).arg(cnt).arg(maxConnTry));
00372 
00373         serverSock = new MythSocket();
00374 
00375         int sleepms = 0;
00376         if (serverSock->connect(hostname, port))
00377         {
00378             if (SetupCommandSocket(
00379                     serverSock, announce, setup_timeout, proto_mismatch))
00380             {
00381                 break;
00382             }
00383 
00384             if (proto_mismatch)
00385             {
00386                 if (p_proto_mismatch)
00387                     *p_proto_mismatch = true;
00388 
00389                 serverSock->DownRef();
00390                 serverSock = NULL;
00391                 break;
00392             }
00393 
00394             setup_timeout = (int)(setup_timeout * 1.5f);
00395         }
00396         else if (!WOLcmd.isEmpty() && (cnt < maxConnTry))
00397         {
00398             if (!we_attempted_wol)
00399             {
00400                 QMutexLocker locker(&d->m_WOLInProgressLock);
00401                 if (d->m_WOLInProgress)
00402                 {
00403                     d->WaitForWOL();
00404                     continue;
00405                 }
00406 
00407                 d->m_WOLInProgress = we_attempted_wol = true;
00408             }
00409 
00410             myth_system(WOLcmd, kMSDontDisableDrawing | kMSDontBlockInputDevs |
00411                                 kMSProcessEvents);
00412             sleepms = WOLsleepTime * 1000;
00413         }
00414 
00415         serverSock->DownRef();
00416         serverSock = NULL;
00417 
00418         if (!serverSock && (cnt == 1))
00419         {
00420             QCoreApplication::postEvent(
00421                 d->m_GUIcontext, new MythEvent("CONNECTION_FAILURE"));
00422         }
00423 
00424         if (sleepms)
00425             usleep(sleepms * 1000);
00426     }
00427 
00428     if (we_attempted_wol)
00429     {
00430         QMutexLocker locker(&d->m_WOLInProgressLock);
00431         d->m_WOLInProgress = false;
00432         d->m_WOLInProgressWaitCondition.wakeAll();
00433     }
00434 
00435     if (!serverSock && !proto_mismatch)
00436     {
00437         LOG(VB_GENERAL, LOG_ERR,
00438                 "Connection to master server timed out.\n\t\t\t"
00439                 "Either the server is down or the master server settings"
00440                 "\n\t\t\t"
00441                 "in mythtv-settings does not contain the proper IP address\n");
00442     }
00443     else
00444     {
00445         QCoreApplication::postEvent(
00446             d->m_GUIcontext, new MythEvent("CONNECTION_RESTABLISHED"));
00447     }
00448 
00449     return serverSock;
00450 }
00451 
00452 MythSocket *MythCoreContext::ConnectEventSocket(const QString &hostname,
00453                                                 int port)
00454 {
00455     MythSocket *m_eventSock = new MythSocket();
00456 
00457     while (m_eventSock->state() != MythSocket::Idle)
00458     {
00459         usleep(5000);
00460     }
00461 
00462     // Assume that since we _just_ connected the command socket,
00463     // this one won't need multiple retries to work...
00464     if (!m_eventSock->connect(hostname, port))
00465     {
00466         LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect event "
00467                                        "socket to master backend");
00468         m_eventSock->DownRef();
00469         m_eventSock = NULL;
00470         return NULL;
00471     }
00472 
00473     m_eventSock->Lock();
00474 
00475     QString str = QString("ANN Monitor %1 %2")
00476         .arg(d->m_localHostname).arg(true);
00477     QStringList strlist(str);
00478     m_eventSock->writeStringList(strlist);
00479     if (!m_eventSock->readStringList(strlist) || strlist.empty() ||
00480         (strlist[0] == "ERROR"))
00481     {
00482         if (!strlist.empty())
00483             LOG(VB_GENERAL, LOG_ERR, LOC + "Problem connecting "
00484                                            "event socket to master backend");
00485         else
00486             LOG(VB_GENERAL, LOG_ERR, LOC + "Timeout connecting "
00487                                            "event socket to master backend");
00488 
00489         m_eventSock->DownRef();
00490         m_eventSock->Unlock();
00491         m_eventSock = NULL;
00492         return NULL;
00493     }
00494 
00495     m_eventSock->Unlock();
00496     m_eventSock->setCallbacks(this);
00497 
00498     return m_eventSock;
00499 }
00500 
00501 bool MythCoreContext::IsConnectedToMaster(void)
00502 {
00503     QMutexLocker locker(&d->m_sockLock);
00504     return d->m_serverSock;
00505 }
00506 
00507 void MythCoreContext::BlockShutdown(void)
00508 {
00509     QStringList strlist;
00510 
00511     QMutexLocker locker(&d->m_sockLock);
00512     if (d->m_serverSock == NULL)
00513         return;
00514 
00515     strlist << "BLOCK_SHUTDOWN";
00516     d->m_serverSock->writeStringList(strlist);
00517     d->m_serverSock->readStringList(strlist);
00518 
00519     if ((d->m_eventSock == NULL) ||
00520         (d->m_eventSock->state() != MythSocket::Connected))
00521         return;
00522 
00523     d->m_blockingClient = true;
00524 
00525     strlist.clear();
00526     strlist << "BLOCK_SHUTDOWN";
00527 
00528     d->m_eventSock->Lock();
00529 
00530     d->m_eventSock->writeStringList(strlist);
00531     d->m_eventSock->readStringList(strlist);
00532 
00533     d->m_eventSock->Unlock();
00534 }
00535 
00536 void MythCoreContext::AllowShutdown(void)
00537 {
00538     QStringList strlist;
00539 
00540     QMutexLocker locker(&d->m_sockLock);
00541     if (d->m_serverSock == NULL)
00542         return;
00543 
00544     strlist << "ALLOW_SHUTDOWN";
00545     d->m_serverSock->writeStringList(strlist);
00546     d->m_serverSock->readStringList(strlist);
00547 
00548     if ((d->m_eventSock == NULL) ||
00549         (d->m_eventSock->state() != MythSocket::Connected))
00550         return;
00551 
00552     d->m_blockingClient = false;
00553 
00554     strlist.clear();
00555     strlist << "ALLOW_SHUTDOWN";
00556 
00557     d->m_eventSock->Lock();
00558 
00559     d->m_eventSock->writeStringList(strlist);
00560     d->m_eventSock->readStringList(strlist);
00561 
00562     d->m_eventSock->Unlock();
00563 }
00564 
00565 bool MythCoreContext::IsBlockingClient(void) const
00566 {
00567     return d->m_blockingClient;
00568 }
00569 
00570 void MythCoreContext::SetBackend(bool backend)
00571 {
00572     d->m_backend = backend;
00573 }
00574 
00575 bool MythCoreContext::IsBackend(void) const
00576 {
00577     return d->m_backend;
00578 }
00579 
00580 bool MythCoreContext::IsMasterHost(void)
00581 {
00582     QString host = GetHostName();
00583     return IsMasterHost(host);
00584 }
00585 
00586 bool MythCoreContext::IsMasterHost(const QString &host)
00587 {
00588     return IsThisHost(GetSetting("MasterServerIP"), host);
00589 }
00590 
00591 bool MythCoreContext::IsMasterBackend(void)
00592 {
00593     return (IsBackend() && IsMasterHost());
00594 }
00595 
00596 bool MythCoreContext::BackendIsRunning(void)
00597 {
00598 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__)
00599     const char *command = "ps -axc | grep -i mythbackend | grep -v grep > /dev/null";
00600 #elif defined USING_MINGW
00601     const char *command = "%systemroot%\\system32\\tasklist.exe "
00602        " | %systemroot%\\system32\\find.exe /i \"mythbackend.exe\" ";
00603 #else
00604     const char *command = "ps ch -C mythbackend -o pid > /dev/null";
00605 #endif
00606     uint res = myth_system(command, kMSDontBlockInputDevs |
00607                                     kMSDontDisableDrawing |
00608                                     kMSProcessEvents);
00609     return (res == GENERIC_EXIT_OK);
00610 }
00611 
00612 bool MythCoreContext::IsThisHost(const QString &addr)
00613 {
00614     return IsThisHost(addr, GetHostName());
00615 }
00616 
00617 bool MythCoreContext::IsThisHost(const QString &addr, const QString &host)
00618 {
00619     QString thisip = GetSettingOnHost("BackendServerIP", host);
00620     QString thisip6 = GetSettingOnHost("BackendServerIP6", host);
00621 
00622     return ((addr == thisip) || (addr == thisip6));
00623 }
00624 
00625 bool MythCoreContext::IsFrontendOnly(void)
00626 {
00627     // find out if a backend runs on this host...
00628     bool backendOnLocalhost = false;
00629 
00630     QStringList strlist("QUERY_IS_ACTIVE_BACKEND");
00631     strlist << GetHostName();
00632 
00633     SendReceiveStringList(strlist);
00634 
00635     if (QString(strlist[0]) == "FALSE")
00636         backendOnLocalhost = false;
00637     else
00638         backendOnLocalhost = true;
00639 
00640     return !backendOnLocalhost;
00641 }
00642 
00643 QString MythCoreContext::GenMythURL(QString host, QString port, QString path, QString storageGroup)
00644 {
00645     return GenMythURL(host,port.toInt(),path,storageGroup);
00646 }
00647 
00648 QString MythCoreContext::GenMythURL(QString host, int port, QString path, QString storageGroup)
00649 {
00650     QString ret;
00651 
00652     QString m_storageGroup;
00653     QString m_host;
00654     QString m_port;
00655 
00656     QHostAddress addr(host);
00657 
00658     if (!storageGroup.isEmpty())
00659         m_storageGroup = storageGroup + "@";
00660 
00661     m_host = host;
00662 
00663 #if !defined(QT_NO_IPV6)
00664     // Basically if it appears to be an IPv6 IP surround the IP with [] otherwise don't bother
00665     if (( addr.protocol() == QAbstractSocket::IPv6Protocol ) || (host.contains(":")))
00666         m_host = "[" + host + "]";
00667 #endif
00668 
00669     if (port > 0)
00670         m_port = QString(":%1").arg(port);
00671     else
00672         m_port = "";
00673 
00674     QString seperator = "/";
00675     if (path.startsWith("/"))
00676         seperator = "";
00677 
00678     ret = QString("myth://%1%2%3%4%5").arg(m_storageGroup).arg(m_host).arg(m_port).arg(seperator).arg(path);
00679 
00680 #if 0
00681     LOG(VB_GENERAL, LOG_DEBUG, LOC +
00682         QString("GenMythURL returning %1").arg(ret));
00683 #endif
00684 
00685     return ret;
00686 }
00687 
00688 QString MythCoreContext::GetMasterHostPrefix(const QString &storageGroup,
00689                                              const QString &path)
00690 {
00691     QString ret;
00692 
00693     if (IsMasterHost())
00694     {
00695         return GenMythURL(GetSetting("MasterServerIP"),
00696                           GetNumSetting("MasterServerPort", 6543),
00697                           path,
00698                           storageGroup);
00699     }
00700 
00701     QMutexLocker locker(&d->m_sockLock);
00702     if (!d->m_serverSock)
00703     {
00704         bool blockingClient = GetNumSetting("idleTimeoutSecs",0) > 0;
00705         ConnectToMasterServer(blockingClient);
00706     }
00707 
00708     if (d->m_serverSock)
00709     {
00710 
00711          ret = GenMythURL(d->m_serverSock->peerAddress().toString(),
00712                           d->m_serverSock->peerPort(),
00713                           path,
00714                           storageGroup);
00715     }
00716 
00717     return ret;
00718 }
00719 
00720 QString MythCoreContext::GetMasterHostName(void)
00721 {
00722     QMutexLocker locker(&d->m_masterHostLock);
00723 
00724     if (d->m_masterHostname.isEmpty())
00725     {
00726 
00727         if (IsMasterBackend())
00728             d->m_masterHostname = d->m_localHostname;
00729         else
00730         {
00731             QStringList strlist("QUERY_HOSTNAME");
00732 
00733             if (SendReceiveStringList(strlist))
00734                 d->m_masterHostname = strlist[0];
00735         }
00736     }
00737 
00738     QString ret = d->m_masterHostname;
00739     ret.detach();
00740 
00741     return ret;
00742 }
00743 
00744 void MythCoreContext::ClearSettingsCache(const QString &myKey)
00745 {
00746     d->m_database->ClearSettingsCache(myKey);
00747 }
00748 
00749 void MythCoreContext::ActivateSettingsCache(bool activate)
00750 {
00751     d->m_database->ActivateSettingsCache(activate);
00752 }
00753 
00754 QString MythCoreContext::GetHostName(void)
00755 {
00756     QMutexLocker (&d->m_localHostLock);
00757     QString tmp = d->m_localHostname;
00758     tmp.detach();
00759     return tmp;
00760 }
00761 
00762 QString MythCoreContext::GetFilePrefix(void)
00763 {
00764     return GetSetting("RecordFilePrefix");
00765 }
00766 
00767 void MythCoreContext::GetResolutionSetting(const QString &type,
00768                                            int &width, int &height,
00769                                            double &forced_aspect,
00770                                            double &refresh_rate,
00771                                            int index)
00772 {
00773     d->m_database->GetResolutionSetting(type, width, height, forced_aspect,
00774                                         refresh_rate, index);
00775 }
00776 
00777 void MythCoreContext::GetResolutionSetting(const QString &t, int &w,
00778                                            int &h, int i)
00779 {
00780     d->m_database->GetResolutionSetting(t, w, h, i);
00781 }
00782 
00783 MDBManager *MythCoreContext::GetDBManager(void)
00784 {
00785     return d->m_database->GetDBManager();
00786 }
00787 
00794 bool MythCoreContext::IsDatabaseIgnored(void) const
00795 {
00796     return d->m_database->IsDatabaseIgnored();
00797 }
00798 
00799 void MythCoreContext::SaveSetting(const QString &key, int newValue)
00800 {
00801     d->m_database->SaveSetting(key, newValue);
00802 }
00803 
00804 void MythCoreContext::SaveSetting(const QString &key, const QString &newValue)
00805 {
00806     d->m_database->SaveSetting(key, newValue);
00807 }
00808 
00809 bool MythCoreContext::SaveSettingOnHost(const QString &key,
00810                                         const QString &newValue,
00811                                         const QString &host)
00812 {
00813     return d->m_database->SaveSettingOnHost(key, newValue, host);
00814 }
00815 
00816 QString MythCoreContext::GetSetting(const QString &key,
00817                                     const QString &defaultval)
00818 {
00819     return d->m_database->GetSetting(key, defaultval);
00820 }
00821 
00822 int MythCoreContext::GetNumSetting(const QString &key, int defaultval)
00823 {
00824     return d->m_database->GetNumSetting(key, defaultval);
00825 }
00826 
00827 double MythCoreContext::GetFloatSetting(const QString &key, double defaultval)
00828 {
00829     return d->m_database->GetFloatSetting(key, defaultval);
00830 }
00831 
00832 QString MythCoreContext::GetSettingOnHost(const QString &key,
00833                                           const QString &host,
00834                                           const QString &defaultval)
00835 {
00836     return d->m_database->GetSettingOnHost(key, host, defaultval);
00837 }
00838 
00839 int MythCoreContext::GetNumSettingOnHost(const QString &key,
00840                                          const QString &host,
00841                                          int defaultval)
00842 {
00843     return d->m_database->GetNumSettingOnHost(key, host, defaultval);
00844 }
00845 
00846 double MythCoreContext::GetFloatSettingOnHost(const QString &key,
00847                                               const QString &host,
00848                                               double defaultval)
00849 {
00850     return d->m_database->GetFloatSettingOnHost(key, host, defaultval);
00851 }
00852 
00853 #if 0
00854 QString MythCoreContext::GetMasterServerIP(void)
00855 {
00856     QString saddr = GetSetting("MasterServerIP");
00857     QHostAddress addr(saddr);
00858 
00859     if (!d->m_IPv6.empty() &&
00860             (addr.protocol() == QAbstractSocket::IPv6Protocol))
00861         // we have IPv6 addresses, assume we can connect to them
00862         return saddr;
00863     else if (!d->m_IPv4.empty() &&
00864             (addr.protocol() == QAbstractSocket::IPv4Protocol))
00865         // we have IPv4 addresses, assume we can connect to them
00866         return saddr;
00867     else
00868         return GetBackendServerIP(GetMasterHostName());
00869 }
00870 #endif
00871 
00872 QString MythCoreContext::GetBackendServerIP(void)
00873 {
00874     return GetBackendServerIP(d->m_localHostname);
00875 }
00876 
00877 QString MythCoreContext::GetBackendServerIP(const QString &host)
00878 {
00879     QString addr4, addr6;
00880 #if !defined(QT_NO_IPV6)
00881     if (!ServerPool::DefaultListenIPv6().isEmpty())
00882         // we have IPv6 addresses, assume we can connect to them
00883         addr6 = GetSettingOnHost("BackendServerIP6", host, "");
00884 #endif
00885     if (!ServerPool::DefaultListenIPv4().isEmpty())
00886         addr4 = GetSettingOnHost("BackendServerIP", host, "");
00887 
00888     if (addr6.isEmpty())
00889     {
00890         if (addr4.isEmpty())
00891         {
00892             LOG(VB_GENERAL, LOG_ERR, "No address defined for host: "+host);
00893             return "";
00894         }
00895 
00896         // IPv6 is empty, so return this regardless
00897         return addr4;
00898     }
00899     else if ((QHostAddress(addr6) == QHostAddress::LocalHostIPv6) &&
00900               !addr4.isEmpty() &&
00901              (QHostAddress(addr4) != QHostAddress::LocalHost))
00902         // IPv6 set to localhost, but IPv4 address if network accessible
00903         return addr4;
00904     else
00905         return addr6;
00906 }
00907 
00908 void MythCoreContext::OverrideSettingForSession(const QString &key,
00909                                                 const QString &value)
00910 {
00911     d->m_database->OverrideSettingForSession(key, value);
00912 }
00913 
00914 void MythCoreContext::ClearOverrideSettingForSession(const QString &key)
00915 {
00916     d->m_database->ClearOverrideSettingForSession(key);
00917 }
00918 
00919 bool MythCoreContext::IsUIThread(void)
00920 {
00921     return is_current_thread(d->m_UIThread);
00922 }
00923 
00924 bool MythCoreContext::SendReceiveStringList(QStringList &strlist,
00925                                         bool quickTimeout, bool block)
00926 {
00927     if (HasGUI() && IsUIThread())
00928     {
00929         QString msg = "SendReceiveStringList(";
00930         for (uint i=0; i<(uint)strlist.size() && i<2; i++)
00931             msg += (i?",":"") + strlist[i];
00932         msg += (strlist.size() > 2) ? "...)" : ")";
00933         msg += " called from UI thread";
00934         LOG(VB_GENERAL, LOG_DEBUG, msg);
00935     }
00936 
00937     QString query_type = "UNKNOWN";
00938 
00939     if (!strlist.isEmpty())
00940         query_type = strlist[0];
00941 
00942     QMutexLocker locker(&d->m_sockLock);
00943     if (!d->m_serverSock)
00944     {
00945         bool blockingClient = GetNumSetting("idleTimeoutSecs",0) > 0;
00946         ConnectToMasterServer(blockingClient);
00947     }
00948 
00949     bool ok = false;
00950 
00951     if (d->m_serverSock)
00952     {
00953         QStringList sendstrlist = strlist;
00954         d->m_serverSock->writeStringList(sendstrlist);
00955         ok = d->m_serverSock->readStringList(strlist, quickTimeout);
00956 
00957         if (!ok)
00958         {
00959             LOG(VB_GENERAL, LOG_NOTICE,
00960                 QString("Connection to backend server lost"));
00961             d->m_serverSock->DownRef();
00962             d->m_serverSock = NULL;
00963 
00964             if (d->m_eventSock)
00965             {
00966                 d->m_eventSock->DownRef();
00967                 d->m_eventSock = NULL;
00968             }
00969 
00970             bool blockingClient = GetNumSetting("idleTimeoutSecs",0);
00971             ConnectToMasterServer(blockingClient);
00972 
00973             if (d->m_serverSock)
00974             {
00975                 d->m_serverSock->writeStringList(sendstrlist);
00976                 ok = d->m_serverSock->readStringList(strlist, quickTimeout);
00977             }
00978         }
00979 
00980         // this should not happen
00981         while (ok && strlist[0] == "BACKEND_MESSAGE")
00982         {
00983             // oops, not for us
00984             LOG(VB_GENERAL, LOG_EMERG, "SRSL you shouldn't see this!!");
00985             QString message = strlist[1];
00986             strlist.pop_front(); strlist.pop_front();
00987 
00988             MythEvent me(message, strlist);
00989             dispatch(me);
00990 
00991             ok = d->m_serverSock->readStringList(strlist, quickTimeout);
00992         }
00993 
00994         if (!ok)
00995         {
00996             if (d->m_serverSock)
00997             {
00998                 d->m_serverSock->DownRef();
00999                 d->m_serverSock = NULL;
01000             }
01001 
01002             LOG(VB_GENERAL, LOG_CRIT,
01003                 QString("Reconnection to backend server failed"));
01004 
01005             QCoreApplication::postEvent(d->m_GUIcontext,
01006                                 new MythEvent("PERSISTENT_CONNECTION_FAILURE"));
01007         }
01008     }
01009 
01010     if (ok)
01011     {
01012         if (strlist.isEmpty())
01013             ok = false;
01014         else if (strlist[0] == "ERROR")
01015         {
01016             if (strlist.size() == 2)
01017                 LOG(VB_GENERAL, LOG_INFO,
01018                     QString("Protocol query '%1' responded with the error '%2'")
01019                         .arg(query_type).arg(strlist[1]));
01020             else
01021                 LOG(VB_GENERAL, LOG_INFO,
01022                     QString("Protocol query '%1' responded with an error, but "
01023                             "no error message.") .arg(query_type));
01024 
01025             ok = false;
01026         }
01027     }
01028 
01029     return ok;
01030 }
01031 
01032 void MythCoreContext::SendMessage(const QString &message)
01033 {
01034     if (IsBackend())
01035     {
01036         dispatch(MythEvent(message));
01037         return;
01038     }
01039 
01040     QStringList strlist( "MESSAGE" );
01041     strlist << message;
01042 
01043     SendReceiveStringList(strlist);
01044 }
01045 
01046 void MythCoreContext::SendEvent(const MythEvent &event)
01047 {
01048     if (IsBackend())
01049     {
01050         dispatch(event);
01051         return;
01052     }
01053 
01054     QStringList strlist( "MESSAGE" );
01055     strlist << event.Message();
01056     strlist << event.ExtraDataList();
01057 
01058     SendReceiveStringList(strlist);
01059 }
01060 
01061 void MythCoreContext::SendSystemEvent(const QString &msg)
01062 {
01063     if (QCoreApplication::applicationName() == MYTH_APPNAME_MYTHTV_SETUP)
01064         return;
01065 
01066     SendMessage(QString("SYSTEM_EVENT %1 SENDER %2")
01067                         .arg(msg).arg(GetHostName()));
01068 }
01069 
01070 void MythCoreContext::SendHostSystemEvent(const QString &msg,
01071                                           const QString &hostname,
01072                                           const QString &args)
01073 {
01074     SendSystemEvent(QString("%1 HOST %2 %3").arg(msg).arg(hostname).arg(args));
01075 }
01076 
01077 
01078 void MythCoreContext::readyRead(MythSocket *sock)
01079 {
01080     while (sock->state() == MythSocket::Connected &&
01081            sock->bytesAvailable() > 0)
01082     {
01083         QStringList strlist;
01084         if (!sock->readStringList(strlist))
01085             continue;
01086 
01087         QString prefix = strlist[0];
01088         QString message = strlist[1];
01089 
01090         if (prefix == "OK")
01091         {
01092         }
01093         else if (prefix != "BACKEND_MESSAGE")
01094         {
01095             LOG(VB_GENERAL, LOG_ERR,
01096                     QString("Received a: %1 message from the backend "
01097                             "but I don't know what to do with it.")
01098                         .arg(prefix));
01099         }
01100         else if (message == "CLEAR_SETTINGS_CACHE")
01101         {
01102             // No need to dispatch this message to ourself, so handle it
01103             LOG(VB_GENERAL, LOG_INFO, "Received remote 'Clear Cache' request");
01104             ClearSettingsCache();
01105         }
01106         else
01107         {
01108             strlist.pop_front();
01109             strlist.pop_front();
01110             MythEvent me(message, strlist);
01111             dispatch(me);
01112         }
01113     }
01114 }
01115 
01116 void MythCoreContext::connectionClosed(MythSocket *sock)
01117 {
01118     (void)sock;
01119 
01120     LOG(VB_GENERAL, LOG_NOTICE,
01121         "Event socket closed.  No connection to the backend.");
01122 
01123     QMutexLocker locker(&d->m_sockLock);
01124     if (d->m_serverSock)
01125     {
01126         d->m_serverSock->DownRef();
01127         d->m_serverSock = NULL;
01128     }
01129 
01130     if (d->m_eventSock)
01131     {
01132         d->m_eventSock->DownRef();
01133         d->m_eventSock = NULL;
01134     }
01135 
01136     dispatch(MythEvent(QString("BACKEND_SOCKETS_CLOSED")));
01137 }
01138 
01139 bool MythCoreContext::CheckProtoVersion(MythSocket *socket, uint timeout_ms,
01140                                         bool error_dialog_desired)
01141 {
01142     if (!socket)
01143         return false;
01144 
01145     QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
01146                         .arg(MYTH_PROTO_VERSION).arg(MYTH_PROTO_TOKEN));
01147     socket->writeStringList(strlist);
01148 
01149     if (!socket->readStringList(strlist, timeout_ms) || strlist.empty())
01150     {
01151         LOG(VB_GENERAL, LOG_CRIT, "Protocol version check failure.\n\t\t\t"
01152                 "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
01153                 "This happens when the backend is too busy to respond,\n\t\t\t"
01154                 "or has deadlocked due to bugs or hardware failure.");
01155 
01156         return false;
01157     }
01158     else if (strlist[0] == "REJECT" && strlist.size() >= 2)
01159     {
01160         LOG(VB_GENERAL, LOG_CRIT, QString("Protocol version or token mismatch "
01161                                           "(frontend=%1/%2,backend=%3/\?\?)\n")
01162                                       .arg(MYTH_PROTO_VERSION)
01163                                       .arg(MYTH_PROTO_TOKEN)
01164                                       .arg(strlist[1]));
01165 
01166         if (error_dialog_desired && d->m_GUIcontext)
01167         {
01168             QStringList list(strlist[1]);
01169             QCoreApplication::postEvent(
01170                 d->m_GUIcontext, new MythEvent("VERSION_MISMATCH", list));
01171         }
01172 
01173         return false;
01174     }
01175     else if (strlist[0] == "ACCEPT")
01176     {
01177         LOG(VB_GENERAL, LOG_INFO, QString("Using protocol version %1")
01178                                       .arg(MYTH_PROTO_VERSION));
01179         return true;
01180     }
01181 
01182     LOG(VB_GENERAL, LOG_ERR,
01183         QString("Unexpected response to MYTH_PROTO_VERSION: %1")
01184             .arg(strlist[0]));
01185     return false;
01186 }
01187 
01188 void MythCoreContext::dispatch(const MythEvent &event)
01189 {
01190     LOG(VB_NETWORK, LOG_INFO, QString("MythEvent: %1").arg(event.Message()));
01191 
01192     MythObservable::dispatch(event);
01193 }
01194 
01195 void MythCoreContext::dispatchNow(const MythEvent &event)
01196 {
01197     LOG(VB_NETWORK, LOG_INFO, QString("MythEvent: %1").arg(event.Message()));
01198 
01199     MythObservable::dispatchNow(event);
01200 }
01201 
01202 void MythCoreContext::SetLocalHostname(const QString &hostname)
01203 {
01204     QMutexLocker locker(&d->m_localHostLock);
01205     d->m_localHostname = hostname;
01206     d->m_database->SetLocalHostname(hostname);
01207 }
01208 
01209 void MythCoreContext::SetGUIObject(QObject *gui)
01210 {
01211     d->m_GUIobject = gui;
01212 }
01213 
01214 bool MythCoreContext::HasGUI(void) const
01215 {
01216     return d->m_GUIobject;
01217 }
01218 
01219 QObject *MythCoreContext::GetGUIObject(void)
01220 {
01221     return d->m_GUIobject;
01222 }
01223 
01224 MythDB *MythCoreContext::GetDB(void)
01225 {
01226     return d->m_database;
01227 }
01228 
01229 MythLocale *MythCoreContext::GetLocale(void) const
01230 {
01231     return d->m_locale;
01232 }
01233 
01238 QString MythCoreContext::GetLanguage(void)
01239 {
01240     return GetLanguageAndVariant().left(2);
01241 }
01242 
01250 QString MythCoreContext::GetLanguageAndVariant(void)
01251 {
01252     if (d->language.isEmpty())
01253         d->language = GetSetting("Language", "en_US").toLower();
01254 
01255     return d->language;
01256 }
01257 
01258 void MythCoreContext::ResetLanguage(void)
01259 {
01260     d->language.clear();
01261 }
01262 
01263 void MythCoreContext::InitLocale(void )
01264 {
01265     if (!d->m_locale)
01266         d->m_locale = new MythLocale();
01267 
01268     QString localeCode = d->m_locale->GetLocaleCode();
01269     LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
01270                                                             .arg(localeCode));
01271     QLocale::setDefault(d->m_locale->ToQLocale());
01272 }
01273 
01274 void MythCoreContext::ReInitLocale(void)
01275 {
01276     if (!d->m_locale)
01277         d->m_locale = new MythLocale();
01278     else
01279         d->m_locale->ReInit();
01280 
01281     QString localeCode = d->m_locale->GetLocaleCode();
01282     LOG(VB_GENERAL, LOG_NOTICE, QString("Setting QT default locale to %1")
01283                                                             .arg(localeCode));
01284     QLocale::setDefault(d->m_locale->ToQLocale());
01285 }
01286 
01287 const QLocale MythCoreContext::GetQLocale(void)
01288 {
01289     if (!d->m_locale)
01290         InitLocale();
01291 
01292     return d->m_locale->ToQLocale();
01293 }
01294 
01295 void MythCoreContext::SaveLocaleDefaults(void)
01296 {
01297     if (!d->m_locale)
01298         InitLocale();
01299 
01300     if (!d->m_locale->GetLocaleCode().isEmpty())
01301     {
01302         LOG(VB_GENERAL, LOG_INFO,
01303             QString("Current locale %1") .arg(d->m_locale->GetLocaleCode()));
01304 
01305         d->m_locale->SaveLocaleDefaults();
01306         return;
01307     }
01308 
01309     LOG(VB_GENERAL, LOG_ERR,
01310         "No locale defined! We weren't able to set locale defaults.");
01311 }
01312 
01313 void MythCoreContext::SetScheduler(MythScheduler *sched)
01314 {
01315     d->m_scheduler = sched;
01316 }
01317 
01318 MythScheduler *MythCoreContext::GetScheduler(void)
01319 {
01320     return d->m_scheduler;
01321 }
01322 
01323 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends