Ticket #8262: 8262-v1.patch

File 8262-v1.patch, 153.6 KB (added by danielk, 14 years ago)

work-in-progress..

  • libs/libmyth/mythcommandlineparser.cpp

     
    22using namespace std;
    33
    44#include <QFile>
     5#include <QSize>
    56
    67#include "mythcommandlineparser.h"
    78#include "exitcodes.h"
     
    1011#include "mythverbose.h"
    1112#include "mythversion.h"
    1213
    13 MythCommandLineParser::MythCommandLineParser(int things_to_parse) :
     14static bool parse_preview_info(
     15    const QString &param,
     16    long long     &previewFrameNumber,
     17    long long     &previewSeconds,
     18    QSize         &previewSize);
     19
     20MythCommandLineParser::MythCommandLineParser(uint64_t things_to_parse) :
    1421    parseTypes(things_to_parse),
    15     display(QString::null), geometry(QString::null),
     22    display(), geometry(),
     23    logfile(),
     24    pidfile(),
     25    infile(),
     26    outfile(),
     27    newverbose(),
     28    username(),
     29    printexpire(),
     30    eventString(),
     31    previewSize(0,0),
     32    starttime(),
     33    chanid(0),
     34    previewFrameNumber(-2),
     35    previewSeconds(-2),
     36    daemonize(false),
     37    printsched(false),
     38    testsched(false),
     39    setverbose(false),
     40    resched(false),
     41    nosched(false),
     42    noupnp(false),
     43    nojobqueue(false),
     44    nohousekeeper(false),
     45    noexpirer(false),
     46    clearsettingscache(false),
     47    wantupnprebuild(false),
     48
    1649    wantsToExit(false)
    1750{
    1851}
     
    95128        }
    96129        return true;
    97130    }
     131    else if ((parseTypes & kCLPSetVerbose) &&
     132             !strcmp(argv[argpos],"--setverbose"))
     133    {
     134        setverbose = true;
     135        if ((argc - 1) > argpos)
     136        {
     137            newverbose = argv[argpos+1];
     138            ++argpos;
     139        }
     140        else
     141        {
     142            cerr << "Missing argument to --setverbose option\n";
     143            return BACKEND_EXIT_INVALID_CMDLINE;
     144        }
     145    }
    98146    else if ((parseTypes & kCLPHelp) &&
    99147             (!strcmp(argv[argpos],"-h") ||
    100148              !strcmp(argv[argpos],"--help") ||
     
    154202        settingsOverride["RunFrontendInWindow"] = "0";
    155203        return true;
    156204    }
     205    else if ((parseTypes & kCLPDaemon) &&
     206             (!strcmp(argv[argpos],"-d") ||
     207              !strcmp(argv[argpos],"--daemon")))
     208    {
     209        daemonize = true;
     210        return true;
     211    }
     212    else if ((parseTypes && kCLPPrintSchedule) &&
     213             !strcmp(argv[argpos],"--printsched"))
     214    {
     215        printsched = true;
     216        return true;
     217    }
     218    else if ((parseTypes && kCLPTestSchedule) &&
     219             !strcmp(argv[argpos],"--testsched"))
     220    {
     221        testsched = true;
     222        return true;
     223    }
     224    else if ((parseTypes && kCLPReschedule) &&
     225             !strcmp(argv[argpos],"--resched"))
     226    {
     227        resched = true;
     228        return true;
     229    }
     230    else if ((parseTypes && kCLPNoSchedule) &&
     231             !strcmp(argv[argpos],"--nosched"))
     232    {
     233        nosched = true;
     234        return true;
     235    }
     236    else if ((parseTypes && kCLPNoUPnP) &&
     237             !strcmp(argv[argpos],"--noupnp"))
     238    {
     239        noupnp = true;
     240        return true;
     241    }
     242    else if ((parseTypes && kCLPUPnPRebuild) &&
     243             !strcmp(argv[argpos],"--upnprebuild"))
     244    {
     245        wantupnprebuild = true;
     246        return true;
     247    }
     248    else if ((parseTypes && kCLPNoJobqueue) &&
     249             !strcmp(argv[argpos],"--nojobqueue"))
     250    {
     251        nojobqueue = true;
     252        return true;
     253    }
     254    else if ((parseTypes && kCLPNoHousekeeper) &&
     255             !strcmp(argv[argpos],"--nohousekeeper"))
     256    {
     257        nohousekeeper = true;
     258        return true;
     259    }
     260    else if ((parseTypes && kCLPNoAutoExpire) &&
     261             !strcmp(argv[argpos],"--noautoexpire"))
     262    {
     263        noexpirer = true;
     264        return true;
     265    }
     266    else if ((parseTypes && kCLPClearCache) &&
     267             !strcmp(argv[argpos],"--clearcache"))
     268    {
     269        clearsettingscache = true;
     270        return true;
     271    }
    157272    else if ((parseTypes & kCLPOverrideSettingsFile) &&
    158273             (!strcmp(argv[argpos],"--override-settings-file")))
    159274    {
     
    252367            QString tmpArg = argv[argpos+1];
    253368            if (tmpArg.startsWith("-"))
    254369            {
    255                 cerr << "Invalid or missing argument to "
     370                cerr << "Invalid argument to "
    256371                     << "-G/--get-setting option\n";
    257372                err = true;
    258373                return true;
     
    262377        }
    263378        else
    264379        {
    265             cerr << "Invalid or missing argument to "
     380            cerr << "Missing argument to "
    266381                 << "-G/--get-setting option\n";
    267382            err = true;
    268383            return true;
     
    271386        ++argpos;
    272387        return true;
    273388    }
     389    else if ((parseTypes & kCLPLogFile) &&
     390             (!strcmp(argv[argpos],"-l") ||
     391              !strcmp(argv[argpos],"--logfile")))
     392    {
     393        if ((argc - 1) > argpos)
     394        {
     395            logfile = argv[argpos+1];
     396            if (logfile.startsWith("-"))
     397            {
     398                cerr << "Invalid argument to -l/--logfile option\n";
     399                err = true;
     400                return true;
     401            }
     402        }
     403        else
     404        {
     405            cerr << "Missing argument to -l/--logfile option\n";
     406            err = true;
     407            return true;
     408        }
     409
     410        ++argpos;
     411        return true;
     412    }
     413    else if ((parseTypes & kCLPPidFile) &&
     414             (!strcmp(argv[argpos],"-p") ||
     415              !strcmp(argv[argpos],"--pidfile")))
     416    {
     417        if ((argc - 1) > argpos)
     418        {
     419            pidfile = argv[argpos+1];
     420            if (pidfile.startsWith("-"))
     421            {
     422                cerr << "Invalid argument to -p/--pidfile option\n";
     423                err = true;
     424                return true;
     425            }
     426        }
     427        else
     428        {
     429            cerr << "Missing argument to -p/--pidfile option\n";
     430            err = true;
     431            return true;
     432        }
     433
     434        ++argpos;
     435        return true;
     436    }
     437    else if ((parseTypes & kCLPInFile) &&
     438             !strcmp(argv[argpos],"--infile"))
     439    {
     440        if ((argc - 1) > argpos)
     441        {
     442            infile = argv[argpos+1];
     443            if (infile.startsWith("-"))
     444            {
     445                cerr << "Invalid argument to --infile option\n";
     446                err = true;
     447                return true;
     448            }
     449        }
     450        else
     451        {
     452            cerr << "Missing argument to --infile option\n";
     453            err = true;
     454            return true;
     455        }
     456
     457        ++argpos;
     458        return true;
     459    }
     460    else if ((parseTypes & kCLPOutFile) &&
     461             !strcmp(argv[argpos],"--outfile"))
     462    {
     463        if ((argc - 1) > argpos)
     464        {
     465            outfile = argv[argpos+1];
     466            if (outfile.startsWith("-"))
     467            {
     468                cerr << "Invalid argument to --outfile option\n";
     469                err = true;
     470                return true;
     471            }
     472        }
     473        else
     474        {
     475            cerr << "Missing argument to --outfile option\n";
     476            err = true;
     477            return true;
     478        }
     479
     480        ++argpos;
     481        return true;
     482    }
     483    else if ((parseTypes & kCLPUsername) &&
     484             !strcmp(argv[argpos],"--user"))
     485    {
     486        if ((argc - 1) > argpos)
     487        {
     488            username = argv[argpos+1];
     489            if (username.startsWith("-"))
     490            {
     491                cerr << "Invalid argument to --user option\n";
     492                err = true;
     493                return true;
     494            }
     495        }
     496        else
     497        {
     498            cerr << "Missing argument to --user option\n";
     499            err = true;
     500            return true;
     501        }
     502
     503        ++argpos;
     504        return true;
     505    }
     506    else if ((parseTypes & kCLPEvent) &&
     507             (!strcmp(argv[argpos],"--event")))
     508    {
     509        if ((argc - 1) > argpos)
     510        {
     511            eventString = argv[argpos+1];
     512            if (eventString.startsWith("-"))
     513            {
     514                cerr << "Invalid argument to --event option\n";
     515                err = true;
     516                return true;
     517            }
     518        }
     519        else
     520        {
     521            cerr << "Missing argument to --event option\n";
     522            err = true;
     523            return true;
     524        }
     525
     526        ++argpos;
     527        return true;
     528    }
     529    else if ((parseTypes & kCLPSystemEvent) &&
     530             (!strcmp(argv[argpos],"--systemevent")))
     531    {
     532        if ((argc - 1) > argpos)
     533        {
     534            eventString = argv[argpos+1];
     535            if (eventString.startsWith("-"))
     536            {
     537                cerr << "Invalid argument to --systemevent option\n";
     538                err = true;
     539                return true;
     540            }
     541        }
     542        else
     543        {
     544            cerr << "Missing argument to --systemevent option\n";
     545            err = true;
     546            return true;
     547        }
     548
     549        ++argpos;
     550        return true;
     551    }
     552    else if ((parseTypes & kCLPChannelId) &&
     553             (!strcmp(argv[argpos],"-c") ||
     554              !strcmp(argv[argpos],"--chanid")))
     555    {
     556        if ((argc - 1) > argpos)
     557        {
     558            chanid = QString(argv[argpos+1]).toUInt();
     559            if (!chanid)
     560            {
     561                cerr << "Invalid argument to -c/--chanid option\n";
     562                err = true;
     563                return true;
     564            }
     565        }
     566        else
     567        {
     568            cerr << "Missing argument to -c/--chanid option\n";
     569            err = true;
     570            return true;
     571        }
     572
     573        ++argpos;
     574        return true;
     575    }
     576    else if ((parseTypes & kCLPStartTime) &&
     577             (!strcmp(argv[argpos],"-s") ||
     578              !strcmp(argv[argpos],"--starttime")))
     579    {
     580        if ((argc - 1) > argpos)
     581        {
     582            QString tmp = argv[argpos+1];
     583            starttime = QDateTime::fromString(tmp, Qt::ISODate);
     584            if (!starttime.isValid())
     585            {
     586                cerr << "Invalid argument to -s/--starttime option\n";
     587                err = true;
     588                return true;
     589            }
     590        }
     591        else
     592        {
     593            cerr << "Missing argument to -s/--starttime option\n";
     594            err = true;
     595            return true;
     596        }
     597
     598        ++argpos;
     599        return true;
     600    }
     601    else if ((parseTypes & kCLPPrintExpire) &&
     602             (!strcmp(argv[argpos],"--printexpire")))
     603    {
     604        printexpire = "ALL";
     605        if (((argc - 1) > argpos) &&
     606            QString(argv[argpos+1]).startsWith("-"))
     607        {
     608            printexpire = argv[argpos+1];
     609            ++argpos;
     610        }
     611        return true;
     612    }
     613    else if ((parseTypes & kCLPGeneratePreview) &&
     614             !strcmp(argv[argpos],"--generate-preview"))
     615    {
     616        QString tmp;
     617        if ((argc - 1) < argpos)
     618        {
     619            tmp = argv[argpos+1];
     620            bool ok = true;
     621            if (tmp.left(1) == "-")
     622                tmp.left(2).toInt(&ok);
     623            if (ok)
     624                argpos++;
     625            else
     626                tmp.clear();
     627        }
     628
     629        if (!parse_preview_info(tmp, previewFrameNumber, previewSeconds,
     630                                previewSize))
     631        {
     632            cerr << "Unable to parse --generate-preview option '"
     633                 << tmp.toAscii().constData() << "'" << endl;
     634
     635            err = true;
     636        }
     637
     638        return true;
     639    }
    274640    else
    275641    {
    276642        return PreParse(argc, argv, argpos, err);
     
    349715
    350716    return str;
    351717}
     718
     719// [WxH] | [WxH@]seconds[S] | [WxH@]frame_numF
     720static bool parse_preview_info(
     721    const QString &param,
     722    long long     &previewFrameNumber,
     723    long long     &previewSeconds,
     724    QSize         &previewSize)
     725{
     726    previewFrameNumber = -1;
     727    previewSeconds = -1;
     728    previewSize = QSize(0,0);
     729    if (param.isEmpty())
     730        return true;
     731
     732    int xat = param.indexOf("x", 0, Qt::CaseInsensitive);
     733    int aat = param.indexOf("@", 0);
     734    if (xat > 0)
     735    {
     736        QString widthStr  = param.left(xat);
     737        QString heightStr;
     738        if (aat > xat)
     739            heightStr = param.mid(xat + 1, aat - xat - 1);
     740        else
     741            heightStr = param.mid(xat + 1);
     742
     743        bool ok1, ok2;
     744        previewSize = QSize(widthStr.toInt(&ok1), heightStr.toInt(&ok2));
     745        if (!ok1 || !ok2)
     746        {
     747            VERBOSE(VB_IMPORTANT, QString(
     748                        "Error: Failed to parse --generate-preview "
     749                        "param '%1'").arg(param));
     750        }
     751    }
     752    if ((xat > 0) && (aat < xat))
     753        return true;
     754
     755    QString lastChar = param.at(param.length() - 1).toLower();
     756    QString frameNumStr;
     757    QString secsStr;
     758    if (lastChar == "f")
     759        frameNumStr = param.mid(aat + 1, param.length() - aat - 2);
     760    else if (lastChar == "s")
     761        secsStr = param.mid(aat + 1, param.length() - aat - 2);
     762    else
     763        secsStr = param.mid(aat + 1, param.length() - aat - 1);
     764
     765    bool ok = false;
     766    if (!frameNumStr.isEmpty())
     767        previewFrameNumber = frameNumStr.toUInt(&ok);
     768    else if (!secsStr.isEmpty())
     769        previewSeconds = secsStr.toUInt(&ok);
     770
     771    if (!ok)
     772    {
     773        VERBOSE(VB_IMPORTANT, QString(
     774                    "Error: Failed to parse --generate-preview "
     775                    "param '%1'").arg(param));
     776    }
     777
     778    return ok;
     779}
  • libs/libmyth/mythcommandlineparser.h

     
    11// -*- Mode: c++ -*-
    22
    33#include <QStringList>
     4#include <QDateTime>
     5#include <QSize>
    46#include <QMap>
    57
    68#include <stdint.h>   // for uint64_t
     
    810#include "mythexp.h"
    911
    1012typedef enum {
    11     kCLPOverrideSettingsFile = 0x00000001,
    12     kCLPOverrideSettings     = 0x00000002,
    13     kCLPWindowed             = 0x00000004,
    14     kCLPNoWindowed           = 0x00000008,
    15     kCLPGetSettings          = 0x00000010,
    16     kCLPQueryVersion         = 0x00000020,
    17     kCLPDisplay              = 0x00000040,
    18     kCLPGeometry             = 0x00000080,
    19     kCLPVerbose              = 0x00000100,
    20     kCLPHelp                 = 0x00000200,
    21     kCLPExtra                = 0x00000400,
     13    kCLPOverrideSettingsFile = 0x0000000001ULL,
     14    kCLPOverrideSettings     = 0x0000000002ULL,
     15    kCLPWindowed             = 0x0000000004ULL,
     16    kCLPNoWindowed           = 0x0000000008ULL,
     17    kCLPGetSettings          = 0x0000000010ULL,
     18    kCLPQueryVersion         = 0x0000000020ULL,
     19    kCLPDisplay              = 0x0000000040ULL,
     20    kCLPGeometry             = 0x0000000080ULL,
     21    kCLPVerbose              = 0x0000000100ULL,
     22    kCLPSetVerbose           = 0x0000000200ULL,
     23    kCLPHelp                 = 0x0000000400ULL,
     24    kCLPExtra                = 0x0000000800ULL,
     25    kCLPDaemon               = 0x0000001000ULL,
     26    kCLPPrintSchedule        = 0x0000002000ULL,
     27    kCLPTestSchedule         = 0x0000004000ULL,
     28    kCLPReschedule           = 0x0000008000ULL,
     29    kCLPNoSchedule           = 0x0000010000ULL,
     30    kCLPNoUPnP               = 0x0000020000ULL,
     31    kCLPUPnPRebuild          = 0x0000040000ULL,
     32    kCLPNoJobqueue           = 0x0000080000ULL,
     33    kCLPNoHousekeeper        = 0x0000100000ULL,
     34    kCLPNoAutoExpire         = 0x0000200000ULL,
     35    kCLPClearCache           = 0x0000400000ULL,
     36    kCLPLogFile              = 0x0000800000ULL,
     37    kCLPPidFile              = 0x0001000000ULL,
     38    kCLPInFile               = 0x0002000000ULL,
     39    kCLPOutFile              = 0x0004000000ULL,
     40    kCLPUsername             = 0x0008000000ULL,
     41    kCLPEvent                = 0x0010000000ULL,
     42    kCLPSystemEvent          = 0x0020000000ULL,
     43    kCLPChannelId            = 0x0040000000ULL,
     44    kCLPStartTime            = 0x0080000000ULL,
     45    kCLPPrintExpire          = 0x0100000000ULL,
     46    kCLPGeneratePreview      = 0x0200000000ULL,
    2247} ParseType;
    2348
    2449class MPUBLIC MythCommandLineParser
    2550{
    2651  public:
    27     MythCommandLineParser(int things_to_parse);
     52    MythCommandLineParser(uint64_t things_to_parse);
    2853
    2954    bool PreParse(int argc, const char * const * argv, int &argpos, bool &err);
    3055    bool Parse(int argc, const char * const * argv, int &argpos, bool &err);
     
    3459        { return settingsOverride; }
    3560    QStringList GetSettingsQuery(void) const
    3661        { return settingsQuery; }
    37     QString GetDisplay(void)  const { return display;     }
    38     QString GetGeometry(void) const { return geometry;    }
     62    QString GetDisplay(void)        const { return display;     }
     63    QString GetGeometry(void)       const { return geometry;    }
     64    QString GetLogFilename(void)    const { return logfile;     }
     65    QString GetPIDFilename(void)    const { return pidfile;     }
     66    QString GetInputFilename(void)  const { return infile;      }
     67    QString GetOutputFilename(void) const { return outfile;     }
     68    QString GetNewVerbose(void)     const { return newverbose;  }
     69    QString GetUsername(void)       const { return username;    }
     70    QString GetPrintExpire(void)    const { return printexpire; }
     71    QString GetEventString(void)    const { return eventString; }
    3972
     73    QSize     GetPreviewSize(void)        const { return previewSize; }
     74    QDateTime GetStartTime(void)          const { return starttime;   }
     75    uint      GetChanID(void)             const { return chanid;      }
     76    long long GetPreviewFrameNumber(void) const { return  previewFrameNumber; }
     77    long long GetPreviewSeconds(void)     const { return previewSeconds; }
     78
     79    bool IsDaemonizeEnabled(void)   const { return daemonize;   }
     80    bool IsPrintScheduleEnabled(void) const { return printsched;  }
     81    bool IsTestSchedulerEnabled(void) const { return testsched;   }
     82    bool IsSchedulerEnabled(void)   const { return !nosched;    }
     83    bool IsUPnPEnabled(void)        const { return !noupnp;     }
     84    bool IsJobQueueEnabled(void)    const { return !nojobqueue; }
     85    bool IsHouseKeeperEnabled(void) const { return !nohousekeeper; }
     86    bool IsAutoExpirerEnabled(void) const { return !noexpirer;  }
     87
     88    bool SetVerbose(void)           const { return setverbose;  }
     89    bool Reschedule(void)           const { return resched;     }
     90    bool ClearSettingsCache(void)   const { return clearsettingscache; }
     91    bool WantUPnPRebuild(void)      const { return wantupnprebuild; }
     92
     93    bool    HasInvalidPreviewGenerationParams(void) const
     94    {
     95        return ((previewFrameNumber >= -1) || previewSeconds >= -1) &&
     96            (!chanid || !starttime.isValid()) && infile.isEmpty();
     97    }
     98
     99    bool    HasBackendCommand(void) const
     100    {
     101        return
     102            !eventString.isEmpty()    || wantupnprebuild       ||
     103            setverbose                || clearsettingscache    ||
     104            printsched                || testsched             ||
     105            resched                   || !printexpire.isEmpty() ||
     106            (previewFrameNumber >= -1) || (previewSeconds >= -1);
     107    }
     108
    40109    bool    WantsToExit(void) const { return wantsToExit; }
    41110
    42111  private:
    43     int                   parseTypes;
     112    uint64_t              parseTypes;
    44113
    45114    QMap<QString,QString> settingsOverride;
    46115    QStringList           settingsQuery;
     116
    47117    QString               display;
    48118    QString               geometry;
     119    QString               logfile;
     120    QString               pidfile;
     121    QString               infile;
     122    QString               outfile;
     123    QString               newverbose;
     124    QString               username;
     125    QString               printexpire;
     126    QString               eventString;
    49127
     128    QSize                 previewSize;
     129    QDateTime             starttime;
     130
     131    uint                  chanid;
     132    long long             previewFrameNumber;
     133    long long             previewSeconds;
     134
     135    bool                  daemonize;
     136    bool                  printsched;
     137    bool                  testsched;
     138    bool                  setverbose;
     139    bool                  resched;
     140    bool                  nosched;
     141    bool                  noupnp;
     142    bool                  nojobqueue;
     143    bool                  nohousekeeper;
     144    bool                  noexpirer;
     145    bool                  clearsettingscache;
     146    bool                  wantupnprebuild;
    50147    bool                  wantsToExit;
    51148};
  • libs/libmyth/mythcontext.cpp

     
    8282    void LoadLogSettings(void);
    8383    void LoadDatabaseSettings(void);
    8484
    85     bool LoadSettingsFile(void);
    86     bool WriteSettingsFile(const DatabaseParams &params,
    87                            bool overwrite = false);
    88     bool FindSettingsProbs(void);
    89 
    9085    bool    PromptForDatabaseParams(const QString &error);
    9186    QString TestDBconnection(void);
    9287    void    SilenceDBerrors(void);
     
    514509 */
    515510void MythContextPrivate::LoadDatabaseSettings(void)
    516511{
    517     if (!LoadSettingsFile())
    518     {
    519         VERBOSE(VB_IMPORTANT, "Unable to read configuration file mysql.txt");
     512    m_database->LoadDatabaseParamsFromDisk(m_DBparams, true);
     513    m_database->SetDatabaseParams(m_DBparams);
    520514
    521         // Sensible connection defaults.
    522         m_DBparams.dbHostName    = "localhost";
    523         m_DBparams.dbHostPing    = true;
    524         m_DBparams.dbPort        = 0;
    525         m_DBparams.dbUserName    = "mythtv";
    526         m_DBparams.dbPassword    = "mythtv";
    527         m_DBparams.dbName        = "mythconverg";
    528         m_DBparams.dbType        = "QMYSQL3";
    529         m_DBparams.localEnabled  = false;
    530         m_DBparams.localHostName = "my-unique-identifier-goes-here";
    531         m_DBparams.wolEnabled    = false;
    532         m_DBparams.wolReconnect  = 0;
    533         m_DBparams.wolRetry      = 5;
    534         m_DBparams.wolCommand    = "echo 'WOLsqlServerCommand not set'";
    535         m_database->SetDatabaseParams(m_DBparams);
    536     }
    537 
    538     // Even if we have loaded the settings file, it may be incomplete,
    539     // so we check for missing values and warn user
    540     FindSettingsProbs();
    541 
    542515    m_localhostname = m_DBparams.localHostName;
    543516    if (m_localhostname.isEmpty() ||
    544517        m_localhostname == "my-unique-identifier-goes-here")
     
    559532    m_database->SetLocalHostname(m_localhostname);
    560533}
    561534
    562 /**
    563  * Load mysql.txt and parse its values into m_DBparams
    564  */
    565 bool MythContextPrivate::LoadSettingsFile(void)
    566 {
    567     Settings *oldsettings = m_database->GetOldSettings();
    568 
    569     if (!oldsettings->LoadSettingsFiles("mysql.txt", GetInstallPrefix(),
    570                                         GetConfDir()))
    571         return false;
    572 
    573     m_DBparams.dbHostName = oldsettings->GetSetting("DBHostName");
    574     m_DBparams.dbHostPing = oldsettings->GetSetting("DBHostPing") != "no";
    575     m_DBparams.dbPort     = oldsettings->GetNumSetting("DBPort");
    576     m_DBparams.dbUserName = oldsettings->GetSetting("DBUserName");
    577     m_DBparams.dbPassword = oldsettings->GetSetting("DBPassword");
    578     m_DBparams.dbName     = oldsettings->GetSetting("DBName");
    579     m_DBparams.dbType     = oldsettings->GetSetting("DBType");
    580 
    581     m_DBparams.localHostName = oldsettings->GetSetting("LocalHostName");
    582     m_DBparams.localEnabled  = m_DBparams.localHostName.length() > 0;
    583 
    584     m_DBparams.wolReconnect
    585         = oldsettings->GetNumSetting("WOLsqlReconnectWaitTime");
    586     m_DBparams.wolEnabled = m_DBparams.wolReconnect > 0;
    587 
    588     m_DBparams.wolRetry   = oldsettings->GetNumSetting("WOLsqlConnectRetry");
    589     m_DBparams.wolCommand = oldsettings->GetSetting("WOLsqlCommand");
    590     m_database->SetDatabaseParams(m_DBparams);
    591 
    592     return true;
    593 }
    594 
    595 bool MythContextPrivate::WriteSettingsFile(const DatabaseParams &params,
    596                                            bool overwrite)
    597 {
    598     QString path = GetConfDir() + "/mysql.txt";
    599     QFile   * f  = new QFile(path);
    600 
    601     if (!overwrite && f->exists())
    602     {
    603         return false;
    604     }
    605 
    606     QString dirpath = GetConfDir();
    607     QDir createDir(dirpath);
    608 
    609     if (!createDir.exists())
    610     {
    611         if (!createDir.mkdir(dirpath))
    612         {
    613             VERBOSE(VB_IMPORTANT, QString("Could not create %1").arg(dirpath));
    614             return false;
    615         }
    616     }
    617 
    618     if (!f->open(QIODevice::WriteOnly))
    619     {
    620         VERBOSE(VB_IMPORTANT, QString("Could not open settings file %1 "
    621                                       "for writing").arg(path));
    622         return false;
    623     }
    624 
    625     VERBOSE(VB_IMPORTANT, QString("Writing settings file %1").arg(path));
    626     QTextStream s(f);
    627     s << "DBHostName=" << params.dbHostName << endl;
    628 
    629     s << "\n"
    630       << "# By default, Myth tries to ping the DB host to see if it exists.\n"
    631       << "# If your DB host or network doesn't accept pings, set this to no:\n"
    632       << "#\n";
    633 
    634     if (params.dbHostPing)
    635         s << "#DBHostPing=no" << endl << endl;
    636     else
    637         s << "DBHostPing=no" << endl << endl;
    638 
    639     if (params.dbPort)
    640         s << "DBPort=" << params.dbPort << endl;
    641 
    642     s << "DBUserName=" << params.dbUserName << endl
    643       << "DBPassword=" << params.dbPassword << endl
    644       << "DBName="     << params.dbName     << endl
    645       << "DBType="     << params.dbType     << endl
    646       << endl
    647       << "# Set the following if you want to use something other than this\n"
    648       << "# machine's real hostname for identifying settings in the database.\n"
    649       << "# This is useful if your hostname changes often, as otherwise you\n"
    650       << "# will need to reconfigure mythtv every time.\n"
    651       << "# NO TWO HOSTS MAY USE THE SAME VALUE\n"
    652       << "#\n";
    653 
    654     if (params.localEnabled)
    655         s << "LocalHostName=" << params.localHostName << endl;
    656     else
    657         s << "#LocalHostName=my-unique-identifier-goes-here\n";
    658 
    659     s << endl
    660       << "# If you want your frontend to be able to wake your MySQL server\n"
    661       << "# using WakeOnLan, have a look at the following settings:\n"
    662       << "#\n"
    663       << "#\n"
    664       << "# The time the frontend waits (in seconds) between reconnect tries.\n"
    665       << "# This should be the rough time your MySQL server needs for startup\n"
    666       << "#\n";
    667 
    668     if (params.wolEnabled)
    669         s << "WOLsqlReconnectWaitTime=" << params.wolReconnect << endl;
    670     else
    671         s << "#WOLsqlReconnectWaitTime=0\n";
    672 
    673     s << "#\n"
    674       << "#\n"
    675       << "# This is the number of retries to wake the MySQL server\n"
    676       << "# until the frontend gives up\n"
    677       << "#\n";
    678 
    679     if (params.wolEnabled)
    680         s << "WOLsqlConnectRetry=" << params.wolRetry << endl;
    681     else
    682         s << "#WOLsqlConnectRetry=5\n";
    683 
    684     s << "#\n"
    685       << "#\n"
    686       << "# This is the command executed to wake your MySQL server.\n"
    687       << "#\n";
    688 
    689     if (params.wolEnabled)
    690         s << "WOLsqlCommand=" << params.wolCommand << endl;
    691     else
    692         s << "#WOLsqlCommand=echo 'WOLsqlServerCommand not set'\n";
    693 
    694     f->close();
    695     return true;
    696 }
    697 
    698 bool MythContextPrivate::FindSettingsProbs(void)
    699 {
    700     bool problems = false;
    701 
    702     if (m_DBparams.dbHostName.isEmpty())
    703     {
    704         problems = true;
    705         VERBOSE(VB_IMPORTANT, "DBHostName is not set in mysql.txt");
    706         VERBOSE(VB_IMPORTANT, "Assuming localhost");
    707         m_DBparams.dbHostName = "localhost";
    708     }
    709     if (m_DBparams.dbUserName.isEmpty())
    710     {
    711         problems = true;
    712         VERBOSE(VB_IMPORTANT, "DBUserName is not set in mysql.txt");
    713     }
    714     if (m_DBparams.dbPassword.isEmpty())
    715     {
    716         problems = true;
    717         VERBOSE(VB_IMPORTANT, "DBPassword is not set in mysql.txt");
    718     }
    719     if (m_DBparams.dbName.isEmpty())
    720     {
    721         problems = true;
    722         VERBOSE(VB_IMPORTANT, "DBName is not set in mysql.txt");
    723     }
    724     m_database->SetDatabaseParams(m_DBparams);
    725     return problems;
    726 }
    727 
    728535bool MythContextPrivate::PromptForDatabaseParams(const QString &error)
    729536{
    730537    bool accepted = false;
     
    1039846    switch (selected)
    1040847    {
    1041848        case kDialogCodeButton0:
    1042             WriteSettingsFile(m_DBparams, true);
     849            MythDB::SaveDatabaseParamsToDisk(m_DBparams, GetConfDir(), true);
    1043850            // User prefers mysql.txt, so throw away default UPnP backend:
    1044851            m_XML->SetValue(kDefaultUSN, "");
    1045852            m_XML->Save();
     
    24132220          params.wolRetry      != cur_params.wolRetry     ||
    24142221          params.wolCommand    != cur_params.wolCommand)))
    24152222    {
    2416         ret = d->WriteSettingsFile(params, true);
     2223        ret = MythDB::SaveDatabaseParamsToDisk(params, GetConfDir(), true);
    24172224        if (ret)
    24182225        {
    24192226            // Save the new settings:
  • libs/libmythupnp/upnp.h

     
    125125                 UPnp();
    126126        virtual ~UPnp();
    127127
    128         void SetConfiguration( Configuration *pConfig );
     128        static void SetConfiguration( Configuration *pConfig );
    129129
    130130        bool Initialize( int nServicePort, HttpServer *pHttpServer );
    131131        bool Initialize( QStringList &sIPAddrList, int nServicePort, HttpServer *pHttpServer );
  • libs/libmythdb/mythdb.cpp

     
    11#include <vector>
    22using namespace std;
    33
     4#include <QReadWriteLock>
     5#include <QSqlError>
    46#include <QMutex>
    5 #include <QReadWriteLock>
     7#include <QFile>
    68#include <QHash>
    7 #include <QSqlError>
     9#include <QDir>
    810
    911#include "mythdb.h"
    1012#include "mythdbcon.h"
    1113#include "mythverbose.h"
    1214#include "oldsettings.h"
     15#include "mythdirs.h"
    1316
    1417static MythDB *mythdb = NULL;
    1518static QMutex dbLock;
     
    822825    d->useSettingsCache = activate;
    823826    ClearSettingsCache();
    824827}
     828
     829bool MythDB::LoadDatabaseParamsFromDisk(
     830    DatabaseParams &params, bool sanitize)
     831{
     832    Settings settings;
     833    if (settings.LoadSettingsFiles(
     834            "mysql.txt", GetInstallPrefix(), GetConfDir()))
     835    {
     836        params.dbHostName = settings.GetSetting("DBHostName");
     837        params.dbHostPing = settings.GetSetting("DBHostPing") != "no";
     838        params.dbPort     = settings.GetNumSetting("DBPort");
     839        params.dbUserName = settings.GetSetting("DBUserName");
     840        params.dbPassword = settings.GetSetting("DBPassword");
     841        params.dbName     = settings.GetSetting("DBName");
     842        params.dbType     = settings.GetSetting("DBType");
     843
     844        params.localHostName = settings.GetSetting("LocalHostName");
     845        params.localEnabled  = !params.localHostName.isEmpty();
     846
     847        params.wolReconnect  =
     848            settings.GetNumSetting("WOLsqlReconnectWaitTime");
     849        params.wolEnabled = params.wolReconnect > 0;
     850
     851        params.wolRetry   = settings.GetNumSetting("WOLsqlConnectRetry");
     852        params.wolCommand = settings.GetSetting("WOLsqlCommand");
     853    }
     854    else if (sanitize)
     855    {
     856        VERBOSE(VB_IMPORTANT, "Unable to read configuration file mysql.txt");
     857
     858        // Sensible connection defaults.
     859        params.dbHostName    = "localhost";
     860        params.dbHostPing    = true;
     861        params.dbPort        = 0;
     862        params.dbUserName    = "mythtv";
     863        params.dbPassword    = "mythtv";
     864        params.dbName        = "mythconverg";
     865        params.dbType        = "QMYSQL3";
     866        params.localEnabled  = false;
     867        params.localHostName = "my-unique-identifier-goes-here";
     868        params.wolEnabled    = false;
     869        params.wolReconnect  = 0;
     870        params.wolRetry      = 5;
     871        params.wolCommand    = "echo 'WOLsqlServerCommand not set'";
     872    }
     873    else
     874    {
     875        return false;
     876    }
     877
     878    // Print some warnings if things look fishy..
     879
     880    if (params.dbHostName.isEmpty())
     881    {
     882        VERBOSE(VB_IMPORTANT, "DBHostName is not set in mysql.txt");
     883        VERBOSE(VB_IMPORTANT, "Assuming localhost");
     884    }
     885    if (params.dbUserName.isEmpty())
     886        VERBOSE(VB_IMPORTANT, "DBUserName is not set in mysql.txt");
     887    if (params.dbPassword.isEmpty())
     888        VERBOSE(VB_IMPORTANT, "DBPassword is not set in mysql.txt");
     889    if (params.dbName.isEmpty())
     890        VERBOSE(VB_IMPORTANT, "DBName is not set in mysql.txt");
     891
     892    // If sanitize set, replace empty dbHostName with "localhost"
     893    if (sanitize && params.dbHostName.isEmpty())
     894        params.dbHostName = "localhost";
     895
     896    return true;
     897}
     898
     899bool MythDB::SaveDatabaseParamsToDisk(
     900    const DatabaseParams &params, const QString &confdir, bool overwrite)
     901{
     902    QString path = confdir + "/mysql.txt";
     903    QFile   * f  = new QFile(path);
     904
     905    if (!overwrite && f->exists())
     906    {
     907        return false;
     908    }
     909
     910    QString dirpath = confdir;
     911    QDir createDir(dirpath);
     912
     913    if (!createDir.exists())
     914    {
     915        if (!createDir.mkdir(dirpath))
     916        {
     917            VERBOSE(VB_IMPORTANT, QString("Could not create %1").arg(dirpath));
     918            return false;
     919        }
     920    }
     921
     922    if (!f->open(QIODevice::WriteOnly))
     923    {
     924        VERBOSE(VB_IMPORTANT, QString("Could not open settings file %1 "
     925                                      "for writing").arg(path));
     926        return false;
     927    }
     928
     929    VERBOSE(VB_IMPORTANT, QString("Writing settings file %1").arg(path));
     930    QTextStream s(f);
     931    s << "DBHostName=" << params.dbHostName << endl;
     932
     933    s << "\n"
     934      << "# By default, Myth tries to ping the DB host to see if it exists.\n"
     935      << "# If your DB host or network doesn't accept pings, set this to no:\n"
     936      << "#\n";
     937
     938    if (params.dbHostPing)
     939        s << "#DBHostPing=no" << endl << endl;
     940    else
     941        s << "DBHostPing=no" << endl << endl;
     942
     943    if (params.dbPort)
     944        s << "DBPort=" << params.dbPort << endl;
     945
     946    s << "DBUserName=" << params.dbUserName << endl
     947      << "DBPassword=" << params.dbPassword << endl
     948      << "DBName="     << params.dbName     << endl
     949      << "DBType="     << params.dbType     << endl
     950      << endl
     951      << "# Set the following if you want to use something other than this\n"
     952      << "# machine's real hostname for identifying settings in the database.\n"
     953      << "# This is useful if your hostname changes often, as otherwise you\n"
     954      << "# will need to reconfigure mythtv every time.\n"
     955      << "# NO TWO HOSTS MAY USE THE SAME VALUE\n"
     956      << "#\n";
     957
     958    if (params.localEnabled)
     959        s << "LocalHostName=" << params.localHostName << endl;
     960    else
     961        s << "#LocalHostName=my-unique-identifier-goes-here\n";
     962
     963    s << endl
     964      << "# If you want your frontend to be able to wake your MySQL server\n"
     965      << "# using WakeOnLan, have a look at the following settings:\n"
     966      << "#\n"
     967      << "#\n"
     968      << "# The time the frontend waits (in seconds) between reconnect tries.\n"
     969      << "# This should be the rough time your MySQL server needs for startup\n"
     970      << "#\n";
     971
     972    if (params.wolEnabled)
     973        s << "WOLsqlReconnectWaitTime=" << params.wolReconnect << endl;
     974    else
     975        s << "#WOLsqlReconnectWaitTime=0\n";
     976
     977    s << "#\n"
     978      << "#\n"
     979      << "# This is the number of retries to wake the MySQL server\n"
     980      << "# until the frontend gives up\n"
     981      << "#\n";
     982
     983    if (params.wolEnabled)
     984        s << "WOLsqlConnectRetry=" << params.wolRetry << endl;
     985    else
     986        s << "#WOLsqlConnectRetry=5\n";
     987
     988    s << "#\n"
     989      << "#\n"
     990      << "# This is the command executed to wake your MySQL server.\n"
     991      << "#\n";
     992
     993    if (params.wolEnabled)
     994        s << "WOLsqlCommand=" << params.wolCommand << endl;
     995    else
     996        s << "#WOLsqlCommand=echo 'WOLsqlServerCommand not set'\n";
     997
     998    f->close();
     999    return true;
     1000}
  • libs/libmythdb/mythdb.h

     
    7575    static void destroyMythDB();
    7676    static QString toCommaList(const QMap<QString, QVariant> &bindings,
    7777                               uint indent = 0, uint softMaxColumn = 80);
     78
     79    static bool LoadDatabaseParamsFromDisk(
     80        DatabaseParams &params, bool sanitize);
     81    static bool SaveDatabaseParamsToDisk(
     82        const DatabaseParams &params, const QString &confdir, bool overwrite);
     83
    7884  protected:
    7985    MythDB();
    8086   ~MythDB();
  • programs/mythbackend/backendcontext.h

     
     1#include <QString>
     2#include <QMap>
     3
     4class EncoderLink;
     5class AutoExpire;
     6class Scheduler;
     7class JobQueue;
     8class HouseKeeper;
     9class MediaServer;
     10
     11extern QMap<int, EncoderLink *> tvList;
     12extern AutoExpire  *expirer;
     13extern Scheduler   *sched;
     14extern JobQueue    *jobqueue;
     15extern HouseKeeper *housekeeping;
     16extern MediaServer *g_pUPnp;
     17extern QString      pidfile;
     18extern QString      logfile;
     19
     20class BackendContext
     21{
     22   
     23};
  • programs/mythbackend/mediaserver.cpp

     
    99//////////////////////////////////////////////////////////////////////////////
    1010
    1111#include "mediaserver.h"
     12#include "httpconfig.h"
    1213#include "mythxml.h"
    1314#include "mythdirs.h"
    1415
     
    2829//
    2930//////////////////////////////////////////////////////////////////////////////
    3031
    31 MediaServer::MediaServer( bool bIsMaster, bool bDisableUPnp /* = FALSE */ )
     32MediaServer::MediaServer(void) :
     33    m_pUPnpCDS(NULL), m_pUPnpCMGR(NULL), upnpMedia(NULL),
     34    m_sSharePath(GetShareDir())
    3235{
    33     VERBOSE(VB_UPNP, QString("MediaServer::Begin"));
     36    VERBOSE(VB_UPNP, "MediaServer:ctor:Begin");
    3437
    3538    // ----------------------------------------------------------------------
    3639    // Initialize Configuration class (Database for Servers)
     
    4346    // ----------------------------------------------------------------------
    4447
    4548    int     nPort = g_pConfig->GetValue( "BackendStatusPort", 6544 );
    46     QString sIP   = g_pConfig->GetValue( "BackendServerIP"  , ""   );
    4749
    48     if (sIP.isEmpty())
    49     {
    50         VERBOSE(VB_IMPORTANT,
    51                 "MediaServer:: No BackendServerIP Address defined");
    52         m_pHttpServer = NULL;
    53         return;
    54     }
    55 
    56 
    5750    m_pHttpServer = new HttpServer();
    5851
    5952    if (!m_pHttpServer->listen(QHostAddress::Any, nPort))
     
    6558        return;
    6659    }
    6760
    68     m_sSharePath = GetShareDir();
    6961    m_pHttpServer->m_sSharePath = m_sSharePath;
    7062
     63    m_pHttpServer->RegisterExtension(new HttpConfig());
     64
     65    VERBOSE(VB_UPNP, "MediaServer:ctor:End");
     66}
     67
     68void MediaServer::Init(bool bIsMaster, bool bDisableUPnp /* = FALSE */)
     69{
     70    VERBOSE(VB_UPNP, "MediaServer:Init:Begin");
     71
     72    int     nPort     = g_pConfig->GetValue( "BackendStatusPort", 6544 );
    7173    QString sFileName = g_pConfig->GetValue( "upnpDescXmlPath",
    7274                                                m_sSharePath );
    7375    QString sDeviceType;
     
    100102
    101103    VERBOSE(VB_UPNP, "MediaServer::Registering MythXML Service." );
    102104
    103     m_pHttpServer->RegisterExtension( new MythXML( pMythDevice , m_sSharePath));
     105    if (m_pHttpServer)
     106        m_pHttpServer->RegisterExtension(
     107            new MythXML( pMythDevice , m_sSharePath));
    104108
     109    QString sIP = g_pConfig->GetValue( "BackendServerIP"  , ""   );
     110    if (sIP.isEmpty())
     111    {
     112        VERBOSE(VB_IMPORTANT,
     113                "MediaServer:: No BackendServerIP Address defined - "
     114                "Disabling UPnP");
     115        return;
     116    }
     117
    105118    if (sIP == "localhost" || sIP.startsWith("127."))
    106119    {
    107120        VERBOSE(VB_IMPORTANT, "MediaServer:: Loopback address specified - "
     
    189202
    190203    }
    191204
    192     VERBOSE(VB_UPNP, QString( "MediaServer::End" ));
     205    VERBOSE(VB_UPNP, "MediaServer:Init:End");
    193206}
    194207
    195208//////////////////////////////////////////////////////////////////////////////
  • programs/mythbackend/mythsettings.h

     
     1// -*- Mode: c++ -*-
     2
     3#ifndef _MYTHSETTINGS_H_
     4#define _MYTHSETTINGS_H_
     5
     6#include <QStringList>
     7#include <QMap>
     8
     9class MythSettingBase
     10{
     11  public:
     12    MythSettingBase() {}
     13    virtual ~MythSettingBase() {}
     14    virtual QString ToHTML(uint) const { return QString(); }
     15};
     16typedef QList<MythSettingBase*> MythSettingList;
     17
     18class MythSettingGroup : public MythSettingBase
     19{
     20  public:
     21    MythSettingGroup(QString hlabel, QString ulabel,
     22                     QString script = "") :
     23        human_label(hlabel), unique_label(ulabel), ecma_script(script) {}
     24
     25    QString ToHTML(uint) const;
     26
     27  public:
     28    QString human_label;
     29    QString unique_label; ///< div name for stylesheets & javascript
     30    MythSettingList settings;
     31    QString ecma_script;
     32};
     33
     34class MythSetting : public MythSettingBase
     35{
     36  public:
     37    typedef enum {
     38        kFile,
     39        kHost,
     40        kGlobal,
     41        kInvalidSettingType,
     42    } SettingType;
     43
     44    typedef enum {
     45        kInteger,
     46        kUnsignedInteger,
     47        kIntegerRange,
     48        kCheckBox,
     49        kSelect,   ///< list where only data_list are valid
     50        kComboBox, ///< list where user input is allowed
     51        kTVFormat,
     52        kFrequencyTable,
     53        kFloat,
     54        kIPAddress,
     55        kLocalIPAddress,
     56        kString,
     57        kTimeOfDay,
     58        kOther,
     59        kInvalidDataType,
     60    } DataType;
     61
     62    MythSetting(QString _value, QString _default_data, SettingType _stype,
     63                QString _label, QString _help_text, DataType _dtype) :
     64        value(_value), data(_default_data), default_data(_default_data),
     65        stype(_stype), label(_label), help_text(_help_text), dtype(_dtype)
     66    {
     67    }
     68
     69    MythSetting(QString _value, QString _default_data, SettingType _stype,
     70            QString _label, QString _help_text, DataType _dtype,
     71            QStringList _data_list, QStringList _display_list) :
     72        value(_value), data(_default_data), default_data(_default_data),
     73        stype(_stype), label(_label), help_text(_help_text), dtype(_dtype),
     74        data_list(_data_list), display_list(_display_list)
     75    {
     76    }
     77
     78    MythSetting(QString _value, QString _default_data, SettingType _stype,
     79                QString _label, QString _help_text, DataType _dtype,
     80                long long _range_min, long long _range_max) :
     81        value(_value), data(_default_data), default_data(_default_data),
     82        stype(_stype), label(_label), help_text(_help_text), dtype(_dtype),
     83        range_min(_range_min), range_max(_range_max)
     84    {
     85    }
     86
     87    MythSetting(QString _value, QString _default_data, SettingType _stype,
     88                QString _label, QString _help_text, DataType _dtype,
     89                QStringList _data_list, QStringList _display_list,
     90                long long _range_min, long long _range_max) :
     91        value(_value), data(_default_data), default_data(_default_data),
     92        stype(_stype), label(_label), help_text(_help_text), dtype(_dtype),
     93        data_list(_data_list), display_list(_display_list),
     94        range_min(_range_min), range_max(_range_max)
     95    {
     96    }
     97
     98    QString ToHTML(uint) const;
     99
     100  public:
     101    QString value;
     102    QString data;
     103    QString default_data;
     104    SettingType stype;
     105    QString label;
     106    QString help_text;
     107    DataType dtype;
     108    QStringList data_list;
     109    QStringList display_list;
     110    long long range_min;
     111    long long range_max;
     112};
     113
     114bool parse_settings(MythSettingList &settings, const QString &filename);
     115bool load_settings(MythSettingList &settings, const QString &hostname);
     116
     117#endif
  • programs/mythbackend/moc_mainserver.cpp

     
     1/****************************************************************************
     2** Meta object code from reading C++ file 'mainserver.h'
     3**
     4** Created: Sun Mar 28 11:53:36 2010
     5**      by: The Qt Meta Object Compiler version 61 (Qt 4.5.2)
     6**
     7** WARNING! All changes made in this file will be lost!
     8*****************************************************************************/
     9
     10#include "mainserver.h"
     11#if !defined(Q_MOC_OUTPUT_REVISION)
     12#error "The header file 'mainserver.h' doesn't include <QObject>."
     13#elif Q_MOC_OUTPUT_REVISION != 61
     14#error "This file was generated using the moc from 4.5.2. It"
     15#error "cannot be used with the include files from this version of Qt."
     16#error "(The moc has changed too much.)"
     17#endif
     18
     19QT_BEGIN_MOC_NAMESPACE
     20static const uint qt_meta_data_MainServer[] = {
     21
     22 // content:
     23       2,       // revision
     24       0,       // classname
     25       0,    0, // classinfo
     26       4,   12, // methods
     27       0,    0, // properties
     28       0,    0, // enums/sets
     29       0,    0, // constructors
     30
     31 // slots: signature, parameters, type, tag, flags
     32      12,   11,   11,   11, 0x09,
     33      31,   11,   11,   11, 0x09,
     34      52,   11,   11,   11, 0x09,
     35      71,   11,   11,   11, 0x08,
     36
     37       0        // eod
     38};
     39
     40static const char qt_meta_stringdata_MainServer[] = {
     41    "MainServer\0\0reconnectTimeout()\0"
     42    "deferredDeleteSlot()\0autoexpireUpdate()\0"
     43    "newConnection(MythSocket*)\0"
     44};
     45
     46const QMetaObject MainServer::staticMetaObject = {
     47    { &QObject::staticMetaObject, qt_meta_stringdata_MainServer,
     48      qt_meta_data_MainServer, 0 }
     49};
     50
     51const QMetaObject *MainServer::metaObject() const
     52{
     53    return &staticMetaObject;
     54}
     55
     56void *MainServer::qt_metacast(const char *_clname)
     57{
     58    if (!_clname) return 0;
     59    if (!strcmp(_clname, qt_meta_stringdata_MainServer))
     60        return static_cast<void*>(const_cast< MainServer*>(this));
     61    if (!strcmp(_clname, "MythSocketCBs"))
     62        return static_cast< MythSocketCBs*>(const_cast< MainServer*>(this));
     63    return QObject::qt_metacast(_clname);
     64}
     65
     66int MainServer::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
     67{
     68    _id = QObject::qt_metacall(_c, _id, _a);
     69    if (_id < 0)
     70        return _id;
     71    if (_c == QMetaObject::InvokeMetaMethod) {
     72        switch (_id) {
     73        case 0: reconnectTimeout(); break;
     74        case 1: deferredDeleteSlot(); break;
     75        case 2: autoexpireUpdate(); break;
     76        case 3: newConnection((*reinterpret_cast< MythSocket*(*)>(_a[1]))); break;
     77        default: ;
     78        }
     79        _id -= 4;
     80    }
     81    return _id;
     82}
     83QT_END_MOC_NAMESPACE
  • programs/mythbackend/httpconfig.h

     
     1// -*- Mode: c++ -*-
     2
     3#ifndef _HTTPCONFIG_H_
     4#define _HTTPCONFIG_H_
     5
     6#include "httpserver.h"
     7#include "mythsettings.h"
     8
     9class QTextStream;
     10
     11class HttpConfig : public HttpServerExtension
     12{
     13  public:
     14    HttpConfig();
     15    virtual ~HttpConfig();
     16
     17    bool ProcessRequest(HttpWorkerThread *pThread, HTTPRequest *pRequest);
     18
     19  private:
     20    void PrintHeader(QTextStream&);
     21    void PrintFooter(QTextStream&);
     22
     23    void ParseDatabaseSettings(void);
     24    void ParseGeneralSettings(void);
     25
     26    bool LoadSettings(MythSettingList&, const QString &hostname);
     27    void PrintSettings(QTextStream&, const MythSettingList&);
     28
     29    MythSettingList database_settings;
     30    MythSettingList general_settings;
     31};
     32
     33#endif
  • programs/mythbackend/main_helpers.h

     
     1// C++ headers
     2#include <iostream>
     3#include <fstream>
     4using namespace std;
     5
     6class MythCommandLineParser;
     7class QString;
     8class QSize;
     9
     10bool setupTVs(bool ismaster, bool &error);
     11bool setup_context(const MythCommandLineParser &cmdline);
     12void cleanup(void);
     13int log_rotate(int report_error);
     14void log_rotate_handler(int);
     15void upnp_rebuild(int);
     16int preview_helper(const QString &chanid, const QString &starttime,
     17                   long long previewFrameNumber, long long previewSeconds,
     18                   const QSize &previewSize,
     19                   const QString &infile, const QString &outfile);
     20void showUsage(const MythCommandLineParser &cmdlineparser,
     21               const QString &version);
     22void setupLogfile(void);
     23bool openPidfile(ofstream &pidfs, const QString &pidfilename);
     24bool setUser(const QString &username);
     25int handle_command(const MythCommandLineParser &cmdline);
     26int connect_to_master(void);
     27int setup_basics(const MythCommandLineParser &cmdline);
     28void print_warnings(const MythCommandLineParser &cmdline);
     29int run_backend(const MythCommandLineParser &cmdline);
     30
     31namespace
     32{
     33    class CleanupGuard
     34    {
     35      public:
     36        typedef void (*CleanupFunc)();
     37
     38      public:
     39        CleanupGuard(CleanupFunc cleanFunction) :
     40            m_cleanFunction(cleanFunction) {}
     41
     42        ~CleanupGuard()
     43        {
     44            m_cleanFunction();
     45        }
     46
     47      private:
     48        CleanupFunc m_cleanFunction;
     49    };
     50}
     51
  • programs/mythbackend/moc_scheduler.cpp

     
     1/****************************************************************************
     2** Meta object code from reading C++ file 'scheduler.h'
     3**
     4** Created: Sun Mar 28 11:53:37 2010
     5**      by: The Qt Meta Object Compiler version 61 (Qt 4.5.2)
     6**
     7** WARNING! All changes made in this file will be lost!
     8*****************************************************************************/
     9
     10#include "scheduler.h"
     11#if !defined(Q_MOC_OUTPUT_REVISION)
     12#error "The header file 'scheduler.h' doesn't include <QObject>."
     13#elif Q_MOC_OUTPUT_REVISION != 61
     14#error "This file was generated using the moc from 4.5.2. It"
     15#error "cannot be used with the include files from this version of Qt."
     16#error "(The moc has changed too much.)"
     17#endif
     18
     19QT_BEGIN_MOC_NAMESPACE
     20static const uint qt_meta_data_Scheduler[] = {
     21
     22 // content:
     23       2,       // revision
     24       0,       // classname
     25       0,    0, // classinfo
     26       0,    0, // methods
     27       0,    0, // properties
     28       0,    0, // enums/sets
     29       0,    0, // constructors
     30
     31       0        // eod
     32};
     33
     34static const char qt_meta_stringdata_Scheduler[] = {
     35    "Scheduler\0"
     36};
     37
     38const QMetaObject Scheduler::staticMetaObject = {
     39    { &QObject::staticMetaObject, qt_meta_stringdata_Scheduler,
     40      qt_meta_data_Scheduler, 0 }
     41};
     42
     43const QMetaObject *Scheduler::metaObject() const
     44{
     45    return &staticMetaObject;
     46}
     47
     48void *Scheduler::qt_metacast(const char *_clname)
     49{
     50    if (!_clname) return 0;
     51    if (!strcmp(_clname, qt_meta_stringdata_Scheduler))
     52        return static_cast<void*>(const_cast< Scheduler*>(this));
     53    return QObject::qt_metacast(_clname);
     54}
     55
     56int Scheduler::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
     57{
     58    _id = QObject::qt_metacall(_c, _id, _a);
     59    if (_id < 0)
     60        return _id;
     61    return _id;
     62}
     63QT_END_MOC_NAMESPACE
  • programs/mythbackend/moc_autoexpire.cpp

     
     1/****************************************************************************
     2** Meta object code from reading C++ file 'autoexpire.h'
     3**
     4** Created: Sun Mar 28 11:53:36 2010
     5**      by: The Qt Meta Object Compiler version 61 (Qt 4.5.2)
     6**
     7** WARNING! All changes made in this file will be lost!
     8*****************************************************************************/
     9
     10#include "autoexpire.h"
     11#if !defined(Q_MOC_OUTPUT_REVISION)
     12#error "The header file 'autoexpire.h' doesn't include <QObject>."
     13#elif Q_MOC_OUTPUT_REVISION != 61
     14#error "This file was generated using the moc from 4.5.2. It"
     15#error "cannot be used with the include files from this version of Qt."
     16#error "(The moc has changed too much.)"
     17#endif
     18
     19QT_BEGIN_MOC_NAMESPACE
     20static const uint qt_meta_data_AutoExpire[] = {
     21
     22 // content:
     23       2,       // revision
     24       0,       // classname
     25       0,    0, // classinfo
     26       0,    0, // methods
     27       0,    0, // properties
     28       0,    0, // enums/sets
     29       0,    0, // constructors
     30
     31       0        // eod
     32};
     33
     34static const char qt_meta_stringdata_AutoExpire[] = {
     35    "AutoExpire\0"
     36};
     37
     38const QMetaObject AutoExpire::staticMetaObject = {
     39    { &QObject::staticMetaObject, qt_meta_stringdata_AutoExpire,
     40      qt_meta_data_AutoExpire, 0 }
     41};
     42
     43const QMetaObject *AutoExpire::metaObject() const
     44{
     45    return &staticMetaObject;
     46}
     47
     48void *AutoExpire::qt_metacast(const char *_clname)
     49{
     50    if (!_clname) return 0;
     51    if (!strcmp(_clname, qt_meta_stringdata_AutoExpire))
     52        return static_cast<void*>(const_cast< AutoExpire*>(this));
     53    return QObject::qt_metacast(_clname);
     54}
     55
     56int AutoExpire::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
     57{
     58    _id = QObject::qt_metacall(_c, _id, _a);
     59    if (_id < 0)
     60        return _id;
     61    return _id;
     62}
     63QT_END_MOC_NAMESPACE
  • programs/mythbackend/main.cpp

     
    1 // POSIX headers
    2 #include <sys/time.h>     // for setpriority
    3 #include <unistd.h>
    4 #include <sys/types.h>
    5 #include <sys/stat.h>
    6 #include <fcntl.h>
    7 #include <libgen.h>
    8 #include <signal.h>
    9 #include <pwd.h>
    101
    11 #include "mythconfig.h"
    12 #if CONFIG_DARWIN
    13     #include <sys/aio.h>    // O_SYNC
    14 #endif
    152
    16 // C headers
    17 #include <cstdlib>
    18 #include <cerrno>
    19 
    20 // C++ headers
    21 #include <iostream>
    22 #include <fstream>
    23 using namespace std;
    24 
    253#ifndef _WIN32
    264#include <QCoreApplication>
    275#else
     
    5634#include "previewgenerator.h"
    5735#include "mythcommandlineparser.h"
    5836#include "mythsystemevent.h"
     37#include "main_helpers.h"
     38#include "backendcontext.h"
    5939
    6040#include "mediaserver.h"
    6141#include "httpstatus.h"
     
    7151    #define UNUSED_FILENO 3
    7252#endif
    7353
    74 QMap<int, EncoderLink *> tvList;
    75 AutoExpire  *expirer      = NULL;
    76 Scheduler   *sched        = NULL;
    77 JobQueue    *jobqueue     = NULL;
    78 QString      pidfile;
    79 HouseKeeper *housekeeping = NULL;
    80 QString      logfile;
     54extern const char *myth_source_version;
     55extern const char *myth_source_path;
    8156
    82 MediaServer *g_pUPnp      = NULL;
    83 
    84 bool setupTVs(bool ismaster, bool &error)
     57class MediaServerThread : public QThread
    8558{
    86     error = false;
    87     QString localhostname = gContext->GetHostName();
    88 
    89     MSqlQuery query(MSqlQuery::InitCon());
    90 
    91     if (ismaster)
     59  public:
     60    void run(void)
    9261    {
    93         // Hack to make sure recorded.basename gets set if the user
    94         // downgrades to a prior version and creates new entries
    95         // without it.
    96         if (!query.exec("UPDATE recorded SET basename = CONCAT(chanid, '_', "
    97                         "DATE_FORMAT(starttime, '%Y%m%d%H%i00'), '_', "
    98                         "DATE_FORMAT(endtime, '%Y%m%d%H%i00'), '.nuv') "
    99                         "WHERE basename = '';"))
    100             MythDB::DBError("Updating record basename",
    101                                  query.lastQuery());
    102 
    103         // Hack to make sure record.station gets set if the user
    104         // downgrades to a prior version and creates new entries
    105         // without it.
    106         if (!query.exec("UPDATE channel SET callsign=chanid "
    107                         "WHERE callsign IS NULL OR callsign='';"))
    108             MythDB::DBError("Updating channel callsign", query.lastQuery());
    109 
    110         if (query.exec("SELECT MIN(chanid) FROM channel;"))
    111         {
    112             query.first();
    113             int min_chanid = query.value(0).toInt();
    114             if (!query.exec(QString("UPDATE record SET chanid = %1 "
    115                                     "WHERE chanid IS NULL;").arg(min_chanid)))
    116                 MythDB::DBError("Updating record chanid", query.lastQuery());
    117         }
    118         else
    119             MythDB::DBError("Querying minimum chanid", query.lastQuery());
    120 
    121         MSqlQuery records_without_station(MSqlQuery::InitCon());
    122         records_without_station.prepare("SELECT record.chanid,"
    123                 " channel.callsign FROM record LEFT JOIN channel"
    124                 " ON record.chanid = channel.chanid WHERE record.station='';");
    125         if (records_without_station.exec() && records_without_station.next())
    126         {
    127             MSqlQuery update_record(MSqlQuery::InitCon());
    128             update_record.prepare("UPDATE record SET station = :CALLSIGN"
    129                     " WHERE chanid = :CHANID;");
    130             do
    131             {
    132                 update_record.bindValue(":CALLSIGN",
    133                         records_without_station.value(1));
    134                 update_record.bindValue(":CHANID",
    135                         records_without_station.value(0));
    136                 if (!update_record.exec())
    137                 {
    138                     MythDB::DBError("Updating record station",
    139                             update_record.lastQuery());
    140                 }
    141             } while (records_without_station.next());
    142         }
     62        g_pUPnp = new MediaServer();
     63        exec();
    14364    }
    14465
    145     if (!query.exec(
    146             "SELECT cardid, hostname "
    147             "FROM capturecard "
    148             "ORDER BY cardid"))
     66    void BlockUntilReloadNeeded(void)
    14967    {
    150         MythDB::DBError("Querying Recorders", query);
    151         return false;
     68        sleep(60); // TODO
    15269    }
     70};
    15371
    154     vector<uint>    cardids;
    155     vector<QString> hosts;
    156     while (query.next())
    157     {
    158         uint    cardid = query.value(0).toUInt();
    159         QString host   = query.value(1).toString();
    160         QString cidmsg = QString("Card %1").arg(cardid);
    161 
    162         if (host.isEmpty())
    163         {
    164             QString msg = cidmsg + " does not have a hostname defined.\n"
    165                 "Please run setup and confirm all of the capture cards.\n";
    166 
    167             VERBOSE(VB_IMPORTANT, msg);
    168             gContext->LogEntry("mythbackend", LP_CRITICAL,
    169                                "Problem with capture cards", msg);
    170             continue;
    171         }
    172 
    173         cardids.push_back(cardid);
    174         hosts.push_back(host);
    175     }
    176 
    177     for (uint i = 0; i < cardids.size(); i++)
    178     {
    179         if (hosts[i] == localhostname)
    180             new TVRec(cardids[i]);
    181     }
    182 
    183     for (uint i = 0; i < cardids.size(); i++)
    184     {
    185         uint    cardid = cardids[i];
    186         QString host   = hosts[i];
    187         QString cidmsg = QString("Card %1").arg(cardid);
    188 
    189         if (!ismaster)
    190         {
    191             if (host == localhostname)
    192             {
    193                 TVRec *tv = TVRec::GetTVRec(cardid);
    194                 if (tv->Init())
    195                 {
    196                     EncoderLink *enc = new EncoderLink(cardid, tv);
    197                     tvList[cardid] = enc;
    198                 }
    199                 else
    200                 {
    201                     gContext->LogEntry("mythbackend", LP_CRITICAL,
    202                                        "Problem with capture cards",
    203                                        cidmsg + " failed init");
    204                     delete tv;
    205                     // The master assumes card comes up so we need to
    206                     // set error and exit if a non-master card fails.
    207                     error = true;
    208                 }
    209             }
    210         }
    211         else
    212         {
    213             if (host == localhostname)
    214             {
    215                 TVRec *tv = TVRec::GetTVRec(cardid);
    216                 if (tv->Init())
    217                 {
    218                     EncoderLink *enc = new EncoderLink(cardid, tv);
    219                     tvList[cardid] = enc;
    220                 }
    221                 else
    222                 {
    223                     gContext->LogEntry("mythbackend", LP_CRITICAL,
    224                                        "Problem with capture cards",
    225                                        cidmsg + "failed init");
    226                     delete tv;
    227                 }
    228             }
    229             else
    230             {
    231                 EncoderLink *enc = new EncoderLink(cardid, NULL, host);
    232                 tvList[cardid] = enc;
    233             }
    234         }
    235     }
    236 
    237     if (tvList.empty())
    238     {
    239         VERBOSE(VB_IMPORTANT, LOC_ERR +
    240                 "No valid capture cards are defined in the database.\n\t\t\t"
    241                 "Perhaps you should re-read the installation instructions?");
    242 
    243         gContext->LogEntry("mythbackend", LP_CRITICAL,
    244                            "No capture cards are defined",
    245                            "Please run the setup program.");
    246         return false;
    247     }
    248 
    249     return true;
    250 }
    251 
    252 void cleanup(void)
    253 {
    254     delete sched;
    255     sched = NULL;
    256 
    257     delete g_pUPnp;
    258     g_pUPnp = NULL;
    259 
    260     delete gContext;
    261     gContext = NULL;
    262 
    263     if (pidfile.size())
    264     {
    265         unlink(pidfile.toAscii().constData());
    266         pidfile.clear();
    267     }
    268 
    269     signal(SIGHUP, SIG_DFL);
    270     signal(SIGUSR1, SIG_DFL);
    271 }
    272 
    273 int log_rotate(int report_error)
    274 {
    275     /* http://www.gossamer-threads.com/lists/mythtv/dev/110113 */
    276 
    277     int new_logfd = open(logfile.toLocal8Bit().constData(),
    278                          O_WRONLY|O_CREAT|O_APPEND|O_SYNC, 0664);
    279     if (new_logfd < 0)
    280     {
    281         // If we can't open the new logfile, send data to /dev/null
    282         if (report_error)
    283         {
    284             VERBOSE(VB_IMPORTANT, LOC_ERR +
    285                     QString("Cannot open logfile '%1'").arg(logfile));
    286             return -1;
    287         }
    288         new_logfd = open("/dev/null", O_WRONLY);
    289         if (new_logfd < 0)
    290         {
    291             // There's not much we can do, so punt.
    292             return -1;
    293         }
    294     }
    295     while (dup2(new_logfd, 1) < 0 && errno == EINTR) ;
    296     while (dup2(new_logfd, 2) < 0 && errno == EINTR) ;
    297     while (close(new_logfd) < 0 && errno == EINTR) ;
    298     return 0;
    299 }
    300 
    301 void log_rotate_handler(int)
    302 {
    303     log_rotate(0);
    304 }
    305 
    306 void upnp_rebuild(int)
    307 {
    308     if (gContext->IsMasterHost())
    309     {
    310         g_pUPnp->RebuildMediaMap();
    311     }
    312 
    313 }
    314 
    315 int preview_helper(const QString &chanid, const QString &starttime,
    316                    long long previewFrameNumber, long long previewSeconds,
    317                    const QSize &previewSize,
    318                    const QString &infile, const QString &outfile)
    319 {
    320     // Lower scheduling priority, to avoid problems with recordings.
    321     if (setpriority(PRIO_PROCESS, 0, 9))
    322         VERBOSE(VB_GENERAL, "Setting priority failed." + ENO);
    323 
    324     ProgramInfo *pginfo = NULL;
    325     if (!chanid.isEmpty() && !starttime.isEmpty())
    326     {
    327         pginfo = ProgramInfo::GetProgramFromRecorded(chanid, starttime);
    328         if (!pginfo)
    329         {
    330             VERBOSE(VB_IMPORTANT, QString(
    331                         "Can not locate recording made on '%1' at '%2'")
    332                     .arg(chanid).arg(starttime));
    333             return GENERIC_EXIT_NOT_OK;
    334         }
    335         pginfo->pathname = pginfo->GetPlaybackURL(false, true);
    336     }
    337     else if (!infile.isEmpty())
    338     {
    339         pginfo = ProgramInfo::GetProgramFromBasename(infile);
    340         if (!pginfo)
    341         {
    342             if (!QFileInfo(infile).exists())
    343             {
    344                 VERBOSE(VB_IMPORTANT, QString(
    345                             "Can not locate recording '%1'").arg(infile));
    346                 return GENERIC_EXIT_NOT_OK;
    347             }
    348             else
    349             {
    350                 pginfo = new ProgramInfo();
    351                 pginfo->isVideo = true;
    352 
    353                 QDir d(infile + "/VIDEO_TS");
    354                 if ((infile.section('.', -1) == "iso") ||
    355                     (infile.section('.', -1) == "img") ||
    356                     d.exists())
    357                 {
    358                     pginfo->pathname = QString("dvd:%1").arg(infile);
    359                 }
    360                 else
    361                 {
    362                     pginfo->pathname = QFileInfo(infile).absoluteFilePath();
    363                 }
    364             }
    365 
    366         }
    367         else
    368         {
    369             pginfo->pathname = pginfo->GetPlaybackURL(false, true);
    370         }
    371     }
    372     else
    373     {
    374         VERBOSE(VB_IMPORTANT, "Can not locate recording for preview");
    375         return GENERIC_EXIT_NOT_OK;
    376     }
    377 
    378     PreviewGenerator *previewgen = new PreviewGenerator(
    379         pginfo, PreviewGenerator::kLocal);
    380 
    381     if (previewFrameNumber >= 0)
    382         previewgen->SetPreviewTimeAsFrameNumber(previewFrameNumber);
    383 
    384     if (previewSeconds >= 0)
    385         previewgen->SetPreviewTimeAsSeconds(previewSeconds);
    386 
    387     previewgen->SetOutputSize(previewSize);
    388     previewgen->SetOutputFilename(outfile);
    389     bool ok = previewgen->RunReal();
    390     previewgen->deleteLater();
    391 
    392     delete pginfo;
    393 
    394     return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_NOT_OK;
    395 }
    396 
    397 // [WxH] | [WxH@]seconds[S] | [WxH@]frame_numF
    398 bool parse_preview_info(const QString &param,
    399                         long long     &previewFrameNumber,
    400                         long long     &previewSeconds,
    401                         QSize         &previewSize)
    402 {
    403     previewFrameNumber = -1;
    404     previewSeconds = -1;
    405     previewSize = QSize(0,0);
    406     if (param.isEmpty())
    407         return true;
    408 
    409     int xat = param.indexOf("x", 0, Qt::CaseInsensitive);
    410     int aat = param.indexOf("@", 0);
    411     if (xat > 0)
    412     {
    413         QString widthStr  = param.left(xat);
    414         QString heightStr;
    415         if (aat > xat)
    416             heightStr = param.mid(xat + 1, aat - xat - 1);
    417         else
    418             heightStr = param.mid(xat + 1);
    419 
    420         bool ok1, ok2;
    421         previewSize = QSize(widthStr.toInt(&ok1), heightStr.toInt(&ok2));
    422         if (!ok1 || !ok2)
    423         {
    424             VERBOSE(VB_IMPORTANT, QString(
    425                         "Error: Failed to parse --generate-preview "
    426                         "param '%1'").arg(param));
    427         }
    428     }
    429     if ((xat > 0) && (aat < xat))
    430         return true;
    431 
    432     QString lastChar = param.at(param.length() - 1).toLower();
    433     QString frameNumStr;
    434     QString secsStr;
    435     if (lastChar == "f")
    436         frameNumStr = param.mid(aat + 1, param.length() - aat - 2);
    437     else if (lastChar == "s")
    438         secsStr = param.mid(aat + 1, param.length() - aat - 2);
    439     else
    440         secsStr = param.mid(aat + 1, param.length() - aat - 1);
    441 
    442     bool ok = false;
    443     if (!frameNumStr.isEmpty())
    444         previewFrameNumber = frameNumStr.toUInt(&ok);
    445     else if (!secsStr.isEmpty())
    446         previewSeconds = secsStr.toUInt(&ok);
    447 
    448     if (!ok)
    449     {
    450         VERBOSE(VB_IMPORTANT, QString(
    451                     "Error: Failed to parse --generate-preview "
    452                     "param '%1'").arg(param));
    453     }
    454 
    455     return ok;
    456 }
    457 
    458 namespace
    459 {
    460     class CleanupGuard
    461     {
    462       public:
    463         typedef void (*CleanupFunc)();
    464 
    465       public:
    466         CleanupGuard(CleanupFunc cleanFunction) :
    467             m_cleanFunction(cleanFunction) {}
    468 
    469         ~CleanupGuard()
    470         {
    471             m_cleanFunction();
    472         }
    473 
    474       private:
    475         CleanupFunc m_cleanFunction;
    476     };
    477 }
    478 
    479 void showUsage(const MythCommandLineParser &cmdlineparser, const QString &version)
    480 {
    481     QString    help  = cmdlineparser.GetHelpString(false);
    482     QByteArray ahelp = help.toLocal8Bit();
    483 
    484     cerr << qPrintable(version) << endl <<
    485     "Valid options are: " << endl <<
    486     "-h or --help                   List valid command line parameters" << endl <<
    487     "-l or --logfile filename       Writes STDERR and STDOUT messages to filename" << endl <<
    488     "-p or --pidfile filename       Write PID of mythbackend to filename" << endl <<
    489     "-d or --daemon                 Runs mythbackend as a daemon" << endl <<
    490     "-v or --verbose debug-level    Use '-v help' for level info" << endl <<
    491     "--setverbose debug-level       Change debug level of running master backend" << endl <<
    492     "--user username                Drop permissions to username after starting"  << endl <<
    493 
    494     "--printexpire                  List of auto-expire programs" << endl <<
    495     "--printsched                   Upcoming scheduled programs" << endl <<
    496     "--testsched                    Test run scheduler (ignore existing schedule)" << endl <<
    497     "--resched                      Force the scheduler to update" << endl <<
    498     "--nosched                      Do not perform any scheduling" << endl <<
    499     "--noupnp                       Do not enable the UPNP server" << endl <<
    500     "--nojobqueue                   Do not start the JobQueue" << endl <<
    501     "--nohousekeeper                Do not start the Housekeeper" << endl <<
    502     "--noautoexpire                 Do not start the AutoExpire thread" << endl <<
    503     "--clearcache                   Clear the settings cache on all myth servers" << endl <<
    504     ahelp.constData() <<
    505     "--generate-preview             Generate a preview image" << endl <<
    506     "--upnprebuild                  Force an update of UPNP media" << endl <<
    507     "--infile                       Input file for preview generation" << endl <<
    508     "--outfile                      Optional output file for preview generation" << endl <<
    509     "--chanid                       Channel ID for preview generation" << endl <<
    510     "--starttime                    Recording start time for preview generation" << endl <<
    511     "--event EVENTTEXT              Send a backend event test message" << endl <<
    512     "--systemevent EVENTTEXT        Send a backend SYSTEM_EVENT test message" << endl
    513     << endl;
    514 
    515 }
    516 
    51772int main(int argc, char **argv)
    51873{
     74    QString binname = basename(argv[0]);
     75    QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org")
     76        .arg(binname).arg(myth_source_path).arg(myth_source_version);
     77
    51978    bool cmdline_err;
    52079    MythCommandLineParser cmdline(
    52180        kCLPOverrideSettingsFile |
    52281        kCLPOverrideSettings     |
    523         kCLPQueryVersion);
     82        kCLPQueryVersion         |
     83        kCLPPrintSchedule        |
     84        kCLPTestSchedule         |
     85        kCLPReschedule           |
     86        kCLPNoSchedule           |
     87        kCLPNoUPnP               |
     88        kCLPUPnPRebuild          |
     89        kCLPNoJobqueue           |
     90        kCLPNoHousekeeper        |
     91        kCLPNoAutoExpire         |
     92        kCLPClearCache           |
     93        kCLPVerbose              |
     94        kCLPSetVerbose           |
     95        kCLPLogFile              |
     96        kCLPPidFile              |
     97        kCLPInFile               |
     98        kCLPOutFile              |
     99        kCLPUsername             |
     100        kCLPEvent                |
     101        kCLPSystemEvent          |
     102        kCLPChannelId            |
     103        kCLPStartTime            |
     104        kCLPPrintExpire          |
     105        kCLPGeneratePreview);
    524106
    525107    for (int argpos = 0; argpos < argc; ++argpos)
    526108    {
     
    544126    QApplication a(argc, argv);
    545127#endif
    546128
    547     QString binname = basename(a.argv()[0]);
    548     extern const char *myth_source_version;
    549     extern const char *myth_source_path;
    550     QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org")
    551                                  .arg(binname)
    552                                  .arg(myth_source_path)
    553                                  .arg(myth_source_version);
    554 
    555     long long previewFrameNumber = -2;
    556     long long previewSeconds     = -2;
    557     QSize previewSize(0,0);
    558     QString chanid;
    559     QString starttime;
    560     QString infile;
    561     QString outfile;
    562 
    563     bool daemonize = false;
    564     bool printsched = false;
    565     bool testsched = false;
    566     bool setverbose = false;
    567     QString newverbose;
    568     QString username;
    569     bool resched = false;
    570     bool nosched = false;
    571     bool noupnp = false;
    572     bool nojobqueue = false;
    573     bool nohousekeeper = false;
    574     bool noexpirer = false;
    575     QString printexpire;
    576     bool clearsettingscache = false;
    577     bool wantupnprebuild = false;
    578     QString eventString;
    579 
    580129    for (int argpos = 1; argpos < a.argc(); ++argpos)
    581130    {
    582         if (!strcmp(a.argv()[argpos],"-l") ||
    583             !strcmp(a.argv()[argpos],"--logfile"))
     131        if (cmdline.Parse(a.argc(), a.argv(), argpos, cmdline_err))
    584132        {
    585             if (a.argc() > argpos)
    586             {
    587                 logfile = a.argv()[argpos+1];
    588                 if (logfile.startsWith("-"))
    589                 {
    590                     cerr << "Invalid or missing argument to -l/--logfile option\n";
    591                     return BACKEND_EXIT_INVALID_CMDLINE;
    592                 }
    593                 else
    594                 {
    595                     ++argpos;
    596                 }
    597             }
    598         }
    599         else if (!strcmp(a.argv()[argpos],"-p") ||
    600                  !strcmp(a.argv()[argpos],"--pidfile"))
    601         {
    602             if (a.argc() > argpos)
    603             {
    604                 pidfile = a.argv()[argpos+1];
    605                 if (pidfile.startsWith("-"))
    606                 {
    607                     cerr << "Invalid or missing argument to -p/--pidfile option\n";
    608                     return BACKEND_EXIT_INVALID_CMDLINE;
    609                 }
    610                 else
    611                 {
    612                    ++argpos;
    613                 }
    614             }
    615         }
    616         else if (!strcmp(a.argv()[argpos],"-d") ||
    617                  !strcmp(a.argv()[argpos],"--daemon"))
    618         {
    619             daemonize = true;
    620 
    621         }
    622         else if (!strcmp(a.argv()[argpos],"-v") ||
    623                  !strcmp(a.argv()[argpos],"--verbose"))
    624         {
    625             if (a.argc()-1 > argpos)
    626             {
    627                 if (parse_verbose_arg(a.argv()[argpos+1]) ==
    628                         GENERIC_EXIT_INVALID_CMDLINE)
    629                     return BACKEND_EXIT_INVALID_CMDLINE;
    630 
    631                 ++argpos;
    632             }
    633             else
    634             {
    635                 cerr << "Missing argument to -v/--verbose option\n";
    636                 return BACKEND_EXIT_INVALID_CMDLINE;
    637             }
    638         }
    639         else if (!strcmp(a.argv()[argpos],"--setverbose"))
    640         {
    641             setverbose = true;
    642             if (a.argc()-1 > argpos)
    643             {
    644                 newverbose = a.argv()[argpos+1];
    645                 ++argpos;
    646             }
    647             else
    648             {
    649                 cerr << "Missing argument to --setverbose option\n";
    650                 return BACKEND_EXIT_INVALID_CMDLINE;
    651             }
    652         }
    653         else if (!strcmp(a.argv()[argpos],"--user"))
    654         {
    655             if (a.argc()-1 > argpos)
    656             {
    657                 username = a.argv()[argpos+1];
    658                 ++argpos;
    659             }
    660             else
    661             {
    662                 cerr << "Missing argument to --user option\n";
    663                 return BACKEND_EXIT_INVALID_CMDLINE;
    664             }
    665         }
    666         else if (!strcmp(a.argv()[argpos],"--printsched"))
    667         {
    668             printsched = true;
    669         }
    670         else if (!strcmp(a.argv()[argpos],"--testsched"))
    671         {
    672             testsched = true;
    673         }
    674         else if (!strcmp(a.argv()[argpos],"--resched"))
    675         {
    676             resched = true;
    677         }
    678         else if (!strcmp(a.argv()[argpos],"--nosched"))
    679         {
    680             nosched = true;
    681         }
    682         else if (!strcmp(a.argv()[argpos],"--noupnp"))
    683         {
    684             noupnp = true;
    685         }
    686         else if (!strcmp(a.argv()[argpos],"--upnprebuild"))
    687         {
    688             wantupnprebuild = true;
    689         }
    690         else if (!strcmp(a.argv()[argpos],"--nojobqueue"))
    691         {
    692             nojobqueue = true;
    693         }
    694         else if (!strcmp(a.argv()[argpos],"--nohousekeeper"))
    695         {
    696             nohousekeeper = true;
    697         }
    698         else if (!strcmp(a.argv()[argpos],"--noautoexpire"))
    699         {
    700             noexpirer = true;
    701         }
    702         else if (!strcmp(a.argv()[argpos],"--printexpire"))
    703         {
    704             printexpire = "ALL";
    705             if ((a.argc()-1 > argpos) && a.argv()[argpos+1][0] != '-')
    706             {
    707                 printexpire = a.argv()[argpos+1];
    708                 ++argpos;
    709             }
    710         }
    711         else if (!strcmp(a.argv()[argpos],"--clearcache"))
    712         {
    713             clearsettingscache = true;
    714         }
    715         else if (!strcmp(a.argv()[argpos],"--event"))
    716         {
    717             if ((a.argc()-1 > argpos) && a.argv()[argpos+1][0] != '-')
    718             {
    719                 eventString = a.argv()[argpos+1];
    720                 ++argpos;
    721             }
    722         }
    723         else if (!strcmp(a.argv()[argpos],"--systemevent"))
    724         {
    725             if ((a.argc()-1 > argpos) && a.argv()[argpos+1][0] != '-')
    726             {
    727                 eventString = QString("SYSTEM_EVENT ") + a.argv()[argpos+1];
    728                 ++argpos;
    729             }
    730         }
    731         else if (!strcmp(a.argv()[argpos],"--generate-preview"))
    732         {
    733             QString tmp;
    734             if ((argpos + 1) < a.argc())
    735             {
    736                 tmp = a.argv()[argpos+1];
    737                 bool ok = true;
    738                 if (tmp.left(1) == "-")
    739                     tmp.left(2).toInt(&ok);
    740                 if (ok)
    741                     argpos++;
    742                 else
    743                     tmp.clear();
    744             }
    745 
    746             if (!parse_preview_info(tmp, previewFrameNumber, previewSeconds,
    747                                     previewSize))
    748             {
    749                 VERBOSE(VB_IMPORTANT,
    750                         QString("Unable to parse --generate-preview "
    751                                 "option '%1'").arg(tmp));
    752 
    753                 return BACKEND_EXIT_INVALID_CMDLINE;
    754             }
    755         }
    756         else if (!strcmp(a.argv()[argpos],"-c") ||
    757                  !strcmp(a.argv()[argpos],"--chanid"))
    758         {
    759             if (((argpos + 1) >= a.argc()) ||
    760                 !strncmp(a.argv()[argpos + 1], "-", 1))
    761             {
    762                 VERBOSE(VB_IMPORTANT,
    763                         "Missing or invalid parameters for --chanid option");
    764 
    765                 return BACKEND_EXIT_INVALID_CMDLINE;
    766             }
    767 
    768             chanid = a.argv()[++argpos];
    769         }
    770         else if (!strcmp(a.argv()[argpos],"-s") ||
    771                  !strcmp(a.argv()[argpos],"--starttime"))
    772         {
    773             if (((argpos + 1) >= a.argc()) ||
    774                 !strncmp(a.argv()[argpos + 1], "-", 1))
    775             {
    776                 VERBOSE(VB_IMPORTANT,
    777                         "Missing or invalid parameters for --starttime option");
    778                 return BACKEND_EXIT_INVALID_CMDLINE;
    779             }
    780 
    781             starttime = a.argv()[++argpos];
    782         }
    783         else if (!strcmp(a.argv()[argpos],"--infile"))
    784         {
    785             if (((argpos + 1) >= a.argc()) ||
    786                 !strncmp(a.argv()[argpos + 1], "-", 1))
    787             {
    788                 VERBOSE(VB_IMPORTANT,
    789                         "Missing or invalid parameters for --infile option");
    790 
    791                 return BACKEND_EXIT_INVALID_CMDLINE;
    792             }
    793 
    794             infile = a.argv()[++argpos];
    795         }
    796         else if (!strcmp(a.argv()[argpos],"--outfile"))
    797         {
    798             if (((argpos + 1) >= a.argc()) ||
    799                 !strncmp(a.argv()[argpos + 1], "-", 1))
    800             {
    801                 VERBOSE(VB_IMPORTANT,
    802                         "Missing or invalid parameters for --outfile option");
    803 
    804                 return BACKEND_EXIT_INVALID_CMDLINE;
    805             }
    806 
    807             outfile = a.argv()[++argpos];
    808         }
    809         else if (cmdline.Parse(a.argc(), a.argv(), argpos, cmdline_err))
    810         {
    811133            if (cmdline_err)
    812134                return BACKEND_EXIT_INVALID_CMDLINE;
    813135
     
    817139        else
    818140        {
    819141            if (!(!strcmp(a.argv()[argpos],"-h") ||
    820                 !strcmp(a.argv()[argpos],"--help")))
     142                  !strcmp(a.argv()[argpos],"--help")))
     143            {
    821144                cerr << "Invalid argument: " << a.argv()[argpos] << endl;
    822145                showUsage(cmdline, versionStr);
    823             return BACKEND_EXIT_INVALID_CMDLINE;
     146                return BACKEND_EXIT_INVALID_CMDLINE;
     147            }
     148            else
     149            {
     150                showUsage(cmdline, versionStr);
     151                return BACKEND_EXIT_OK;
     152            }
    824153        }
    825154    }
    826155
    827     if (((previewFrameNumber >= -1) || previewSeconds >= -1) &&
    828         (chanid.isEmpty() || starttime.isEmpty()) && infile.isEmpty())
     156    if (cmdline.HasInvalidPreviewGenerationParams())
    829157    {
    830158        cerr << "--generate-preview must be accompanied by either " <<endl
    831159             << "\nboth --chanid and --starttime parameters, " << endl
     
    833161        return BACKEND_EXIT_INVALID_CMDLINE;
    834162    }
    835163
    836     if (!logfile.isEmpty())
    837     {
    838         if (log_rotate(1) < 0)
    839         {
    840             VERBOSE(VB_IMPORTANT, LOC_WARN +
    841                     "Cannot open logfile; using stdout/stderr instead");
    842         }
    843         else
    844             signal(SIGHUP, &log_rotate_handler);
    845     }
     164    logfile = cmdline.GetLogFilename();
     165    pidfile = cmdline.GetPIDFilename();
    846166
    847     CleanupGuard callCleanup(cleanup);
     167    ///////////////////////////////////////////////////////////////////////
    848168
    849     ofstream pidfs;
    850     if (pidfile.size())
    851     {
    852         pidfs.open(pidfile.toAscii().constData());
    853         if (!pidfs)
    854         {
    855             VERBOSE(VB_IMPORTANT, LOC_ERR +
    856                     "Could not open pid file" + ENO);
    857             return BACKEND_EXIT_OPENING_PIDFILE_ERROR;
    858         }
    859     }
     169    // Don't listen to console input
     170    close(0);
    860171
    861     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
    862         VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to ignore SIGPIPE");
     172    setupLogfile();
    863173
    864     if (daemonize && (daemon(0, 1) < 0))
    865     {
    866         VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to daemonize" + ENO);
    867         return BACKEND_EXIT_DAEMONIZING_ERROR;
    868     }
     174    CleanupGuard callCleanup(cleanup);
    869175
    870     if (!username.isEmpty())
    871     {
    872         struct passwd *user_info = getpwnam(username.toLocal8Bit().constData());
    873         const uid_t user_id = geteuid();
     176    int exitCode = setup_basics(cmdline);
     177    if (BACKEND_EXIT_OK != exitCode)
     178        return exitCode;
    874179
    875         if (user_id && (!user_info || user_id != user_info->pw_uid))
    876         {
    877             VERBOSE(VB_IMPORTANT,
    878                     "You must be running as root to use the --user switch.");
    879             return BACKEND_EXIT_PERMISSIONS_ERROR;
    880         }
    881         else if (user_info && user_id == user_info->pw_uid)
    882         {
    883             VERBOSE(VB_IMPORTANT,
    884                     QString("Already running as '%1'").arg(username));
    885         }
    886         else if (!user_id && user_info)
    887         {
    888             if (setenv("HOME", user_info->pw_dir,1) == -1)
    889             {
    890                 VERBOSE(VB_IMPORTANT, "Error setting home directory.");
    891                 return BACKEND_EXIT_PERMISSIONS_ERROR;
    892             }
    893             if (setgid(user_info->pw_gid) == -1)
    894             {
    895                 VERBOSE(VB_IMPORTANT, "Error setting effective group.");
    896                 return BACKEND_EXIT_PERMISSIONS_ERROR;
    897             }
    898             if (setuid(user_info->pw_uid) == -1)
    899             {
    900                 VERBOSE(VB_IMPORTANT, "Error setting effective user.");
    901                 return BACKEND_EXIT_PERMISSIONS_ERROR;
    902             }
    903         }
    904         else
    905         {
    906             VERBOSE(VB_IMPORTANT,
    907                     QString("Invalid user '%1' specified with --user")
    908                     .arg(username));
    909             return BACKEND_EXIT_PERMISSIONS_ERROR;
    910         }
    911     }
     180    VERBOSE(VB_IMPORTANT, versionStr);
    912181
    913     if (pidfs)
     182    if (cmdline.HasBackendCommand())
    914183    {
    915         pidfs << getpid() << endl;
    916         pidfs.close();
     184        if (!setup_context(cmdline))
     185            return BACKEND_EXIT_NO_MYTHCONTEXT;
     186        return handle_command(cmdline);
    917187    }
    918188
    919     VERBOSE(VB_IMPORTANT, versionStr);
    920 
    921189    gContext = new MythContext(MYTH_BINARY_VERSION);
    922     if (!gContext->Init(false))
    923     {
    924         VERBOSE(VB_IMPORTANT, "Failed to init MythContext, exiting.");
    925         return BACKEND_EXIT_NO_MYTHCONTEXT;
    926     }
    927     gContext->SetBackend(true);
    928190
    929     if (!eventString.isEmpty())
    930     {
    931         gContext->SetBackend(false);
    932         if (gContext->ConnectToMasterServer())
    933         {
    934             if (eventString.startsWith("SYSTEM_EVENT"))
    935                 eventString += QString(" SENDER %1")
    936                                        .arg(gContext->GetHostName());
     191    ///////////////////////////////////////////
    937192
    938             RemoteSendMessage(eventString);
    939             return BACKEND_EXIT_OK;
    940         }
    941         return BACKEND_EXIT_NO_MYTHCONTEXT;
    942     }
     193    MediaServerThread *mst = new MediaServerThread();
     194    mst->start();
     195    while (!mst->isRunning())
     196        usleep(5000);
     197    while (!g_pUPnp)
     198        usleep(5000);
    943199
    944     if (wantupnprebuild)
    945     {
    946         VERBOSE(VB_GENERAL, "Rebuilding UPNP Media Map");
     200    ///////////////////////////////////////////
    947201
    948         UPnpMedia *rebuildit = new UPnpMedia(false,false);
    949         rebuildit->BuildMediaMap();
    950 
    951         return BACKEND_EXIT_OK;
    952     }
    953 
    954     if (setverbose)
     202    while (true)
    955203    {
    956         gContext->SetBackend(false);
    957 
    958         if (gContext->ConnectToMasterServer())
    959         {
    960             QString message = "SET_VERBOSE ";
    961             message += newverbose;
    962 
    963             RemoteSendMessage(message);
    964             VERBOSE(VB_IMPORTANT, QString("Sent '%1' message").arg(message));
    965             return BACKEND_EXIT_OK;
    966         }
    967         else
    968         {
    969             VERBOSE(VB_IMPORTANT,
    970                     "Unable to connect to backend, verbose level unchanged ");
    971             return BACKEND_EXIT_NO_CONNECT;
    972         }
     204        exitCode = run_backend(cmdline);
     205        mst->BlockUntilReloadNeeded();
    973206    }
    974207
    975     if (clearsettingscache)
    976     {
    977         gContext->SetBackend(false);
    978 
    979         if (gContext->ConnectToMasterServer())
    980         {
    981             RemoteSendMessage("CLEAR_SETTINGS_CACHE");
    982             VERBOSE(VB_IMPORTANT, "Sent CLEAR_SETTINGS_CACHE message");
    983             return BACKEND_EXIT_OK;
    984         }
    985         else
    986         {
    987             VERBOSE(VB_IMPORTANT, "Unable to connect to backend, settings "
    988                     "cache will not be cleared.");
    989             return BACKEND_EXIT_NO_CONNECT;
    990         }
    991     }
    992 
    993     QMap<QString,QString> settingsOverride = cmdline.GetSettingsOverride();
    994     if (settingsOverride.size())
    995     {
    996         QMap<QString, QString>::iterator it;
    997         for (it = settingsOverride.begin(); it != settingsOverride.end(); ++it)
    998         {
    999             VERBOSE(VB_IMPORTANT, QString("Setting '%1' being forced to '%2'")
    1000                                           .arg(it.key()).arg(*it));
    1001             gContext->OverrideSettingForSession(it.key(), *it);
    1002         }
    1003     }
    1004 
    1005     if (!gContext->IsMasterHost())
    1006     {
    1007         MythSocket *tempMonitorConnection = new MythSocket();
    1008         if (tempMonitorConnection->connect(
    1009                         gContext->GetSetting("MasterServerIP", "127.0.0.1"),
    1010                         gContext->GetNumSetting("MasterServerPort", 6543)))
    1011         {
    1012             if (!gContext->CheckProtoVersion(tempMonitorConnection))
    1013             {
    1014                 VERBOSE(VB_IMPORTANT, "Master backend is incompatible with "
    1015                                       "this backend.\nCannot become a slave.");
    1016                 return BACKEND_EXIT_NO_CONNECT;
    1017             }
    1018 
    1019             QStringList tempMonitorDone("DONE");
    1020 
    1021             QStringList tempMonitorAnnounce("ANN Monitor tzcheck 0");
    1022             tempMonitorConnection->writeStringList(tempMonitorAnnounce);
    1023             tempMonitorConnection->readStringList(tempMonitorAnnounce);
    1024             if (tempMonitorAnnounce.empty() ||
    1025                 tempMonitorAnnounce[0] == "ERROR")
    1026             {
    1027                 tempMonitorConnection->DownRef();
    1028                 tempMonitorConnection = NULL;
    1029                 if (tempMonitorAnnounce.empty())
    1030                 {
    1031                     VERBOSE(VB_IMPORTANT, LOC_ERR +
    1032                             "Failed to open event socket, timeout");
    1033                 }
    1034                 else
    1035                 {
    1036                     VERBOSE(VB_IMPORTANT, LOC_ERR +
    1037                             "Failed to open event socket" +
    1038                         ((tempMonitorAnnounce.size() >= 2) ?
    1039                          QString(", error was %1").arg(tempMonitorAnnounce[1]) :
    1040                          QString(", remote error")));
    1041                 }
    1042             }
    1043 
    1044             QStringList tzCheck("QUERY_TIME_ZONE");
    1045             if (tempMonitorConnection)
    1046             {
    1047                 tempMonitorConnection->writeStringList(tzCheck);
    1048                 tempMonitorConnection->readStringList(tzCheck);
    1049             }
    1050             if (tzCheck.size() && !checkTimeZone(tzCheck))
    1051             {
    1052                 // Check for different time zones, different offsets, different
    1053                 // times
    1054                 VERBOSE(VB_IMPORTANT, "The time and/or time zone settings on "
    1055                         "this system do not match those in use on the master "
    1056                         "backend. Please ensure all frontend and backend "
    1057                         "systems are configured to use the same time zone and "
    1058                         "have the current time properly set.");
    1059                 VERBOSE(VB_IMPORTANT,
    1060                         "Unable to run with invalid time settings. Exiting.");
    1061                 tempMonitorConnection->writeStringList(tempMonitorDone);
    1062                 tempMonitorConnection->DownRef();
    1063                 return BACKEND_EXIT_INVALID_TIMEZONE;
    1064             }
    1065             else
    1066             {
    1067                 VERBOSE(VB_IMPORTANT,
    1068                         QString("Backend is running in %1 time zone.")
    1069                         .arg(getTimeZoneID()));
    1070             }
    1071             if (tempMonitorConnection)
    1072                 tempMonitorConnection->writeStringList(tempMonitorDone);
    1073         }
    1074         if (tempMonitorConnection)
    1075             tempMonitorConnection->DownRef();
    1076     }
    1077 
    1078     // Don't allow upgrade for --printsched, --testsched, --resched,
    1079     // --printexpire, --generate-preview
    1080     bool allowUpgrade = (!(printsched || testsched || resched)) &&
    1081                         (printexpire.isEmpty()) &&
    1082                         ((previewFrameNumber < -1) && (previewSeconds < -1));
    1083     if (!UpgradeTVDatabaseSchema(allowUpgrade, allowUpgrade))
    1084     {
    1085         VERBOSE(VB_IMPORTANT, "Couldn't upgrade database to new schema");
    1086         return BACKEND_EXIT_DB_OUTOFDATE;
    1087     }
    1088 
    1089     close(0);
    1090 
    1091     if (printsched || testsched)
    1092     {
    1093         gContext->SetBackend(false);
    1094         sched = new Scheduler(false, &tvList);
    1095         if (!testsched && gContext->ConnectToMasterServer())
    1096         {
    1097             cout << "Retrieving Schedule from Master backend.\n";
    1098             sched->FillRecordListFromMaster();
    1099         }
    1100         else
    1101         {
    1102             cout << "Calculating Schedule from database.\n" <<
    1103                     "Inputs, Card IDs, and Conflict info may be invalid "
    1104                     "if you have multiple tuners.\n";
    1105             sched->FillRecordListFromDB();
    1106         }
    1107 
    1108         print_verbose_messages |= VB_SCHEDULE;
    1109         sched->PrintList(true);
    1110         return BACKEND_EXIT_OK;
    1111     }
    1112 
    1113     if (resched)
    1114     {
    1115         gContext->SetBackend(false);
    1116 
    1117         bool ok = false;
    1118         if (gContext->ConnectToMasterServer())
    1119         {
    1120             VERBOSE(VB_IMPORTANT, "Connected to master for reschedule");
    1121             ScheduledRecording::signalChange(-1);
    1122             ok = true;
    1123         }
    1124         else
    1125             VERBOSE(VB_IMPORTANT, "Cannot connect to master for reschedule");
    1126 
    1127         return (ok) ? BACKEND_EXIT_OK : BACKEND_EXIT_NO_CONNECT;
    1128     }
    1129 
    1130     if (!printexpire.isEmpty())
    1131     {
    1132         expirer = new AutoExpire();
    1133         expirer->PrintExpireList(printexpire);
    1134         return BACKEND_EXIT_OK;
    1135     }
    1136 
    1137     if ((previewFrameNumber >= -1) || (previewSeconds >= -1))
    1138     {
    1139         int ret = preview_helper(
    1140             chanid, starttime,
    1141             previewFrameNumber, previewSeconds, previewSize,
    1142             infile, outfile);
    1143         return ret;
    1144     }
    1145 
    1146     MythSystemEventHandler *sysEventHandler = new MythSystemEventHandler();
    1147 
    1148     int port = gContext->GetNumSetting("BackendServerPort", 6543);
    1149 
    1150     QString myip = gContext->GetSetting("BackendServerIP");
    1151     if (myip.isEmpty())
    1152     {
    1153         cerr << "No setting found for this machine's BackendServerIP.\n"
    1154              << "Please run setup on this machine and modify the first page\n"
    1155              << "of the general settings.\n";
    1156         delete sysEventHandler;
    1157         return BACKEND_EXIT_NO_IP_ADDRESS;
    1158     }
    1159 
    1160     bool ismaster = gContext->IsMasterHost();
    1161 
    1162     if (ismaster)
    1163     {
    1164         VERBOSE(VB_GENERAL, LOC + "Starting up as the master server.");
    1165         gContext->LogEntry("mythbackend", LP_INFO,
    1166                            "MythBackend started as master server", "");
    1167 
    1168         if (nosched)
    1169         {
    1170             VERBOSE(VB_IMPORTANT, LOC_WARN +
    1171                     "********** The Scheduler has been DISABLED with "
    1172                     "the --nosched option **********");
    1173         }
    1174 
    1175         // kill -USR1 mythbackendpid will force a upnpmedia rebuild
    1176         signal(SIGUSR1, &upnp_rebuild);
    1177     }
    1178     else
    1179     {
    1180         VERBOSE(VB_GENERAL, LOC + "Running as a slave backend.");
    1181         gContext->LogEntry("mythbackend", LP_INFO,
    1182                            "MythBackend started as a slave backend", "");
    1183     }
    1184 
    1185     bool fatal_error = false;
    1186     bool runsched = setupTVs(ismaster, fatal_error);
    1187     if (fatal_error)
    1188     {
    1189         delete sysEventHandler;
    1190         return BACKEND_EXIT_CAP_CARD_SETUP_ERROR;
    1191     }
    1192 
    1193     if (ismaster && runsched)
    1194     {
    1195         sched = new Scheduler(true, &tvList);
    1196         int err = sched->GetError();
    1197         if (err)
    1198         {
    1199             return err;
    1200         }
    1201 
    1202         if (nosched)
    1203             sched->DisableScheduling();
    1204     }
    1205 
    1206     // Get any initial housekeeping done before we fire up anything else
    1207     if (nohousekeeper)
    1208     {
    1209         VERBOSE(VB_IMPORTANT, LOC_WARN +
    1210                 "****** The Housekeeper has been DISABLED with "
    1211                 "the --nohousekeeper option ******");
    1212     }
    1213     else
    1214         housekeeping = new HouseKeeper(true, ismaster, sched);
    1215 
    1216     if (ismaster)
    1217     {
    1218         if (noexpirer)
    1219         {
    1220             VERBOSE(VB_IMPORTANT, LOC_WARN +
    1221                     "********* Auto-Expire has been DISABLED with "
    1222                     "the --noautoexpire option ********");
    1223         }
    1224         else
    1225             expirer = new AutoExpire(&tvList);
    1226     }
    1227 
    1228     if (sched && expirer)
    1229         sched->SetExpirer(expirer);
    1230 
    1231     if (nojobqueue)
    1232     {
    1233         VERBOSE(VB_IMPORTANT, LOC_WARN +
    1234                 "********* The JobQueue has been DISABLED with "
    1235                 "the --nojobqueue option *********");
    1236     }
    1237     else
    1238         jobqueue = new JobQueue(ismaster);
    1239 
    1240     // Start UPnP Services
    1241 
    1242     g_pUPnp = new MediaServer( ismaster, noupnp );
    1243 
    1244     HttpServer *pHS = g_pUPnp->GetHttpServer();
    1245     if (pHS)
    1246     {
    1247         VERBOSE(VB_IMPORTANT, "Main::Registering HttpStatus Extension");
    1248 
    1249         pHS->RegisterExtension( new HttpStatus( &tvList, sched,
    1250                                                 expirer, ismaster ));
    1251     }
    1252 
    1253     VERBOSE(VB_IMPORTANT, QString("Enabled verbose msgs: %1").arg(verboseString));
    1254 
    1255     MainServer *mainServer = new MainServer(ismaster, port, &tvList, sched,
    1256             expirer);
    1257 
    1258     int exitCode = mainServer->GetExitCode();
    1259     if (exitCode != BACKEND_EXIT_OK)
    1260     {
    1261         VERBOSE(VB_IMPORTANT, "Backend exiting, MainServer initialization "
    1262                 "error.");
    1263         delete mainServer;
    1264         return exitCode;
    1265     }
    1266 
    1267     StorageGroup::CheckAllStorageGroupDirs();
    1268 
    1269     if (gContext->IsMasterBackend())
    1270         SendMythSystemEvent("MASTER_STARTED");
    1271 
    1272     exitCode = a.exec();
    1273 
    1274     if (gContext->IsMasterBackend())
    1275     {
    1276         SendMythSystemEvent("MASTER_SHUTDOWN");
    1277         a.processEvents();
    1278     }
    1279 
    1280     gContext->LogEntry("mythbackend", LP_INFO, "MythBackend exiting", "");
    1281 
    1282     delete sysEventHandler;
    1283     delete mainServer;
    1284 
    1285     return exitCode ? exitCode : BACKEND_EXIT_OK;
     208    return exitCode;
    1286209}
    1287210
    1288211/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • programs/mythbackend/backendcontext.cpp

     
     1#include "backendcontext.h"
     2
     3QMap<int, EncoderLink *> tvList;
     4AutoExpire  *expirer      = NULL;
     5Scheduler   *sched        = NULL;
     6JobQueue    *jobqueue     = NULL;
     7HouseKeeper *housekeeping = NULL;
     8MediaServer *g_pUPnp      = NULL;
     9QString      pidfile;
     10QString      logfile;
  • programs/mythbackend/mythsettings.cpp

     
     1#include <QNetworkInterface>
     2#include <QDomDocument>
     3#include <QFile>
     4
     5#include "channelsettings.h" // for ChannelTVFormat::GetFormats()
     6#include "mythsettings.h"
     7#include "frequencies.h"
     8#include "mythcontext.h"
     9#include "mythdb.h"
     10
     11static QString indent(uint level)
     12{
     13    QString ret;
     14    for (uint i = 0; i < level; i++)
     15        ret += "    ";
     16    return ret;
     17}
     18
     19static QString extract_query_list(
     20    const MythSettingList &settings, MythSetting::SettingType stype)
     21{
     22    QString list;
     23
     24    MythSettingList::const_iterator it = settings.begin();
     25    for (; it != settings.end(); ++it)
     26    {
     27        const MythSettingGroup *group =
     28            dynamic_cast<const MythSettingGroup*>(*it);
     29        if (group)
     30        {
     31            list += extract_query_list(group->settings, stype);
     32            continue;
     33        }
     34        const MythSetting *setting = dynamic_cast<const MythSetting*>(*it);
     35        if (setting && (setting->stype == stype))
     36            list += QString(",'%1'").arg(setting->value);
     37    }
     38    if (!list.isEmpty() && (list[0] == QChar(',')))
     39        list = list.mid(1);
     40
     41    return list;
     42}
     43
     44static void fill_setting(
     45    MythSettingBase *sb, const QMap<QString,QString> &map,
     46    MythSetting::SettingType stype)
     47{
     48    const MythSettingGroup *group =
     49        dynamic_cast<const MythSettingGroup*>(sb);
     50    if (group)
     51    {
     52        MythSettingList::const_iterator it = group->settings.begin();
     53        for (; it != group->settings.end(); ++it)
     54            fill_setting(*it, map, stype);
     55        return;
     56    }
     57
     58    MythSetting *setting = dynamic_cast<MythSetting*>(sb);
     59    if (setting && (setting->stype == stype))
     60    {
     61        QMap<QString,QString>::const_iterator it = map.find(setting->value);
     62        if (it != map.end())
     63            setting->data = *it;
     64
     65        bool do_option_check = false;
     66        if (MythSetting::kLocalIPAddress == setting->dtype)
     67        {
     68            setting->data_list.clear();
     69            setting->display_list.clear();
     70            QList<QHostAddress> list = QNetworkInterface::allAddresses();
     71            for (uint i = 0; i < (uint)list.size(); i++)
     72            {
     73                if (list[i].toString().contains(":"))
     74                    continue; // ignore IP6 addresses for now
     75                setting->data_list.push_back(list[i].toString());
     76                setting->display_list.push_back(setting->data_list.back());
     77            }
     78            if (setting->data_list.isEmpty())
     79                setting->data_list.push_back("127.0.0.1");
     80            do_option_check = true;
     81        }
     82        else if (MythSetting::kSelect == setting->dtype)
     83        {
     84            do_option_check = true;
     85        }
     86        else if (MythSetting::kTVFormat == setting->dtype)
     87        {
     88            setting->data_list = setting->display_list =
     89                ChannelTVFormat::GetFormats();
     90            do_option_check = true;
     91        }
     92        else if (MythSetting::kFrequencyTable == setting->dtype)
     93        {
     94            setting->data_list.clear();
     95            for (uint i = 0; chanlists[i].name; i++)
     96                setting->data_list.push_back(chanlists[i].name);
     97            setting->display_list = setting->data_list;
     98            do_option_check = true;
     99        }
     100
     101        if (do_option_check)
     102        {
     103            if (!setting->data_list.empty() &&
     104                !setting->data_list.contains(setting->data.toLower(),
     105                                             Qt::CaseInsensitive))
     106            {
     107                bool ok;
     108                long long idata = setting->data.toLongLong(&ok);
     109                if (ok)
     110                {
     111                    uint sel = 0;
     112                    for (uint i = setting->data_list.size(); i >= 0; i--)
     113                    {
     114                        if (idata < setting->data_list[i].toLongLong())
     115                            break;
     116                        sel = i;
     117                    }
     118                    setting->data = setting->data_list[sel];
     119                }
     120                else
     121                {
     122                    setting->data =
     123                        (setting->data_list.contains(
     124                            setting->default_data, Qt::CaseInsensitive)) ?
     125                        setting->default_data : setting->data_list[0];
     126                }
     127            }
     128        }
     129    }
     130}
     131
     132static void fill_settings(
     133    MythSettingList &settings, MSqlQuery &query, MythSetting::SettingType stype)
     134{
     135    QMap<QString,QString> map;
     136    while (query.next())
     137        map[query.value(0).toString()] = query.value(1).toString();
     138
     139    MythSettingList::const_iterator it = settings.begin();
     140    for (; it != settings.end(); ++it)
     141        fill_setting(*it, map, stype);
     142}
     143
     144QString MythSettingGroup::ToHTML(uint depth) const
     145{
     146    QString ret;
     147
     148    ret = indent(depth) +
     149        QString("<div class=\"group\" id=\"%1\">\r\n").arg(unique_label);
     150    if (!human_label.isEmpty())
     151    {
     152        ret += indent(depth+1) + QString("<h%1>%2</h%3>\r\n")
     153            .arg(depth+1).arg(human_label).arg(depth+1);
     154    }
     155
     156    MythSettingList::const_iterator it = settings.begin();
     157    for (; it != settings.end(); ++it)
     158        ret += (*it)->ToHTML(depth+1);
     159
     160    ret += indent(depth) +"</div>";
     161
     162    return ret;
     163}
     164
     165QString MythSetting::ToHTML(uint level) const
     166{
     167    QString ret = indent(level) +
     168        QString("<div class=\"setting\" id=\"%1_div\">\r\n").arg(value);
     169
     170    switch (dtype)
     171    {
     172        case kInteger:
     173        case kUnsignedInteger:
     174        case kIntegerRange:
     175        case kFloat:
     176        case kComboBox:
     177        case kIPAddress:
     178        case kString:
     179        case kTimeOfDay:
     180        case kOther:
     181            ret += indent(level) +
     182                "<div class=\"setting_label\">" + label + "</div>\r\n";
     183            ret += indent(level) +
     184                QString("<input name=\"%1\" id=\"%2_input\" type=\"text\""
     185                        " value=\"%3\"/>\r\n")
     186                .arg(value).arg(value).arg(data);
     187            ret += indent(level) +
     188                QString("<div style=\"display:none;"
     189                        "position:absolute;left:-4000px\" "
     190                        "id=\"%1_default\">%2</div>\r\n")
     191                .arg(value).arg(default_data);
     192            break;
     193        case kCheckBox:
     194            ret += indent(level) +
     195                "<div class=\"setting_label\">" + label + "</div>\r\n";
     196            ret += indent(level) +
     197                QString("<input name=\"%1\" id=\"%2_input\" type=\"checkbox\""
     198                        " value=\"1\" %3/>\r\n")
     199                .arg(value).arg(value).arg((data.toUInt()) ? "checked" : "");
     200            ret += indent(level) +
     201                QString("<div style=\"display:none;"
     202                        "position:absolute;left:-4000px\" "
     203                        "id=\"%1_default\">%2</div>\r\n")
     204                .arg(value).arg(default_data);
     205            break;
     206        case kLocalIPAddress:
     207        case kTVFormat:
     208        case kFrequencyTable:
     209        case kSelect:
     210            ret +=  indent(level) +
     211                "<div class=\"setting_label\">" + label + "</div>\r\n";
     212            ret +=  indent(level) +
     213                QString("<select name=\"%1\" id=\"%2_input\">\r\n")
     214                .arg(value).arg(value);
     215            for (uint i = 0; (i < (uint)data_list.size()) &&
     216                     (i < (uint)display_list.size()); i++)
     217            {
     218                ret += indent(level+1) +
     219                    QString("<option value=\"%1\" %2>%3</option>\r\n")
     220                    .arg(data_list[i])
     221                    .arg((data_list[i].toLower() == data.toLower()) ?
     222                         "selected" : "")
     223                    .arg(display_list[i]);
     224            }
     225            ret += indent(level) + "</select>\r\n";
     226            ret += indent(level) +
     227                QString("<div style=\"display:none;"
     228                        "position:absolute;left:-4000px\" "
     229                        "id=\"%1_default\">%2</div>\r\n")
     230                .arg(value).arg(default_data);
     231            break;
     232    }
     233
     234    ret += indent(level) + "</div>\r\n";
     235
     236    return ret;
     237}
     238
     239MythSetting::SettingType parse_setting_type(const QString &str)
     240{
     241    QString s = str.toLower();
     242    if (s=="file")
     243        return MythSetting::kFile;
     244    if (s=="host")
     245        return MythSetting::kHost;
     246    if (s=="global")
     247        return MythSetting::kGlobal;
     248    return MythSetting::kInvalidSettingType;
     249}
     250
     251MythSetting::DataType parse_data_type(const QString &str)
     252{
     253    QString s = str.toLower();
     254    if (s == "integer")
     255        return MythSetting::kInteger;
     256    if (s == "unsigned")
     257        return MythSetting::kUnsignedInteger;
     258    if (s == "integer_range")
     259        return MythSetting::kIntegerRange;
     260    if (s == "checkbox")
     261        return MythSetting::kCheckBox;
     262    if (s == "select")
     263        return MythSetting::kSelect;
     264    if (s == "combobox")
     265        return MythSetting::kComboBox;
     266    if (s == "tvformat")
     267        return MythSetting::kTVFormat;
     268    if (s == "frequency_table")
     269        return MythSetting::kFrequencyTable;
     270    if (s == "float")
     271        return MythSetting::kFloat;
     272    if (s == "ipaddress")
     273        return MythSetting::kIPAddress;
     274    if (s == "localipaddress")
     275        return MythSetting::kLocalIPAddress;
     276    if (s == "string")
     277        return MythSetting::kString;
     278    if (s == "timeofday")
     279        return MythSetting::kTimeOfDay;
     280    if (s == "other")
     281        return MythSetting::kOther;
     282    VERBOSE(VB_IMPORTANT, QString("Unknown type: %1").arg(str));
     283    return MythSetting::kInvalidDataType;
     284}
     285
     286bool parse_dom(MythSettingList &settings, const QDomElement &element,
     287               const QString &filename)
     288{
     289#define LOC QString("parse_dom(%1@~%2), error: ") \
     290            .arg(filename).arg(e.lineNumber())
     291
     292    QDomNode n = element.firstChild();
     293    while (!n.isNull())
     294    {
     295        const QDomElement e = n.toElement();
     296        if (e.isNull())
     297        {
     298            n = n.nextSibling();
     299            continue;
     300        }
     301
     302        if (e.tagName() == "group")
     303        {
     304            QString human_label  = e.attribute("human_label");
     305            QString unique_label = e.attribute("unique_label");
     306            QString ecma_script  = e.attribute("ecma_script");
     307
     308            MythSettingGroup *g = new MythSettingGroup(
     309                human_label, unique_label, ecma_script);
     310
     311            if (e.hasChildNodes() && !parse_dom(g->settings, e, filename))
     312                return false;
     313
     314            settings.push_back(g);
     315        }
     316        else if (e.tagName() == "setting")
     317        {
     318            QMap<QString,QString> m;
     319            m["value"]        = e.attribute("value");
     320            m["setting_type"] = e.attribute("setting_type");
     321            m["label"]        = e.attribute("label");
     322            m["help_text"]    = e.attribute("help_text");
     323            m["data_type"]    = e.attribute("data_type");
     324
     325            MythSetting::DataType dtype = parse_data_type(m["data_type"]);
     326            if (MythSetting::kInvalidDataType == dtype)
     327            {
     328                VERBOSE(VB_IMPORTANT, LOC +
     329                        "Setting has invalid or missing data_type attribute.");
     330                return false;
     331            }
     332
     333            QStringList data_list;
     334            QStringList display_list;
     335            if ((MythSetting::kComboBox == dtype) ||
     336                (MythSetting::kSelect   == dtype))
     337            {
     338                if (!e.hasChildNodes())
     339                {
     340                    VERBOSE(VB_IMPORTANT, LOC +
     341                            "Setting missing selection items.");
     342                    return false;
     343                }
     344
     345                QDomNode n2 = e.firstChild();
     346                while (!n2.isNull())
     347                {
     348                    const QDomElement e2 = n2.toElement();
     349                    if (e2.tagName() != "option")
     350                    {
     351                        VERBOSE(VB_IMPORTANT, LOC +
     352                                "Setting selection contains invalid tags.");
     353                        return false;
     354                    }
     355                    QString display = e2.attribute("display");
     356                    QString data    = e2.attribute("data");
     357                    if (data.isEmpty())
     358                    {
     359                        VERBOSE(VB_IMPORTANT, LOC +
     360                                "Setting selection item missing data.");
     361                        return false;
     362                    }
     363                    display = (display.isEmpty()) ? data : display;
     364                    data_list.push_back(data);
     365                    display_list.push_back(display);
     366
     367                    n2 = n2.nextSibling();
     368                }
     369            }
     370
     371            if (MythSetting::kIntegerRange == dtype)
     372            {
     373                m["range_min"] = e.attribute("range_min");
     374                m["range_max"] = e.attribute("range_max");
     375            }
     376
     377            QMap<QString,QString>::const_iterator it = m.begin();
     378            for (; it != m.end(); ++it)
     379            {
     380                if ((*it).isEmpty())
     381                {
     382                    VERBOSE(VB_IMPORTANT, LOC +
     383                            QString("Setting has invalid or missing "
     384                                    "%1 attribute")
     385                            .arg(it.key()));
     386                    return false;
     387                }
     388            }
     389
     390            m["default_data"] = e.attribute("default_data");
     391
     392            MythSetting::SettingType stype =
     393                parse_setting_type(m["setting_type"]);
     394            if (MythSetting::kInvalidSettingType == stype)
     395            {
     396                VERBOSE(VB_IMPORTANT, LOC +
     397                        "Setting has invalid setting_type attribute.");
     398                return false;
     399            }
     400
     401            long long range_min = m["range_min"].toLongLong();
     402            long long range_max = m["range_max"].toLongLong();
     403            if (range_max < range_min)
     404            {
     405                VERBOSE(VB_IMPORTANT, LOC +
     406                        "Setting has invalid range attributes");
     407                return false;
     408            }
     409
     410            MythSetting *s = new MythSetting(
     411                m["value"], m["default_data"], stype,
     412                m["label"], m["help_text"], dtype,
     413                data_list, display_list, range_min, range_max);
     414
     415            settings.push_back(s);
     416        }
     417        else
     418        {
     419            VERBOSE(VB_IMPORTANT, LOC +
     420                    QString("Unknown element: %1").arg(e.tagName()));
     421            return false;
     422        }
     423        n = n.nextSibling();
     424    }
     425    return true;
     426#undef LOC
     427}
     428
     429bool parse_settings(MythSettingList &settings, const QString &filename)
     430{
     431    QDomDocument doc;
     432    QFile f(filename);
     433
     434    if (!f.open(QIODevice::ReadOnly))
     435    {
     436        VERBOSE(VB_IMPORTANT, QString("parse_settings: Can't open: '%1'")
     437                .arg(filename));
     438        return false;
     439    }
     440
     441    QString errorMsg;
     442    int errorLine = 0;
     443    int errorColumn = 0;
     444
     445    if (!doc.setContent(&f, false, &errorMsg, &errorLine, &errorColumn))
     446    {
     447        VERBOSE(VB_IMPORTANT, QString("parse_settings: ") +
     448                QString("Parsing: %1 at line: %2 column: %3")
     449                .arg(filename).arg(errorLine).arg(errorColumn) +
     450                QString("\n\t\t\t%1").arg(errorMsg));
     451        f.close();
     452        return false;
     453    }
     454    f.close();
     455
     456    settings.clear();
     457    return parse_dom(settings, doc.documentElement(), filename);
     458}
     459
     460bool load_settings(MythSettingList &settings, const QString &hostname)
     461{
     462    MSqlQuery query(MSqlQuery::InitCon());
     463
     464    QString list = extract_query_list(settings, MythSetting::kFile);
     465    if (!list.isEmpty())
     466    {
     467        DatabaseParams params;
     468        bool ok = MythDB::LoadDatabaseParamsFromDisk(params, true);
     469        if (!ok)
     470            return false;
     471
     472        QMap<QString,QString> map;
     473        map["host"]                = params.dbHostName;
     474        map["port"] = QString::number(params.dbPort);
     475        map["ping"] = QString::number(params.dbHostPing);
     476        map["database"]            = params.dbName;
     477        map["user"]                = params.dbUserName;
     478        map["password"]            = params.dbPassword;
     479        map["uniqueid"]            = params.localHostName;
     480        map["wol_enabled"]         =
     481            QString::number(params.wolEnabled);
     482        map["wol_reconnect_count"] =
     483            QString::number(params.wolReconnect);
     484        map["wol_retry_count "]    =
     485            QString::number(params.wolRetry);
     486        map["wol_command"]         = params.wolCommand;
     487
     488        MythSettingList::const_iterator it = settings.begin();
     489        for (; it != settings.end(); ++it)
     490            fill_setting(*it, map, MythSetting::kFile);
     491    }
     492
     493    list = extract_query_list(settings, MythSetting::kHost);
     494    QString qstr =
     495        "SELECT value, data "
     496        "FROM settings "
     497        "WHERE hostname = '" + hostname + "' AND "
     498        "      value in (" + list + ")";
     499
     500    if (!list.isEmpty())
     501    {
     502        if (!query.exec(qstr))
     503        {
     504            MythDB::DBError("HttpConfig::LoadMythSettings() 1", query);
     505            return false;
     506        }
     507        fill_settings(settings, query, MythSetting::kHost);
     508    }
     509
     510    list = extract_query_list(settings, MythSetting::kGlobal);
     511    qstr =
     512        "SELECT value, data "
     513        "FROM settings "
     514        "WHERE hostname IS NULL AND "
     515        "      value in (" + list + ")";
     516   
     517    if (!list.isEmpty())
     518    {
     519        if (!query.exec(qstr))
     520        {
     521            MythDB::DBError("HttpConfig::LoadMythSettings() 2", query);
     522            return false;
     523        }
     524        fill_settings(settings, query, MythSetting::kGlobal);
     525    }
     526
     527    return true;
     528}
     529
  • programs/mythbackend/moc_server.cpp

     
     1/****************************************************************************
     2** Meta object code from reading C++ file 'server.h'
     3**
     4** Created: Sun Mar 28 11:53:37 2010
     5**      by: The Qt Meta Object Compiler version 61 (Qt 4.5.2)
     6**
     7** WARNING! All changes made in this file will be lost!
     8*****************************************************************************/
     9
     10#include "server.h"
     11#if !defined(Q_MOC_OUTPUT_REVISION)
     12#error "The header file 'server.h' doesn't include <QObject>."
     13#elif Q_MOC_OUTPUT_REVISION != 61
     14#error "This file was generated using the moc from 4.5.2. It"
     15#error "cannot be used with the include files from this version of Qt."
     16#error "(The moc has changed too much.)"
     17#endif
     18
     19QT_BEGIN_MOC_NAMESPACE
     20static const uint qt_meta_data_MythServer[] = {
     21
     22 // content:
     23       2,       // revision
     24       0,       // classname
     25       0,    0, // classinfo
     26       1,   12, // methods
     27       0,    0, // properties
     28       0,    0, // enums/sets
     29       0,    0, // constructors
     30
     31 // signals: signature, parameters, type, tag, flags
     32      12,   11,   11,   11, 0x05,
     33
     34       0        // eod
     35};
     36
     37static const char qt_meta_stringdata_MythServer[] = {
     38    "MythServer\0\0newConnect(MythSocket*)\0"
     39};
     40
     41const QMetaObject MythServer::staticMetaObject = {
     42    { &QTcpServer::staticMetaObject, qt_meta_stringdata_MythServer,
     43      qt_meta_data_MythServer, 0 }
     44};
     45
     46const QMetaObject *MythServer::metaObject() const
     47{
     48    return &staticMetaObject;
     49}
     50
     51void *MythServer::qt_metacast(const char *_clname)
     52{
     53    if (!_clname) return 0;
     54    if (!strcmp(_clname, qt_meta_stringdata_MythServer))
     55        return static_cast<void*>(const_cast< MythServer*>(this));
     56    return QTcpServer::qt_metacast(_clname);
     57}
     58
     59int MythServer::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
     60{
     61    _id = QTcpServer::qt_metacall(_c, _id, _a);
     62    if (_id < 0)
     63        return _id;
     64    if (_c == QMetaObject::InvokeMetaMethod) {
     65        switch (_id) {
     66        case 0: newConnect((*reinterpret_cast< MythSocket*(*)>(_a[1]))); break;
     67        default: ;
     68        }
     69        _id -= 1;
     70    }
     71    return _id;
     72}
     73
     74// SIGNAL 0
     75void MythServer::newConnect(MythSocket * _t1)
     76{
     77    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
     78    QMetaObject::activate(this, &staticMetaObject, 0, _a);
     79}
     80QT_END_MOC_NAMESPACE
  • programs/mythbackend/httpconfig.cpp

     
     1// Qt headers
     2#include <QTextStream>
     3
     4// MythTV headers
     5#include "httpconfig.h"
     6#include "backendutil.h"
     7#include "mythxml.h"
     8
     9#include "mythcontext.h"
     10#include "mythdb.h"
     11
     12HttpConfig::HttpConfig() : HttpServerExtension("HttpConfig", QString())
     13{
     14}
     15
     16HttpConfig::~HttpConfig()
     17{
     18}
     19
     20bool HttpConfig::ProcessRequest(HttpWorkerThread*, HTTPRequest *request)
     21{
     22    if (!request)
     23        return false;
     24
     25    if (request->m_sBaseUrl != "/" && request->m_sBaseUrl != "/config")
     26        return false;
     27
     28    bool handled = false;
     29    if ((request->m_sMethod.toLower() == "config") || (NULL == gContext))
     30    {
     31        PrintHeader(request->m_response);
     32        ParseDatabaseSettings();
     33        load_settings(database_settings, "");
     34        PrintSettings(request->m_response, database_settings);
     35        PrintFooter(request->m_response);
     36        handled = true;
     37    }
     38    else if (request->m_sMethod.toLower() == "general")
     39    {
     40        PrintHeader(request->m_response);
     41        parse_settings(general_settings, "/home/danielk/settings.xml");
     42        load_settings(general_settings, gContext->GetHostName());
     43        PrintSettings(request->m_response, general_settings);
     44        PrintFooter(request->m_response);
     45        handled = true;
     46    }
     47    else if (request->m_sMethod.toLower() == "parsed")
     48    {
     49        PrintHeader(request->m_response);
     50        parse_settings(general_settings, "/home/danielk/settings.xml");
     51        load_settings(general_settings, gContext->GetHostName());
     52        PrintSettings(request->m_response, general_settings);
     53        PrintFooter(request->m_response);
     54        handled = true;
     55    }
     56
     57    if (handled)
     58    {
     59        request->m_eResponseType = ResponseTypeHTML;
     60        request->m_mapRespHeaders[ "Cache-Control" ] =
     61            "no-cache=\"Ext\", max-age = 0";
     62    }
     63
     64    return handled;
     65}
     66
     67void HttpConfig::PrintHeader(QTextStream &os)
     68{
     69    os.setCodec("UTF-8");
     70
     71    os << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
     72       << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
     73       << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
     74       << " xml:lang=\"en\" lang=\"en\">\r\n"
     75       << "<head>\r\n"
     76       << "  <meta http-equiv=\"Content-Type\""
     77       << "content=\"text/html; charset=UTF-8\" />\r\n"
     78       << "  <style type=\"text/css\" title=\"Default\" media=\"all\">\r\n"
     79       << "  body {\r\n"
     80       << "    background-color:#fff;\r\n"
     81       << "    font:11px verdana, arial, helvetica, sans-serif;\r\n"
     82       << "    margin:20px;\r\n"
     83       << "  }\r\n"
     84       << "  h1 {\r\n"
     85       << "    font-size:28px;\r\n"
     86       << "    font-weight:900;\r\n"
     87       << "    color:#ccc;\r\n"
     88       << "    letter-spacing:0.5em;\r\n"
     89       << "    margin-bottom:30px;\r\n"
     90       << "    width:650px;\r\n"
     91       << "    text-align:center;\r\n"
     92       << "  }\r\n"
     93       << "  h2 {\r\n"
     94       << "    font-size:18px;\r\n"
     95       << "    font-weight:800;\r\n"
     96       << "    color:#360;\r\n"
     97       << "    border:none;\r\n"
     98       << "    letter-spacing:0.3em;\r\n"
     99       << "    padding:0px;\r\n"
     100       << "    margin-bottom:10px;\r\n"
     101       << "    margin-top:0px;\r\n"
     102       << "  }\r\n"
     103       << "  h3 {\r\n"
     104       << "    font-size:14px;\r\n"
     105       << "    font-weight:800;\r\n"
     106       << "    color:#360;\r\n"
     107       << "    border:none;\r\n"
     108       << "    letter-spacing:0.3em;\r\n"
     109       << "    padding:0px;\r\n"
     110       << "    margin-bottom:10px;\r\n"
     111       << "    margin-top:0px;\r\n"
     112       << "  }\r\n"
     113       << "  </style>\r\n"
     114       << "  <title>MythTV Config</title>"
     115       << "</head>\r\n"
     116       << "<body>\r\n\r\n"
     117       << "  <h1>MythTV Configuration</h1>\r\n"
     118       << "  <form>\r\n";
     119}
     120
     121void HttpConfig::PrintFooter(QTextStream &os)
     122{
     123    os << "  </form>\r\n"
     124       << "\r\n</body>\r\n</html>\r\n";
     125}
     126
     127void HttpConfig::ParseDatabaseSettings(void)
     128{
     129    database_settings.clear();
     130
     131    MythSettingGroup *database =
     132        new MythSettingGroup(QObject::tr("Database Setup"), "database");
     133
     134    database->settings.push_back(
     135        new MythSetting(
     136            "host", "localhost",
     137            MythSetting::kFile, QObject::tr("Host"),
     138            QObject::tr(
     139                "DNS name or IP of server containing database or "
     140                "'localhost' which will connect to the server using "
     141                "a unix pipe."),
     142            MythSetting::kString));
     143   
     144    database->settings.push_back(
     145        new MythSetting(
     146            "port", "3306", MythSetting::kFile, QObject::tr("Port"),
     147            QObject::tr(
     148                "Port on which the remote database server listens."),
     149            MythSetting::kIntegerRange, 0, 0xffff));
     150
     151    database->settings.push_back(
     152        new MythSetting(
     153            "ping", "1", MythSetting::kFile, QObject::tr("Ping"),
     154            QObject::tr("Ping the remote server."),
     155            MythSetting::kCheckBox));
     156
     157    database->settings.push_back(
     158        new MythSetting(
     159            "database", "mythconverg",
     160            MythSetting::kFile, QObject::tr("Database"),
     161            QObject::tr("Database containing MythTV tables."),
     162            MythSetting::kString));
     163
     164    database->settings.push_back(
     165        new MythSetting(
     166            "user", "mythtv",
     167            MythSetting::kFile, QObject::tr("User"),
     168            QObject::tr("Database user name with read and write "
     169                        "access to MythTV tables"),
     170            MythSetting::kString));
     171
     172    database->settings.push_back(
     173        new MythSetting(
     174            "password", "mythtv",
     175            MythSetting::kFile, QObject::tr("Password"),
     176            QObject::tr("Password for database user."),
     177            MythSetting::kString));
     178
     179    MythSettingGroup *database_wol =
     180        new MythSettingGroup(QObject::tr("Database Wake on LAN"), "database_wol");
     181
     182    database_wol->settings.push_back(
     183        new MythSetting(
     184            "wol_enabled", "1", MythSetting::kFile, QObject::tr("Enabled"),
     185            "",
     186            MythSetting::kCheckBox));
     187
     188    database_wol->settings.push_back(
     189        new MythSetting(
     190            "wol_reconnect_count", "0", MythSetting::kFile,
     191            QObject::tr("Reconnect Count"),
     192            "",
     193            MythSetting::kIntegerRange, 0, 10));
     194
     195    database_wol->settings.push_back(
     196        new MythSetting(
     197            "wol_retry_count", "5", MythSetting::kFile,
     198            QObject::tr("Retry Count"),
     199            "",
     200            MythSetting::kIntegerRange, 0, 10));
     201
     202    database_wol->settings.push_back(
     203        new MythSetting(
     204            "wol_command", "echo 'WOLsqlServerCommand not set'",
     205            MythSetting::kFile, QObject::tr("Command"), "",
     206            MythSetting::kString));
     207
     208    database->settings.push_back(database_wol);
     209
     210    database_settings.push_back(database);
     211}
     212
     213void HttpConfig::PrintSettings(QTextStream &os, const MythSettingList &settings)
     214{
     215    MythSettingList::const_iterator it = settings.begin();
     216    for (; it != settings.end(); ++it)
     217        os << (*it)->ToHTML(1);
     218}
  • programs/mythbackend/main_helpers.cpp

     
     1// POSIX headers
     2#include <sys/time.h>     // for setpriority
     3#include <unistd.h>
     4#include <sys/types.h>
     5#include <sys/stat.h>
     6#include <fcntl.h>
     7#include <libgen.h>
     8#include <signal.h>
     9#include <pwd.h>
     10
     11#include "mythconfig.h"
     12#if CONFIG_DARWIN
     13    #include <sys/aio.h>    // O_SYNC
     14#endif
     15
     16// C headers
     17#include <cstdlib>
     18#include <cerrno>
     19
     20#include <QCoreApplication>
     21#include <QFileInfo>
     22#include <QRegExp>
     23#include <QFile>
     24#include <QDir>
     25#include <QMap>
     26
     27#include "tv_rec.h"
     28#include "scheduledrecording.h"
     29#include "autoexpire.h"
     30#include "scheduler.h"
     31#include "mainserver.h"
     32#include "encoderlink.h"
     33#include "remoteutil.h"
     34#include "housekeeper.h"
     35
     36#include "mythcontext.h"
     37#include "mythverbose.h"
     38#include "mythversion.h"
     39#include "mythdb.h"
     40#include "exitcodes.h"
     41#include "compat.h"
     42#include "storagegroup.h"
     43#include "programinfo.h"
     44#include "dbcheck.h"
     45#include "jobqueue.h"
     46#include "previewgenerator.h"
     47#include "mythcommandlineparser.h"
     48#include "mythsystemevent.h"
     49#include "main_helpers.h"
     50#include "backendcontext.h"
     51
     52#include "mediaserver.h"
     53#include "httpstatus.h"
     54
     55#define LOC      QString("MythBackend: ")
     56#define LOC_WARN QString("MythBackend, Warning: ")
     57#define LOC_ERR  QString("MythBackend, Error: ")
     58
     59bool setupTVs(bool ismaster, bool &error)
     60{
     61    error = false;
     62    QString localhostname = gContext->GetHostName();
     63
     64    MSqlQuery query(MSqlQuery::InitCon());
     65
     66    if (ismaster)
     67    {
     68        // Hack to make sure recorded.basename gets set if the user
     69        // downgrades to a prior version and creates new entries
     70        // without it.
     71        if (!query.exec("UPDATE recorded SET basename = CONCAT(chanid, '_', "
     72                        "DATE_FORMAT(starttime, '%Y%m%d%H%i00'), '_', "
     73                        "DATE_FORMAT(endtime, '%Y%m%d%H%i00'), '.nuv') "
     74                        "WHERE basename = '';"))
     75            MythDB::DBError("Updating record basename",
     76                                 query.lastQuery());
     77
     78        // Hack to make sure record.station gets set if the user
     79        // downgrades to a prior version and creates new entries
     80        // without it.
     81        if (!query.exec("UPDATE channel SET callsign=chanid "
     82                        "WHERE callsign IS NULL OR callsign='';"))
     83            MythDB::DBError("Updating channel callsign", query.lastQuery());
     84
     85        if (query.exec("SELECT MIN(chanid) FROM channel;"))
     86        {
     87            query.first();
     88            int min_chanid = query.value(0).toInt();
     89            if (!query.exec(QString("UPDATE record SET chanid = %1 "
     90                                    "WHERE chanid IS NULL;").arg(min_chanid)))
     91                MythDB::DBError("Updating record chanid", query.lastQuery());
     92        }
     93        else
     94            MythDB::DBError("Querying minimum chanid", query.lastQuery());
     95
     96        MSqlQuery records_without_station(MSqlQuery::InitCon());
     97        records_without_station.prepare("SELECT record.chanid,"
     98                " channel.callsign FROM record LEFT JOIN channel"
     99                " ON record.chanid = channel.chanid WHERE record.station='';");
     100        if (records_without_station.exec() && records_without_station.next())
     101        {
     102            MSqlQuery update_record(MSqlQuery::InitCon());
     103            update_record.prepare("UPDATE record SET station = :CALLSIGN"
     104                    " WHERE chanid = :CHANID;");
     105            do
     106            {
     107                update_record.bindValue(":CALLSIGN",
     108                        records_without_station.value(1));
     109                update_record.bindValue(":CHANID",
     110                        records_without_station.value(0));
     111                if (!update_record.exec())
     112                {
     113                    MythDB::DBError("Updating record station",
     114                            update_record.lastQuery());
     115                }
     116            } while (records_without_station.next());
     117        }
     118    }
     119
     120    if (!query.exec(
     121            "SELECT cardid, hostname "
     122            "FROM capturecard "
     123            "ORDER BY cardid"))
     124    {
     125        MythDB::DBError("Querying Recorders", query);
     126        return false;
     127    }
     128
     129    vector<uint>    cardids;
     130    vector<QString> hosts;
     131    while (query.next())
     132    {
     133        uint    cardid = query.value(0).toUInt();
     134        QString host   = query.value(1).toString();
     135        QString cidmsg = QString("Card %1").arg(cardid);
     136
     137        if (host.isEmpty())
     138        {
     139            QString msg = cidmsg + " does not have a hostname defined.\n"
     140                "Please run setup and confirm all of the capture cards.\n";
     141
     142            VERBOSE(VB_IMPORTANT, msg);
     143            gContext->LogEntry("mythbackend", LP_CRITICAL,
     144                               "Problem with capture cards", msg);
     145            continue;
     146        }
     147
     148        cardids.push_back(cardid);
     149        hosts.push_back(host);
     150    }
     151
     152    for (uint i = 0; i < cardids.size(); i++)
     153    {
     154        if (hosts[i] == localhostname)
     155            new TVRec(cardids[i]);
     156    }
     157
     158    for (uint i = 0; i < cardids.size(); i++)
     159    {
     160        uint    cardid = cardids[i];
     161        QString host   = hosts[i];
     162        QString cidmsg = QString("Card %1").arg(cardid);
     163
     164        if (!ismaster)
     165        {
     166            if (host == localhostname)
     167            {
     168                TVRec *tv = TVRec::GetTVRec(cardid);
     169                if (tv->Init())
     170                {
     171                    EncoderLink *enc = new EncoderLink(cardid, tv);
     172                    tvList[cardid] = enc;
     173                }
     174                else
     175                {
     176                    gContext->LogEntry("mythbackend", LP_CRITICAL,
     177                                       "Problem with capture cards",
     178                                       cidmsg + " failed init");
     179                    delete tv;
     180                    // The master assumes card comes up so we need to
     181                    // set error and exit if a non-master card fails.
     182                    error = true;
     183                }
     184            }
     185        }
     186        else
     187        {
     188            if (host == localhostname)
     189            {
     190                TVRec *tv = TVRec::GetTVRec(cardid);
     191                if (tv->Init())
     192                {
     193                    EncoderLink *enc = new EncoderLink(cardid, tv);
     194                    tvList[cardid] = enc;
     195                }
     196                else
     197                {
     198                    gContext->LogEntry("mythbackend", LP_CRITICAL,
     199                                       "Problem with capture cards",
     200                                       cidmsg + "failed init");
     201                    delete tv;
     202                }
     203            }
     204            else
     205            {
     206                EncoderLink *enc = new EncoderLink(cardid, NULL, host);
     207                tvList[cardid] = enc;
     208            }
     209        }
     210    }
     211
     212    if (tvList.empty())
     213    {
     214        VERBOSE(VB_IMPORTANT, LOC_ERR +
     215                "No valid capture cards are defined in the database.\n\t\t\t"
     216                "Perhaps you should re-read the installation instructions?");
     217
     218        gContext->LogEntry("mythbackend", LP_CRITICAL,
     219                           "No capture cards are defined",
     220                           "Please run the setup program.");
     221        return false;
     222    }
     223
     224    return true;
     225}
     226
     227bool setup_context(const MythCommandLineParser &cmdline)
     228{
     229    if (!gContext->Init(false))
     230    {
     231        VERBOSE(VB_IMPORTANT, "Failed to init MythContext.");
     232        delete gContext;
     233        gContext = NULL;
     234        return false;
     235    }
     236    gContext->SetBackend(!cmdline.HasBackendCommand());
     237
     238    QMap<QString,QString> settingsOverride = cmdline.GetSettingsOverride();
     239    if (settingsOverride.size())
     240    {
     241        QMap<QString, QString>::iterator it;
     242        for (it = settingsOverride.begin(); it != settingsOverride.end(); ++it)
     243        {
     244            VERBOSE(VB_IMPORTANT, QString("Setting '%1' being forced to '%2'")
     245                    .arg(it.key()).arg(*it));
     246            gContext->OverrideSettingForSession(it.key(), *it);
     247        }
     248    }
     249
     250    return true;
     251}
     252
     253void cleanup(void)
     254{
     255    delete sched;
     256    sched = NULL;
     257
     258    delete g_pUPnp;
     259    g_pUPnp = NULL;
     260
     261    delete gContext;
     262    gContext = NULL;
     263
     264    if (pidfile.size())
     265    {
     266        unlink(pidfile.toAscii().constData());
     267        pidfile.clear();
     268    }
     269
     270    signal(SIGHUP, SIG_DFL);
     271    signal(SIGUSR1, SIG_DFL);
     272}
     273
     274int log_rotate(int report_error)
     275{
     276    /* http://www.gossamer-threads.com/lists/mythtv/dev/110113 */
     277
     278    int new_logfd = open(logfile.toLocal8Bit().constData(),
     279                         O_WRONLY|O_CREAT|O_APPEND|O_SYNC, 0664);
     280    if (new_logfd < 0)
     281    {
     282        // If we can't open the new logfile, send data to /dev/null
     283        if (report_error)
     284        {
     285            VERBOSE(VB_IMPORTANT, LOC_ERR +
     286                    QString("Cannot open logfile '%1'").arg(logfile));
     287            return -1;
     288        }
     289        new_logfd = open("/dev/null", O_WRONLY);
     290        if (new_logfd < 0)
     291        {
     292            // There's not much we can do, so punt.
     293            return -1;
     294        }
     295    }
     296    while (dup2(new_logfd, 1) < 0 && errno == EINTR) ;
     297    while (dup2(new_logfd, 2) < 0 && errno == EINTR) ;
     298    while (close(new_logfd) < 0 && errno == EINTR) ;
     299    return 0;
     300}
     301
     302void log_rotate_handler(int)
     303{
     304    log_rotate(0);
     305}
     306
     307void upnp_rebuild(int)
     308{
     309    if (gContext->IsMasterHost())
     310    {
     311        g_pUPnp->RebuildMediaMap();
     312    }
     313
     314}
     315
     316int preview_helper(const QString &chanid, const QString &starttime,
     317                   long long previewFrameNumber, long long previewSeconds,
     318                   const QSize &previewSize,
     319                   const QString &infile, const QString &outfile)
     320{
     321    // Lower scheduling priority, to avoid problems with recordings.
     322    if (setpriority(PRIO_PROCESS, 0, 9))
     323        VERBOSE(VB_GENERAL, "Setting priority failed." + ENO);
     324
     325    ProgramInfo *pginfo = NULL;
     326    if (!chanid.isEmpty() && !starttime.isEmpty())
     327    {
     328        pginfo = ProgramInfo::GetProgramFromRecorded(chanid, starttime);
     329        if (!pginfo)
     330        {
     331            VERBOSE(VB_IMPORTANT, QString(
     332                        "Can not locate recording made on '%1' at '%2'")
     333                    .arg(chanid).arg(starttime));
     334            return GENERIC_EXIT_NOT_OK;
     335        }
     336        pginfo->pathname = pginfo->GetPlaybackURL(false, true);
     337    }
     338    else if (!infile.isEmpty())
     339    {
     340        pginfo = ProgramInfo::GetProgramFromBasename(infile);
     341        if (!pginfo)
     342        {
     343            if (!QFileInfo(infile).exists())
     344            {
     345                VERBOSE(VB_IMPORTANT, QString(
     346                            "Can not locate recording '%1'").arg(infile));
     347                return GENERIC_EXIT_NOT_OK;
     348            }
     349            else
     350            {
     351                pginfo = new ProgramInfo();
     352                pginfo->isVideo = true;
     353
     354                QDir d(infile + "/VIDEO_TS");
     355                if ((infile.section('.', -1) == "iso") ||
     356                    (infile.section('.', -1) == "img") ||
     357                    d.exists())
     358                {
     359                    pginfo->pathname = QString("dvd:%1").arg(infile);
     360                }
     361                else
     362                {
     363                    pginfo->pathname = QFileInfo(infile).absoluteFilePath();
     364                }
     365            }
     366
     367        }
     368        else
     369        {
     370            pginfo->pathname = pginfo->GetPlaybackURL(false, true);
     371        }
     372    }
     373    else
     374    {
     375        VERBOSE(VB_IMPORTANT, "Can not locate recording for preview");
     376        return GENERIC_EXIT_NOT_OK;
     377    }
     378
     379    PreviewGenerator *previewgen = new PreviewGenerator(
     380        pginfo, PreviewGenerator::kLocal);
     381
     382    if (previewFrameNumber >= 0)
     383        previewgen->SetPreviewTimeAsFrameNumber(previewFrameNumber);
     384
     385    if (previewSeconds >= 0)
     386        previewgen->SetPreviewTimeAsSeconds(previewSeconds);
     387
     388    previewgen->SetOutputSize(previewSize);
     389    previewgen->SetOutputFilename(outfile);
     390    bool ok = previewgen->RunReal();
     391    previewgen->deleteLater();
     392
     393    delete pginfo;
     394
     395    return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_NOT_OK;
     396}
     397
     398void showUsage(const MythCommandLineParser &cmdlineparser, const QString &version)
     399{
     400    QString    help  = cmdlineparser.GetHelpString(false);
     401    QByteArray ahelp = help.toLocal8Bit();
     402
     403    cerr << qPrintable(version) << endl <<
     404    "Valid options are: " << endl <<
     405    "-h or --help                   List valid command line parameters" << endl <<
     406    "-l or --logfile filename       Writes STDERR and STDOUT messages to filename" << endl <<
     407    "-p or --pidfile filename       Write PID of mythbackend to filename" << endl <<
     408    "-d or --daemon                 Runs mythbackend as a daemon" << endl <<
     409    "-v or --verbose debug-level    Use '-v help' for level info" << endl <<
     410    "--setverbose debug-level       Change debug level of running master backend" << endl <<
     411    "--user username                Drop permissions to username after starting"  << endl <<
     412
     413    "--printexpire                  List of auto-expire programs" << endl <<
     414    "--printsched                   Upcoming scheduled programs" << endl <<
     415    "--testsched                    Test run scheduler (ignore existing schedule)" << endl <<
     416    "--resched                      Force the scheduler to update" << endl <<
     417    "--nosched                      Do not perform any scheduling" << endl <<
     418    "--noupnp                       Do not enable the UPNP server" << endl <<
     419    "--nojobqueue                   Do not start the JobQueue" << endl <<
     420    "--nohousekeeper                Do not start the Housekeeper" << endl <<
     421    "--noautoexpire                 Do not start the AutoExpire thread" << endl <<
     422    "--clearcache                   Clear the settings cache on all myth servers" << endl <<
     423    ahelp.constData() <<
     424    "--generate-preview             Generate a preview image" << endl <<
     425    "--upnprebuild                  Force an update of UPNP media" << endl <<
     426    "--infile                       Input file for preview generation" << endl <<
     427    "--outfile                      Optional output file for preview generation" << endl <<
     428    "--chanid                       Channel ID for preview generation" << endl <<
     429    "--starttime                    Recording start time for preview generation" << endl <<
     430    "--event EVENTTEXT              Send a backend event test message" << endl <<
     431    "--systemevent EVENTTEXT        Send a backend SYSTEM_EVENT test message" << endl
     432    << endl;
     433
     434}
     435
     436void setupLogfile(void)
     437{
     438    if (!logfile.isEmpty())
     439    {
     440        if (log_rotate(1) < 0)
     441        {
     442            VERBOSE(VB_IMPORTANT, LOC_WARN +
     443                    "Cannot open logfile; using stdout/stderr instead");
     444        }
     445        else
     446            signal(SIGHUP, &log_rotate_handler);
     447    }
     448}
     449
     450bool openPidfile(ofstream &pidfs, const QString &pidfile)
     451{
     452    if (!pidfile.isEmpty())
     453    {
     454        pidfs.open(pidfile.toAscii().constData());
     455        if (!pidfs)
     456        {
     457            VERBOSE(VB_IMPORTANT, LOC_ERR +
     458                    "Could not open pid file" + ENO);
     459            return false;
     460        }
     461    }
     462    return true;
     463}
     464
     465bool setUser(const QString &username)
     466{
     467    if (username.isEmpty())
     468        return true;
     469
     470    struct passwd *user_info = getpwnam(username.toLocal8Bit().constData());
     471    const uid_t user_id = geteuid();
     472
     473    if (user_id && (!user_info || user_id != user_info->pw_uid))
     474    {
     475        VERBOSE(VB_IMPORTANT,
     476                "You must be running as root to use the --user switch.");
     477        return false;
     478    }
     479    else if (user_info && user_id == user_info->pw_uid)
     480    {
     481        VERBOSE(VB_IMPORTANT,
     482                QString("Already running as '%1'").arg(username));
     483    }
     484    else if (!user_id && user_info)
     485    {
     486        if (setenv("HOME", user_info->pw_dir,1) == -1)
     487        {
     488            VERBOSE(VB_IMPORTANT, "Error setting home directory.");
     489            return false;
     490        }
     491        if (setgid(user_info->pw_gid) == -1)
     492        {
     493            VERBOSE(VB_IMPORTANT, "Error setting effective group.");
     494            return false;
     495        }
     496        if (setuid(user_info->pw_uid) == -1)
     497        {
     498            VERBOSE(VB_IMPORTANT, "Error setting effective user.");
     499            return false;
     500        }
     501    }
     502    else
     503    {
     504        VERBOSE(VB_IMPORTANT,
     505                QString("Invalid user '%1' specified with --user")
     506                .arg(username));
     507        return false;
     508    }
     509    return true;
     510}
     511
     512int handle_command(const MythCommandLineParser &cmdline)
     513{
     514    QString eventString = cmdline.GetEventString();
     515    if (!eventString.isEmpty())
     516    {
     517        if (gContext->ConnectToMasterServer())
     518        {
     519            if (eventString.startsWith("SYSTEM_EVENT"))
     520            {
     521                eventString += QString(" SENDER %1")
     522                    .arg(gContext->GetHostName());
     523            }
     524
     525            RemoteSendMessage(eventString);
     526            return BACKEND_EXIT_OK;
     527        }
     528        return BACKEND_EXIT_NO_MYTHCONTEXT;
     529    }
     530
     531    if (cmdline.WantUPnPRebuild())
     532    {
     533        VERBOSE(VB_GENERAL, "Rebuilding UPNP Media Map");
     534
     535        UPnpMedia *rebuildit = new UPnpMedia(false,false);
     536        rebuildit->BuildMediaMap();
     537
     538        return BACKEND_EXIT_OK;
     539    }
     540
     541    if (cmdline.SetVerbose())
     542    {
     543        if (gContext->ConnectToMasterServer())
     544        {
     545            QString message = "SET_VERBOSE ";
     546            message += cmdline.GetNewVerbose();
     547
     548            RemoteSendMessage(message);
     549            VERBOSE(VB_IMPORTANT, QString("Sent '%1' message").arg(message));
     550            return BACKEND_EXIT_OK;
     551        }
     552        else
     553        {
     554            VERBOSE(VB_IMPORTANT,
     555                    "Unable to connect to backend, verbose level unchanged ");
     556            return BACKEND_EXIT_NO_CONNECT;
     557        }
     558    }
     559
     560    if (cmdline.ClearSettingsCache())
     561    {
     562        if (gContext->ConnectToMasterServer())
     563        {
     564            RemoteSendMessage("CLEAR_SETTINGS_CACHE");
     565            VERBOSE(VB_IMPORTANT, "Sent CLEAR_SETTINGS_CACHE message");
     566            return BACKEND_EXIT_OK;
     567        }
     568        else
     569        {
     570            VERBOSE(VB_IMPORTANT, "Unable to connect to backend, settings "
     571                    "cache will not be cleared.");
     572            return BACKEND_EXIT_NO_CONNECT;
     573        }
     574    }
     575
     576    if (cmdline.IsPrintScheduleEnabled() ||
     577        cmdline.IsTestSchedulerEnabled())
     578    {
     579        sched = new Scheduler(false, &tvList);
     580        if (!cmdline.IsTestSchedulerEnabled() &&
     581            gContext->ConnectToMasterServer())
     582        {
     583            cout << "Retrieving Schedule from Master backend.\n";
     584            sched->FillRecordListFromMaster();
     585        }
     586        else
     587        {
     588            cout << "Calculating Schedule from database.\n" <<
     589                    "Inputs, Card IDs, and Conflict info may be invalid "
     590                    "if you have multiple tuners.\n";
     591            sched->FillRecordListFromDB();
     592        }
     593
     594        print_verbose_messages |= VB_SCHEDULE;
     595        sched->PrintList(true);
     596        return BACKEND_EXIT_OK;
     597    }
     598
     599    if (cmdline.Reschedule())
     600    {
     601        bool ok = false;
     602        if (gContext->ConnectToMasterServer())
     603        {
     604            VERBOSE(VB_IMPORTANT, "Connected to master for reschedule");
     605            ScheduledRecording::signalChange(-1);
     606            ok = true;
     607        }
     608        else
     609            VERBOSE(VB_IMPORTANT, "Cannot connect to master for reschedule");
     610
     611        return (ok) ? BACKEND_EXIT_OK : BACKEND_EXIT_NO_CONNECT;
     612    }
     613
     614    if (!cmdline.GetPrintExpire().isEmpty())
     615    {
     616        expirer = new AutoExpire();
     617        expirer->PrintExpireList(cmdline.GetPrintExpire());
     618        return BACKEND_EXIT_OK;
     619    }
     620
     621    if ((cmdline.GetPreviewFrameNumber() >= -1) ||
     622        (cmdline.GetPreviewSeconds() >= -1))
     623    {
     624        int ret = preview_helper(
     625            QString::number(cmdline.GetChanID()),
     626            cmdline.GetStartTime().toString(Qt::ISODate),
     627            cmdline.GetPreviewFrameNumber(), cmdline.GetPreviewSeconds(),
     628            cmdline.GetPreviewSize(),
     629            cmdline.GetInputFilename(), cmdline.GetOutputFilename());
     630        return ret;
     631    }
     632
     633    // This should never actually be reached..
     634    return BACKEND_EXIT_OK;
     635}
     636
     637int connect_to_master(void)
     638{
     639    MythSocket *tempMonitorConnection = new MythSocket();
     640    if (tempMonitorConnection->connect(
     641            gContext->GetSetting("MasterServerIP", "127.0.0.1"),
     642            gContext->GetNumSetting("MasterServerPort", 6543)))
     643    {
     644        if (!gContext->CheckProtoVersion(tempMonitorConnection))
     645        {
     646            VERBOSE(VB_IMPORTANT, "Master backend is incompatible with "
     647                    "this backend.\nCannot become a slave.");
     648            return BACKEND_EXIT_NO_CONNECT;
     649        }
     650
     651        QStringList tempMonitorDone("DONE");
     652
     653        QStringList tempMonitorAnnounce("ANN Monitor tzcheck 0");
     654        tempMonitorConnection->writeStringList(tempMonitorAnnounce);
     655        tempMonitorConnection->readStringList(tempMonitorAnnounce);
     656        if (tempMonitorAnnounce.empty() ||
     657            tempMonitorAnnounce[0] == "ERROR")
     658        {
     659            tempMonitorConnection->DownRef();
     660            tempMonitorConnection = NULL;
     661            if (tempMonitorAnnounce.empty())
     662            {
     663                VERBOSE(VB_IMPORTANT, LOC_ERR +
     664                        "Failed to open event socket, timeout");
     665            }
     666            else
     667            {
     668                VERBOSE(VB_IMPORTANT, LOC_ERR +
     669                        "Failed to open event socket" +
     670                        ((tempMonitorAnnounce.size() >= 2) ?
     671                         QString(", error was %1").arg(tempMonitorAnnounce[1]) :
     672                         QString(", remote error")));
     673            }
     674        }
     675
     676        QStringList tzCheck("QUERY_TIME_ZONE");
     677        if (tempMonitorConnection)
     678        {
     679            tempMonitorConnection->writeStringList(tzCheck);
     680            tempMonitorConnection->readStringList(tzCheck);
     681        }
     682        if (tzCheck.size() && !checkTimeZone(tzCheck))
     683        {
     684            // Check for different time zones, different offsets, different
     685            // times
     686            VERBOSE(VB_IMPORTANT, "The time and/or time zone settings on "
     687                    "this system do not match those in use on the master "
     688                    "backend. Please ensure all frontend and backend "
     689                    "systems are configured to use the same time zone and "
     690                    "have the current time properly set.");
     691            VERBOSE(VB_IMPORTANT,
     692                    "Unable to run with invalid time settings. Exiting.");
     693            tempMonitorConnection->writeStringList(tempMonitorDone);
     694            tempMonitorConnection->DownRef();
     695            return BACKEND_EXIT_INVALID_TIMEZONE;
     696        }
     697        else
     698        {
     699            VERBOSE(VB_IMPORTANT,
     700                    QString("Backend is running in %1 time zone.")
     701                    .arg(getTimeZoneID()));
     702        }
     703        if (tempMonitorConnection)
     704            tempMonitorConnection->writeStringList(tempMonitorDone);
     705    }
     706    if (tempMonitorConnection)
     707        tempMonitorConnection->DownRef();
     708
     709    return BACKEND_EXIT_OK;
     710}
     711
     712int setup_basics(const MythCommandLineParser &cmdline)
     713{
     714    ofstream pidfs;
     715    if (!openPidfile(pidfs, cmdline.GetPIDFilename()))
     716        return BACKEND_EXIT_OPENING_PIDFILE_ERROR;
     717
     718    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
     719        VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to ignore SIGPIPE");
     720
     721    if (cmdline.IsDaemonizeEnabled() && (daemon(0, 1) < 0))
     722    {
     723        VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to daemonize" + ENO);
     724        return BACKEND_EXIT_DAEMONIZING_ERROR;
     725    }
     726
     727    QString username = cmdline.GetUsername();
     728    if (!username.isEmpty() && !setUser(username))
     729        return BACKEND_EXIT_PERMISSIONS_ERROR;
     730
     731    if (pidfs)
     732    {
     733        pidfs << getpid() << endl;
     734        pidfs.close();
     735    }
     736
     737    return BACKEND_EXIT_OK;
     738}
     739
     740void print_warnings(const MythCommandLineParser &cmdline)
     741{
     742    if (!cmdline.IsHouseKeeperEnabled())
     743    {
     744        VERBOSE(VB_IMPORTANT, LOC_WARN +
     745                "****** The Housekeeper has been DISABLED with "
     746                "the --nohousekeeper option ******");
     747    }
     748    if (!cmdline.IsSchedulerEnabled())
     749    {
     750        VERBOSE(VB_IMPORTANT, LOC_WARN +
     751                "********** The Scheduler has been DISABLED with "
     752                "the --nosched option **********");
     753    }
     754    if (!cmdline.IsAutoExpirerEnabled())
     755    {
     756        VERBOSE(VB_IMPORTANT, LOC_WARN +
     757                "********* Auto-Expire has been DISABLED with "
     758                "the --noautoexpire option ********");
     759    }
     760    if (!cmdline.IsJobQueueEnabled())
     761    {
     762        VERBOSE(VB_IMPORTANT, LOC_WARN +
     763                "********* The JobQueue has been DISABLED with "
     764                "the --nojobqueue option *********");
     765    }
     766}
     767
     768int run_backend(const MythCommandLineParser &cmdline)
     769{
     770    if (!setup_context(cmdline))
     771        return BACKEND_EXIT_NO_MYTHCONTEXT;
     772
     773    if (!UpgradeTVDatabaseSchema(true, true))
     774    {
     775        VERBOSE(VB_IMPORTANT, "Couldn't upgrade database to new schema");
     776        return BACKEND_EXIT_DB_OUTOFDATE;
     777    }
     778
     779    ///////////////////////////////////////////
     780
     781    bool ismaster = gContext->IsMasterHost();
     782
     783    g_pUPnp->Init(ismaster, cmdline.IsUPnPEnabled());
     784
     785    if (!ismaster)
     786    {
     787        int ret = connect_to_master();
     788        if (BACKEND_EXIT_OK != ret)
     789            return ret;
     790    }
     791
     792    QString myip = gContext->GetSetting("BackendServerIP");
     793    int     port = gContext->GetNumSetting("BackendServerPort", 6543);
     794    if (myip.isEmpty())
     795    {
     796        cerr << "No setting found for this machine's BackendServerIP.\n"
     797             << "Please run setup on this machine and modify the first page\n"
     798             << "of the general settings.\n";
     799        return BACKEND_EXIT_NO_IP_ADDRESS;
     800    }
     801
     802    MythSystemEventHandler *sysEventHandler = new MythSystemEventHandler();
     803
     804    if (ismaster)
     805    {
     806        VERBOSE(VB_GENERAL, LOC + "Starting up as the master server.");
     807        gContext->LogEntry("mythbackend", LP_INFO,
     808                           "MythBackend started as master server", "");
     809    }
     810    else
     811    {
     812        VERBOSE(VB_GENERAL, LOC + "Running as a slave backend.");
     813        gContext->LogEntry("mythbackend", LP_INFO,
     814                           "MythBackend started as a slave backend", "");
     815    }
     816
     817    bool fatal_error = false;
     818    bool runsched = setupTVs(ismaster, fatal_error);
     819    if (fatal_error)
     820    {
     821        delete sysEventHandler;
     822        return BACKEND_EXIT_CAP_CARD_SETUP_ERROR;
     823    }
     824
     825    if (ismaster)
     826    {
     827        if (runsched)
     828        {
     829            sched = new Scheduler(true, &tvList);
     830            int err = sched->GetError();
     831            if (err)
     832                return err;
     833
     834            if (!cmdline.IsSchedulerEnabled())
     835                sched->DisableScheduling();
     836        }
     837
     838        if (cmdline.IsHouseKeeperEnabled())
     839            housekeeping = new HouseKeeper(true, ismaster, sched);
     840
     841        if (!cmdline.IsAutoExpirerEnabled())
     842        {
     843            expirer = new AutoExpire(&tvList);
     844            if (sched)
     845                sched->SetExpirer(expirer);
     846        }
     847    }
     848    else if (cmdline.IsHouseKeeperEnabled())
     849    {
     850        housekeeping = new HouseKeeper(true, ismaster, NULL);
     851    }
     852
     853    if (cmdline.IsJobQueueEnabled())
     854        jobqueue = new JobQueue(ismaster);
     855
     856    // Setup status server
     857    HttpServer *pHS = g_pUPnp->GetHttpServer();
     858    if (pHS)
     859    {
     860        VERBOSE(VB_IMPORTANT, "Main::Registering HttpStatus Extension");
     861
     862        pHS->RegisterExtension( new HttpStatus( &tvList, sched,
     863                                                expirer, ismaster ));
     864    }
     865
     866    if (ismaster)
     867    {
     868        // kill -USR1 mythbackendpid will force a upnpmedia rebuild
     869        signal(SIGUSR1, &upnp_rebuild);
     870    }
     871
     872    VERBOSE(VB_IMPORTANT, QString("Enabled verbose msgs: %1")
     873            .arg(verboseString));
     874
     875    MainServer *mainServer = new MainServer(
     876        ismaster, port, &tvList, sched, expirer);
     877
     878    int exitCode = mainServer->GetExitCode();
     879    if (exitCode != BACKEND_EXIT_OK)
     880    {
     881        VERBOSE(VB_IMPORTANT, "Backend exiting, MainServer initialization "
     882                "error.");
     883        delete mainServer;
     884        return exitCode;
     885    }
     886
     887    StorageGroup::CheckAllStorageGroupDirs();
     888
     889    if (gContext->IsMasterBackend())
     890        SendMythSystemEvent("MASTER_STARTED");
     891
     892    ///////////////////////////////
     893    ///////////////////////////////
     894    exitCode = qApp->exec();
     895    ///////////////////////////////
     896    ///////////////////////////////
     897
     898    if (gContext->IsMasterBackend())
     899    {
     900        SendMythSystemEvent("MASTER_SHUTDOWN");
     901        qApp->processEvents();
     902    }
     903
     904    gContext->LogEntry("mythbackend", LP_INFO, "MythBackend exiting", "");
     905
     906    delete sysEventHandler;
     907    delete mainServer;
     908
     909    return exitCode;
     910}
  • programs/mythbackend/mythbackend.pro

     
    2121HEADERS += autoexpire.h encoderlink.h filetransfer.h httpstatus.h mainserver.h
    2222HEADERS += playbacksock.h scheduler.h server.h housekeeper.h backendutil.h
    2323HEADERS += upnpcdstv.h upnpcdsmusic.h upnpcdsvideo.h mediaserver.h
    24 HEADERS += mythxml.h upnpmedia.h
     24HEADERS += mythxml.h upnpmedia.h main_helpers.h backendcontext.h
     25HEADERS += httpconfig.h mythsettings.h
    2526
    2627SOURCES += autoexpire.cpp encoderlink.cpp filetransfer.cpp httpstatus.cpp
    2728SOURCES += main.cpp mainserver.cpp playbacksock.cpp scheduler.cpp server.cpp
    2829SOURCES += housekeeper.cpp backendutil.cpp
    2930SOURCES += upnpcdstv.cpp upnpcdsmusic.cpp upnpcdsvideo.cpp mediaserver.cpp
    30 SOURCES += mythxml.cpp upnpmedia.cpp
     31SOURCES += mythxml.cpp upnpmedia.cpp main_helpers.cpp backendcontext.cpp
     32SOURCES += httpconfig.cpp mythsettings.cpp
    3133
    3234using_oss:DEFINES += USING_OSS
    3335
  • programs/mythbackend/mediaserver.h

     
    3939        QString          m_sSharePath;
    4040
    4141    public:
    42         explicit MediaServer( bool bMaster, bool bDisableUPnp = false );
     42        explicit MediaServer();
     43        void Init(bool bMaster, bool bDisableUPnp = false);
    4344
    4445        virtual ~MediaServer();
    4546