MythTV  0.26-pre
main_helpers.cpp
Go to the documentation of this file.
00001 // POSIX headers
00002 #include <sys/time.h>     // for setpriority
00003 #include <unistd.h>
00004 #include <sys/types.h>
00005 #include <sys/stat.h>
00006 #include <fcntl.h>
00007 #include <libgen.h>
00008 #include <signal.h>
00009 
00010 #include "mythconfig.h"
00011 #if CONFIG_DARWIN
00012     #include <sys/aio.h>    // O_SYNC
00013 #endif
00014 
00015 // C headers
00016 #include <cstdlib>
00017 #include <cerrno>
00018 
00019 #include <QCoreApplication>
00020 #include <QFileInfo>
00021 #include <QRegExp>
00022 #include <QFile>
00023 #include <QDir>
00024 #include <QMap>
00025 
00026 #include "tv_rec.h"
00027 #include "scheduledrecording.h"
00028 #include "mythsocketthread.h"
00029 #include "autoexpire.h"
00030 #include "scheduler.h"
00031 #include "mainserver.h"
00032 #include "encoderlink.h"
00033 #include "remoteutil.h"
00034 #include "housekeeper.h"
00035 
00036 #include "mythcontext.h"
00037 #include "mythversion.h"
00038 #include "mythdb.h"
00039 #include "exitcodes.h"
00040 #include "compat.h"
00041 #include "storagegroup.h"
00042 #include "programinfo.h"
00043 #include "dbcheck.h"
00044 #include "jobqueue.h"
00045 #include "previewgenerator.h"
00046 #include "commandlineparser.h"
00047 #include "mythsystemevent.h"
00048 #include "main_helpers.h"
00049 #include "backendcontext.h"
00050 #include "mythtranslation.h"
00051 
00052 #include "mediaserver.h"
00053 #include "httpstatus.h"
00054 #include "mythlogging.h"
00055 
00056 #define LOC      QString("MythBackend: ")
00057 #define LOC_WARN QString("MythBackend, Warning: ")
00058 #define LOC_ERR  QString("MythBackend, Error: ")
00059 
00060 static MainServer *mainServer = NULL;
00061 
00062 bool setupTVs(bool ismaster, bool &error)
00063 {
00064     error = false;
00065     QString localhostname = gCoreContext->GetHostName();
00066 
00067     MSqlQuery query(MSqlQuery::InitCon());
00068 
00069     if (ismaster)
00070     {
00071         // Hack to make sure recorded.basename gets set if the user
00072         // downgrades to a prior version and creates new entries
00073         // without it.
00074         if (!query.exec("UPDATE recorded SET basename = CONCAT(chanid, '_', "
00075                         "DATE_FORMAT(starttime, '%Y%m%d%H%i00'), '_', "
00076                         "DATE_FORMAT(endtime, '%Y%m%d%H%i00'), '.nuv') "
00077                         "WHERE basename = '';"))
00078             MythDB::DBError("Updating record basename", query);
00079 
00080         // Hack to make sure record.station gets set if the user
00081         // downgrades to a prior version and creates new entries
00082         // without it.
00083         if (!query.exec("UPDATE channel SET callsign=chanid "
00084                         "WHERE callsign IS NULL OR callsign='';"))
00085             MythDB::DBError("Updating channel callsign", query);
00086 
00087         if (query.exec("SELECT MIN(chanid) FROM channel;"))
00088         {
00089             query.first();
00090             int min_chanid = query.value(0).toInt();
00091             if (!query.exec(QString("UPDATE record SET chanid = %1 "
00092                                     "WHERE chanid IS NULL;").arg(min_chanid)))
00093                 MythDB::DBError("Updating record chanid", query);
00094         }
00095         else
00096             MythDB::DBError("Querying minimum chanid", query);
00097 
00098         MSqlQuery records_without_station(MSqlQuery::InitCon());
00099         records_without_station.prepare("SELECT record.chanid,"
00100                 " channel.callsign FROM record LEFT JOIN channel"
00101                 " ON record.chanid = channel.chanid WHERE record.station='';");
00102         if (records_without_station.exec() && records_without_station.next())
00103         {
00104             MSqlQuery update_record(MSqlQuery::InitCon());
00105             update_record.prepare("UPDATE record SET station = :CALLSIGN"
00106                     " WHERE chanid = :CHANID;");
00107             do
00108             {
00109                 update_record.bindValue(":CALLSIGN",
00110                         records_without_station.value(1));
00111                 update_record.bindValue(":CHANID",
00112                         records_without_station.value(0));
00113                 if (!update_record.exec())
00114                 {
00115                     MythDB::DBError("Updating record station", update_record);
00116                 }
00117             } while (records_without_station.next());
00118         }
00119     }
00120 
00121     if (!query.exec(
00122             "SELECT cardid, hostname "
00123             "FROM capturecard "
00124             "ORDER BY cardid"))
00125     {
00126         MythDB::DBError("Querying Recorders", query);
00127         return false;
00128     }
00129 
00130     vector<uint>    cardids;
00131     vector<QString> hosts;
00132     while (query.next())
00133     {
00134         uint    cardid = query.value(0).toUInt();
00135         QString host   = query.value(1).toString();
00136         QString cidmsg = QString("Card %1").arg(cardid);
00137 
00138         if (host.isEmpty())
00139         {
00140             LOG(VB_GENERAL, LOG_ERR, cidmsg +
00141                 " does not have a hostname defined.\n"
00142                 "Please run setup and confirm all of the capture cards.\n");
00143             continue;
00144         }
00145 
00146         cardids.push_back(cardid);
00147         hosts.push_back(host);
00148     }
00149 
00150     for (uint i = 0; i < cardids.size(); i++)
00151     {
00152         if (hosts[i] == localhostname)
00153             new TVRec(cardids[i]);
00154     }
00155 
00156     for (uint i = 0; i < cardids.size(); i++)
00157     {
00158         uint    cardid = cardids[i];
00159         QString host   = hosts[i];
00160         QString cidmsg = QString("Card %1").arg(cardid);
00161 
00162         if (!ismaster)
00163         {
00164             if (host == localhostname)
00165             {
00166                 TVRec *tv = TVRec::GetTVRec(cardid);
00167                 if (tv && tv->Init())
00168                 {
00169                     EncoderLink *enc = new EncoderLink(cardid, tv);
00170                     tvList[cardid] = enc;
00171                 }
00172                 else
00173                 {
00174                     LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards. " +
00175                             cidmsg + " failed init");
00176                     delete tv;
00177                     // The master assumes card comes up so we need to
00178                     // set error and exit if a non-master card fails.
00179                     error = true;
00180                 }
00181             }
00182         }
00183         else
00184         {
00185             if (host == localhostname)
00186             {
00187                 TVRec *tv = TVRec::GetTVRec(cardid);
00188                 if (tv && tv->Init())
00189                 {
00190                     EncoderLink *enc = new EncoderLink(cardid, tv);
00191                     tvList[cardid] = enc;
00192                 }
00193                 else
00194                 {
00195                     LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards" +
00196                             cidmsg + "failed init");
00197                     delete tv;
00198                 }
00199             }
00200             else
00201             {
00202                 EncoderLink *enc = new EncoderLink(cardid, NULL, host);
00203                 tvList[cardid] = enc;
00204             }
00205         }
00206     }
00207 
00208     if (tvList.empty())
00209     {
00210         LOG(VB_GENERAL, LOG_WARNING, LOC +
00211                 "No valid capture cards are defined in the database.");
00212     }
00213 
00214     return true;
00215 }
00216 
00217 void cleanup(void)
00218 {
00219     signal(SIGTERM, SIG_DFL);
00220 #ifndef _MSC_VER
00221     signal(SIGUSR1, SIG_DFL);
00222 #endif
00223 
00224     if (mainServer)
00225         mainServer->Stop();
00226 
00227     delete housekeeping;
00228     housekeeping = NULL;
00229 
00230     if (gCoreContext)
00231     {
00232         delete gCoreContext->GetScheduler();
00233         gCoreContext->SetScheduler(NULL);
00234     }
00235 
00236     delete expirer;
00237     expirer = NULL;
00238 
00239     delete jobqueue;
00240     jobqueue = NULL;
00241 
00242     delete g_pUPnp;
00243     g_pUPnp = NULL;
00244 
00245     if (SSDP::Instance())
00246     {
00247         SSDP::Instance()->RequestTerminate();
00248         SSDP::Instance()->wait();
00249     }
00250 
00251     if (TaskQueue::Instance())
00252     {
00253         TaskQueue::Instance()->RequestTerminate();
00254         TaskQueue::Instance()->wait();
00255     }
00256 
00257     while (!TVRec::cards.empty())
00258     {
00259         TVRec *rec = *TVRec::cards.begin();
00260         delete rec;
00261     }
00262 
00263     delete gContext;
00264     gContext = NULL;
00265 
00266     delete mainServer;
00267     mainServer = NULL;
00268 
00269     if (pidfile.size())
00270     {
00271         unlink(pidfile.toAscii().constData());
00272         pidfile.clear();
00273     }
00274 }
00275 
00276 int handle_command(const MythBackendCommandLineParser &cmdline)
00277 {
00278     QString eventString;
00279 
00280     if (cmdline.toBool("event"))
00281         eventString = cmdline.toString("event");
00282     else if (cmdline.toBool("systemevent"))
00283         eventString = "SYSTEM_EVENT " +
00284                       cmdline.toString("systemevent") +
00285                       QString(" SENDER %1").arg(gCoreContext->GetHostName());
00286 
00287     if (!eventString.isEmpty())
00288     {
00289         if (gCoreContext->ConnectToMasterServer())
00290         {
00291             gCoreContext->SendMessage(eventString);
00292             return GENERIC_EXIT_OK;
00293         }
00294         return GENERIC_EXIT_NO_MYTHCONTEXT;
00295     }
00296 
00297     if (cmdline.toBool("setverbose"))
00298     {
00299         if (gCoreContext->ConnectToMasterServer())
00300         {
00301             QString message = "SET_VERBOSE ";
00302             message += cmdline.toString("setverbose");
00303 
00304             gCoreContext->SendMessage(message);
00305             LOG(VB_GENERAL, LOG_INFO,
00306                 QString("Sent '%1' message").arg(message));
00307             return GENERIC_EXIT_OK;
00308         }
00309         else
00310         {
00311             LOG(VB_GENERAL, LOG_ERR,
00312                 "Unable to connect to backend, verbose mask unchanged ");
00313             return GENERIC_EXIT_CONNECT_ERROR;
00314         }
00315     }
00316 
00317     if (cmdline.toBool("setloglevel"))
00318     {
00319         if (gCoreContext->ConnectToMasterServer())
00320         {
00321             QString message = "SET_LOG_LEVEL ";
00322             message += cmdline.toString("setloglevel");
00323 
00324             gCoreContext->SendMessage(message);
00325             LOG(VB_GENERAL, LOG_INFO,
00326                 QString("Sent '%1' message").arg(message));
00327             return GENERIC_EXIT_OK;
00328         }
00329         else
00330         {
00331             LOG(VB_GENERAL, LOG_ERR,
00332                 "Unable to connect to backend, log level unchanged ");
00333             return GENERIC_EXIT_CONNECT_ERROR;
00334         }
00335     }
00336 
00337     if (cmdline.toBool("clearcache"))
00338     {
00339         if (gCoreContext->ConnectToMasterServer())
00340         {
00341             gCoreContext->SendMessage("CLEAR_SETTINGS_CACHE");
00342             LOG(VB_GENERAL, LOG_INFO, "Sent CLEAR_SETTINGS_CACHE message");
00343             return GENERIC_EXIT_OK;
00344         }
00345         else
00346         {
00347             LOG(VB_GENERAL, LOG_ERR, "Unable to connect to backend, settings "
00348                     "cache will not be cleared.");
00349             return GENERIC_EXIT_CONNECT_ERROR;
00350         }
00351     }
00352 
00353     if (cmdline.toBool("printsched") ||
00354         cmdline.toBool("testsched"))
00355     {
00356         Scheduler *sched = new Scheduler(false, &tvList);
00357         if (!cmdline.toBool("testsched") &&
00358             gCoreContext->ConnectToMasterServer())
00359         {
00360             cout << "Retrieving Schedule from Master backend.\n";
00361             sched->FillRecordListFromMaster();
00362         }
00363         else
00364         {
00365             cout << "Calculating Schedule from database.\n" <<
00366                     "Inputs, Card IDs, and Conflict info may be invalid "
00367                     "if you have multiple tuners.\n";
00368             ProgramInfo::CheckProgramIDAuthorities();
00369             sched->FillRecordListFromDB();
00370         }
00371 
00372         verboseMask |= VB_SCHEDULE;
00373         LogLevel_t oldLogLevel = logLevel;
00374         logLevel = LOG_DEBUG;
00375         sched->PrintList(true);
00376         logLevel = oldLogLevel;
00377         delete sched;
00378         return GENERIC_EXIT_OK;
00379     }
00380 
00381     if (cmdline.toBool("resched"))
00382     {
00383         bool ok = false;
00384         if (gCoreContext->ConnectToMasterServer())
00385         {
00386             LOG(VB_GENERAL, LOG_INFO, "Connected to master for reschedule");
00387             ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(),
00388                                                 "MythBackendCommand");
00389             ok = true;
00390         }
00391         else
00392             LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master for reschedule");
00393 
00394         return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_CONNECT_ERROR;
00395     }
00396 
00397     if (cmdline.toBool("scanvideos"))
00398     {
00399         bool ok = false;
00400         if (gCoreContext->ConnectToMasterServer())
00401         {
00402             gCoreContext->SendReceiveStringList(QStringList() << "SCAN_VIDEOS");
00403             LOG(VB_GENERAL, LOG_INFO, "Requested video scan");
00404             ok = true;
00405         }
00406         else
00407             LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master for video scan");
00408 
00409         return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_CONNECT_ERROR;
00410     }
00411 
00412     if (!cmdline.toBool("printexpire"))
00413     {
00414         expirer = new AutoExpire();
00415         expirer->PrintExpireList(cmdline.toString("printexpire"));
00416         return GENERIC_EXIT_OK;
00417     }
00418 
00419     // This should never actually be reached..
00420     return GENERIC_EXIT_OK;
00421 }
00422 
00423 int connect_to_master(void)
00424 {
00425     MythSocket *tempMonitorConnection = new MythSocket();
00426     if (tempMonitorConnection->connect(
00427             gCoreContext->GetSetting("MasterServerIP", "127.0.0.1"),
00428             gCoreContext->GetNumSetting("MasterServerPort", 6543)))
00429     {
00430         if (!gCoreContext->CheckProtoVersion(tempMonitorConnection))
00431         {
00432             LOG(VB_GENERAL, LOG_ERR, "Master backend is incompatible with "
00433                     "this backend.\nCannot become a slave.");
00434             return GENERIC_EXIT_CONNECT_ERROR;
00435         }
00436 
00437         QStringList tempMonitorDone("DONE");
00438 
00439         QStringList tempMonitorAnnounce("ANN Monitor tzcheck 0");
00440         tempMonitorConnection->writeStringList(tempMonitorAnnounce);
00441         tempMonitorConnection->readStringList(tempMonitorAnnounce);
00442         if (tempMonitorAnnounce.empty() ||
00443             tempMonitorAnnounce[0] == "ERROR")
00444         {
00445             tempMonitorConnection->DownRef();
00446             tempMonitorConnection = NULL;
00447             if (tempMonitorAnnounce.empty())
00448             {
00449                 LOG(VB_GENERAL, LOG_ERR, LOC +
00450                     "Failed to open event socket, timeout");
00451             }
00452             else
00453             {
00454                 LOG(VB_GENERAL, LOG_ERR, LOC +
00455                     "Failed to open event socket" +
00456                     ((tempMonitorAnnounce.size() >= 2) ?
00457                     QString(", error was %1").arg(tempMonitorAnnounce[1]) :
00458                     QString(", remote error")));
00459             }
00460         }
00461 
00462         QStringList tzCheck("QUERY_TIME_ZONE");
00463         if (tempMonitorConnection)
00464         {
00465             tempMonitorConnection->writeStringList(tzCheck);
00466             tempMonitorConnection->readStringList(tzCheck);
00467         }
00468         if (tzCheck.size() && !checkTimeZone(tzCheck))
00469         {
00470             // Check for different time zones, different offsets, different
00471             // times
00472             LOG(VB_GENERAL, LOG_ERR, "The time and/or time zone settings on "
00473                     "this system do not match those in use on the master "
00474                     "backend. Please ensure all frontend and backend "
00475                     "systems are configured to use the same time zone and "
00476                     "have the current time properly set.");
00477             LOG(VB_GENERAL, LOG_ERR,
00478                     "Unable to run with invalid time settings. Exiting.");
00479             tempMonitorConnection->writeStringList(tempMonitorDone);
00480             tempMonitorConnection->DownRef();
00481             return GENERIC_EXIT_INVALID_TIMEZONE;
00482         }
00483         else
00484         {
00485             LOG(VB_GENERAL, LOG_INFO,
00486                 QString("Backend is running in %1 time zone.")
00487                     .arg(getTimeZoneID()));
00488         }
00489         if (tempMonitorConnection)
00490             tempMonitorConnection->writeStringList(tempMonitorDone);
00491     }
00492     if (tempMonitorConnection)
00493         tempMonitorConnection->DownRef();
00494 
00495     return GENERIC_EXIT_OK;
00496 }
00497 
00498 
00499 void print_warnings(const MythBackendCommandLineParser &cmdline)
00500 {
00501     if (cmdline.toBool("nohousekeeper"))
00502     {
00503         LOG(VB_GENERAL, LOG_WARNING, LOC +
00504             "****** The Housekeeper has been DISABLED with "
00505             "the --nohousekeeper option ******");
00506     }
00507     if (cmdline.toBool("nosched"))
00508     {
00509         LOG(VB_GENERAL, LOG_WARNING, LOC +
00510             "********** The Scheduler has been DISABLED with "
00511             "the --nosched option **********");
00512     }
00513     if (cmdline.toBool("noautoexpire"))
00514     {
00515         LOG(VB_GENERAL, LOG_WARNING, LOC +
00516             "********* Auto-Expire has been DISABLED with "
00517             "the --noautoexpire option ********");
00518     }
00519     if (cmdline.toBool("nojobqueue"))
00520     {
00521         LOG(VB_GENERAL, LOG_WARNING, LOC +
00522             "********* The JobQueue has been DISABLED with "
00523             "the --nojobqueue option *********");
00524     }
00525 }
00526 
00527 int run_backend(MythBackendCommandLineParser &cmdline)
00528 {
00529     bool ismaster = gCoreContext->IsMasterHost();
00530 
00531     if (!UpgradeTVDatabaseSchema(ismaster, ismaster))
00532     {
00533         LOG(VB_GENERAL, LOG_ERR, "Couldn't upgrade database to new schema");
00534         return GENERIC_EXIT_DB_OUTOFDATE;
00535     }
00536 
00537     MythTranslation::load("mythfrontend");
00538 
00539     if (!ismaster)
00540     {
00541         int ret = connect_to_master();
00542         if (ret != GENERIC_EXIT_OK)
00543             return ret;
00544     }
00545 
00546     int     port = gCoreContext->GetNumSetting("BackendServerPort", 6543);
00547     if (gCoreContext->GetSetting("BackendServerIP").isEmpty() &&
00548         gCoreContext->GetSetting("BackendServerIP6").isEmpty())
00549     {
00550         cerr << "No setting found for this machine's BackendServerIP.\n"
00551              << "Please run setup on this machine and modify the first page\n"
00552              << "of the general settings.\n";
00553         return GENERIC_EXIT_SETUP_ERROR;
00554     }
00555 
00556     MythSystemEventHandler *sysEventHandler = new MythSystemEventHandler();
00557 
00558     if (ismaster)
00559     {
00560         LOG(VB_GENERAL, LOG_NOTICE, LOC + "Starting up as the master server.");
00561     }
00562     else
00563     {
00564         LOG(VB_GENERAL, LOG_NOTICE, LOC + "Running as a slave backend.");
00565     }
00566 
00567     print_warnings(cmdline);
00568 
00569     bool fatal_error = false;
00570     bool runsched = setupTVs(ismaster, fatal_error);
00571     if (fatal_error)
00572     {
00573         delete sysEventHandler;
00574         return GENERIC_EXIT_SETUP_ERROR;
00575     }
00576 
00577     Scheduler *sched = NULL;
00578     if (ismaster)
00579     {
00580         if (runsched)
00581         {
00582             sched = new Scheduler(true, &tvList);
00583             int err = sched->GetError();
00584             if (err)
00585                 return err;
00586 
00587             if (cmdline.toBool("nosched"))
00588                 sched->DisableScheduling();
00589         }
00590 
00591         if (!cmdline.toBool("nohousekeeper"))
00592             housekeeping = new HouseKeeper(true, ismaster, sched);
00593 
00594         if (!cmdline.toBool("noautoexpire"))
00595         {
00596             expirer = new AutoExpire(&tvList);
00597             if (sched)
00598                 sched->SetExpirer(expirer);
00599         }
00600         gCoreContext->SetScheduler(sched);
00601     }
00602     else if (!cmdline.toBool("nohousekeeper"))
00603     {
00604         housekeeping = new HouseKeeper(true, ismaster, NULL);
00605     }
00606 
00607     if (!cmdline.toBool("nojobqueue"))
00608         jobqueue = new JobQueue(ismaster);
00609 
00610     // ----------------------------------------------------------------------
00611     //
00612     // ----------------------------------------------------------------------
00613 
00614     if (g_pUPnp == NULL)
00615     {
00616         g_pUPnp = new MediaServer();
00617 
00618         g_pUPnp->Init(ismaster, cmdline.toBool("noupnp"));
00619     }
00620 
00621     // ----------------------------------------------------------------------
00622     // Setup status server
00623     // ----------------------------------------------------------------------
00624 
00625     HttpStatus *httpStatus = NULL;
00626     HttpServer *pHS = g_pUPnp->GetHttpServer();
00627 
00628     if (pHS)
00629     {
00630         LOG(VB_GENERAL, LOG_INFO, "Main::Registering HttpStatus Extension");
00631 
00632         httpStatus = new HttpStatus( &tvList, sched, expirer, ismaster );
00633         pHS->RegisterExtension( httpStatus );
00634     }
00635 
00636     mainServer = new MainServer(
00637         ismaster, port, &tvList, sched, expirer);
00638 
00639     int exitCode = mainServer->GetExitCode();
00640     if (exitCode != GENERIC_EXIT_OK)
00641     {
00642         LOG(VB_GENERAL, LOG_CRIT,
00643             "Backend exiting, MainServer initialization error.");
00644         delete mainServer;
00645         return exitCode;
00646     }
00647 
00648     if (httpStatus && mainServer)
00649         httpStatus->SetMainServer(mainServer);
00650 
00651     StorageGroup::CheckAllStorageGroupDirs();
00652 
00653     if (gCoreContext->IsMasterBackend())
00654         gCoreContext->SendSystemEvent("MASTER_STARTED");
00655 
00658     exitCode = qApp->exec();
00661 
00662     if (gCoreContext->IsMasterBackend())
00663     {
00664         gCoreContext->SendSystemEvent("MASTER_SHUTDOWN");
00665         qApp->processEvents();
00666     }
00667 
00668     LOG(VB_GENERAL, LOG_NOTICE, "MythBackend exiting");
00669 
00670     delete sysEventHandler;
00671 
00672     return exitCode;
00673 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends