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>
4static inline void be_sd_notify(const char *str) { sd_notify(0, str); };
5#else
6static 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 <QtGlobal>
21#include <QCoreApplication>
22#include <QFileInfo>
23#include <QFile>
24#include <QDir>
25#include <QMap>
26
27// MythTV
28#include "libmythbase/compat.h"
29#include "libmythbase/dbutil.h"
33#include "libmythbase/mythdb.h"
37#include "libmythbase/mythversion.h"
39#include "libmythtv/dbcheck.h"
40#include "libmythtv/eitcache.h"
41#include "libmythtv/jobqueue.h"
46#include "libmythtv/tv_rec.h"
47#include "libmythupnp/ssdp.h"
49
50// MythBackend
51#include "autoexpire.h"
52#include "backendcontext.h"
53#include "backendhousekeeper.h"
54#include "encoderlink.h"
55#include "httpstatus.h"
56#include "mainserver.h"
57#include "mediaserver.h"
60#include "scheduler.h"
61
62// New webserver
66#include "servicesv2/v2myth.h"
67#include "servicesv2/v2video.h"
68#include "servicesv2/v2dvr.h"
70#include "servicesv2/v2guide.h"
72#include "servicesv2/v2status.h"
74#include "servicesv2/v2music.h"
75#include "servicesv2/v2config.h"
76
77#define LOC QString("MythBackend: ")
78#define LOC_WARN QString("MythBackend, Warning: ")
79#define LOC_ERR QString("MythBackend, Error: ")
80
81static HouseKeeper *gHousekeeping { nullptr };
82static JobQueue *gJobQueue { nullptr };
84static MediaServer *g_pUPnp { nullptr };
85static MainServer *mainServer { nullptr };
86
88{
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 {
117 MythDB::DBError("Querying minimum chanid", query);
118 }
119
120 MSqlQuery records_without_station(MSqlQuery::InitCon());
121 records_without_station.prepare("SELECT record.chanid,"
122 " channel.callsign FROM record LEFT JOIN channel"
123 " ON record.chanid = channel.chanid WHERE record.station='';");
124 if (records_without_station.exec())
125 {
126 MSqlQuery update_record(MSqlQuery::InitCon());
127 update_record.prepare("UPDATE record SET station = :CALLSIGN"
128 " WHERE chanid = :CHANID;");
129 while (records_without_station.next())
130 {
131 update_record.bindValue(":CALLSIGN",
132 records_without_station.value(1));
133 update_record.bindValue(":CHANID",
134 records_without_station.value(0));
135 if (!update_record.exec())
136 {
137 MythDB::DBError("Updating record station", update_record);
138 }
139 }
140 }
141}
142
143bool createTVRecorders(bool ismaster, bool retry)
144{
145 QString localhostname = gCoreContext->GetHostName();
146
148
149 if (!query.exec(
150 "SELECT cardid, parentid, videodevice, hostname, sourceid "
151 "FROM capturecard "
152 "ORDER BY cardid"))
153 {
154 MythDB::DBError("Querying Recorders", query);
155 return false;
156 }
157
158 std::vector<unsigned int> cardids;
159 std::vector<QString> hosts;
160 while (query.next())
161 {
162 uint cardid = query.value(0).toUInt();
163 uint parentid = query.value(1).toUInt();
164 QString videodevice = query.value(2).toString();
165 QString hostname = query.value(3).toString();
166 uint sourceid = query.value(4).toUInt();
167 QString cidmsg = QString("Card[%1](%2)").arg(cardid).arg(videodevice);
168
169 if (hostname.isEmpty() && !retry)
170 {
171 LOG(VB_GENERAL, LOG_ERR, cidmsg +
172 " does not have a hostname defined.\n"
173 "Please run setup and confirm all of the capture cards.\n");
174 continue;
175 }
176
177 // Skip all cards that do not have a video source
178 if (sourceid == 0)
179 {
180 if (parentid == 0 && !retry)
181 {
182 LOG(VB_GENERAL, LOG_WARNING, cidmsg +
183 " does not have a video source");
184 }
185 continue;
186 }
187
188 if (retry && (TVRec::GetTVRec(cardid) != nullptr))
189 {
190 // We're retrying, so ignore existing encoders
191 continue;
192 }
193
194 cardids.push_back(cardid);
195 hosts.push_back(hostname);
196 }
197
198 QWriteLocker tvlocker(&TVRec::s_inputsLock);
199
200 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
201 for (size_t i = 0; i < cardids.size(); i++)
202 {
203 if (hosts[i] == localhostname) {
204 // No memory leak. The constructor for TVRec adds the item
205 // to the static map TVRec::s_inputs.
206 new TVRec(cardids[i]);
207 }
208 }
209
210 for (size_t i = 0; i < cardids.size(); i++)
211 {
212 uint cardid = cardids[i];
213 const QString& host = hosts[i];
214 QString cidmsg = QString("Card %1").arg(cardid);
215
216 if (!ismaster)
217 {
218 if (host == localhostname)
219 {
220 TVRec *tv = TVRec::GetTVRec(cardid);
221 if (tv && tv->Init())
222 {
223 auto *enc = new EncoderLink(cardid, tv);
224 gTVList[cardid] = enc;
225 }
226 else
227 {
228 LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards. " +
229 cidmsg + " failed init");
230 delete tv;
231 // No longer set an error here, because we need the
232 // slave backend to be able to start without a capture
233 // card, so that it can be setup through the web app
234 }
235 }
236 }
237 else
238 {
239 if (host == localhostname)
240 {
241 TVRec *tv = TVRec::GetTVRec(cardid);
242 if (tv && tv->Init())
243 {
244 auto *enc = new EncoderLink(cardid, tv);
245 gTVList[cardid] = enc;
246 }
247 else
248 {
249 LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards. " +
250 cidmsg + " failed init");
251 delete tv;
252 }
253 }
254 else
255 {
256 auto *enc = new EncoderLink(cardid, nullptr, host);
257 gTVList[cardid] = enc;
258 }
259 }
260 }
261
262 if (gTVList.empty() && !retry)
263 {
264 LOG(VB_GENERAL, LOG_WARNING, LOC +
265 "No valid capture cards are defined in the database.");
266 }
267
268 return true;
269}
270
271void cleanup(void)
272{
273 if (mainServer)
274 {
275 mainServer->Stop();
276 qApp->processEvents();
277 }
278
279 if (gCoreContext)
281
282 delete gSysEventHandler;
283 gSysEventHandler = nullptr;
284
285 delete gHousekeeping;
286 gHousekeeping = nullptr;
287
288 if (gCoreContext)
289 {
290 delete gCoreContext->GetScheduler();
291 gCoreContext->SetScheduler(nullptr);
292 }
293
294 delete gExpirer;
295 gExpirer = nullptr;
296
297 delete gJobQueue;
298 gJobQueue = nullptr;
299
300 delete g_pUPnp;
301 g_pUPnp = nullptr;
302
304 {
307 }
308
309 while (!TVRec::s_inputs.empty())
310 {
311 TVRec *rec = *TVRec::s_inputs.begin();
312 delete rec;
313 }
314
315 delete mainServer;
316 mainServer = nullptr;
317
318 delete gBackendContext;
319 gBackendContext = nullptr;
320}
321
323{
324 if (cmdline.toBool("setverbose"))
325 {
327 {
328 QString message = "SET_VERBOSE ";
329 message += cmdline.toString("setverbose");
330
331 gCoreContext->SendMessage(message);
332 LOG(VB_GENERAL, LOG_INFO,
333 QString("Sent '%1' message").arg(message));
334 return GENERIC_EXIT_OK;
335 }
336 LOG(VB_GENERAL, LOG_ERR,
337 "Unable to connect to backend, verbose mask unchanged ");
339 }
340
341 if (cmdline.toBool("setloglevel"))
342 {
344 {
345 QString message = "SET_LOG_LEVEL ";
346 message += cmdline.toString("setloglevel");
347
348 gCoreContext->SendMessage(message);
349 LOG(VB_GENERAL, LOG_INFO,
350 QString("Sent '%1' message").arg(message));
351 return GENERIC_EXIT_OK;
352 }
353 LOG(VB_GENERAL, LOG_ERR,
354 "Unable to connect to backend, log level unchanged ");
356 }
357
358 if (cmdline.toBool("printsched") ||
359 cmdline.toBool("testsched"))
360 {
361 auto *sched = new Scheduler(false, &gTVList);
362 if (cmdline.toBool("printsched"))
363 {
365 {
366 LOG(VB_GENERAL, LOG_ERR, "Cannot connect to master");
367 delete sched;
369 }
370 std::cout << "Retrieving Schedule from Master backend.\n";
372 }
373 else
374 {
375 std::cout << "Calculating Schedule from database.\n" <<
376 "Inputs, Card IDs, and Conflict info may be invalid "
377 "if you have multiple tuners.\n";
380 }
381
382 verboseMask |= VB_SCHEDULE;
383 LogLevel_t oldLogLevel = logLevel;
384 logLevel = LOG_DEBUG;
385 sched->PrintList(true);
386 logLevel = oldLogLevel;
387 delete sched;
388 return GENERIC_EXIT_OK;
389 }
390
391 if (cmdline.toBool("printexpire"))
392 {
393 gExpirer = new AutoExpire();
394 gExpirer->PrintExpireList(cmdline.toString("printexpire"));
395 return GENERIC_EXIT_OK;
396 }
397
398 // This should never actually be reached..
399 return GENERIC_EXIT_OK;
400}
401using namespace MythTZ;
402
404{
405 auto *tempMonitorConnection = new MythSocket();
406 if (tempMonitorConnection->ConnectToHost(
409 {
410 if (!gCoreContext->CheckProtoVersion(tempMonitorConnection))
411 {
412 LOG(VB_GENERAL, LOG_ERR, "Master backend is incompatible with "
413 "this backend.\nCannot become a slave.");
414 tempMonitorConnection->DecrRef();
416 }
417
418 QStringList tempMonitorDone("DONE");
419
420 QStringList tempMonitorAnnounce(QString("ANN Monitor %1 0")
421 .arg(gCoreContext->GetHostName()));
422 tempMonitorConnection->SendReceiveStringList(tempMonitorAnnounce);
423 if (tempMonitorAnnounce.empty() ||
424 tempMonitorAnnounce[0] == "ERROR")
425 {
426 tempMonitorConnection->DecrRef();
427 tempMonitorConnection = nullptr;
428 if (tempMonitorAnnounce.empty())
429 {
430 LOG(VB_GENERAL, LOG_ERR, LOC +
431 "Failed to open event socket, timeout");
432 }
433 else
434 {
435 LOG(VB_GENERAL, LOG_ERR, LOC +
436 "Failed to open event socket" +
437 ((tempMonitorAnnounce.size() >= 2) ?
438 QString(", error was %1").arg(tempMonitorAnnounce[1]) :
439 QString(", remote error")));
440 }
441 }
442
443 QStringList timeCheck;
444 if (tempMonitorConnection)
445 {
446 timeCheck.push_back("QUERY_TIME_ZONE");
447 tempMonitorConnection->SendReceiveStringList(timeCheck);
448 tempMonitorConnection->WriteStringList(tempMonitorDone);
449 }
450 if (timeCheck.size() < 3)
451 {
452 if (tempMonitorConnection)
453 tempMonitorConnection->DecrRef();
455 }
456
457 QDateTime our_time = MythDate::current();
458 QDateTime master_time = MythDate::fromString(timeCheck[2]);
459 int timediff = abs(our_time.secsTo(master_time));
460
461 if (timediff > 300)
462 {
463 LOG(VB_GENERAL, LOG_ERR,
464 QString("Current time on the master backend differs by "
465 "%1 seconds from time on this system. Exiting.")
466 .arg(timediff));
467 if (tempMonitorConnection)
468 tempMonitorConnection->DecrRef();
470 }
471
472 if (timediff > 20)
473 {
474 LOG(VB_GENERAL, LOG_WARNING,
475 QString("Time difference between the master "
476 "backend and this system is %1 seconds.")
477 .arg(timediff));
478 }
479 }
480 if (tempMonitorConnection)
481 tempMonitorConnection->DecrRef();
482
483 return GENERIC_EXIT_OK;
484}
485
486
488{
489 if (cmdline.toBool("nohousekeeper"))
490 {
491 LOG(VB_GENERAL, LOG_WARNING, LOC +
492 "****** The Housekeeper has been DISABLED with "
493 "the --nohousekeeper option ******");
494 }
495 if (cmdline.toBool("nosched"))
496 {
497 LOG(VB_GENERAL, LOG_WARNING, LOC +
498 "********** The Scheduler has been DISABLED with "
499 "the --nosched option **********");
500 }
501 if (cmdline.toBool("noautoexpire"))
502 {
503 LOG(VB_GENERAL, LOG_WARNING, LOC +
504 "********* Auto-Expire has been DISABLED with "
505 "the --noautoexpire option ********");
506 }
507 if (cmdline.toBool("nojobqueue"))
508 {
509 LOG(VB_GENERAL, LOG_WARNING, LOC +
510 "********* The JobQueue has been DISABLED with "
511 "the --nojobqueue option *********");
512 }
513}
514
516{
518
520 {
521 return run_setup_webserver();
522 }
524 {
525 LOG(VB_GENERAL, LOG_ERR,
526 "MySQL time zone support is missing. "
527 "Please install it and try again. "
528 "See 'mysql_tzinfo_to_sql' for assistance.");
529 gCoreContext->GetDB()->IgnoreDatabase(true);
531 return run_setup_webserver();
532 }
533 bool ismaster = gCoreContext->IsMasterHost();
534
535 if (!UpgradeTVDatabaseSchema(ismaster, ismaster, true))
536 {
537 LOG(VB_GENERAL, LOG_ERR,
538 QString("Couldn't upgrade database to new schema on %1 backend.")
539 .arg(ismaster ? "master" : "slave"));
541 return run_setup_webserver();
542 }
543#ifndef NDEBUG
544 if (cmdline.toBool("upgradedbonly"))
545 {
546 LOG(VB_GENERAL, LOG_ERR, "Exiting as requested.");
547 return GENERIC_EXIT_OK;
548 }
549#endif
550
551 be_sd_notify("STATUS=Loading translation");
552 MythTranslation::load("mythfrontend");
553
554 if (cmdline.toBool("webonly"))
555 {
557 return run_setup_webserver();
558 }
559 if (!ismaster)
560 {
561 be_sd_notify("STATUS=Connecting to master backend");
562 int ret = connect_to_master();
563 if (ret != GENERIC_EXIT_OK)
564 return ret;
565 }
566
567 be_sd_notify("STATUS=Get backend server port");
569 if (gCoreContext->GetBackendServerIP().isEmpty())
570 {
571 std::cerr << "No setting found for this machine's BackendServerAddr.\n"
572 << "MythBackend starting in Web App only mode for initial setup.\n"
573 << "Use http://<yourBackend>:6544 to perform setup.\n";
575 return run_setup_webserver();
576 }
577
579
580 if (ismaster)
581 {
582 LOG(VB_GENERAL, LOG_NOTICE, LOC + "Starting up as the master server.");
583 }
584 else
585 {
586 LOG(VB_GENERAL, LOG_NOTICE, LOC + "Running as a slave backend.");
587 }
588
589 if (ismaster)
590 {
592 }
593
595
596 if (ismaster)
598 bool runsched = createTVRecorders(ismaster);
599
600 Scheduler *sched = nullptr;
601 if (ismaster)
602 {
603 if (runsched)
604 {
605 be_sd_notify("STATUS=Creating scheduler");
606 sched = new Scheduler(true, &gTVList);
607 int err = sched->GetError();
608 if (err)
609 {
610 delete sched;
611 return err;
612 }
613
614 if (cmdline.toBool("nosched"))
616 }
617
618 if (!cmdline.toBool("noautoexpire"))
619 {
621 if (sched)
623 }
626 }
627
628 if (!cmdline.toBool("nohousekeeper"))
629 {
630 be_sd_notify("STATUS=Creating housekeeper");
632
633 if (ismaster)
634 {
640
641 // only run this task if MythMusic is installed and we have a new enough schema
642 if (gCoreContext->GetNumSetting("MusicDBSchemaVer", 0) >= 1024)
644 }
645
647#ifdef Q_OS_LINUX
648 #ifdef CONFIG_BINDINGS_PYTHON
650 #endif
651#endif
653
655 }
656
657 if (!cmdline.toBool("nojobqueue"))
658 gJobQueue = new JobQueue(ismaster);
659
660 // ----------------------------------------------------------------------
661 //
662 // ----------------------------------------------------------------------
663
664 if (g_pUPnp == nullptr)
665 {
666 be_sd_notify("STATUS=Creating UPnP media server");
667 g_pUPnp = new MediaServer();
668
669 g_pUPnp->Init(ismaster, cmdline.toBool("noupnp"));
670 }
671
672 if (cmdline.toBool("dvbv3"))
673 {
674 LOG(VB_GENERAL, LOG_INFO, LOC + "Use legacy DVBv3 API");
675 gCoreContext->SetDVBv3(true);
676 }
677
678 // ----------------------------------------------------------------------
679 // Setup status server
680 // ----------------------------------------------------------------------
681
682 HttpStatus *httpStatus = nullptr;
684
685 if (pHS)
686 {
687 LOG(VB_GENERAL, LOG_INFO, "Main::Registering HttpStatus Extension");
688 be_sd_notify("STATUS=Registering HttpStatus Extension");
689
690 httpStatus = new HttpStatus( &gTVList, sched, ismaster );
691 pHS->RegisterExtension( httpStatus );
692 }
693
694 be_sd_notify("STATUS=Creating main server");
696 ismaster, port, &gTVList, sched, gExpirer);
697
698 int exitCode = mainServer->GetExitCode();
699 if (exitCode != GENERIC_EXIT_OK)
700 {
701 LOG(VB_GENERAL, LOG_CRIT,
702 "Backend exiting, MainServer initialization error.");
703 cleanup();
704 return exitCode;
705 }
706
707 if (httpStatus && mainServer)
708 httpStatus->SetMainServer(mainServer);
709
710 be_sd_notify("STATUS=Check all storage groups");
712
713 be_sd_notify("STATUS=Sending \"master started\" message");
715 gCoreContext->SendSystemEvent("MASTER_STARTED");
716
717 // Provide systemd ready notification (for Type=notify)
718 be_sd_notify("READY=1");
719
720 const HTTPServices be_services = {
721 { VIDEO_SERVICE, &MythHTTPService::Create<V2Video> },
722 { MYTH_SERVICE, &MythHTTPService::Create<V2Myth> },
723 { DVR_SERVICE, &MythHTTPService::Create<V2Dvr> },
724 { CONTENT_SERVICE, &MythHTTPService::Create<V2Content> },
725 { GUIDE_SERVICE, &MythHTTPService::Create<V2Guide> },
726 { CHANNEL_SERVICE, &MythHTTPService::Create<V2Channel> },
727 { STATUS_SERVICE, &MythHTTPService::Create<V2Status> },
728 { CAPTURE_SERVICE, &MythHTTPService::Create<V2Capture> },
729 { MUSIC_SERVICE, &MythHTTPService::Create<V2Music> },
730 { CONFIG_SERVICE, &MythHTTPService::Create<V2Config> },
731 };
732
734
735 // Send all unknown requests into the web app. make bookmarks and direct access work.
736 // Also all js files not found will be redirected to the apps/backend directory
737 auto spa_index = [](auto && PH1) { return MythHTTPRewrite::RewriteToSPA(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
738 MythHTTPInstance::AddErrorPageHandler({ "=404", spa_index });
739
740 // Serve components of the backend web app as if they were hosted at '/'
741 // These are no needed as the RewrtiteToSPA will handle redirects of js files
742 // auto main_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/main.js"); };
743 // auto styles_css = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/styles.css"); };
744 // auto polyfills_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/polyfills.js"); };
745 // auto runtime_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/runtime.js"); };
746
747 // Default index page
748 auto root = [](auto && PH1) { return MythHTTPRoot::RedirectRoot(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
749
750 const HTTPHandlers be_handlers = {
751 // { "/main.js", main_js },
752 // { "/styles.css", styles_css },
753 // { "/polyfills.js", polyfills_js },
754 // { "/runtime.js", runtime_js },
755 { "/", root }
756 };
757
758 MythHTTPScopedInstance webserver(be_handlers);
759
762 exitCode = qApp->exec();
765
767 {
768 gCoreContext->SendSystemEvent("MASTER_SHUTDOWN");
769 qApp->processEvents();
770 }
771
772 LOG(VB_GENERAL, LOG_NOTICE, "MythBackend exiting");
773 be_sd_notify("STOPPING=1\nSTATUS=Exiting");
774
775 return exitCode;
776}
777
778// This is a copy of the code from above, to start backend in a restricted mode, only running the web server
779// when the database is unusable, so thet the user can use the web app to fix the settings.
780
782{
783 LOG(VB_GENERAL, LOG_NOTICE, "***********************************************************************");
784 LOG(VB_GENERAL, LOG_NOTICE, "***** MythBackend starting in Web App only mode for initial setup *****");
785 LOG(VB_GENERAL, LOG_NOTICE, "***** Use http://<yourBackend>:6544 to perform setup *****");
786 LOG(VB_GENERAL, LOG_NOTICE, "***********************************************************************");
787
788 const HTTPServices be_services = {
789 { VIDEO_SERVICE, &MythHTTPService::Create<V2Video> },
790 { MYTH_SERVICE, &MythHTTPService::Create<V2Myth> },
791 { DVR_SERVICE, &MythHTTPService::Create<V2Dvr> },
792 { CONTENT_SERVICE, &MythHTTPService::Create<V2Content> },
793 { GUIDE_SERVICE, &MythHTTPService::Create<V2Guide> },
794 { CHANNEL_SERVICE, &MythHTTPService::Create<V2Channel> },
795 { STATUS_SERVICE, &MythHTTPService::Create<V2Status> },
796 { CAPTURE_SERVICE, &MythHTTPService::Create<V2Capture> },
797 { MUSIC_SERVICE, &MythHTTPService::Create<V2Music> },
798 { CONFIG_SERVICE, &MythHTTPService::Create<V2Config> },
799 };
800
802
803 // Send all unknown requests into the web app. make bookmarks and direct access work.
804 auto spa_index = [](auto && PH1) { return MythHTTPRewrite::RewriteToSPA(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
805 MythHTTPInstance::AddErrorPageHandler({ "=404", spa_index });
806
807 // Serve components of the backend web app as if they were hosted at '/'
808 auto main_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/main.js"); };
809 auto styles_css = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/styles.css"); };
810 auto polyfills_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/polyfills.js"); };
811 auto runtime_js = [](auto && PH1) { return MythHTTPRewrite::RewriteFile(std::forward<decltype(PH1)>(PH1), "apps/backend/runtime.js"); };
812
813 // Default index page
814 auto root = [](auto && PH1) { return MythHTTPRoot::RedirectRoot(std::forward<decltype(PH1)>(PH1), "apps/backend/index.html"); };
815
816 const HTTPHandlers be_handlers = {
817 { "/main.js", main_js },
818 { "/styles.css", styles_css },
819 { "/polyfills.js", polyfills_js },
820 { "/runtime.js", runtime_js },
821 { "/", root }
822 };
823
824 MythHTTPScopedInstance webserver(be_handlers);
825
828 // Provide systemd ready notification (for Type=notify)
829 be_sd_notify("READY=1\nSTATUS=Started in 'Web App only mode'");
830 int exitCode = qApp->exec();
831
832 be_sd_notify("STOPPING=1\nSTATUS='Exiting Web App only mode'");
833 LOG(VB_GENERAL, LOG_NOTICE, "MythBackend Web App only mode exiting");
834 return exitCode;
835}
AutoExpire * gExpirer
QMap< int, EncoderLink * > gTVList
BackendContext * gBackendContext
Used to expire recordings to make space for new recordings.
Definition: autoexpire.h:60
void PrintExpireList(const QString &expHost="ALL")
Prints a summary of the files that can be deleted.
Definition: autoexpire.cpp:807
static void UpdateChannelGroups(void)
static bool CheckTimeZoneSupport(void)
Check if MySQL has working timz zone support.
Definition: dbutil.cpp:869
static MTV_PUBLIC void ClearChannelLocks(void)
Removes old channel locks, use it only at master backend start.
Definition: eitcache.cpp:462
Manages registered HouseKeeperTasks and queues tasks for operation.
Definition: housekeeper.h:153
void Start(void)
void RegisterTask(HouseKeeperTask *task)
void RegisterExtension(HttpServerExtension *pExtension)
Definition: httpserver.cpp:313
void SetMainServer(MainServer *mainServer)
Definition: httpstatus.h:87
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
Definition: mythdbcon.cpp:823
QVariant value(int i) const
Definition: mythdbcon.h:204
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:282
int GetExitCode() const
Definition: mainserver.h:153
void Stop(void)
Definition: mainserver.cpp:359
void Init(bool bIsMaster, bool bDisableUPnp=false)
Definition: mediaserver.cpp:52
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
MythDB * GetDB(void)
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
QString GetHostName(void)
void SetExiting(bool exiting=true)
MythScheduler * GetScheduler(void)
void SendSystemEvent(const QString &msg)
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
bool CheckProtoVersion(MythSocket *socket, std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
int GetBackendServerPort(void)
Returns the locally defined backend control port.
void SetDVBv3(bool dvbv3)
void SetScheduler(MythScheduler *sched)
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
void SendMessage(const QString &message)
bool IsMasterHost(void)
is this the same host as the master
int GetNumSetting(const QString &key, int defaultval=0)
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
bool IsMasterBackend(void)
is this the actual MBE process
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
static void AddErrorPageHandler(const HTTPHandler &Handler)
static void Addservices(const HTTPServices &Services)
static HTTPResponse RewriteFile(const HTTPRequest2 &Request, const QString &File)
A convenience method to seemlessly redirect requests for files to a context specific file.
static HTTPResponse RewriteToSPA(const HTTPRequest2 &Request, const QString &File)
A convenience method to seemlessly redirect requests to a Single Page web app (SPA)
static HTTPResponse RedirectRoot(const HTTPRequest2 &Request, const QString &File)
A convenience method to seemlessly redirect requests for index.html to a context specific file.
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
Handles incoming MythSystemEvent messages.
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
static void CheckProgramIDAuthorities(void)
void FillRecordListFromDB(uint recordid=0)
Definition: scheduler.cpp:496
void PrintList(bool onlyFutureRecordings=false)
Definition: scheduler.h:98
int GetError(void) const
Definition: scheduler.h:119
void DisableScheduling(void)
Definition: scheduler.h:109
void SetExpirer(AutoExpire *autoExpirer)
Definition: scheduler.h:54
void FillRecordListFromMaster(void)
Definition: scheduler.cpp:579
static void CheckAllStorageGroupDirs(void)
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
bool Init(void)
Performs instance initialization, returns true on success.
Definition: tv_rec.cpp:156
static TVRec * GetTVRec(uint inputid)
Definition: tv_rec.cpp:4912
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:434
static QMap< uint, TVRec * > s_inputs
Definition: tv_rec.h:435
void RequestTerminate()
Definition: taskqueue.cpp:100
static TaskQueue * Instance()
Definition: taskqueue.cpp:57
HttpServer * GetHttpServer()
Definition: upnp.h:67
static WebOnlyStartup s_WebOnlyStartup
Definition: v2myth.h:68
@ kWebOnlyIPAddress
Definition: v2myth.h:65
@ kWebOnlyDBTimezone
Definition: v2myth.h:63
@ kWebOnlyWebOnlyParm
Definition: v2myth.h:64
@ kWebOnlySchemaUpdate
Definition: v2myth.h:66
unsigned int uint
Definition: compat.h:60
bool UpgradeTVDatabaseSchema(const bool upgradeAllowed, const bool upgradeIfNoUI, const bool informSystemd)
Called from outside dbcheck.cpp to update the schema.
Definition: dbcheck.cpp:362
@ GENERIC_EXIT_CONNECT_ERROR
Can't connect to master backend.
Definition: exitcodes.h:23
@ GENERIC_EXIT_INVALID_TIME
Invalid time.
Definition: exitcodes.h:25
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ GENERIC_EXIT_SOCKET_ERROR
Socket error.
Definition: exitcodes.h:21
uint64_t verboseMask
Definition: logging.cpp:101
LogLevel_t logLevel
Definition: logging.cpp:89
#define LOC
void doDatabaseHacks()
static HouseKeeper * gHousekeeping
int handle_command(const MythBackendCommandLineParser &cmdline)
int connect_to_master(void)
static MediaServer * g_pUPnp
static JobQueue * gJobQueue
static MainServer * mainServer
void print_warnings(const MythBackendCommandLineParser &cmdline)
static void be_sd_notify(const char *)
bool createTVRecorders(bool ismaster, bool retry)
int run_backend(MythBackendCommandLineParser &cmdline)
int run_setup_webserver()
void cleanup(void)
static MythSystemEventHandler * gSysEventHandler
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
std::vector< HTTPHandler > HTTPHandlers
Definition: mythhttptypes.h:48
std::vector< HTTPService > HTTPServices
Definition: mythhttptypes.h:55
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
MythCommFlagCommandLineParser cmdline
string hostname
Definition: caa.py:17
#define CAPTURE_SERVICE
Definition: v2capture.h:33
#define CHANNEL_SERVICE
Definition: v2channel.h:39
#define CONFIG_SERVICE
Definition: v2config.h:11
#define CONTENT_SERVICE
Definition: v2content.h:34
#define DVR_SERVICE
Definition: v2dvr.h:41
Scheduler * sched
#define GUIDE_SERVICE
Definition: v2guide.h:42
#define MUSIC_SERVICE
Definition: v2music.h:17
#define MYTH_SERVICE
Definition: v2myth.h:16
#define STATUS_SERVICE
Definition: v2status.h:35
#define VIDEO_SERVICE
Definition: v2video.h:13