MythTV  master
mythbackend_main_helpers.cpp
Go to the documentation of this file.
1 #include "libmythbase/mythconfig.h"
2 #if CONFIG_SYSTEMD_NOTIFY
3 #include <systemd/sd-daemon.h>
4 static inline void be_sd_notify(const char *str) { sd_notify(0, str); };
5 #else
6 static inline void be_sd_notify(const char */*str*/) {};
7 #endif
8 
9 // C++ headers
10 #include <cerrno>
11 #include <csignal>
12 #include <cstdlib>
13 #include <fcntl.h>
14 #include <sys/stat.h>
15 #include <sys/time.h> // for setpriority
16 #include <sys/types.h>
17 #include <unistd.h>
18 
19 // Qt
20 #include <QCoreApplication>
21 #include <QFileInfo>
22 #include <QFile>
23 #include <QDir>
24 #include <QMap>
25 
26 // MythTV
27 #include "libmyth/mythcontext.h"
28 #include "libmythbase/compat.h"
29 #include "libmythbase/dbutil.h"
30 #include "libmythbase/exitcodes.h"
32 #include "libmythbase/mythdb.h"
36 #include "libmythbase/mythversion.h"
38 #include "libmythbase/remoteutil.h"
41 #include "libmythtv/dbcheck.h"
42 #include "libmythtv/eitcache.h"
43 #include "libmythtv/jobqueue.h"
47 #include "libmythtv/tv_rec.h"
48 
49 // MythBackend
50 #include "autoexpire.h"
51 #include "backendcontext.h"
52 #include "backendhousekeeper.h"
53 #include "encoderlink.h"
54 #include "httpstatus.h"
55 #include "mainserver.h"
56 #include "mediaserver.h"
59 #include "scheduler.h"
60 
61 // New webserver
65 #include "servicesv2/v2myth.h"
66 #include "servicesv2/v2video.h"
67 #include "servicesv2/v2dvr.h"
68 #include "servicesv2/v2content.h"
69 #include "servicesv2/v2guide.h"
70 #include "servicesv2/v2channel.h"
71 #include "servicesv2/v2status.h"
72 #include "servicesv2/v2capture.h"
73 #include "servicesv2/v2music.h"
74 #include "servicesv2/v2config.h"
75 
76 #define LOC QString("MythBackend: ")
77 #define LOC_WARN QString("MythBackend, Warning: ")
78 #define LOC_ERR QString("MythBackend, Error: ")
79 
80 static MainServer *mainServer = nullptr;
81 
82 bool setupTVs(bool ismaster, bool &error)
83 {
84  error = false;
85  QString localhostname = gCoreContext->GetHostName();
86 
88 
89  if (ismaster)
90  {
91  // Hack to make sure recorded.basename gets set if the user
92  // downgrades to a prior version and creates new entries
93  // without it.
94  if (!query.exec("UPDATE recorded SET basename = CONCAT(chanid, '_', "
95  "DATE_FORMAT(starttime, '%Y%m%d%H%i00'), '_', "
96  "DATE_FORMAT(endtime, '%Y%m%d%H%i00'), '.nuv') "
97  "WHERE basename = '';"))
98  MythDB::DBError("Updating record basename", query);
99 
100  // Hack to make sure record.station gets set if the user
101  // downgrades to a prior version and creates new entries
102  // without it.
103  if (!query.exec("UPDATE channel SET callsign=chanid "
104  "WHERE callsign IS NULL OR callsign='';"))
105  MythDB::DBError("Updating channel callsign", query);
106 
107  if (query.exec("SELECT MIN(chanid) FROM channel;"))
108  {
109  query.first();
110  int min_chanid = query.value(0).toInt();
111  if (!query.exec(QString("UPDATE record SET chanid = %1 "
112  "WHERE chanid IS NULL;").arg(min_chanid)))
113  MythDB::DBError("Updating record chanid", query);
114  }
115  else
116  MythDB::DBError("Querying minimum chanid", query);
117 
118  MSqlQuery records_without_station(MSqlQuery::InitCon());
119  records_without_station.prepare("SELECT record.chanid,"
120  " channel.callsign FROM record LEFT JOIN channel"
121  " ON record.chanid = channel.chanid WHERE record.station='';");
122  if (records_without_station.exec() && records_without_station.next())
123  {
124  MSqlQuery update_record(MSqlQuery::InitCon());
125  update_record.prepare("UPDATE record SET station = :CALLSIGN"
126  " WHERE chanid = :CHANID;");
127  do
128  {
129  update_record.bindValue(":CALLSIGN",
130  records_without_station.value(1));
131  update_record.bindValue(":CHANID",
132  records_without_station.value(0));
133  if (!update_record.exec())
134  {
135  MythDB::DBError("Updating record station", update_record);
136  }
137  } while (records_without_station.next());
138  }
139  }
140 
141  if (!query.exec(
142  "SELECT cardid, parentid, videodevice, hostname, sourceid "
143  "FROM capturecard "
144  "ORDER BY cardid"))
145  {
146  MythDB::DBError("Querying Recorders", query);
147  return false;
148  }
149 
150  std::vector<unsigned int> cardids;
151  std::vector<QString> hosts;
152  while (query.next())
153  {
154  uint cardid = query.value(0).toUInt();
155  uint parentid = query.value(1).toUInt();
156  QString videodevice = query.value(2).toString();
157  QString hostname = query.value(3).toString();
158  uint sourceid = query.value(4).toUInt();
159  QString cidmsg = QString("Card[%1](%2)").arg(cardid).arg(videodevice);
160 
161  if (hostname.isEmpty())
162  {
163  LOG(VB_GENERAL, LOG_ERR, cidmsg +
164  " does not have a hostname defined.\n"
165  "Please run setup and confirm all of the capture cards.\n");
166  continue;
167  }
168 
169  // Skip all cards that do not have a video source
170  if (sourceid == 0)
171  {
172  if (parentid == 0)
173  {
174  LOG(VB_GENERAL, LOG_WARNING, cidmsg +
175  " does not have a video source");
176  }
177  continue;
178  }
179 
180  cardids.push_back(cardid);
181  hosts.push_back(hostname);
182  }
183 
184  QWriteLocker tvlocker(&TVRec::s_inputsLock);
185 
186  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
187  for (size_t i = 0; i < cardids.size(); i++)
188  {
189  if (hosts[i] == localhostname) {
190  // No memory leak. The constructor for TVRec adds the item
191  // to the static map TVRec::s_inputs.
192  new TVRec(cardids[i]);
193  }
194  }
195 
196  for (size_t i = 0; i < cardids.size(); i++)
197  {
198  uint cardid = cardids[i];
199  QString host = hosts[i];
200  QString cidmsg = QString("Card %1").arg(cardid);
201 
202  if (!ismaster)
203  {
204  if (host == localhostname)
205  {
206  TVRec *tv = TVRec::GetTVRec(cardid);
207  if (tv && tv->Init())
208  {
209  auto *enc = new EncoderLink(cardid, tv);
210  gTVList[cardid] = enc;
211  }
212  else
213  {
214  LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards. " +
215  cidmsg + " failed init");
216  delete tv;
217  // The master assumes card comes up so we need to
218  // set error and exit if a non-master card fails.
219  error = true;
220  }
221  }
222  }
223  else
224  {
225  if (host == localhostname)
226  {
227  TVRec *tv = TVRec::GetTVRec(cardid);
228  if (tv && tv->Init())
229  {
230  auto *enc = new EncoderLink(cardid, tv);
231  gTVList[cardid] = enc;
232  }
233  else
234  {
235  LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards. " +
236  cidmsg + " failed init");
237  delete tv;
238  }
239  }
240  else
241  {
242  auto *enc = new EncoderLink(cardid, nullptr, host);
243  gTVList[cardid] = enc;
244  }
245  }
246  }
247 
248  if (gTVList.empty())
249  {
250  LOG(VB_GENERAL, LOG_WARNING, LOC +
251  "No valid capture cards are defined in the database.");
252  }
253 
254  return true;
255 }
256 
257 void cleanup(void)
258 {
259  if (mainServer)
260  {
261  mainServer->Stop();
262  qApp->processEvents();
263  }
264 
265  if (gCoreContext)
267 
268  delete gSysEventHandler;
269  gSysEventHandler = nullptr;
270 
271  delete gHousekeeping;
272  gHousekeeping = nullptr;
273 
274  if (gCoreContext)
275  {
276  delete gCoreContext->GetScheduler();
277  gCoreContext->SetScheduler(nullptr);
278  }
279 
280  delete gExpirer;
281  gExpirer = nullptr;
282 
283  delete gJobQueue;
284  gJobQueue = nullptr;
285 
286  delete g_pUPnp;
287  g_pUPnp = nullptr;
288 
289  if (SSDP::Instance())
290  {
292  SSDP::Instance()->wait();
293  }
294 
295  if (TaskQueue::Instance())
296  {
299  }
300 
301  while (!TVRec::s_inputs.empty())
302  {
303  TVRec *rec = *TVRec::s_inputs.begin();
304  delete rec;
305  }
306 
307 
308  delete gContext;
309  gContext = nullptr;
310 
311  delete mainServer;
312  mainServer = nullptr;
313 
314  delete gBackendContext;
315  gBackendContext = nullptr;
316 
317  if (!gPidFile.isEmpty())
318  {
319  unlink(gPidFile.toLatin1().constData());
320  gPidFile.clear();
321  }
322 
324 }
325 
327 {
328  QString eventString;
329 
330  if (cmdline.toBool("event"))
331  eventString = cmdline.toString("event");
332  else if (cmdline.toBool("systemevent"))
333  {
334  eventString = "SYSTEM_EVENT " +
335  cmdline.toString("systemevent") +
336  QString(" SENDER %1").arg(gCoreContext->GetHostName());
337  }
338 
339  if (!eventString.isEmpty())
340  {
342  {
343  gCoreContext->SendMessage(eventString);
344  return GENERIC_EXIT_OK;
345  }
347  }
348 
349  if (cmdline.toBool("setverbose"))
350  {
352  {
353  QString message = "SET_VERBOSE ";
354  message += cmdline.toString("setverbose");
355 
356  gCoreContext->SendMessage(message);
357  LOG(VB_GENERAL, LOG_INFO,
358  QString("Sent '%1' message").arg(message));
359  return GENERIC_EXIT_OK;
360  }
361  LOG(VB_GENERAL, LOG_ERR,
362  "Unable to connect to backend, verbose mask unchanged ");
364  }
365 
366  if (cmdline.toBool("setloglevel"))
367  {
369  {
370  QString message = "SET_LOG_LEVEL ";
371  message += cmdline.toString("setloglevel");
372 
373  gCoreContext->SendMessage(message);
374  LOG(VB_GENERAL, LOG_INFO,
375  QString("Sent '%1' message").arg(message));
376  return GENERIC_EXIT_OK;
377  }
378  LOG(VB_GENERAL, LOG_ERR,
379  "Unable to connect to backend, log level unchanged ");
381  }
382 
383  if (cmdline.toBool("clearcache"))
384  {
386  {
387  gCoreContext->SendMessage("CLEAR_SETTINGS_CACHE");
388  LOG(VB_GENERAL, LOG_INFO, "Sent CLEAR_SETTINGS_CACHE message");
389  return GENERIC_EXIT_OK;
390  }
391  LOG(VB_GENERAL, LOG_ERR, "Unable to connect to backend, settings "
392  "cache will not be cleared.");
394  }
395 
396  if (cmdline.toBool("printsched") ||
397  cmdline.toBool("testsched"))
398  {
399  auto *sched = new Scheduler(false, &gTVList);
400  if (cmdline.toBool("printsched"))
401  {
403  {
404  LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master");
405  delete sched;
407  }
408  std::cout << "Retrieving Schedule from Master backend.\n";
410  }
411  else
412  {
413  std::cout << "Calculating Schedule from database.\n" <<
414  "Inputs, Card IDs, and Conflict info may be invalid "
415  "if you have multiple tuners.\n";
418  }
419 
420  verboseMask |= VB_SCHEDULE;
421  LogLevel_t oldLogLevel = logLevel;
422  logLevel = LOG_DEBUG;
423  sched->PrintList(true);
424  logLevel = oldLogLevel;
425  delete sched;
426  return GENERIC_EXIT_OK;
427  }
428 
429  if (cmdline.toBool("resched"))
430  {
431  bool ok = false;
433  {
434  LOG(VB_GENERAL, LOG_INFO, "Connected to master for reschedule");
435  ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(),
436  "MythBackendCommand");
437  ok = true;
438  }
439  else
440  LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master for reschedule");
441 
443  }
444 
445  if (cmdline.toBool("scanvideos"))
446  {
447  bool ok = false;
449  {
450  gCoreContext->SendReceiveStringList(QStringList() << "SCAN_VIDEOS");
451  LOG(VB_GENERAL, LOG_INFO, "Requested video scan");
452  ok = true;
453  }
454  else
455  LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master for video scan");
456 
458  }
459 
460  if (cmdline.toBool("printexpire"))
461  {
462  gExpirer = new AutoExpire();
463  gExpirer->PrintExpireList(cmdline.toString("printexpire"));
464  return GENERIC_EXIT_OK;
465  }
466 
467  // This should never actually be reached..
468  return GENERIC_EXIT_OK;
469 }
470 using namespace MythTZ;
471 
473 {
474  auto *tempMonitorConnection = new MythSocket();
475  if (tempMonitorConnection->ConnectToHost(
478  {
479  if (!gCoreContext->CheckProtoVersion(tempMonitorConnection))
480  {
481  LOG(VB_GENERAL, LOG_ERR, "Master backend is incompatible with "
482  "this backend.\nCannot become a slave.");
483  tempMonitorConnection->DecrRef();
485  }
486 
487  QStringList tempMonitorDone("DONE");
488 
489  QStringList tempMonitorAnnounce(QString("ANN Monitor %1 0")
490  .arg(gCoreContext->GetHostName()));
491  tempMonitorConnection->SendReceiveStringList(tempMonitorAnnounce);
492  if (tempMonitorAnnounce.empty() ||
493  tempMonitorAnnounce[0] == "ERROR")
494  {
495  tempMonitorConnection->DecrRef();
496  tempMonitorConnection = nullptr;
497  if (tempMonitorAnnounce.empty())
498  {
499  LOG(VB_GENERAL, LOG_ERR, LOC +
500  "Failed to open event socket, timeout");
501  }
502  else
503  {
504  LOG(VB_GENERAL, LOG_ERR, LOC +
505  "Failed to open event socket" +
506  ((tempMonitorAnnounce.size() >= 2) ?
507  QString(", error was %1").arg(tempMonitorAnnounce[1]) :
508  QString(", remote error")));
509  }
510  }
511 
512  QStringList timeCheck;
513  if (tempMonitorConnection)
514  {
515  timeCheck.push_back("QUERY_TIME_ZONE");
516  tempMonitorConnection->SendReceiveStringList(timeCheck);
517  tempMonitorConnection->WriteStringList(tempMonitorDone);
518  }
519  if (timeCheck.size() < 3)
520  {
521  if (tempMonitorConnection)
522  tempMonitorConnection->DecrRef();
524  }
525 
526  QDateTime our_time = MythDate::current();
527  QDateTime master_time = MythDate::fromString(timeCheck[2]);
528  int timediff = abs(our_time.secsTo(master_time));
529 
530  if (timediff > 300)
531  {
532  LOG(VB_GENERAL, LOG_ERR,
533  QString("Current time on the master backend differs by "
534  "%1 seconds from time on this system. Exiting.")
535  .arg(timediff));
536  if (tempMonitorConnection)
537  tempMonitorConnection->DecrRef();
539  }
540 
541  if (timediff > 20)
542  {
543  LOG(VB_GENERAL, LOG_WARNING,
544  QString("Time difference between the master "
545  "backend and this system is %1 seconds.")
546  .arg(timediff));
547  }
548  }
549  if (tempMonitorConnection)
550  tempMonitorConnection->DecrRef();
551 
552  return GENERIC_EXIT_OK;
553 }
554 
555 
557 {
558  if (cmdline.toBool("nohousekeeper"))
559  {
560  LOG(VB_GENERAL, LOG_WARNING, LOC +
561  "****** The Housekeeper has been DISABLED with "
562  "the --nohousekeeper option ******");
563  }
564  if (cmdline.toBool("nosched"))
565  {
566  LOG(VB_GENERAL, LOG_WARNING, LOC +
567  "********** The Scheduler has been DISABLED with "
568  "the --nosched option **********");
569  }
570  if (cmdline.toBool("noautoexpire"))
571  {
572  LOG(VB_GENERAL, LOG_WARNING, LOC +
573  "********* Auto-Expire has been DISABLED with "
574  "the --noautoexpire option ********");
575  }
576  if (cmdline.toBool("nojobqueue"))
577  {
578  LOG(VB_GENERAL, LOG_WARNING, LOC +
579  "********* The JobQueue has been DISABLED with "
580  "the --nojobqueue option *********");
581  }
582 }
583 
585 {
587 
589  {
590  return run_setup_webserver();
591  }
593  {
594  LOG(VB_GENERAL, LOG_ERR,
595  "MySQL time zone support is missing. "
596  "Please install it and try again. "
597  "See 'mysql_tzinfo_to_sql' for assistance.");
598  gCoreContext->GetDB()->IgnoreDatabase(true);
599  return run_setup_webserver();
600  }
601  bool ismaster = gCoreContext->IsMasterHost();
602 
603  if (!UpgradeTVDatabaseSchema(ismaster, ismaster, true))
604  {
605  LOG(VB_GENERAL, LOG_ERR,
606  QString("Couldn't upgrade database to new schema on %1 backend.")
607  .arg(ismaster ? "master" : "slave"));
609  }
610 
611  be_sd_notify("STATUS=Loading translation");
612  MythTranslation::load("mythfrontend");
613 
614  if (!ismaster)
615  {
616  be_sd_notify("STATUS=Connecting to master backend");
617  int ret = connect_to_master();
618  if (ret != GENERIC_EXIT_OK)
619  return ret;
620  }
621 
622  be_sd_notify("STATUS=Get backend server port");
623  int port = gCoreContext->GetBackendServerPort();
624  if (gCoreContext->GetBackendServerIP().isEmpty())
625  {
626  std::cerr << "No setting found for this machine's BackendServerAddr.\n"
627  << "Please run mythtv-setup on this machine.\n"
628  << "Go to page \"General\" / \"Host Address Backend Setup\" and examine the values.\n"
629  << "N.B. The default values are correct for a combined frontend/backend machine.\n"
630  << "Press Escape, select \"Save and Exit\" and exit mythtv-setup.\n"
631  << "Then start mythbackend again.\n";
632  return run_setup_webserver();
633  }
634 
636 
637  if (ismaster)
638  {
639  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Starting up as the master server.");
640  }
641  else
642  {
643  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Running as a slave backend.");
644  }
645 
646  if (ismaster)
647  {
649  }
650 
652 
653  bool fatal_error = false;
654  bool runsched = setupTVs(ismaster, fatal_error);
655  if (fatal_error)
657 
658  Scheduler *sched = nullptr;
659  if (ismaster)
660  {
661  if (runsched)
662  {
663  be_sd_notify("STATUS=Creating scheduler");
664  sched = new Scheduler(true, &gTVList);
665  int err = sched->GetError();
666  if (err)
667  {
668  delete sched;
669  return err;
670  }
671 
672  if (cmdline.toBool("nosched"))
674  }
675 
676  if (!cmdline.toBool("noautoexpire"))
677  {
678  gExpirer = new AutoExpire(&gTVList);
679  if (sched)
681  }
683  }
684 
685  if (!cmdline.toBool("nohousekeeper"))
686  {
687  be_sd_notify("STATUS=Creating housekeeper");
688  gHousekeeping = new HouseKeeper();
689 
690  if (ismaster)
691  {
696 
697  // only run this task if MythMusic is installed and we have a new enough schema
698  if (gCoreContext->GetNumSetting("MusicDBSchemaVer", 0) >= 1024)
700  }
701 
703 #ifdef __linux__
704  #ifdef CONFIG_BINDINGS_PYTHON
706  #endif
707 #endif
708 
709  gHousekeeping->Start();
710  }
711 
712  if (!cmdline.toBool("nojobqueue"))
713  gJobQueue = new JobQueue(ismaster);
714 
715  // ----------------------------------------------------------------------
716  //
717  // ----------------------------------------------------------------------
718 
719  if (g_pUPnp == nullptr)
720  {
721  be_sd_notify("STATUS=Creating UPnP media server");
722  g_pUPnp = new MediaServer();
723 
724  g_pUPnp->Init(ismaster, cmdline.toBool("noupnp"));
725  }
726 
727  if (cmdline.toBool("dvbv3"))
728  {
729  LOG(VB_GENERAL, LOG_INFO, LOC + "Use legacy DVBv3 API");
730  gCoreContext->SetDVBv3(true);
731  }
732 
733  // ----------------------------------------------------------------------
734  // Setup status server
735  // ----------------------------------------------------------------------
736 
737  HttpStatus *httpStatus = nullptr;
738  HttpServer *pHS = g_pUPnp->GetHttpServer();
739 
740  if (pHS)
741  {
742  LOG(VB_GENERAL, LOG_INFO, "Main::Registering HttpStatus Extension");
743  be_sd_notify("STATUS=Registering HttpStatus Extension");
744 
745  httpStatus = new HttpStatus( &gTVList, sched, gExpirer, ismaster );
746  pHS->RegisterExtension( httpStatus );
747  }
748 
749  be_sd_notify("STATUS=Creating main server");
750  mainServer = new MainServer(
751  ismaster, port, &gTVList, sched, gExpirer);
752 
753  int exitCode = mainServer->GetExitCode();
754  if (exitCode != GENERIC_EXIT_OK)
755  {
756  LOG(VB_GENERAL, LOG_CRIT,
757  "Backend exiting, MainServer initialization error.");
758  cleanup();
759  return exitCode;
760  }
761 
762  if (httpStatus && mainServer)
763  httpStatus->SetMainServer(mainServer);
764 
765  be_sd_notify("STATUS=Check all storage groups");
767 
768  be_sd_notify("STATUS=Sending \"master started\" message");
770  gCoreContext->SendSystemEvent("MASTER_STARTED");
771 
772  // Provide systemd ready notification (for type=notify units)
773  be_sd_notify("READY=1");
774 
775  const HTTPServices be_services = {
776  { VIDEO_SERVICE, &MythHTTPService::Create<V2Video> },
777  { MYTH_SERVICE, &MythHTTPService::Create<V2Myth> },
778  { DVR_SERVICE, &MythHTTPService::Create<V2Dvr> },
779  { CONTENT_SERVICE, &MythHTTPService::Create<V2Content> },
780  { GUIDE_SERVICE, &MythHTTPService::Create<V2Guide> },
781  { CHANNEL_SERVICE, &MythHTTPService::Create<V2Channel> },
782  { STATUS_SERVICE, &MythHTTPService::Create<V2Status> },
783  { CAPTURE_SERVICE, &MythHTTPService::Create<V2Capture> },
784  { MUSIC_SERVICE, &MythHTTPService::Create<V2Music> },
785  { CONFIG_SERVICE, &MythHTTPService::Create<V2Config> },
786  };
787 
788  MythHTTPInstance::Addservices(be_services);
789 
790  // Send all unknown requests into the web app. make bookmarks and direct access work.
791  auto spa_index = [](auto && PH1) { return MythHTTPRewrite::RewriteToSPA(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
792  MythHTTPInstance::AddErrorPageHandler({ "=404", spa_index });
793 
794  // Serve components of the backend web app as if they were hosted at '/'
795  auto main_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/main.js"); };
796  auto styles_css = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/styles.css"); };
797  auto polyfills_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/polyfills.js"); };
798  auto runtime_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/runtime.js"); };
799 
800  // Default index page
801  auto root = [](auto && PH1) { return MythHTTPRoot::RedirectRoot(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
802 
803  const HTTPHandlers be_handlers = {
804  { "/main.js", main_js },
805  { "/styles.css", styles_css },
806  { "/polyfills.js", polyfills_js },
807  { "/runtime.js", runtime_js },
808  { "/", root }
809  };
810 
811  MythHTTPScopedInstance webserver(be_handlers);
812 
815  exitCode = qApp->exec();
818 
820  {
821  gCoreContext->SendSystemEvent("MASTER_SHUTDOWN");
822  qApp->processEvents();
823  }
824 
825  LOG(VB_GENERAL, LOG_NOTICE, "MythBackend exiting");
826  be_sd_notify("STOPPING=1\nSTATUS=Exiting");
827 
828  return exitCode;
829 }
830 
831 // This is a copy of the code from above, to start backend in a restricted mode, only running the web server
832 // when the database is unusable, so thet the user can use the web app to fix the settings.
833 
835 {
836  LOG(VB_GENERAL, LOG_NOTICE, "**********************************************************************");
837  LOG(VB_GENERAL, LOG_NOTICE, "***** MythBackend starting in webapp only mode for initial setup *****");
838  LOG(VB_GENERAL, LOG_NOTICE, "***** Use http://localhost:6744 to perform setup *****");
839  LOG(VB_GENERAL, LOG_NOTICE, "**********************************************************************");
840 
841  const HTTPServices be_services = {
842  { VIDEO_SERVICE, &MythHTTPService::Create<V2Video> },
843  { MYTH_SERVICE, &MythHTTPService::Create<V2Myth> },
844  { DVR_SERVICE, &MythHTTPService::Create<V2Dvr> },
845  { CONTENT_SERVICE, &MythHTTPService::Create<V2Content> },
846  { GUIDE_SERVICE, &MythHTTPService::Create<V2Guide> },
847  { CHANNEL_SERVICE, &MythHTTPService::Create<V2Channel> },
848  { STATUS_SERVICE, &MythHTTPService::Create<V2Status> },
849  { CAPTURE_SERVICE, &MythHTTPService::Create<V2Capture> },
850  { MUSIC_SERVICE, &MythHTTPService::Create<V2Music> },
851  { CONFIG_SERVICE, &MythHTTPService::Create<V2Config> },
852  };
853 
854  MythHTTPInstance::Addservices(be_services);
855 
856  // Send all unknown requests into the web app. make bookmarks and direct access work.
857  auto spa_index = [](auto && PH1) { return MythHTTPRewrite::RewriteToSPA(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
858  MythHTTPInstance::AddErrorPageHandler({ "=404", spa_index });
859 
860  // Serve components of the backend web app as if they were hosted at '/'
861  auto main_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/main.js"); };
862  auto styles_css = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/styles.css"); };
863  auto polyfills_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/polyfills.js"); };
864  auto runtime_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/runtime.js"); };
865 
866  // Default index page
867  auto root = [](auto && PH1) { return MythHTTPRoot::RedirectRoot(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
868 
869  const HTTPHandlers be_handlers = {
870  { "/main.js", main_js },
871  { "/styles.css", styles_css },
872  { "/polyfills.js", polyfills_js },
873  { "/runtime.js", runtime_js },
874  { "/", root }
875  };
876 
877  MythHTTPScopedInstance webserver(be_handlers);
878 
881  int exitCode = qApp->exec();
882 
883  LOG(VB_GENERAL, LOG_NOTICE, "MythBackend setup webapp exiting");
884  return exitCode;
885 }
setupTVs
bool setupTVs(bool ismaster, bool &error)
Definition: mythbackend_main_helpers.cpp:82
run_setup_webserver
int run_setup_webserver()
Definition: mythbackend_main_helpers.cpp:834
Scheduler
Definition: scheduler.h:45
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:811
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
hardwareprofile.h
v2video.h
HouseKeeper
Manages registered HouseKeeperTasks and queues tasks for operation.
Definition: housekeeper.h:149
MythCoreContext::SendMessage
void SendMessage(const QString &message)
Definition: mythcorecontext.cpp:1515
GENERIC_EXIT_SETUP_ERROR
@ GENERIC_EXIT_SETUP_ERROR
Incorrectly setup system.
Definition: exitcodes.h:22
AutoExpire::PrintExpireList
void PrintExpireList(const QString &expHost="ALL")
Prints a summary of the files that can be deleted.
Definition: autoexpire.cpp:792
MythHTTPScopedInstance
Definition: mythhttpinstance.h:35
ThemeUpdateTask
Definition: backendhousekeeper.h:34
v2music.h
Scheduler::GetError
int GetError(void) const
Definition: scheduler.h:119
dbutil.h
backendcontext.h
EITCache::ClearChannelLocks
static MTV_PUBLIC void ClearChannelLocks(void)
Removes old channel locks, use it only at master backend start.
Definition: eitcache.cpp:438
CONTENT_SERVICE
#define CONTENT_SERVICE
Definition: v2content.h:34
error
static void error(const char *str,...)
Definition: vbi.cpp:36
MediaServer
Definition: mediaserver.h:32
MythCoreContext::GetScheduler
MythScheduler * GetScheduler(void)
Definition: mythcorecontext.cpp:1875
connect_to_master
int connect_to_master(void)
Definition: mythbackend_main_helpers.cpp:472
CONFIG_SERVICE
#define CONFIG_SERVICE
Definition: v2config.h:11
mythdb.h
MythCoreContext::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Definition: mythcorecontext.cpp:1369
UpgradeTVDatabaseSchema
bool UpgradeTVDatabaseSchema(const bool upgradeAllowed, const bool upgradeIfNoUI, const bool informSystemd)
Called from outside dbcheck.cpp to update the schema.
Definition: dbcheck.cpp:361
HttpStatus
Definition: httpstatus.h:42
cmdline
MythCommFlagCommandLineParser cmdline
Definition: mythcommflag.cpp:72
TVRec::Init
bool Init(void)
Performs instance initialization, returns true on success.
Definition: tv_rec.cpp:155
BackendContext
Definition: backendcontext.h:42
MythCoreContext::ConnectToMasterServer
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
Definition: mythcorecontext.cpp:355
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
logLevel
LogLevel_t logLevel
Definition: logging.cpp:85
TaskQueue::RequestTerminate
void RequestTerminate()
Definition: taskqueue.cpp:102
gExpirer
AutoExpire * gExpirer
Definition: backendcontext.cpp:8
HttpServer::RegisterExtension
void RegisterExtension(HttpServerExtension *pExtension)
Definition: httpserver.cpp:321
MythCoreContext::SetScheduler
void SetScheduler(MythScheduler *sched)
Definition: mythcorecontext.cpp:1870
Scheduler::FillRecordListFromDB
void FillRecordListFromDB(uint recordid=0)
Definition: scheduler.cpp:491
v2content.h
httpstatus.h
sched
Scheduler * sched
mythhttpinstance.h
RadioStreamUpdateTask
Definition: backendhousekeeper.h:22
MythCoreContext::IsDatabaseIgnored
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
Definition: mythcorecontext.cpp:874
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
MainServer
Definition: mainserver.h:115
AutoExpire
Used to expire recordings to make space for new recordings.
Definition: autoexpire.h:60
v2myth.h
Scheduler::DisableScheduling
void DisableScheduling(void)
Definition: scheduler.h:109
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:617
UPnp::GetHttpServer
HttpServer * GetHttpServer()
Definition: upnp.h:128
MythCoreContext::CheckProtoVersion
bool CheckProtoVersion(MythSocket *socket, std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
Definition: mythcorecontext.cpp:1665
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
mythsystemevent.h
GENERIC_EXIT_INVALID_TIME
@ GENERIC_EXIT_INVALID_TIME
Invalid time.
Definition: exitcodes.h:23
HouseKeeper::RegisterTask
void RegisterTask(HouseKeeperTask *task)
Definition: housekeeper.cpp:638
gBackendContext
BackendContext * gBackendContext
Definition: backendcontext.cpp:12
HttpStatus::SetMainServer
void SetMainServer(MainServer *mainServer)
Definition: httpstatus.h:88
GENERIC_EXIT_OK
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:11
gHousekeeping
HouseKeeper * gHousekeeping
Definition: backendcontext.cpp:10
v2dvr.h
remoteutil.h
scheduler.h
be_sd_notify
static void be_sd_notify(const char *)
Definition: mythbackend_main_helpers.cpp:6
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:14
VIDEO_SERVICE
#define VIDEO_SERVICE
Definition: v2video.h:13
MythSocket
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:25
mythbackend_commandlineparser.h
SSDP::Instance
static SSDP * Instance()
Definition: ssdp.cpp:55
MythCoreContext::IsMasterBackend
bool IsMasterBackend(void)
is this the actual MBE process
Definition: mythcorecontext.cpp:693
MythHTTPRewrite::RewriteFile
static HTTPResponse RewriteFile(const HTTPRequest2 &Request, const QString &File)
A convenience method to seemlessly redirect requests for files to a context specific file.
Definition: mythhttprewrite.cpp:22
v2channel.h
autoexpire.h
MythHTTPInstance::Addservices
static void Addservices(const HTTPServices &Services)
Definition: mythhttpinstance.cpp:102
programinfo.h
g_pUPnp
MediaServer * g_pUPnp
Definition: backendcontext.cpp:11
mythlogging.h
GENERIC_EXIT_NO_MYTHCONTEXT
@ GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:14
dbcheck.h
MythCoreContext::GetBackendServerPort
int GetBackendServerPort(void)
Returns the locally defined backend control port.
Definition: mythcorecontext.cpp:1062
MythCoreContext::SendSystemEvent
void SendSystemEvent(const QString &msg)
Definition: mythcorecontext.cpp:1542
MythCoreContext::GetMasterServerIP
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
Definition: mythcorecontext.cpp:964
signalhandling.h
verboseMask
uint64_t verboseMask
Definition: logging.cpp:97
MSqlQuery::first
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
Definition: mythdbcon.cpp:821
TaskQueue::Instance
static TaskQueue * Instance()
Definition: taskqueue.cpp:59
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:549
gSysEventHandler
MythSystemEventHandler * gSysEventHandler
Definition: backendcontext.cpp:14
compat.h
MythCoreContext::GetBackendServerIP
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
Definition: mythcorecontext.cpp:1002
MythCoreContext::SetDVBv3
void SetDVBv3(bool dvbv3)
Definition: mythcorecontext.h:327
eitcache.h
GENERIC_EXIT_SOCKET_ERROR
@ GENERIC_EXIT_SOCKET_ERROR
Socket error.
Definition: exitcodes.h:19
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
mythhttprewrite.h
Scheduler::SetExpirer
void SetExpirer(AutoExpire *autoExpirer)
Definition: scheduler.h:55
MythCoreContext::GetDB
MythDB * GetDB(void)
Definition: mythcorecontext.cpp:1757
mythtranslation.h
v2capture.h
MythTZ
Definition: mythtimezone.cpp:17
HardwareProfileTask
Definition: hardwareprofile.h:52
scheduledrecording.h
ArtworkTask
Definition: backendhousekeeper.h:49
DBUtil::CheckTimeZoneSupport
static bool CheckTimeZoneSupport(void)
Check if MySQL has working timz zone support.
Definition: dbutil.cpp:867
CHANNEL_SERVICE
#define CHANNEL_SERVICE
Definition: v2channel.h:38
storagegroup.h
jobqueue.h
Scheduler::PrintList
void PrintList(bool onlyFutureRecordings=false)
Definition: scheduler.h:98
TVRec::s_inputsLock
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:432
DVR_SERVICE
#define DVR_SERVICE
Definition: v2dvr.h:39
uint
unsigned int uint
Definition: compat.h:81
MythHTTPRoot::RedirectRoot
static HTTPResponse RedirectRoot(const HTTPRequest2 &Request, const QString &File)
A convenience method to seemlessly redirect requests for index.html to a context specific file.
Definition: mythhttproot.cpp:24
gTVList
QMap< int, EncoderLink * > gTVList
Definition: backendcontext.cpp:7
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
JobQueueRecoverTask
Definition: backendhousekeeper.h:62
v2config.h
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:910
MediaServer::Init
void Init(bool bIsMaster, bool bDisableUPnp=false)
Definition: mediaserver.cpp:64
mainServer
static MainServer * mainServer
Definition: mythbackend_main_helpers.cpp:80
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:34
MainServer::GetExitCode
int GetExitCode() const
Definition: mainserver.h:154
gJobQueue
JobQueue * gJobQueue
Definition: backendcontext.cpp:9
STATUS_SERVICE
#define STATUS_SERVICE
Definition: v2status.h:34
GUIDE_SERVICE
#define GUIDE_SERVICE
Definition: v2guide.h:40
MythCoreContext::GetMasterServerPort
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
Definition: mythcorecontext.cpp:978
backendhousekeeper.h
mediaserver.h
GENERIC_EXIT_CONNECT_ERROR
@ GENERIC_EXIT_CONNECT_ERROR
Can't connect to master backend.
Definition: exitcodes.h:21
CleanupTask
Definition: backendhousekeeper.h:7
MythCommandLineParser::toString
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
Definition: mythcommandlineparser.cpp:2358
gPidFile
QString gPidFile
Definition: backendcontext.cpp:13
mythhttproot.h
MythCommandLineParser::toBool
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
Definition: mythcommandlineparser.cpp:2201
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:887
CAPTURE_SERVICE
#define CAPTURE_SERVICE
Definition: v2capture.h:33
TVRec
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:144
GENERIC_EXIT_DB_OUTOFDATE
@ GENERIC_EXIT_DB_OUTOFDATE
Database needs upgrade.
Definition: exitcodes.h:17
mythcontext.h
tv_rec.h
v2guide.h
handle_command
int handle_command(const MythBackendCommandLineParser &cmdline)
Definition: mythbackend_main_helpers.cpp:326
MYTH_SERVICE
#define MYTH_SERVICE
Definition: v2myth.h:16
HttpServer
Definition: httpserver.h:112
mainserver.h
MUSIC_SERVICE
#define MUSIC_SERVICE
Definition: v2music.h:17
JobQueue
Definition: jobqueue.h:130
mythbackend_main_helpers.h
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:836
MythBackendCommandLineParser
Definition: mythbackend_commandlineparser.h:7
HTTPServices
std::vector< HTTPService > HTTPServices
Definition: mythhttptypes.h:54
MythCoreContext::IsMasterHost
bool IsMasterHost(void)
is this the same host as the master
Definition: mythcorecontext.cpp:657
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
previewgenerator.h
MythFillDatabaseTask
Definition: backendhousekeeper.h:71
ProgramInfo::CheckProgramIDAuthorities
static void CheckProgramIDAuthorities(void)
Definition: programinfo.cpp:2361
MythTranslation::load
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
Definition: mythtranslation.cpp:37
exitcodes.h
MainServer::Stop
void Stop(void)
Definition: mainserver.cpp:361
run_backend
int run_backend(MythBackendCommandLineParser &cmdline)
Definition: mythbackend_main_helpers.cpp:584
MythSystemEventHandler
Handles incoming MythSystemEvent messages.
Definition: mythsystemevent.h:24
Scheduler::FillRecordListFromMaster
void FillRecordListFromMaster(void)
Definition: scheduler.cpp:574
ScheduledRecording::RescheduleMatch
static void RescheduleMatch(uint recordid, uint sourceid, uint mplexid, const QDateTime &maxstarttime, const QString &why)
Definition: scheduledrecording.h:17
cleanup
void cleanup(void)
Definition: mythbackend_main_helpers.cpp:257
print_warnings
void print_warnings(const MythBackendCommandLineParser &cmdline)
Definition: mythbackend_main_helpers.cpp:556
StorageGroup::CheckAllStorageGroupDirs
static void CheckAllStorageGroupDirs(void)
Definition: storagegroup.cpp:729
gContext
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:57
HouseKeeper::Start
void Start(void)
Definition: housekeeper.cpp:672
MythHTTPRewrite::RewriteToSPA
static HTTPResponse RewriteToSPA(const HTTPRequest2 &Request, const QString &File)
A convenience method to seemlessly redirect requests to a Single Page web app (SPA)
Definition: mythhttprewrite.cpp:50
mythtimezone.h
SignalHandler::Done
static void Done(void)
Definition: signalhandling.cpp:134
MythCoreContext::SetExiting
void SetExiting(bool exiting=true)
Definition: mythcorecontext.cpp:2082
TVRec::GetTVRec
static TVRec * GetTVRec(uint inputid)
Definition: tv_rec.cpp:4779
v2status.h
SSDP::RequestTerminate
void RequestTerminate(void)
Definition: ssdp.cpp:139
HTTPHandlers
std::vector< HTTPHandler > HTTPHandlers
Definition: mythhttptypes.h:47
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:836
TVRec::s_inputs
static QMap< uint, TVRec * > s_inputs
Definition: tv_rec.h:433
MythHTTPInstance::AddErrorPageHandler
static void AddErrorPageHandler(const HTTPHandler &Handler)
Definition: mythhttpinstance.cpp:112
LOC
#define LOC
Definition: mythbackend_main_helpers.cpp:76