Beispiel #1
0
int setup_basics(const MythBackendCommandLineParser &cmdline)
{
    ofstream pidfs;
    if (!openPidfile(pidfs, cmdline.toString("pidfile")))
        return GENERIC_EXIT_PERMISSIONS_ERROR;

    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
        VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to ignore SIGPIPE");

    if (cmdline.toBool("daemon") && (daemon(0, 1) < 0))
    {
        VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to daemonize" + ENO);
        return GENERIC_EXIT_DAEMONIZING_ERROR;
    }

    QString username = cmdline.toString("username");
    if (!username.isEmpty() && !setUser(username))
        return GENERIC_EXIT_PERMISSIONS_ERROR;

    if (pidfs)
    {
        pidfs << getpid() << endl;
        pidfs.close();
    }

    return GENERIC_EXIT_OK;
}
Beispiel #2
0
void print_warnings(const MythBackendCommandLineParser &cmdline)
{
    if (cmdline.toBool("nohousekeeper"))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "****** The Housekeeper has been DISABLED with "
            "the --nohousekeeper option ******");
    }
    if (cmdline.toBool("nosched"))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "********** The Scheduler has been DISABLED with "
            "the --nosched option **********");
    }
    if (cmdline.toBool("noautoexpire"))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "********* Auto-Expire has been DISABLED with "
            "the --noautoexpire option ********");
    }
    if (cmdline.toBool("nojobqueue"))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "********* The JobQueue has been DISABLED with "
            "the --nojobqueue option *********");
    }
}
Beispiel #3
0
bool setup_context(MythBackendCommandLineParser &cmdline)
{
    if (!gContext->Init(false))
    {
        VERBOSE(VB_IMPORTANT, "Failed to init MythContext.");
        return false;
    }

    if (cmdline.toBool("event")         || cmdline.toBool("systemevent") ||
        cmdline.toBool("setverbose")    || cmdline.toBool("printsched") ||
        cmdline.toBool("testsched")     || cmdline.toBool("resched") ||
        cmdline.toBool("scanvideos")    || cmdline.toBool("clearcache") ||
        cmdline.toBool("printexpire"))
            gCoreContext->SetBackend(false);
    else
        gCoreContext->SetBackend(true);

    QMap<QString,QString> settingsOverride = cmdline.GetSettingsOverride();
    if (settingsOverride.size())
    {
        QMap<QString, QString>::iterator it;
        for (it = settingsOverride.begin(); it != settingsOverride.end(); ++it)
        {
            VERBOSE(VB_IMPORTANT, QString("Setting '%1' being forced to '%2'")
                    .arg(it.key()).arg(*it));
            gCoreContext->OverrideSettingForSession(it.key(), *it);
        }
    }

    return true;
}
Beispiel #4
0
void showUsage(const MythBackendCommandLineParser &cmdlineparser, const QString &version)
{
    QString    help  = cmdlineparser.GetHelpString(false);
    QByteArray ahelp = help.toLocal8Bit();

    cerr << qPrintable(version) << endl <<
    "Valid options are: " << endl <<
    "-h or --help                   List valid command line parameters"
         << endl << ahelp.constData() << endl;
}
Beispiel #5
0
int main(int argc, char **argv)
{
    MythBackendCommandLineParser cmdline;
    if (!cmdline.Parse(argc, argv))
    {
        cmdline.PrintHelp();
        return GENERIC_EXIT_INVALID_CMDLINE;
    }

    if (cmdline.toBool("showhelp"))
    {
        cmdline.PrintHelp();
        return GENERIC_EXIT_OK;
    }

    if (cmdline.toBool("showversion"))
    {
        cmdline.PrintVersion();
        return GENERIC_EXIT_OK;
    }

#ifndef _WIN32
    for (int i = UNUSED_FILENO; i < sysconf(_SC_OPEN_MAX) - 1; ++i)
        close(i);
    QCoreApplication a(argc, argv);
#else
    // MINGW application needs a window to receive messages
    // such as socket notifications :[
    QApplication a(argc, argv);
#endif
    QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHBACKEND);

    pidfile = cmdline.toString("pidfile");
    int retval = cmdline.Daemonize();
    if (retval != GENERIC_EXIT_OK)
        return retval;

    bool daemonize = cmdline.toBool("daemon");
    QString mask("general");
    if ((retval = cmdline.ConfigureLogging(mask, daemonize)) != GENERIC_EXIT_OK)
        return retval;

    if (daemonize)
        // Don't listen to console input if daemonized
        close(0);

    CleanupGuard callCleanup(cleanup);

#ifndef _WIN32
    QList<int> signallist;
    signallist << SIGINT << SIGTERM << SIGSEGV << SIGABRT;
    SignalHandler handler(signallist);
    signal(SIGHUP, SIG_IGN);
#endif

    gContext = new MythContext(MYTH_BINARY_VERSION);
    if (!gContext->Init(false))
    {
        LOG(VB_GENERAL, LOG_CRIT, "Failed to init MythContext.");
        return GENERIC_EXIT_NO_MYTHCONTEXT;
    }

    setHttpProxy();

    cmdline.ApplySettingsOverride();

    if (cmdline.toBool("event")         || cmdline.toBool("systemevent") ||
        cmdline.toBool("setverbose")    || cmdline.toBool("printsched") ||
        cmdline.toBool("testsched")     || cmdline.toBool("resched") ||
        cmdline.toBool("scanvideos")    || cmdline.toBool("clearcache") ||
        cmdline.toBool("printexpire")   || cmdline.toBool("setloglevel"))
    {
        gCoreContext->SetBackend(false);
        return handle_command(cmdline);
    }

    gCoreContext->SetBackend(true);
    retval = run_backend(cmdline);
    return retval;
}
Beispiel #6
0
int run_backend(MythBackendCommandLineParser &cmdline)
{
    gBackendContext = new BackendContext();

    if (!DBUtil::CheckTimeZoneSupport())
    {
        LOG(VB_GENERAL, LOG_ERR,
            "MySQL time zone support is missing.  "
            "Please install it and try again.  "
            "See 'mysql_tzinfo_to_sql' for assistance.");
        return GENERIC_EXIT_DB_NOTIMEZONE;
    }

    bool ismaster = gCoreContext->IsMasterHost();

    if (!UpgradeTVDatabaseSchema(ismaster, ismaster))
    {
        LOG(VB_GENERAL, LOG_ERR, "Couldn't upgrade database to new schema");
        return GENERIC_EXIT_DB_OUTOFDATE;
    }

    MythTranslation::load("mythfrontend");

    if (!ismaster)
    {
        int ret = connect_to_master();
        if (ret != GENERIC_EXIT_OK)
            return ret;
    }

    int     port = gCoreContext->GetBackendServerPort();
    if (gCoreContext->GetBackendServerIP().isEmpty())
    {
        cerr << "No setting found for this machine's BackendServerIP.\n"
             << "Please run setup on this machine and modify the first page\n"
             << "of the general settings.\n";
        return GENERIC_EXIT_SETUP_ERROR;
    }

    MythSystemEventHandler *sysEventHandler = new MythSystemEventHandler();

    if (ismaster)
    {
        LOG(VB_GENERAL, LOG_NOTICE, LOC + "Starting up as the master server.");
    }
    else
    {
        LOG(VB_GENERAL, LOG_NOTICE, LOC + "Running as a slave backend.");
    }

    print_warnings(cmdline);

    bool fatal_error = false;
    bool runsched = setupTVs(ismaster, fatal_error);
    if (fatal_error)
    {
        delete sysEventHandler;
        return GENERIC_EXIT_SETUP_ERROR;
    }

    Scheduler *sched = NULL;
    if (ismaster)
    {
        if (runsched)
        {
            sched = new Scheduler(true, &tvList);
            int err = sched->GetError();
            if (err)
                return err;

            if (cmdline.toBool("nosched"))
                sched->DisableScheduling();
        }

        if (!cmdline.toBool("noautoexpire"))
        {
            expirer = new AutoExpire(&tvList);
            if (sched)
                sched->SetExpirer(expirer);
        }
        gCoreContext->SetScheduler(sched);
    }

    if (!cmdline.toBool("nohousekeeper"))
    {
        housekeeping = new HouseKeeper();

        if (ismaster)
        {
            housekeeping->RegisterTask(new LogCleanerTask());
            housekeeping->RegisterTask(new CleanupTask());
            housekeeping->RegisterTask(new ThemeUpdateTask());
            housekeeping->RegisterTask(new ArtworkTask());
            housekeeping->RegisterTask(new MythFillDatabaseTask());

            // only run this task if MythMusic is installed and we have a new enough schema
            if (gCoreContext->GetNumSetting("MusicDBSchemaVer", 0) >= 1024)
                housekeeping->RegisterTask(new RadioStreamUpdateTask());
        }

        housekeeping->RegisterTask(new JobQueueRecoverTask());
#ifdef __linux__
 #ifdef CONFIG_BINDINGS_PYTHON
        housekeeping->RegisterTask(new HardwareProfileTask());
 #endif
#endif

        housekeeping->Start();
    }

    if (!cmdline.toBool("nojobqueue"))
        jobqueue = new JobQueue(ismaster);

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    if (g_pUPnp == NULL)
    {
        g_pUPnp = new MediaServer();

        g_pUPnp->Init(ismaster, cmdline.toBool("noupnp"));
    }

    // ----------------------------------------------------------------------
    // Setup status server
    // ----------------------------------------------------------------------

    HttpStatus *httpStatus = NULL;
    HttpServer *pHS = g_pUPnp->GetHttpServer();

    if (pHS)
    {
        LOG(VB_GENERAL, LOG_INFO, "Main::Registering HttpStatus Extension");

        httpStatus = new HttpStatus( &tvList, sched, expirer, ismaster );
        pHS->RegisterExtension( httpStatus );
    }

    mainServer = new MainServer(
        ismaster, port, &tvList, sched, expirer);

    int exitCode = mainServer->GetExitCode();
    if (exitCode != GENERIC_EXIT_OK)
    {
        LOG(VB_GENERAL, LOG_CRIT,
            "Backend exiting, MainServer initialization error.");
        cleanup();
        return exitCode;
    }

    if (httpStatus && mainServer)
        httpStatus->SetMainServer(mainServer);

    StorageGroup::CheckAllStorageGroupDirs();

    if (gCoreContext->IsMasterBackend())
        gCoreContext->SendSystemEvent("MASTER_STARTED");

#ifdef USING_SYSTEMD_NOTIFY
    // Provide systemd ready notification (for type=notify units)
    (void)sd_notify(0, "READY=1");
#endif

    ///////////////////////////////
    ///////////////////////////////
    exitCode = qApp->exec();
    ///////////////////////////////
    ///////////////////////////////

    if (gCoreContext->IsMasterBackend())
    {
        gCoreContext->SendSystemEvent("MASTER_SHUTDOWN");
        qApp->processEvents();
    }

    LOG(VB_GENERAL, LOG_NOTICE, "MythBackend exiting");

    delete sysEventHandler;

    return exitCode;
}
Beispiel #7
0
int handle_command(const MythBackendCommandLineParser &cmdline)
{
    QString eventString;

    if (cmdline.toBool("event"))
        eventString = cmdline.toString("event");
    else if (cmdline.toBool("systemevent"))
        eventString = "SYSTEM_EVENT " +
                      cmdline.toString("systemevent") +
                      QString(" SENDER %1").arg(gCoreContext->GetHostName());

    if (!eventString.isEmpty())
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            gCoreContext->SendMessage(eventString);
            return GENERIC_EXIT_OK;
        }
        return GENERIC_EXIT_NO_MYTHCONTEXT;
    }

    if (cmdline.toBool("setverbose"))
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            QString message = "SET_VERBOSE ";
            message += cmdline.toString("setverbose");

            gCoreContext->SendMessage(message);
            LOG(VB_GENERAL, LOG_INFO,
                QString("Sent '%1' message").arg(message));
            return GENERIC_EXIT_OK;
        }
        else
        {
            LOG(VB_GENERAL, LOG_ERR,
                "Unable to connect to backend, verbose mask unchanged ");
            return GENERIC_EXIT_CONNECT_ERROR;
        }
    }

    if (cmdline.toBool("setloglevel"))
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            QString message = "SET_LOG_LEVEL ";
            message += cmdline.toString("setloglevel");

            gCoreContext->SendMessage(message);
            LOG(VB_GENERAL, LOG_INFO,
                QString("Sent '%1' message").arg(message));
            return GENERIC_EXIT_OK;
        }
        else
        {
            LOG(VB_GENERAL, LOG_ERR,
                "Unable to connect to backend, log level unchanged ");
            return GENERIC_EXIT_CONNECT_ERROR;
        }
    }

    if (cmdline.toBool("clearcache"))
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            gCoreContext->SendMessage("CLEAR_SETTINGS_CACHE");
            LOG(VB_GENERAL, LOG_INFO, "Sent CLEAR_SETTINGS_CACHE message");
            return GENERIC_EXIT_OK;
        }
        else
        {
            LOG(VB_GENERAL, LOG_ERR, "Unable to connect to backend, settings "
                    "cache will not be cleared.");
            return GENERIC_EXIT_CONNECT_ERROR;
        }
    }

    if (cmdline.toBool("printsched") ||
        cmdline.toBool("testsched"))
    {
        Scheduler *sched = new Scheduler(false, &tvList);
        if (cmdline.toBool("printsched"))
        {
            if (!gCoreContext->ConnectToMasterServer())
            {
                LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master");
                delete sched;
                return GENERIC_EXIT_CONNECT_ERROR;
            }
            cout << "Retrieving Schedule from Master backend.\n";
            sched->FillRecordListFromMaster();
        }
        else
        {
            cout << "Calculating Schedule from database.\n" <<
                    "Inputs, Card IDs, and Conflict info may be invalid "
                    "if you have multiple tuners.\n";
            ProgramInfo::CheckProgramIDAuthorities();
            sched->FillRecordListFromDB();
        }

        verboseMask |= VB_SCHEDULE;
        LogLevel_t oldLogLevel = logLevel;
        logLevel = LOG_DEBUG;
        sched->PrintList(true);
        logLevel = oldLogLevel;
        delete sched;
        return GENERIC_EXIT_OK;
    }

    if (cmdline.toBool("resched"))
    {
        bool ok = false;
        if (gCoreContext->ConnectToMasterServer())
        {
            LOG(VB_GENERAL, LOG_INFO, "Connected to master for reschedule");
            ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(),
                                                "MythBackendCommand");
            ok = true;
        }
        else
            LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master for reschedule");

        return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_CONNECT_ERROR;
    }

    if (cmdline.toBool("scanvideos"))
    {
        bool ok = false;
        if (gCoreContext->ConnectToMasterServer())
        {
            gCoreContext->SendReceiveStringList(QStringList() << "SCAN_VIDEOS");
            LOG(VB_GENERAL, LOG_INFO, "Requested video scan");
            ok = true;
        }
        else
            LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master for video scan");

        return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_CONNECT_ERROR;
    }

    if (cmdline.toBool("printexpire"))
    {
        expirer = new AutoExpire();
        expirer->PrintExpireList(cmdline.toString("printexpire"));
        return GENERIC_EXIT_OK;
    }

    // This should never actually be reached..
    return GENERIC_EXIT_OK;
}
Beispiel #8
0
int run_backend(MythBackendCommandLineParser &cmdline)
{
    bool ismaster = gCoreContext->IsMasterHost();

    if (!UpgradeTVDatabaseSchema(ismaster, ismaster))
    {
        LOG(VB_GENERAL, LOG_ERR, "Couldn't upgrade database to new schema");
        return GENERIC_EXIT_DB_OUTOFDATE;
    }

    MythTranslation::load("mythfrontend");

    if (!ismaster)
    {
        int ret = connect_to_master();
        if (ret != GENERIC_EXIT_OK)
            return ret;
    }

    QString myip = gCoreContext->GetSetting("BackendServerIP");
    int     port = gCoreContext->GetNumSetting("BackendServerPort", 6543);
    if (myip.isEmpty())
    {
        cerr << "No setting found for this machine's BackendServerIP.\n"
             << "Please run setup on this machine and modify the first page\n"
             << "of the general settings.\n";
        return GENERIC_EXIT_SETUP_ERROR;
    }

    MythSystemEventHandler *sysEventHandler = new MythSystemEventHandler();

    if (ismaster)
    {
        LOG(VB_GENERAL, LOG_NOTICE, LOC + "Starting up as the master server.");
    }
    else
    {
        LOG(VB_GENERAL, LOG_NOTICE, LOC + "Running as a slave backend.");
    }

    print_warnings(cmdline);

    bool fatal_error = false;
    bool runsched = setupTVs(ismaster, fatal_error);
    if (fatal_error)
    {
        delete sysEventHandler;
        return GENERIC_EXIT_SETUP_ERROR;
    }

    if (ismaster)
    {
        if (runsched)
        {
            sched = new Scheduler(true, &tvList);
            int err = sched->GetError();
            if (err)
                return err;

            if (cmdline.toBool("nosched"))
                sched->DisableScheduling();
        }

        if (!cmdline.toBool("nohousekeeper"))
            housekeeping = new HouseKeeper(true, ismaster, sched);

        if (!cmdline.toBool("noautoexpire"))
        {
            expirer = new AutoExpire(&tvList);
            if (sched)
                sched->SetExpirer(expirer);
        }
    }
    else if (!cmdline.toBool("nohousekeeper"))
    {
        housekeeping = new HouseKeeper(true, ismaster, NULL);
    }

    if (!cmdline.toBool("nojobqueue"))
        jobqueue = new JobQueue(ismaster);

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    if (g_pUPnp == NULL)
    {
        g_pUPnp = new MediaServer();

        g_pUPnp->Init(ismaster, cmdline.toBool("noupnp"));
    }

    // ----------------------------------------------------------------------
    // Setup status server
    // ----------------------------------------------------------------------

    HttpStatus *httpStatus = NULL;
    HttpServer *pHS = g_pUPnp->GetHttpServer();

    if (pHS)
    {
        LOG(VB_GENERAL, LOG_INFO, "Main::Registering HttpStatus Extension");

        httpStatus = new HttpStatus( &tvList, sched, expirer, ismaster );
        pHS->RegisterExtension( httpStatus );
    }

    MainServer *mainServer = new MainServer(ismaster, port, &tvList, sched,
                                            expirer);

    int exitCode = mainServer->GetExitCode();
    if (exitCode != GENERIC_EXIT_OK)
    {
        LOG(VB_GENERAL, LOG_CRIT,
            "Backend exiting, MainServer initialization error.");
        delete mainServer;
        return exitCode;
    }

    if (httpStatus && mainServer)
        httpStatus->SetMainServer(mainServer);

    StorageGroup::CheckAllStorageGroupDirs();

    if (gCoreContext->IsMasterBackend())
        SendMythSystemEvent("MASTER_STARTED");

    ///////////////////////////////
    ///////////////////////////////
    exitCode = qApp->exec();
    ///////////////////////////////
    ///////////////////////////////

    if (gCoreContext->IsMasterBackend())
    {
        SendMythSystemEvent("MASTER_SHUTDOWN");
        qApp->processEvents();
    }

    LOG(VB_GENERAL, LOG_NOTICE, "MythBackend exiting");

    delete sysEventHandler;
    delete mainServer;

    cleanup();

    return exitCode;
}
Beispiel #9
0
int handle_command(const MythBackendCommandLineParser &cmdline)
{
    QString eventString;

    if (cmdline.toBool("event"))
        eventString = cmdline.toString("event");
    else if (cmdline.toBool("systemevent"))
        eventString = "SYSTEM_EVENT " +
                      cmdline.toString("systemevent") +
                      QString(" SENDER %1").arg(gCoreContext->GetHostName());

    if (!eventString.isEmpty())
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            RemoteSendMessage(eventString);
            return GENERIC_EXIT_OK;
        }
        return GENERIC_EXIT_NO_MYTHCONTEXT;
    }

    if (cmdline.toBool("setverbose"))
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            QString message = "SET_VERBOSE ";
            message += cmdline.toString("setverbose");

            RemoteSendMessage(message);
            VERBOSE(VB_IMPORTANT, QString("Sent '%1' message").arg(message));
            return GENERIC_EXIT_OK;
        }
        else
        {
            VERBOSE(VB_IMPORTANT,
                    "Unable to connect to backend, verbose level unchanged ");
            return GENERIC_EXIT_CONNECT_ERROR;
        }
    }

    if (cmdline.toBool("clearcache"))
    {
        if (gCoreContext->ConnectToMasterServer())
        {
            RemoteSendMessage("CLEAR_SETTINGS_CACHE");
            VERBOSE(VB_IMPORTANT, "Sent CLEAR_SETTINGS_CACHE message");
            return GENERIC_EXIT_OK;
        }
        else
        {
            VERBOSE(VB_IMPORTANT, "Unable to connect to backend, settings "
                    "cache will not be cleared.");
            return GENERIC_EXIT_CONNECT_ERROR;
        }
    }

    if (cmdline.toBool("printsched") ||
        cmdline.toBool("testsched"))
    {
        sched = new Scheduler(false, &tvList);
        if (!cmdline.toBool("testsched") &&
            gCoreContext->ConnectToMasterServer())
        {
            cout << "Retrieving Schedule from Master backend.\n";
            sched->FillRecordListFromMaster();
        }
        else
        {
            cout << "Calculating Schedule from database.\n" <<
                    "Inputs, Card IDs, and Conflict info may be invalid "
                    "if you have multiple tuners.\n";
            sched->FillRecordListFromDB();
        }

        print_verbose_messages |= VB_SCHEDULE;
        sched->PrintList(true);
        return GENERIC_EXIT_OK;
    }

    if (cmdline.toBool("resched"))
    {
        bool ok = false;
        if (gCoreContext->ConnectToMasterServer())
        {
            VERBOSE(VB_IMPORTANT, "Connected to master for reschedule");
            ScheduledRecording::signalChange(-1);
            ok = true;
        }
        else
            VERBOSE(VB_IMPORTANT, "Cannot connect to master for reschedule");

        return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_CONNECT_ERROR;
    }

    if (cmdline.toBool("scanvideos"))
    {
        bool ok = false;
        if (gCoreContext->ConnectToMasterServer())
        {
            gCoreContext->SendReceiveStringList(QStringList() << "SCAN_VIDEOS");
            VERBOSE(VB_IMPORTANT, "Requested video scan");
            ok = true;
        }
        else
            VERBOSE(VB_IMPORTANT, "Cannot connect to master for video scan");

        return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_CONNECT_ERROR;
    }

    if (!cmdline.toBool("printexpire"))
    {
        expirer = new AutoExpire();
        expirer->PrintExpireList(cmdline.toString("printexpire"));
        return GENERIC_EXIT_OK;
    }

    // This should never actually be reached..
    return GENERIC_EXIT_OK;
}