MythTV  0.26-pre
housekeeper.cpp
Go to the documentation of this file.
00001 // POSIX headers
00002 #include <unistd.h>
00003 #include <sys/types.h>
00004 #include <unistd.h>
00005 
00006 // ANSI C headers
00007 #include <cstdlib>
00008 
00009 // C++ headers
00010 #include <iostream>
00011 using namespace std;
00012 
00013 // Qt headers
00014 #include <QStringList>
00015 #include <QDateTime>
00016 #include <QDir>
00017 #include <QFileInfo>
00018 
00019 // MythTV headers
00020 #include "housekeeper.h"
00021 #include "jobqueue.h"
00022 #include "mythcorecontext.h"
00023 #include "mythdb.h"
00024 #include "mythmiscutil.h"
00025 #include "compat.h"
00026 #include "mythdirs.h"
00027 #include "programinfo.h"
00028 #include "eitcache.h"
00029 #include "scheduler.h"
00030 #include "mythcoreutil.h"
00031 #include "mythdownloadmanager.h"
00032 #include "exitcodes.h"
00033 #include "mythversion.h"
00034 #include "mythlogging.h"
00035 
00036 void HouseKeepingThread::run(void)
00037 {
00038     RunProlog();
00039     m_parent->RunHouseKeeping();
00040     RunEpilog();
00041 }
00042 
00043 void MythFillDatabaseThread::run(void)
00044 {
00045     RunProlog();
00046     m_parent->RunMFD();
00047     RunEpilog();
00048 }
00049 
00050 HouseKeeper::HouseKeeper(bool runthread, bool master, Scheduler *lsched) :
00051     isMaster(master),           sched(lsched),
00052     houseKeepingRun(runthread), houseKeepingThread(NULL),
00053     fillDBThread(NULL),         fillDBStarted(false),
00054     fillDBMythSystem(NULL)
00055 {
00056     CleanupMyOldRecordings();
00057 
00058     if (runthread)
00059     {
00060         houseKeepingThread = new HouseKeepingThread(this);
00061         houseKeepingThread->start();
00062 
00063         QMutexLocker locker(&houseKeepingLock);
00064         while (houseKeepingRun && !houseKeepingThread->isRunning())
00065             houseKeepingWait.wait(locker.mutex());
00066     }
00067 }
00068 
00069 HouseKeeper::~HouseKeeper()
00070 {
00071     if (houseKeepingThread)
00072     {
00073         {
00074             QMutexLocker locker(&houseKeepingLock);
00075             houseKeepingRun = false;
00076             houseKeepingWait.wakeAll();
00077         }
00078         houseKeepingThread->wait();
00079         delete houseKeepingThread;
00080         houseKeepingThread = NULL;
00081     }
00082 
00083     if (fillDBThread)
00084     {
00085         KillMFD();
00086         delete fillDBThread;
00087         fillDBThread = NULL;
00088     }
00089 }
00090 
00091 bool HouseKeeper::wantToRun(const QString &dbTag, int period, int minhour,
00092                             int maxhour, bool nowIfPossible)
00093 {
00094     bool runOK = false;
00095     unsigned int oneday = 60 * 60 * 24;
00096     int longEnough = 0;
00097 
00098     if (period)
00099         longEnough = ((period * oneday) - oneday/2);
00100     else
00101         longEnough = oneday / 8;
00102 
00103     QDateTime now = QDateTime::currentDateTime();
00104     QDateTime lastrun;
00105     lastrun.setTime_t(0);
00106 
00107     if (minhour < 0)
00108         minhour = 0;
00109     if (maxhour > 23)
00110         maxhour = 23;
00111 
00112     MSqlQuery result(MSqlQuery::InitCon());
00113     if (result.isConnected())
00114     {
00115         result.prepare("SELECT lastrun FROM housekeeping WHERE tag = :TAG ;");
00116         result.bindValue(":TAG", dbTag);
00117 
00118         if (result.exec() && result.next())
00119         {
00120             lastrun = result.value(0).toDateTime();
00121 
00122             if ((lastrun.secsTo(now) > longEnough) &&
00123                 (now.date().day() != lastrun.date().day()))
00124             {
00125                 int hour = now.time().hour();
00126 
00127                 if (((minhour > maxhour) &&
00128                      ((hour <= maxhour) || (hour >= minhour))) ||
00129                     ((hour >= minhour) && (hour <= maxhour)))
00130                 {
00131                     int minute = now.time().minute();
00132                     // Allow the job run if
00133                     // a) we want to run now rather than at a random time
00134                     // b) we have reached the last half hour of the window, or
00135                     // c) we win a random draw with a probability of 1/N.
00136                     //
00137                     // N gets smaller the nearer we are to the end of the
00138                     // window. The "(24 + ...) % 24" makes sure the calculation
00139                     // is correct even for the case hour > minhour > maxhour.
00140                     if ((nowIfPossible) ||
00141                         (hour == maxhour && minute > 30) ||
00142                         ((random()%((((24+maxhour-hour)%24)*12+(60-minute)/5 - 6) + 1)) == 0))
00143                         runOK = true;
00144                 }
00145             }
00146         }
00147         else
00148         {
00149             result.prepare("INSERT INTO housekeeping(tag,lastrun) "
00150                            "values(:TAG ,now());");
00151             result.bindValue(":TAG", dbTag);
00152             if (!result.exec())
00153                 MythDB::DBError("HouseKeeper::wantToRun -- insert", result);
00154 
00155             runOK = true;
00156         }
00157     }
00158 
00159     return runOK;
00160 }
00161 
00162 void HouseKeeper::updateLastrun(const QString &dbTag)
00163 {
00164     MSqlQuery result(MSqlQuery::InitCon());
00165     if (result.isConnected())
00166     {
00167         result.prepare("DELETE FROM housekeeping WHERE tag = :TAG ;");
00168         result.bindValue(":TAG", dbTag);
00169         if (!result.exec())
00170             MythDB::DBError("HouseKeeper::updateLastrun -- delete", result);
00171 
00172         result.prepare("INSERT INTO housekeeping(tag,lastrun) "
00173                        "values(:TAG ,now()) ;");
00174         result.bindValue(":TAG", dbTag);
00175         if (!result.exec())
00176             MythDB::DBError("HouseKeeper::updateLastrun -- insert", result);
00177     }
00178 }
00179 
00180 QDateTime HouseKeeper::getLastRun(const QString &dbTag)
00181 {
00182     QDateTime lastRun;
00183     MSqlQuery result(MSqlQuery::InitCon());
00184 
00185     lastRun.setTime_t(0);
00186 
00187     result.prepare("SELECT lastrun FROM housekeeping WHERE tag = :TAG ;");
00188     result.bindValue(":TAG", dbTag);
00189 
00190     if (result.exec() && result.next())
00191     {
00192         lastRun = result.value(0).toDateTime();
00193     }
00194 
00195     return lastRun;
00196 }
00197 
00198 void HouseKeeper::RunHouseKeeping(void)
00199 {
00200     // tell constructor that we've started..
00201     {
00202         QMutexLocker locker(&houseKeepingLock);
00203         houseKeepingWait.wakeAll();
00204     }
00205 
00206     int period, maxhr, minhr;
00207     QString dbTag;
00208     bool initialRun = true;
00209 
00210     // wait a little for main server to come up and things to settle down
00211     {
00212         QMutexLocker locker(&houseKeepingLock);
00213         houseKeepingWait.wait(locker.mutex(), 10 * 1000);
00214     }
00215 
00216     RunStartupTasks();
00217 
00218     QMutexLocker locker(&houseKeepingLock);
00219     while (houseKeepingRun)
00220     {
00221         locker.unlock();
00222 
00223         LOG(VB_GENERAL, LOG_INFO, "Running housekeeping thread");
00224 
00225         // These tasks are only done from the master backend
00226         if (isMaster)
00227         {
00228             // Clean out old database logging entries
00229             if (wantToRun("LogClean", 1, 0, 24))
00230             {
00231                 LOG(VB_GENERAL, LOG_INFO, "Running LogClean");
00232                 flushDBLogs();
00233                 updateLastrun("LogClean");
00234             }
00235 
00236             // Run mythfilldatabase to grab the TV listings
00237             if (gCoreContext->GetNumSetting("MythFillEnabled", 1))
00238             {
00239                 if (fillDBThread && fillDBThread->isRunning())
00240                 {
00241                     LOG(VB_GENERAL, LOG_INFO,
00242                         "mythfilldatabase still running, skipping checks.");
00243                 }
00244                 else
00245                 {
00246                     period = 1;
00247                     minhr = gCoreContext->GetNumSetting("MythFillMinHour", -1);
00248                     if (minhr == -1)
00249                     {
00250                         minhr = 0;
00251                         maxhr = 24;
00252                     }
00253                     else
00254                     {
00255                         maxhr = gCoreContext->GetNumSetting("MythFillMaxHour", 24);
00256                     }
00257 
00258                     bool grabberSupportsNextTime = false;
00259                     MSqlQuery result(MSqlQuery::InitCon());
00260                     if (result.isConnected())
00261                     {
00262                         result.prepare("SELECT COUNT(*) FROM videosource "
00263                                        "WHERE xmltvgrabber IN "
00264                                            "( 'datadirect', 'technovera',"
00265                                            " 'schedulesdirect1' );");
00266 
00267                         if ((result.exec()) &&
00268                             (result.next()) &&
00269                             (result.value(0).toInt() > 0))
00270                             grabberSupportsNextTime = true;
00271                     }
00272 
00273                     bool runMythFill = false;
00274                     if (grabberSupportsNextTime &&
00275                         gCoreContext->GetNumSetting("MythFillGrabberSuggestsTime", 1))
00276                     {
00277                         QDateTime nextRun = QDateTime::fromString(
00278                             gCoreContext->GetSetting("MythFillSuggestedRunTime",
00279                             "1970-01-01T00:00:00"), Qt::ISODate);
00280                         QDateTime lastRun = getLastRun("MythFillDB");
00281                         QDateTime now = QDateTime::currentDateTime();
00282 
00283                         if ((nextRun < now) &&
00284                             (lastRun.secsTo(now) > (3 * 60 * 60)))
00285                             runMythFill = true;
00286                     }
00287                     else if (wantToRun("MythFillDB", period, minhr, maxhr,
00288                                        initialRun))
00289                     {
00290                         runMythFill = true;
00291                     }
00292 
00293                     if (runMythFill)
00294                     {
00295                         LOG(VB_GENERAL, LOG_INFO, "Running mythfilldatabase");
00296                         StartMFD();
00297                         updateLastrun("MythFillDB");
00298                     }
00299                 }
00300             }
00301 
00302             if (wantToRun("DailyCleanup", 1, 0, 24)) {
00303                 JobQueue::CleanupOldJobsInQueue();
00304                 CleanupAllOldInUsePrograms();
00305                 CleanupOrphanedLivetvChains();
00306                 CleanupRecordedTables();
00307                 CleanupProgramListings();
00308                 updateLastrun("DailyCleanup");
00309             }
00310 
00311             if ((gCoreContext->GetNumSetting("ThemeUpdateNofications", 1)) &&
00312                 (wantToRun("ThemeChooserInfoCacheUpdate", 1, 0, 24, true)))
00313             {
00314                 UpdateThemeChooserInfoCache();
00315                 updateLastrun("ThemeChooserInfoCacheUpdate");
00316             }
00317 
00318 #if CONFIG_BINDINGS_PYTHON
00319             if ((gCoreContext->GetNumSetting("DailyArtworkUpdates", 0)) &&
00320                 (wantToRun("RecordedArtworkUpdate", 1, 0, 24, true)))
00321             {
00322                 UpdateRecordedArtwork();
00323                 updateLastrun("RecordedArtworkUpdate");
00324             }
00325 #endif
00326         }
00327 
00328         dbTag = QString("JobQueueRecover-%1").arg(gCoreContext->GetHostName());
00329         if (wantToRun(dbTag, 1, 0, 24))
00330         {
00331             JobQueue::RecoverOldJobsInQueue();
00332             updateLastrun(dbTag);
00333         }
00334 
00335         if (wantToRun("DBCleanup", 1, 0, 24))
00336         {
00337             gCoreContext->GetDBManager()->PurgeIdleConnections();
00338         }
00339 
00340         initialRun = false;
00341 
00342         locker.relock();
00343         if (houseKeepingRun)
00344             houseKeepingWait.wait(locker.mutex(), (300 + (random()%8)) * 1000);
00345     }
00346 }
00347 
00348 void HouseKeeper::flushDBLogs()
00349 {
00350     int numdays = 14;
00351     uint64_t maxrows = 10000 * numdays;  // likely high enough to keep numdays
00352 
00353     MSqlQuery query(MSqlQuery::InitCon());
00354     if (query.isConnected())
00355     {
00356         // Remove less-important logging after 1/2 * numdays days
00357         QDateTime days = QDateTime::currentDateTime();
00358         days = days.addDays(0 - (numdays / 2));
00359         QString sql = "DELETE FROM logging "
00360                       " WHERE application NOT IN (:MYTHBACKEND, :MYTHFRONTEND) "
00361                       "   AND msgtime < :DAYS ;";
00362         query.prepare(sql);
00363         query.bindValue(":MYTHBACKEND", MYTH_APPNAME_MYTHBACKEND);
00364         query.bindValue(":MYTHFRONTEND", MYTH_APPNAME_MYTHFRONTEND);
00365         query.bindValue(":DAYS", days);
00366         LOG(VB_GENERAL, LOG_DEBUG,
00367             QString("Deleting helper application database log entries "
00368                     "from before %1.") .arg(days.toString()));
00369         if (!query.exec())
00370             MythDB::DBError("Delete helper application log entries", query);
00371 
00372         // Remove backend/frontend logging after numdays days
00373         days = QDateTime::currentDateTime();
00374         days = days.addDays(0 - numdays);
00375         sql = "DELETE FROM logging WHERE msgtime < :DAYS ;";
00376         query.prepare(sql);
00377         query.bindValue(":DAYS", days);
00378         LOG(VB_GENERAL, LOG_DEBUG,
00379             QString("Deleting database log entries from before %1.")
00380                 .arg(days.toString()));
00381         if (!query.exec())
00382             MythDB::DBError("Delete old log entries", query);
00383 
00384         sql = "SELECT COUNT(id) FROM logging;";
00385         query.prepare(sql);
00386         if (query.exec())
00387         {
00388             uint64_t totalrows = 0;
00389             while (query.next())
00390             {
00391                 totalrows = query.value(0).toLongLong();
00392                 LOG(VB_GENERAL, LOG_DEBUG,
00393                     QString("Database has %1 log entries.").arg(totalrows));
00394             }
00395             if (totalrows > maxrows)
00396             {
00397                 sql = "DELETE FROM logging ORDER BY msgtime LIMIT :ROWS;";
00398                 query.prepare(sql);
00399                 quint64 extrarows = totalrows - maxrows;
00400                 query.bindValue(":ROWS", extrarows);
00401                 LOG(VB_GENERAL, LOG_DEBUG,
00402                     QString("Deleting oldest %1 database log entries.")
00403                         .arg(extrarows));
00404                 if (!query.exec())
00405                     MythDB::DBError("Delete excess log entries", query);
00406             }
00407         }
00408         else
00409             MythDB::DBError("Query logging table size", query);
00410     }
00411 }
00412 
00413 void HouseKeeper::RunMFD(void)
00414 {
00415     {
00416         QMutexLocker locker(&fillDBLock);
00417         fillDBStarted = true;
00418         fillDBWait.wakeAll();
00419     }
00420 
00421     QString mfpath = gCoreContext->GetSetting("MythFillDatabasePath",
00422                                           "mythfilldatabase");
00423     QString mfarg = gCoreContext->GetSetting("MythFillDatabaseArgs", "");
00424 
00425     if (mfpath == "mythfilldatabase")
00426         mfpath = GetInstallPrefix() + "/bin/mythfilldatabase";
00427 
00428     QString command = QString("%1 %2 %3").arg(mfpath).arg(logPropagateArgs)
00429                         .arg(mfarg);
00430 
00431     {
00432         QMutexLocker locker(&fillDBLock);
00433         fillDBMythSystem = new MythSystem(command, kMSRunShell |
00434                                                    kMSAutoCleanup);
00435         fillDBMythSystem->Run(0);
00436         fillDBWait.wakeAll();
00437     }
00438 
00439     MythFillDatabaseThread::setTerminationEnabled(true);
00440 
00441     uint result = fillDBMythSystem->Wait(0);
00442 
00443     MythFillDatabaseThread::setTerminationEnabled(false);
00444 
00445     {
00446         QMutexLocker locker(&fillDBLock);
00447         fillDBMythSystem->deleteLater();
00448         fillDBMythSystem = NULL;
00449         fillDBWait.wakeAll();
00450     }
00451 
00452     if (result != GENERIC_EXIT_OK)
00453     {
00454         LOG(VB_GENERAL, LOG_ERR, QString("MythFillDatabase command '%1' failed")
00455                 .arg(command));
00456     }
00457 }
00458 
00459 void HouseKeeper::StartMFD(void)
00460 {
00461     if (fillDBThread)
00462     {
00463         KillMFD();
00464         delete fillDBThread;
00465         fillDBThread = NULL;
00466         fillDBStarted = false;
00467     }
00468 
00469     fillDBThread = new MythFillDatabaseThread(this);
00470     fillDBThread->start();
00471 
00472     QMutexLocker locker(&fillDBLock);
00473     while (!fillDBStarted)
00474         fillDBWait.wait(locker.mutex());
00475 }
00476 
00477 void HouseKeeper::KillMFD(void)
00478 {
00479     if (!fillDBThread->isRunning())
00480         return;
00481 
00482     QMutexLocker locker(&fillDBLock);
00483     if (fillDBMythSystem && fillDBThread->isRunning())
00484     {
00485         fillDBMythSystem->Term(false);
00486         fillDBWait.wait(locker.mutex(), 50);
00487     }
00488 
00489     if (fillDBMythSystem && fillDBThread->isRunning())
00490     {
00491         fillDBMythSystem->Term(true);
00492         fillDBWait.wait(locker.mutex(), 50);
00493     }
00494 
00495     if (fillDBThread->isRunning())
00496     {
00497         fillDBThread->terminate();
00498         usleep(5000);
00499     }
00500 
00501     if (fillDBThread->isRunning())
00502     {
00503         locker.unlock();
00504         fillDBThread->wait();
00505     }
00506 }
00507 
00508 void HouseKeeper::CleanupMyOldRecordings(void)
00509 {
00510     MSqlQuery query(MSqlQuery::InitCon());
00511 
00512     query.prepare("DELETE FROM inuseprograms "
00513                   "WHERE hostname = :HOSTNAME AND "
00514                     "( recusage = 'recorder' OR recusage LIKE 'Unknown %' );");
00515     query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
00516     if (!query.exec())
00517         MythDB::DBError("HouseKeeper::CleanupMyOldRecordings", query);
00518 }
00519 
00520 void HouseKeeper::CleanupAllOldInUsePrograms(void)
00521 {
00522     QDateTime fourHoursAgo = QDateTime::currentDateTime().addSecs(-4 * 60 * 60);
00523     MSqlQuery query(MSqlQuery::InitCon());
00524 
00525     query.prepare("DELETE FROM inuseprograms "
00526                   "WHERE lastupdatetime < :FOURHOURSAGO ;");
00527     query.bindValue(":FOURHOURSAGO", fourHoursAgo);
00528     if (!query.exec())
00529         MythDB::DBError("HouseKeeper::CleanupAllOldInUsePrograms", query);
00530 }
00531 
00532 void HouseKeeper::CleanupOrphanedLivetvChains(void)
00533 {
00534     QDateTime fourHoursAgo = QDateTime::currentDateTime().addSecs(-4 * 60 * 60);
00535     MSqlQuery query(MSqlQuery::InitCon());
00536     MSqlQuery deleteQuery(MSqlQuery::InitCon());
00537 
00538     // Keep these tvchains, they may be in use.
00539     query.prepare("SELECT DISTINCT chainid FROM tvchain "
00540                   "WHERE endtime > :FOURHOURSAGO ;");
00541     query.bindValue(":FOURHOURSAGO", fourHoursAgo);
00542 
00543     if (!query.exec() || !query.isActive())
00544     {
00545         MythDB::DBError("HouseKeeper Cleaning TVChain Table", query);
00546         return;
00547     }
00548 
00549     QString msg, keepChains;
00550     while (query.next())
00551         if (keepChains.isEmpty())
00552             keepChains = "'" + query.value(0).toString() + "'";
00553         else
00554             keepChains += ", '" + query.value(0).toString() + "'";
00555 
00556     if (keepChains.isEmpty())
00557         msg = "DELETE FROM tvchain WHERE endtime < now();";
00558     else
00559     {
00560         msg = QString("DELETE FROM tvchain "
00561                       "WHERE chainid NOT IN ( %1 ) AND endtime < now();")
00562                       .arg(keepChains);
00563     }
00564     deleteQuery.prepare(msg);
00565     if (!deleteQuery.exec())
00566         MythDB::DBError("HouseKeeper Cleaning TVChain Table", deleteQuery);
00567 }
00568 
00569 void HouseKeeper::CleanupRecordedTables(void)
00570 {
00571     MSqlQuery query(MSqlQuery::InitCon());
00572     MSqlQuery deleteQuery(MSqlQuery::InitCon());
00573     int tableIndex = 0;
00574     // tables[tableIndex][0] is the table name
00575     // tables[tableIndex][1] is the name of the column on which the join is
00576     // performed
00577     QString tables[][2] = {
00578         { "recordedprogram", "progstart" },
00579         { "recordedrating", "progstart" },
00580         { "recordedcredits", "progstart" },
00581         { "recordedmarkup", "starttime" },
00582         { "recordedseek", "starttime" },
00583         { "", "" } }; // This blank entry must exist, do not remove.
00584     QString table = tables[tableIndex][0];
00585     QString column = tables[tableIndex][1];
00586 
00587     // Because recordedseek can have millions of rows, we don't want to JOIN it
00588     // with recorded.  Instead, pull out DISTINCT chanid and starttime into a
00589     // temporary table (resulting in tens, hundreds, or--at most--a few
00590     // thousand rows) for the JOIN
00591     QString querystr;
00592     querystr = "CREATE TEMPORARY TABLE IF NOT EXISTS temprecordedcleanup ( "
00593                    "chanid int(10) unsigned NOT NULL default '0', "
00594                    "starttime datetime NOT NULL default '0000-00-00 00:00:00' "
00595                    ");";
00596 
00597     if (!query.exec(querystr))
00598     {
00599         MythDB::DBError("Housekeeper Creating Temporary Table", query);
00600         return;
00601     }
00602 
00603     while (!table.isEmpty())
00604     {
00605         query.prepare(QString("TRUNCATE TABLE temprecordedcleanup;"));
00606         if (!query.exec() || !query.isActive())
00607         {
00608             MythDB::DBError("Housekeeper Truncating Temporary Table", query);
00609             return;
00610         }
00611 
00612         query.prepare(QString("INSERT INTO temprecordedcleanup "
00613                               "( chanid, starttime ) "
00614                               "SELECT DISTINCT chanid, starttime "
00615                               "FROM %1;")
00616                               .arg(table));
00617 
00618         if (!query.exec() || !query.isActive())
00619         {
00620             MythDB::DBError("HouseKeeper Cleaning Recorded Tables", query);
00621             return;
00622         }
00623 
00624         query.prepare(QString("SELECT DISTINCT p.chanid, p.starttime "
00625                               "FROM temprecordedcleanup p "
00626                               "LEFT JOIN recorded r "
00627                               "ON p.chanid = r.chanid "
00628                               "AND p.starttime = r.%1 "
00629                               "WHERE r.chanid IS NULL;").arg(column));
00630         if (!query.exec() || !query.isActive())
00631         {
00632             MythDB::DBError("HouseKeeper Cleaning Recorded Tables", query);
00633             return;
00634         }
00635 
00636         deleteQuery.prepare(QString("DELETE FROM %1 "
00637                                     "WHERE chanid = :CHANID "
00638                                     "AND starttime = :STARTTIME;")
00639                                     .arg(table));
00640         while (query.next())
00641         {
00642             deleteQuery.bindValue(":CHANID", query.value(0).toString());
00643             deleteQuery.bindValue(":STARTTIME", query.value(1).toDateTime());
00644             if (!deleteQuery.exec())
00645                 MythDB::DBError("HouseKeeper Cleaning Recorded Tables",
00646                                 deleteQuery);
00647         }
00648 
00649         tableIndex++;
00650         table = tables[tableIndex][0];
00651         column = tables[tableIndex][1];
00652     }
00653 
00654     if (!query.exec("DROP TABLE temprecordedcleanup;"))
00655         MythDB::DBError("Housekeeper Dropping Temporary Table", query);
00656 
00657 }
00658 
00659 void HouseKeeper::CleanupProgramListings(void)
00660 {
00661 
00662     MSqlQuery query(MSqlQuery::InitCon());
00663     QString querystr;
00664     // Keep as many days of listings data as we keep matching, non-recorded
00665     // oldrecorded entries to allow for easier post-mortem analysis
00666     int offset = gCoreContext->GetNumSetting( "CleanOldRecorded", 10);
00667 
00668     query.prepare("DELETE FROM oldprogram WHERE airdate < "
00669                   "DATE_SUB(CURRENT_DATE, INTERVAL 320 DAY);");
00670     if (!query.exec())
00671         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00672 
00673     query.prepare("REPLACE INTO oldprogram (oldtitle,airdate) "
00674                   "SELECT title,starttime FROM program "
00675                   "WHERE starttime < NOW() AND manualid = 0 "
00676                   "GROUP BY title;");
00677     if (!query.exec())
00678         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00679 
00680     query.prepare("DELETE FROM program WHERE starttime <= "
00681                   "DATE_SUB(CURRENT_DATE, INTERVAL :OFFSET DAY);");
00682     query.bindValue(":OFFSET", offset);
00683     if (!query.exec())
00684         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00685 
00686     query.prepare("DELETE FROM programrating WHERE starttime <= "
00687                   "DATE_SUB(CURRENT_DATE, INTERVAL :OFFSET DAY);");
00688     query.bindValue(":OFFSET", offset);
00689     if (!query.exec())
00690         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00691 
00692     query.prepare("DELETE FROM programgenres WHERE starttime <= "
00693                   "DATE_SUB(CURRENT_DATE, INTERVAL :OFFSET DAY);");
00694     query.bindValue(":OFFSET", offset);
00695     if (!query.exec())
00696         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00697 
00698     query.prepare("DELETE FROM credits WHERE starttime <= "
00699                   "DATE_SUB(CURRENT_DATE, INTERVAL :OFFSET DAY);");
00700     query.bindValue(":OFFSET", offset);
00701     if (!query.exec())
00702         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00703 
00704     query.prepare("DELETE FROM record WHERE (type = :SINGLE "
00705                   "OR type = :OVERRIDE OR type = :DONTRECORD) "
00706                   "AND enddate < CURDATE();");
00707     query.bindValue(":SINGLE", kSingleRecord);
00708     query.bindValue(":OVERRIDE", kOverrideRecord);
00709     query.bindValue(":DONTRECORD", kDontRecord);
00710     if (!query.exec())
00711         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00712 
00713     MSqlQuery findq(MSqlQuery::InitCon());
00714     findq.prepare("SELECT record.recordid FROM record "
00715                   "LEFT JOIN oldfind ON oldfind.recordid = record.recordid "
00716                   "WHERE type = :FINDONE AND oldfind.findid IS NOT NULL;");
00717     findq.bindValue(":FINDONE", kFindOneRecord);
00718 
00719     if (findq.exec())
00720     {
00721         query.prepare("DELETE FROM record WHERE recordid = :RECORDID;");
00722         while (findq.next())
00723         {
00724             query.bindValue(":RECORDID", findq.value(0).toInt());
00725             if (!query.exec())
00726                 MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00727         }
00728     }
00729     query.prepare("DELETE FROM oldfind WHERE findid < TO_DAYS(NOW()) - 14;");
00730     if (!query.exec())
00731         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00732 
00733     query.prepare("DELETE FROM oldrecorded WHERE "
00734                   "recstatus <> :RECORDED AND duplicate = 0 AND "
00735                   "endtime < DATE_SUB(CURRENT_DATE, INTERVAL :CLEAN DAY);");
00736     query.bindValue(":RECORDED", rsRecorded);
00737     query.bindValue(":CLEAN", offset);
00738     if (!query.exec())
00739         MythDB::DBError("HouseKeeper Cleaning Program Listings", query);
00740 
00741 }
00742 
00743 void HouseKeeper::UpdateThemeChooserInfoCache(void)
00744 {
00745     QString MythVersion = MYTH_SOURCE_PATH;
00746 
00747     // FIXME: For now, treat git master the same as svn trunk
00748     if (MythVersion == "master")
00749         MythVersion = "trunk";
00750 
00751     if (MythVersion != "trunk")
00752     {
00753         MythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
00754         MythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
00755     }
00756 
00757     QString remoteThemesDir = GetConfDir();
00758     remoteThemesDir.append("/tmp/remotethemes");
00759 
00760     QDir dir(remoteThemesDir);
00761     if (!dir.exists() && !dir.mkpath(remoteThemesDir))
00762     {
00763         LOG(VB_GENERAL, LOG_ERR,
00764             QString("HouseKeeper: Error creating %1"
00765                     "directory for remote themes info cache.")
00766                 .arg(remoteThemesDir));
00767         return;
00768     }
00769 
00770     QString remoteThemesFile = remoteThemesDir;
00771     remoteThemesFile.append("/themes.zip");
00772 
00773     QString url = QString("%1/%2/themes.zip")
00774         .arg(gCoreContext->GetSetting("ThemeRepositoryURL",
00775              "http://themes.mythtv.org/themes/repository")).arg(MythVersion);
00776 
00777     bool result = GetMythDownloadManager()->download(url, remoteThemesFile);
00778 
00779     if (!result)
00780     {
00781         LOG(VB_GENERAL, LOG_ERR,
00782             QString("HouseKeeper: Error downloading %1"
00783                     "remote themes info package.").arg(url));
00784         return;
00785     }
00786 
00787     if (!extractZIP(remoteThemesFile, remoteThemesDir))
00788     {
00789         LOG(VB_GENERAL, LOG_ERR,
00790             QString("HouseKeeper: Error extracting %1"
00791                     "remote themes info package.").arg(remoteThemesFile));
00792         QFile::remove(remoteThemesFile);
00793         return;
00794     }
00795 }
00796 
00797 void HouseKeeper::UpdateRecordedArtwork(void)
00798 {
00799     QString command = GetInstallPrefix() + "/bin/mythmetadatalookup";
00800     QStringList args;
00801     args << "--refresh-all-artwork";
00802     args << logPropagateArgs;
00803 
00804     LOG(VB_GENERAL, LOG_INFO, QString("Performing Artwork Refresh: %1 %2")
00805         .arg(command).arg(args.join(" ")));
00806 
00807     MythSystem artupd(command, args, kMSRunShell | kMSAutoCleanup);
00808 
00809     artupd.Run();
00810     artupd.Wait();
00811 
00812     LOG(VB_GENERAL, LOG_INFO, QString("Artwork Refresh Complete"));
00813 }
00814 
00815 void HouseKeeper::RunStartupTasks(void)
00816 {
00817     if (isMaster)
00818         EITCache::ClearChannelLocks();
00819 }
00820 
00821 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends