Ticket #8262: 8262-v1.patch
File 8262-v1.patch, 153.6 KB (added by , 14 years ago) |
---|
-
libs/libmyth/mythcommandlineparser.cpp
2 2 using namespace std; 3 3 4 4 #include <QFile> 5 #include <QSize> 5 6 6 7 #include "mythcommandlineparser.h" 7 8 #include "exitcodes.h" … … 10 11 #include "mythverbose.h" 11 12 #include "mythversion.h" 12 13 13 MythCommandLineParser::MythCommandLineParser(int things_to_parse) : 14 static bool parse_preview_info( 15 const QString ¶m, 16 long long &previewFrameNumber, 17 long long &previewSeconds, 18 QSize &previewSize); 19 20 MythCommandLineParser::MythCommandLineParser(uint64_t things_to_parse) : 14 21 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 16 49 wantsToExit(false) 17 50 { 18 51 } … … 95 128 } 96 129 return true; 97 130 } 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 } 98 146 else if ((parseTypes & kCLPHelp) && 99 147 (!strcmp(argv[argpos],"-h") || 100 148 !strcmp(argv[argpos],"--help") || … … 154 202 settingsOverride["RunFrontendInWindow"] = "0"; 155 203 return true; 156 204 } 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 } 157 272 else if ((parseTypes & kCLPOverrideSettingsFile) && 158 273 (!strcmp(argv[argpos],"--override-settings-file"))) 159 274 { … … 252 367 QString tmpArg = argv[argpos+1]; 253 368 if (tmpArg.startsWith("-")) 254 369 { 255 cerr << "Invalid or missingargument to "370 cerr << "Invalid argument to " 256 371 << "-G/--get-setting option\n"; 257 372 err = true; 258 373 return true; … … 262 377 } 263 378 else 264 379 { 265 cerr << " Invalid or missing argument to "380 cerr << "Missing argument to " 266 381 << "-G/--get-setting option\n"; 267 382 err = true; 268 383 return true; … … 271 386 ++argpos; 272 387 return true; 273 388 } 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 } 274 640 else 275 641 { 276 642 return PreParse(argc, argv, argpos, err); … … 349 715 350 716 return str; 351 717 } 718 719 // [WxH] | [WxH@]seconds[S] | [WxH@]frame_numF 720 static bool parse_preview_info( 721 const QString ¶m, 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
1 1 // -*- Mode: c++ -*- 2 2 3 3 #include <QStringList> 4 #include <QDateTime> 5 #include <QSize> 4 6 #include <QMap> 5 7 6 8 #include <stdint.h> // for uint64_t … … 8 10 #include "mythexp.h" 9 11 10 12 typedef 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, 22 47 } ParseType; 23 48 24 49 class MPUBLIC MythCommandLineParser 25 50 { 26 51 public: 27 MythCommandLineParser( int things_to_parse);52 MythCommandLineParser(uint64_t things_to_parse); 28 53 29 54 bool PreParse(int argc, const char * const * argv, int &argpos, bool &err); 30 55 bool Parse(int argc, const char * const * argv, int &argpos, bool &err); … … 34 59 { return settingsOverride; } 35 60 QStringList GetSettingsQuery(void) const 36 61 { 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; } 39 72 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 40 109 bool WantsToExit(void) const { return wantsToExit; } 41 110 42 111 private: 43 intparseTypes;112 uint64_t parseTypes; 44 113 45 114 QMap<QString,QString> settingsOverride; 46 115 QStringList settingsQuery; 116 47 117 QString display; 48 118 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; 49 127 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; 50 147 bool wantsToExit; 51 148 }; -
libs/libmyth/mythcontext.cpp
82 82 void LoadLogSettings(void); 83 83 void LoadDatabaseSettings(void); 84 84 85 bool LoadSettingsFile(void);86 bool WriteSettingsFile(const DatabaseParams ¶ms,87 bool overwrite = false);88 bool FindSettingsProbs(void);89 90 85 bool PromptForDatabaseParams(const QString &error); 91 86 QString TestDBconnection(void); 92 87 void SilenceDBerrors(void); … … 514 509 */ 515 510 void MythContextPrivate::LoadDatabaseSettings(void) 516 511 { 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); 520 514 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 user540 FindSettingsProbs();541 542 515 m_localhostname = m_DBparams.localHostName; 543 516 if (m_localhostname.isEmpty() || 544 517 m_localhostname == "my-unique-identifier-goes-here") … … 559 532 m_database->SetLocalHostname(m_localhostname); 560 533 } 561 534 562 /**563 * Load mysql.txt and parse its values into m_DBparams564 */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.wolReconnect585 = 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 ¶ms,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 else637 s << "DBHostPing=no" << endl << endl;638 639 if (params.dbPort)640 s << "DBPort=" << params.dbPort << endl;641 642 s << "DBUserName=" << params.dbUserName << endl643 << "DBPassword=" << params.dbPassword << endl644 << "DBName=" << params.dbName << endl645 << "DBType=" << params.dbType << endl646 << endl647 << "# 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 else657 s << "#LocalHostName=my-unique-identifier-goes-here\n";658 659 s << endl660 << "# 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 else671 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 else682 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 else692 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 728 535 bool MythContextPrivate::PromptForDatabaseParams(const QString &error) 729 536 { 730 537 bool accepted = false; … … 1039 846 switch (selected) 1040 847 { 1041 848 case kDialogCodeButton0: 1042 WriteSettingsFile(m_DBparams, true);849 MythDB::SaveDatabaseParamsToDisk(m_DBparams, GetConfDir(), true); 1043 850 // User prefers mysql.txt, so throw away default UPnP backend: 1044 851 m_XML->SetValue(kDefaultUSN, ""); 1045 852 m_XML->Save(); … … 2413 2220 params.wolRetry != cur_params.wolRetry || 2414 2221 params.wolCommand != cur_params.wolCommand))) 2415 2222 { 2416 ret = d->WriteSettingsFile(params, true);2223 ret = MythDB::SaveDatabaseParamsToDisk(params, GetConfDir(), true); 2417 2224 if (ret) 2418 2225 { 2419 2226 // Save the new settings: -
libs/libmythupnp/upnp.h
125 125 UPnp(); 126 126 virtual ~UPnp(); 127 127 128 void SetConfiguration( Configuration *pConfig );128 static void SetConfiguration( Configuration *pConfig ); 129 129 130 130 bool Initialize( int nServicePort, HttpServer *pHttpServer ); 131 131 bool Initialize( QStringList &sIPAddrList, int nServicePort, HttpServer *pHttpServer ); -
libs/libmythdb/mythdb.cpp
1 1 #include <vector> 2 2 using namespace std; 3 3 4 #include <QReadWriteLock> 5 #include <QSqlError> 4 6 #include <QMutex> 5 #include <Q ReadWriteLock>7 #include <QFile> 6 8 #include <QHash> 7 #include <Q SqlError>9 #include <QDir> 8 10 9 11 #include "mythdb.h" 10 12 #include "mythdbcon.h" 11 13 #include "mythverbose.h" 12 14 #include "oldsettings.h" 15 #include "mythdirs.h" 13 16 14 17 static MythDB *mythdb = NULL; 15 18 static QMutex dbLock; … … 822 825 d->useSettingsCache = activate; 823 826 ClearSettingsCache(); 824 827 } 828 829 bool MythDB::LoadDatabaseParamsFromDisk( 830 DatabaseParams ¶ms, 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 899 bool MythDB::SaveDatabaseParamsToDisk( 900 const DatabaseParams ¶ms, 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
75 75 static void destroyMythDB(); 76 76 static QString toCommaList(const QMap<QString, QVariant> &bindings, 77 77 uint indent = 0, uint softMaxColumn = 80); 78 79 static bool LoadDatabaseParamsFromDisk( 80 DatabaseParams ¶ms, bool sanitize); 81 static bool SaveDatabaseParamsToDisk( 82 const DatabaseParams ¶ms, const QString &confdir, bool overwrite); 83 78 84 protected: 79 85 MythDB(); 80 86 ~MythDB(); -
programs/mythbackend/backendcontext.h
1 #include <QString> 2 #include <QMap> 3 4 class EncoderLink; 5 class AutoExpire; 6 class Scheduler; 7 class JobQueue; 8 class HouseKeeper; 9 class MediaServer; 10 11 extern QMap<int, EncoderLink *> tvList; 12 extern AutoExpire *expirer; 13 extern Scheduler *sched; 14 extern JobQueue *jobqueue; 15 extern HouseKeeper *housekeeping; 16 extern MediaServer *g_pUPnp; 17 extern QString pidfile; 18 extern QString logfile; 19 20 class BackendContext 21 { 22 23 }; -
programs/mythbackend/mediaserver.cpp
9 9 ////////////////////////////////////////////////////////////////////////////// 10 10 11 11 #include "mediaserver.h" 12 #include "httpconfig.h" 12 13 #include "mythxml.h" 13 14 #include "mythdirs.h" 14 15 … … 28 29 // 29 30 ////////////////////////////////////////////////////////////////////////////// 30 31 31 MediaServer::MediaServer( bool bIsMaster, bool bDisableUPnp /* = FALSE */ ) 32 MediaServer::MediaServer(void) : 33 m_pUPnpCDS(NULL), m_pUPnpCMGR(NULL), upnpMedia(NULL), 34 m_sSharePath(GetShareDir()) 32 35 { 33 VERBOSE(VB_UPNP, QString("MediaServer::Begin"));36 VERBOSE(VB_UPNP, "MediaServer:ctor:Begin"); 34 37 35 38 // ---------------------------------------------------------------------- 36 39 // Initialize Configuration class (Database for Servers) … … 43 46 // ---------------------------------------------------------------------- 44 47 45 48 int nPort = g_pConfig->GetValue( "BackendStatusPort", 6544 ); 46 QString sIP = g_pConfig->GetValue( "BackendServerIP" , "" );47 49 48 if (sIP.isEmpty())49 {50 VERBOSE(VB_IMPORTANT,51 "MediaServer:: No BackendServerIP Address defined");52 m_pHttpServer = NULL;53 return;54 }55 56 57 50 m_pHttpServer = new HttpServer(); 58 51 59 52 if (!m_pHttpServer->listen(QHostAddress::Any, nPort)) … … 65 58 return; 66 59 } 67 60 68 m_sSharePath = GetShareDir();69 61 m_pHttpServer->m_sSharePath = m_sSharePath; 70 62 63 m_pHttpServer->RegisterExtension(new HttpConfig()); 64 65 VERBOSE(VB_UPNP, "MediaServer:ctor:End"); 66 } 67 68 void MediaServer::Init(bool bIsMaster, bool bDisableUPnp /* = FALSE */) 69 { 70 VERBOSE(VB_UPNP, "MediaServer:Init:Begin"); 71 72 int nPort = g_pConfig->GetValue( "BackendStatusPort", 6544 ); 71 73 QString sFileName = g_pConfig->GetValue( "upnpDescXmlPath", 72 74 m_sSharePath ); 73 75 QString sDeviceType; … … 100 102 101 103 VERBOSE(VB_UPNP, "MediaServer::Registering MythXML Service." ); 102 104 103 m_pHttpServer->RegisterExtension( new MythXML( pMythDevice , m_sSharePath)); 105 if (m_pHttpServer) 106 m_pHttpServer->RegisterExtension( 107 new MythXML( pMythDevice , m_sSharePath)); 104 108 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 105 118 if (sIP == "localhost" || sIP.startsWith("127.")) 106 119 { 107 120 VERBOSE(VB_IMPORTANT, "MediaServer:: Loopback address specified - " … … 189 202 190 203 } 191 204 192 VERBOSE(VB_UPNP, QString( "MediaServer::End" ));205 VERBOSE(VB_UPNP, "MediaServer:Init:End"); 193 206 } 194 207 195 208 ////////////////////////////////////////////////////////////////////////////// -
programs/mythbackend/mythsettings.h
1 // -*- Mode: c++ -*- 2 3 #ifndef _MYTHSETTINGS_H_ 4 #define _MYTHSETTINGS_H_ 5 6 #include <QStringList> 7 #include <QMap> 8 9 class MythSettingBase 10 { 11 public: 12 MythSettingBase() {} 13 virtual ~MythSettingBase() {} 14 virtual QString ToHTML(uint) const { return QString(); } 15 }; 16 typedef QList<MythSettingBase*> MythSettingList; 17 18 class 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 34 class 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 114 bool parse_settings(MythSettingList &settings, const QString &filename); 115 bool 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 19 QT_BEGIN_MOC_NAMESPACE 20 static 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 40 static const char qt_meta_stringdata_MainServer[] = { 41 "MainServer\0\0reconnectTimeout()\0" 42 "deferredDeleteSlot()\0autoexpireUpdate()\0" 43 "newConnection(MythSocket*)\0" 44 }; 45 46 const QMetaObject MainServer::staticMetaObject = { 47 { &QObject::staticMetaObject, qt_meta_stringdata_MainServer, 48 qt_meta_data_MainServer, 0 } 49 }; 50 51 const QMetaObject *MainServer::metaObject() const 52 { 53 return &staticMetaObject; 54 } 55 56 void *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 66 int 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 } 83 QT_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 9 class QTextStream; 10 11 class 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> 4 using namespace std; 5 6 class MythCommandLineParser; 7 class QString; 8 class QSize; 9 10 bool setupTVs(bool ismaster, bool &error); 11 bool setup_context(const MythCommandLineParser &cmdline); 12 void cleanup(void); 13 int log_rotate(int report_error); 14 void log_rotate_handler(int); 15 void upnp_rebuild(int); 16 int 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); 20 void showUsage(const MythCommandLineParser &cmdlineparser, 21 const QString &version); 22 void setupLogfile(void); 23 bool openPidfile(ofstream &pidfs, const QString &pidfilename); 24 bool setUser(const QString &username); 25 int handle_command(const MythCommandLineParser &cmdline); 26 int connect_to_master(void); 27 int setup_basics(const MythCommandLineParser &cmdline); 28 void print_warnings(const MythCommandLineParser &cmdline); 29 int run_backend(const MythCommandLineParser &cmdline); 30 31 namespace 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 19 QT_BEGIN_MOC_NAMESPACE 20 static 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 34 static const char qt_meta_stringdata_Scheduler[] = { 35 "Scheduler\0" 36 }; 37 38 const QMetaObject Scheduler::staticMetaObject = { 39 { &QObject::staticMetaObject, qt_meta_stringdata_Scheduler, 40 qt_meta_data_Scheduler, 0 } 41 }; 42 43 const QMetaObject *Scheduler::metaObject() const 44 { 45 return &staticMetaObject; 46 } 47 48 void *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 56 int 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 } 63 QT_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 19 QT_BEGIN_MOC_NAMESPACE 20 static 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 34 static const char qt_meta_stringdata_AutoExpire[] = { 35 "AutoExpire\0" 36 }; 37 38 const QMetaObject AutoExpire::staticMetaObject = { 39 { &QObject::staticMetaObject, qt_meta_stringdata_AutoExpire, 40 qt_meta_data_AutoExpire, 0 } 41 }; 42 43 const QMetaObject *AutoExpire::metaObject() const 44 { 45 return &staticMetaObject; 46 } 47 48 void *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 56 int 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 } 63 QT_END_MOC_NAMESPACE -
programs/mythbackend/main.cpp
1 // POSIX headers2 #include <sys/time.h> // for setpriority3 #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 1 11 #include "mythconfig.h"12 #if CONFIG_DARWIN13 #include <sys/aio.h> // O_SYNC14 #endif15 2 16 // C headers17 #include <cstdlib>18 #include <cerrno>19 20 // C++ headers21 #include <iostream>22 #include <fstream>23 using namespace std;24 25 3 #ifndef _WIN32 26 4 #include <QCoreApplication> 27 5 #else … … 56 34 #include "previewgenerator.h" 57 35 #include "mythcommandlineparser.h" 58 36 #include "mythsystemevent.h" 37 #include "main_helpers.h" 38 #include "backendcontext.h" 59 39 60 40 #include "mediaserver.h" 61 41 #include "httpstatus.h" … … 71 51 #define UNUSED_FILENO 3 72 52 #endif 73 53 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; 54 extern const char *myth_source_version; 55 extern const char *myth_source_path; 81 56 82 MediaServer *g_pUPnp = NULL; 83 84 bool setupTVs(bool ismaster, bool &error) 57 class MediaServerThread : public QThread 85 58 { 86 error = false; 87 QString localhostname = gContext->GetHostName(); 88 89 MSqlQuery query(MSqlQuery::InitCon()); 90 91 if (ismaster) 59 public: 60 void run(void) 92 61 { 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(); 143 64 } 144 65 145 if (!query.exec( 146 "SELECT cardid, hostname " 147 "FROM capturecard " 148 "ORDER BY cardid")) 66 void BlockUntilReloadNeeded(void) 149 67 { 150 MythDB::DBError("Querying Recorders", query); 151 return false; 68 sleep(60); // TODO 152 69 } 70 }; 153 71 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 else200 {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 to206 // set error and exit if a non-master card fails.207 error = true;208 }209 }210 }211 else212 {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 else222 {223 gContext->LogEntry("mythbackend", LP_CRITICAL,224 "Problem with capture cards",225 cidmsg + "failed init");226 delete tv;227 }228 }229 else230 {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/null282 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 else349 {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 else361 {362 pginfo->pathname = QFileInfo(infile).absoluteFilePath();363 }364 }365 366 }367 else368 {369 pginfo->pathname = pginfo->GetPlaybackURL(false, true);370 }371 }372 else373 {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_numF398 bool parse_preview_info(const QString ¶m,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 else418 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 else440 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 namespace459 {460 class CleanupGuard461 {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" << endl513 << endl;514 515 }516 517 72 int main(int argc, char **argv) 518 73 { 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 519 78 bool cmdline_err; 520 79 MythCommandLineParser cmdline( 521 80 kCLPOverrideSettingsFile | 522 81 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); 524 106 525 107 for (int argpos = 0; argpos < argc; ++argpos) 526 108 { … … 544 126 QApplication a(argc, argv); 545 127 #endif 546 128 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 580 129 for (int argpos = 1; argpos < a.argc(); ++argpos) 581 130 { 582 if (!strcmp(a.argv()[argpos],"-l") || 583 !strcmp(a.argv()[argpos],"--logfile")) 131 if (cmdline.Parse(a.argc(), a.argv(), argpos, cmdline_err)) 584 132 { 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 else594 {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 else611 {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 else634 {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 else648 {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 else661 {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 else743 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 {811 133 if (cmdline_err) 812 134 return BACKEND_EXIT_INVALID_CMDLINE; 813 135 … … 817 139 else 818 140 { 819 141 if (!(!strcmp(a.argv()[argpos],"-h") || 820 !strcmp(a.argv()[argpos],"--help"))) 142 !strcmp(a.argv()[argpos],"--help"))) 143 { 821 144 cerr << "Invalid argument: " << a.argv()[argpos] << endl; 822 145 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 } 824 153 } 825 154 } 826 155 827 if (((previewFrameNumber >= -1) || previewSeconds >= -1) && 828 (chanid.isEmpty() || starttime.isEmpty()) && infile.isEmpty()) 156 if (cmdline.HasInvalidPreviewGenerationParams()) 829 157 { 830 158 cerr << "--generate-preview must be accompanied by either " <<endl 831 159 << "\nboth --chanid and --starttime parameters, " << endl … … 833 161 return BACKEND_EXIT_INVALID_CMDLINE; 834 162 } 835 163 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(); 846 166 847 CleanupGuard callCleanup(cleanup);167 /////////////////////////////////////////////////////////////////////// 848 168 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); 860 171 861 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 862 VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to ignore SIGPIPE"); 172 setupLogfile(); 863 173 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); 869 175 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; 874 179 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); 912 181 913 if ( pidfs)182 if (cmdline.HasBackendCommand()) 914 183 { 915 pidfs << getpid() << endl; 916 pidfs.close(); 184 if (!setup_context(cmdline)) 185 return BACKEND_EXIT_NO_MYTHCONTEXT; 186 return handle_command(cmdline); 917 187 } 918 188 919 VERBOSE(VB_IMPORTANT, versionStr);920 921 189 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);928 190 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 /////////////////////////////////////////// 937 192 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); 943 199 944 if (wantupnprebuild) 945 { 946 VERBOSE(VB_GENERAL, "Rebuilding UPNP Media Map"); 200 /////////////////////////////////////////// 947 201 948 UPnpMedia *rebuildit = new UPnpMedia(false,false); 949 rebuildit->BuildMediaMap(); 950 951 return BACKEND_EXIT_OK; 952 } 953 954 if (setverbose) 202 while (true) 955 203 { 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(); 973 206 } 974 207 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; 1286 209 } 1287 210 1288 211 /* vim: set expandtab tabstop=4 shiftwidth=4: */ -
programs/mythbackend/backendcontext.cpp
1 #include "backendcontext.h" 2 3 QMap<int, EncoderLink *> tvList; 4 AutoExpire *expirer = NULL; 5 Scheduler *sched = NULL; 6 JobQueue *jobqueue = NULL; 7 HouseKeeper *housekeeping = NULL; 8 MediaServer *g_pUPnp = NULL; 9 QString pidfile; 10 QString 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 11 static QString indent(uint level) 12 { 13 QString ret; 14 for (uint i = 0; i < level; i++) 15 ret += " "; 16 return ret; 17 } 18 19 static 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 44 static 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 132 static 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 144 QString 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 165 QString 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 239 MythSetting::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 251 MythSetting::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 286 bool 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 429 bool 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 460 bool 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 19 QT_BEGIN_MOC_NAMESPACE 20 static 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 37 static const char qt_meta_stringdata_MythServer[] = { 38 "MythServer\0\0newConnect(MythSocket*)\0" 39 }; 40 41 const QMetaObject MythServer::staticMetaObject = { 42 { &QTcpServer::staticMetaObject, qt_meta_stringdata_MythServer, 43 qt_meta_data_MythServer, 0 } 44 }; 45 46 const QMetaObject *MythServer::metaObject() const 47 { 48 return &staticMetaObject; 49 } 50 51 void *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 59 int 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 75 void 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 } 80 QT_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 12 HttpConfig::HttpConfig() : HttpServerExtension("HttpConfig", QString()) 13 { 14 } 15 16 HttpConfig::~HttpConfig() 17 { 18 } 19 20 bool 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 67 void 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 121 void HttpConfig::PrintFooter(QTextStream &os) 122 { 123 os << " </form>\r\n" 124 << "\r\n</body>\r\n</html>\r\n"; 125 } 126 127 void 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 213 void 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 59 bool 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 227 bool 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 253 void 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 274 int 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 302 void log_rotate_handler(int) 303 { 304 log_rotate(0); 305 } 306 307 void upnp_rebuild(int) 308 { 309 if (gContext->IsMasterHost()) 310 { 311 g_pUPnp->RebuildMediaMap(); 312 } 313 314 } 315 316 int 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 398 void 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 436 void 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 450 bool 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 465 bool 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 512 int 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 637 int 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 712 int 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 740 void 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 768 int 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
21 21 HEADERS += autoexpire.h encoderlink.h filetransfer.h httpstatus.h mainserver.h 22 22 HEADERS += playbacksock.h scheduler.h server.h housekeeper.h backendutil.h 23 23 HEADERS += upnpcdstv.h upnpcdsmusic.h upnpcdsvideo.h mediaserver.h 24 HEADERS += mythxml.h upnpmedia.h 24 HEADERS += mythxml.h upnpmedia.h main_helpers.h backendcontext.h 25 HEADERS += httpconfig.h mythsettings.h 25 26 26 27 SOURCES += autoexpire.cpp encoderlink.cpp filetransfer.cpp httpstatus.cpp 27 28 SOURCES += main.cpp mainserver.cpp playbacksock.cpp scheduler.cpp server.cpp 28 29 SOURCES += housekeeper.cpp backendutil.cpp 29 30 SOURCES += upnpcdstv.cpp upnpcdsmusic.cpp upnpcdsvideo.cpp mediaserver.cpp 30 SOURCES += mythxml.cpp upnpmedia.cpp 31 SOURCES += mythxml.cpp upnpmedia.cpp main_helpers.cpp backendcontext.cpp 32 SOURCES += httpconfig.cpp mythsettings.cpp 31 33 32 34 using_oss:DEFINES += USING_OSS 33 35 -
programs/mythbackend/mediaserver.h
39 39 QString m_sSharePath; 40 40 41 41 public: 42 explicit MediaServer( bool bMaster, bool bDisableUPnp = false ); 42 explicit MediaServer(); 43 void Init(bool bMaster, bool bDisableUPnp = false); 43 44 44 45 virtual ~MediaServer(); 45 46