MythTV master
mythshutdown.cpp
Go to the documentation of this file.
1
2// C/C++
3#include <cstdlib>
4#include <iostream>
5#include <unistd.h>
6
7// Qt
8#include <QtGlobal>
9#include <QCoreApplication>
10#include <QFile>
11#include <QTimeZone>
12
13// MythTV
14#include "libmyth/mythcontext.h"
15#include "libmythbase/compat.h"
20#include "libmythbase/mythdb.h"
23#include "libmythbase/mythversion.h"
24#include "libmythtv/jobqueue.h"
26#include "libmythtv/tv.h"
28
29// MythShutdown
31
32static void setGlobalSetting(const QString &key, const QString &v)
33{
34 QString value = (v.isNull()) ? QString("") : v;
35
37 if (query.isConnected())
38 {
39 query.prepare("DELETE FROM settings WHERE value = :KEY;");
40 query.bindValue(":KEY", key);
41
42 if (!query.exec() || !query.isActive())
43 MythDB::DBError("Clear setting", query);
44
45 query.prepare("INSERT INTO settings ( value, data ) "
46 "VALUES ( :VALUE, :DATA );");
47 query.bindValue(":VALUE", key);
48 query.bindValue(":DATA", value);
49
50 if (!query.exec() || !query.isActive())
51 MythDB::DBError("Save new global setting", query);
52 }
53 else
54 {
55 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
56 QString("Error: Database not open while trying "
57 "to save setting: %1\n").arg(key));
58 }
59}
60
61static QString getGlobalSetting(const QString &key, const QString &defaultval)
62{
63 QString value = defaultval;
64
66 if (query.isConnected())
67 {
68 query.prepare("SELECT data FROM settings WHERE value = :KEY AND "
69 "hostname IS NULL;");
70 query.bindValue(":KEY", key);
71 if (query.exec() && query.next())
72 value = query.value(0).toString();
73 }
74 else
75 {
76 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
77 QObject::tr("Error: Database not open while trying to "
78 "load setting: %1", "mythshutdown").arg(key) + "\n");
79 }
80
81 return value;
82}
83
84static int lockShutdown()
85{
86 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --lock");
87
89
90 // lock setting table
91 int tries = 0;
92 while (!query.exec("LOCK TABLE settings WRITE;") && tries < 5)
93 {
94 LOG(VB_GENERAL, LOG_INFO, "Waiting for lock on setting table");
95 sleep(1);
96 tries++;
97 }
98
99 if (tries >= 5)
100 {
101 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
102 QObject::tr("Error: Waited too long to obtain "
103 "lock on setting table", "mythshutdown") + "\n");
104 return 1;
105 }
106
107 // does the setting already exist?
108 query.prepare("SELECT * FROM settings "
109 "WHERE value = 'MythShutdownLock' AND hostname IS NULL;");
110 if (!query.exec())
111 MythDB::DBError("lockShutdown -- select", query);
112
113 if (query.size() < 1)
114 {
115 // add the lock setting
116 query.prepare("INSERT INTO settings (value, data) "
117 "VALUES ('MythShutdownLock', '1');");
118 if (!query.exec())
119 MythDB::DBError("lockShutdown -- insert", query);
120 }
121 else
122 {
123 // update the lock setting
124 query.prepare("UPDATE settings SET data = data + 1 "
125 "WHERE value = 'MythShutdownLock' "
126 "AND hostname IS NULL;");
127 if (!query.exec())
128 MythDB::DBError("lockShutdown -- update", query);
129 }
130
131 // unlock settings table
132 if (!query.exec("UNLOCK TABLES;"))
133 MythDB::DBError("lockShutdown -- unlock", query);
134
135 return 0;
136}
137
138static int unlockShutdown()
139{
140 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --unlock");
141
143
144 // lock setting table
145 int tries = 0;
146 while (!query.exec("LOCK TABLE settings WRITE;") && tries < 5)
147 {
148 LOG(VB_GENERAL, LOG_INFO, "Waiting for lock on setting table");
149 sleep(1);
150 tries++;
151 }
152
153 if (tries >= 5)
154 {
155 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
156 QObject::tr("Error: Waited too long to obtain "
157 "lock on setting table", "mythshutdown") + "\n");
158 return 1;
159 }
160
161 // does the setting exist?
162 query.prepare("SELECT * FROM settings "
163 "WHERE value = 'MythShutdownLock' AND hostname IS NULL;");
164 if (!query.exec())
165 MythDB::DBError("unlockShutdown -- select", query);
166
167 if (query.size() < 1)
168 {
169 // add the lock setting
170 query.prepare("INSERT INTO settings (value, data) "
171 "VALUES ('MythShutdownLock', '0');");
172 if (!query.exec())
173 MythDB::DBError("unlockShutdown -- insert", query);
174 }
175 else
176 {
177 // update lock setting
178 query.prepare("UPDATE settings SET data = GREATEST(0, data - 1) "
179 "WHERE value = 'MythShutdownLock' "
180 "AND hostname IS NULL;");
181 if (!query.exec())
182 MythDB::DBError("unlockShutdown -- update", query);
183 }
184
185 // unlock table
186 if (!query.exec("UNLOCK TABLES;"))
187 MythDB::DBError("unlockShutdown -- unlock", query);
188
189 // tell the master BE to reset its idle time
190 gCoreContext->SendMessage("RESET_IDLETIME");
191
192 return 0;
193}
194
208static bool isRunning(const char *program)
209{
210 QString command = QString("ps ch -C %1 -o pid > /dev/null").arg(program);
211 return (myth_system(command) == GENERIC_EXIT_OK);
212}
213
214static QDateTime getDailyWakeupTime(const QString& sPeriod)
215{
216 QString sTime = getGlobalSetting(sPeriod, "00:00");
217 QTime tTime = QTime::fromString(sTime, "hh:mm");
218#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
219 return QDateTime(MythDate::current().toLocalTime().date(),
220 tTime, Qt::LocalTime).toUTC();
221#else
222 return QDateTime(MythDate::current().toLocalTime().date(),
223 tTime, QTimeZone(QTimeZone::LocalTime)).toUTC();
224#endif
225}
226
227static bool isRecording()
228{
230 {
231 LOG(VB_GENERAL, LOG_INFO,
232 "isRecording: Attempting to connect to master server...");
234 {
235 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
236 QObject::tr("Error: Could not connect to master server",
237 "mythshutdown") + "\n");
238 return false;
239 }
240 }
241
242 return RemoteGetRecordingStatus(nullptr, false);
243}
244
245static int getStatus(bool bWantRecStatus)
246{
247 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --status");
248
249 int res = 0;
250
251 if (isRunning("mythtranscode"))
252 {
253 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
254 QObject::tr("Transcoding in progress...", "mythshutdown") + "\n");
255 res |= 1;
256 }
257
258 if (isRunning("mythcommflag"))
259 {
260 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
261 QObject::tr("Commercial Detection in progress...",
262 "mythshutdown") + "\n");
263 res |= 2;
264 }
265
266 if (isRunning("mythfilldatabase"))
267 {
268 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
269 QObject::tr("Grabbing EPG data in progress...", "mythshutdown") +
270 "\n");
271 res |= 4;
272 }
273
274 if (bWantRecStatus && isRecording())
275 {
276 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
277 QObject::tr("Recording in progress...", "mythshutdown") + "\n");
278 res |= 8;
279 }
280
281 if (getGlobalSetting("MythShutdownLock", "0") != "0")
282 {
283 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
284 QObject::tr("Shutdown is locked", "mythshutdown") + "\n");
285 res |= 16;
286 }
287
289 {
290 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
291 QObject::tr("Has queued or pending jobs", "mythshutdown") + "\n");
292 res |= 32;
293 }
294
295 QDateTime dtPeriod1Start = getDailyWakeupTime("DailyWakeupStartPeriod1");
296 QDateTime dtPeriod1End = getDailyWakeupTime("DailyWakeupEndPeriod1");
297 QDateTime dtPeriod2Start = getDailyWakeupTime("DailyWakeupStartPeriod2");
298 QDateTime dtPeriod2End = getDailyWakeupTime("DailyWakeupEndPeriod2");
299 QDateTime dtCurrent = MythDate::current();
300
301 // Check for time periods that cross midnight
302 if (dtPeriod1End < dtPeriod1Start)
303 {
304 if (dtCurrent > dtPeriod1End)
305 dtPeriod1End = dtPeriod1End.addDays(1);
306 else
307 dtPeriod1Start = dtPeriod1Start.addDays(-1);
308 }
309
310 if (dtPeriod2End < dtPeriod2Start)
311 {
312 if (dtCurrent > dtPeriod2End)
313 dtPeriod2End = dtPeriod2End.addDays(1);
314 else
315 dtPeriod2Start = dtPeriod2Start.addDays(-1);
316 }
317
318 // Check for one of the daily wakeup periods
319 if (dtPeriod1Start != dtPeriod1End)
320 {
321 if (dtCurrent >= dtPeriod1Start && dtCurrent <= dtPeriod1End)
322 {
323 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
324 QObject::tr("In a daily wakeup period (1).", "mythshutdown") +
325 "\n");
326 res |= 64;
327 }
328 }
329
330 if (dtPeriod2Start != dtPeriod2End)
331 {
332 if (dtCurrent >= dtPeriod2Start && dtCurrent <= dtPeriod2End)
333 {
334 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
335 QObject::tr("In a daily wakeup period (2).", "mythshutdown") +
336 "\n");
337 res |= 64;
338 }
339 }
340
341 // Are we about to start a daily wakeup period
342 // allow for a 15 minute window
343 if (dtPeriod1Start != dtPeriod1End)
344 {
345 auto delta = std::chrono::seconds(dtCurrent.secsTo(dtPeriod1Start));
346 if (delta >= 0s && delta <= 15min)
347 {
348 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
349 QObject::tr("About to start daily wakeup period (1)",
350 "mythshutdown") + "\n");
351 res |= 128;
352 }
353 }
354
355 if (dtPeriod2Start != dtPeriod2End)
356 {
357 auto delta = std::chrono::seconds(dtCurrent.secsTo(dtPeriod2Start));
358 if (delta >= 0s && delta <= 15min)
359 {
360 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
361 QObject::tr("About to start daily wakeup period (2)",
362 "mythshutdown") + "\n");
363 res |= 128;
364 }
365 }
366
367 if (isRunning("mythtv-setup"))
368 {
369 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
370 QObject::tr("Setup is running...", "mythshutdown") + "\n");
371 res = 255;
372 }
373
374 LOG(VB_GENERAL, LOG_INFO,
375 QObject::tr("Mythshutdown: --status returned: %1",
376 "mythshutdown").arg(res) + "\n");
377
378 return res;
379}
380
381static int checkOKShutdown(bool bWantRecStatus)
382{
383 // mythbackend wants 0=ok to shutdown,
384 // 1=reset idle count, 2=wait for frontend
385
386 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --check");
387
388 int res = getStatus(bWantRecStatus);
389
390 if (res > 0)
391 {
392 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
393 QObject::tr("Not OK to shutdown", "mythshutdown") + "\n");
394 res = 1;
395 }
396 else
397 {
398 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
399 QObject::tr("OK to shutdown", "mythshutdown") + "\n");
400 res = 0;
401 }
402
403 LOG(VB_GENERAL, LOG_INFO,
404 QString("Mythshutdown: --check returned: %1").arg(res));
405
406 return res;
407}
408
409static void setWakeupTime(const QDateTime &wakeupTime)
410{
411 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --setwakeup");
412
413 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
414 QObject::tr("Wakeup time given is: %1 (local time)", "mythshutdown")
415 .arg(MythDate::toString(wakeupTime, MythDate::kDateTimeShort)) + "\n");
416
417 setGlobalSetting("MythShutdownNextScheduled",
419}
420
422{
424 {
425 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
426 QObject::tr("Setting scheduled wakeup time: "
427 "Attempting to connect to master server...",
428 "mythshutdown") + "\n");
430 {
431 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
432 QObject::tr("Setting scheduled wakeup time: "
433 "Could not connect to master server!",
434 "mythshutdown") + "\n");
435 return 1;
436 }
437 }
438
439 QDateTime nextRecordingStart;
440 GetNextRecordingList(nextRecordingStart);
441
442 // set the wakeup time for the next scheduled recording
443 if (!nextRecordingStart.isNull())
444 {
445 int m_preRollSeconds = gCoreContext->GetNumSetting("RecordPreRoll");
446 QDateTime restarttime = nextRecordingStart
447 .addSecs((-1LL) * m_preRollSeconds);
448
449 int add = gCoreContext->GetNumSetting(
450 "StartupSecsBeforeRecording", 240);
451
452 if (add)
453 restarttime = restarttime.addSecs((-1LL) * add);
454
455 setWakeupTime(restarttime);
456
457 return 0;
458 }
459 return 1;
460}
461
462static int shutdown()
463{
464 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --shutdown");
465
466 // get next daily wakeup times if any are set
467 QDateTime dtPeriod1Start = getDailyWakeupTime("DailyWakeupStartPeriod1");
468 QDateTime dtPeriod1End = getDailyWakeupTime("DailyWakeupEndPeriod1");
469 QDateTime dtPeriod2Start = getDailyWakeupTime("DailyWakeupStartPeriod2");
470 QDateTime dtPeriod2End = getDailyWakeupTime("DailyWakeupEndPeriod2");
471 QDateTime dtCurrent = MythDate::current();
472 QDateTime dtNextDailyWakeup = QDateTime();
473
474 // Make sure Period1 is before Period2
475 if (dtPeriod2Start < dtPeriod1Start)
476 {
477 QDateTime temp = dtPeriod1Start;
478 dtPeriod1Start = dtPeriod2Start;
479 dtPeriod2Start = temp;
480 temp = dtPeriod1End;
481 dtPeriod1End = dtPeriod2End;
482 dtPeriod2End = temp;
483 }
484
485 // Check for time periods that cross midnight
486 if (dtPeriod1End < dtPeriod1Start)
487 {
488 if (dtCurrent > dtPeriod1End)
489 dtPeriod1End = dtPeriod1End.addDays(1);
490 else
491 dtPeriod1Start = dtPeriod1Start.addDays(-1);
492 }
493
494 if (dtPeriod2End < dtPeriod2Start)
495 {
496 if (dtCurrent > dtPeriod2End)
497 dtPeriod2End = dtPeriod2End.addDays(1);
498 else
499 dtPeriod2Start = dtPeriod2Start.addDays(-1);
500 }
501
502 // have we passed the first wakeup time today
503 if (dtPeriod1Start != dtPeriod1End)
504 {
505 if (dtCurrent < dtPeriod1Start)
506 {
507 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
508 QObject::tr("Daily wakeup today at %1", "mythshutdown")
509 .arg(MythDate::toString(dtPeriod1Start, MythDate::kTime)) +
510 "\n");
511 dtNextDailyWakeup = dtPeriod1Start;
512 }
513 }
514
515 // have we passed the second wakeup time today
516 if (!dtNextDailyWakeup.isValid() && dtPeriod2Start != dtPeriod2End)
517 {
518 if (dtCurrent < dtPeriod2Start)
519 {
520 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
521 QObject::tr("Daily wakeup today at %1", "mythshutdown")
522 .arg(MythDate::toString(dtPeriod2Start, MythDate::kTime)) +
523 "\n");
524 dtNextDailyWakeup = dtPeriod2Start;
525 }
526 }
527
528 // if we have at least one valid daily wakeup time
529 // and dtNextDailyWakeup is still not valid
530 // then next daily wakeup is tomorrow
531 if (!dtNextDailyWakeup.isValid())
532 {
533 if (dtPeriod1Start != dtPeriod1End)
534 dtNextDailyWakeup = dtPeriod1Start;
535 else if (dtPeriod2Start != dtPeriod2End)
536 dtNextDailyWakeup = dtPeriod2Start;
537
538 if (dtNextDailyWakeup.isValid())
539 {
540 dtNextDailyWakeup = dtNextDailyWakeup.addDays(1);
541
542 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
543 QObject::tr("Next daily wakeup is tomorrow at %1",
544 "mythshutdown")
545 .arg(MythDate::toString(dtNextDailyWakeup, MythDate::kTime)) +
546 "\n");
547 }
548 }
549
550 // if dtNextDailyWakeup is still not valid then no daily wakeups are set
551 if (!dtNextDailyWakeup.isValid())
552 {
553 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
554 QObject::tr("Error: no daily wakeup times are set",
555 "mythshutdown") + "\n");
556 }
557
558 // get next scheduled wake up for a recording if any
559 QDateTime dtNextRecordingStart = QDateTime();
560 QString s = getGlobalSetting("MythShutdownNextScheduled", "");
561 if (!s.isEmpty())
562 dtNextRecordingStart = MythDate::fromString(s);
563
564 if (!dtNextRecordingStart.isValid())
565 {
566 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
567 QObject::tr("Error: no recording time is set", "mythshutdown") +
568 "\n");
569 }
570 else
571 {
572 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
573 QObject::tr("Recording scheduled at: %1", "mythshutdown")
574 .arg(MythDate::toString(dtNextRecordingStart, MythDate::kTime)) +
575 "\n");
576 }
577
578 // check if scheduled recording time has already passed
579 if (dtNextRecordingStart.isValid())
580 {
581 int delta = dtCurrent.secsTo(dtNextRecordingStart);
582
583 if (delta < 0)
584 {
585 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
586 QObject::tr("Scheduled recording time has already passed. "
587 "Schedule deleted", "mythshutdown") + "\n");
588
589 dtNextRecordingStart = QDateTime();
590 setGlobalSetting("MythShutdownNextScheduled", "");
591 }
592 }
593
594 QDateTime dtWakeupTime = QDateTime();
595
596 // simple case
597 // no daily wakeup set
598 // no scheduled program set
599 // just shut down
600 if (!dtNextRecordingStart.isValid() && !dtNextDailyWakeup.isValid())
601 {
602 dtWakeupTime = QDateTime();
603 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
604 QObject::tr("Error: no wake up time set and no scheduled program",
605 "mythshutdown") + "\n");
606 }
607
608 // no daily wakeup set
609 // scheduled program is set
610 if (dtNextRecordingStart.isValid() && !dtNextDailyWakeup.isValid())
611 {
612 dtWakeupTime = dtNextRecordingStart;
613 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
614 QObject::tr("Will wake up at next scheduled program",
615 "mythshutdown") + "\n");
616 }
617
618 // daily wakeup is set
619 // no scheduled program is set
620 if (!dtNextRecordingStart.isValid() && dtNextDailyWakeup.isValid())
621 {
622 dtWakeupTime = dtNextDailyWakeup;
623 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
624 QObject::tr("Will wake up at next daily wakeup",
625 "mythshutdown") + "\n");
626 }
627
628 // daily wakeup is set
629 // scheduled program is set
630 // wake up at which ever is the earliest
631 if (dtNextRecordingStart.isValid() && dtNextDailyWakeup.isValid())
632 {
633 if (dtNextDailyWakeup < dtNextRecordingStart)
634 {
635 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
636 QObject::tr("Program is scheduled but will "
637 "wake up at next daily wakeup",
638 "mythshutdown") + "\n");
639 dtWakeupTime = dtNextDailyWakeup;
640 }
641 else
642 {
643 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
644 QObject::tr("Daily wakeup is set but will wake up "
645 "at next scheduled program",
646 "mythshutdown") + "\n");
647 dtWakeupTime = dtNextRecordingStart;
648 }
649 }
650
651 // save the next wakuptime in the db
652 setGlobalSetting("MythShutdownWakeupTime",
654
655 // stop here to debug
656 //return 0;
657
658 int shutdownmode = 0; // default to poweroff no reboot
659 QString nvramRestartCmd =
660 gCoreContext->GetSetting("MythShutdownNvramRestartCmd", "");
661
662 if (dtWakeupTime.isValid())
663 {
664 // dont't shutdown if we are within idleWait mins of the next wakeup time
665 std::chrono::seconds idleWaitForRecordingTime =
666 gCoreContext->GetDurSetting<std::chrono::minutes>("idleWaitForRecordingTime", 15min);
667 if (dtCurrent.secsTo(dtWakeupTime) > idleWaitForRecordingTime.count())
668 {
669 QString nvramCommand =
671 "MythShutdownNvramCmd",
672 "/usr/bin/nvram-wakeup --settime $time");
673
674 QString wakeup_timeformat = gCoreContext->GetSetting(
675 "MythShutdownWakeupTimeFmt", "time_t");
676
677 if (wakeup_timeformat == "time_t")
678 {
679 QString time_ts;
680 nvramCommand.replace(
681 "$time", time_ts.setNum(dtWakeupTime.toSecsSinceEpoch())
682 );
683 }
684 else
685 {
686 nvramCommand.replace(
687 "$time", dtWakeupTime.toLocalTime()
688 .toString(wakeup_timeformat));
689 }
690
691 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
692 QObject::tr("Sending command to set time in BIOS %1",
693 "mythshutdown")
694 .arg(nvramCommand) + "\n");
695
696 shutdownmode = myth_system(nvramCommand);
697
698 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
699 QObject::tr("Program %1 exited with code %2", "mythshutdown")
700 .arg(nvramCommand).arg(shutdownmode) + "\n");
701
702 if (shutdownmode == 2)
703 {
704 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
705 QObject::tr("Error: nvram-wakeup failed to "
706 "set time in BIOS", "mythshutdown") + "\n");
707 return 1;
708 }
709
710 // we don't trust the return code from nvram-wakeup so only reboot
711 // if the user has set a restart command
712 if (nvramRestartCmd.isEmpty())
713 shutdownmode = 0;
714 else
715 shutdownmode = 1;
716 }
717 else
718 {
719 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
720 QObject::tr("The next wakeup time is less than "
721 "15 mins away, not shutting down.",
722 "mythshutdown") + "\n");
723 return 0;
724 }
725 }
726
727 int res = 0;
728
729 switch (shutdownmode)
730 {
731 case 0:
732 {
733 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
734 QObject::tr("everything looks fine, shutting down ...",
735 "mythshutdown") + "\n");
736 QString poweroffCmd = gCoreContext->GetSetting(
737 "MythShutdownPoweroff", "/sbin/poweroff");
738 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
739 "..\n.\n" + QObject::tr("shutting down", "mythshutdown") +
740 " ...\n");
741
742 myth_system(poweroffCmd);
743 res = 0;
744 break;
745 }
746 case 1:
747 {
748 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
749 QObject::tr("Everything looks fine, but reboot is needed",
750 "mythshutdown") + "\n");
751 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
752 QObject::tr("Sending command to bootloader", "mythshutdown") +
753 " ...\n");
754 LOG(VB_STDIO|VB_FLUSH, LOG_ERR, nvramRestartCmd);
755
756 myth_system(nvramRestartCmd);
757
758 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
759 "..\n.\n" + QObject::tr("rebooting", "mythshutdown") +
760 " ...\n");
761
762 QString rebootCmd =
763 gCoreContext->GetSetting("MythShutdownReboot", "/sbin/reboot");
764 myth_system(rebootCmd);
765 res = 0;
766 break;
767 }
768 default:
769 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
770 QObject::tr("Error: Invalid shutdown mode, doing nothing.",
771 "mythshutdown") + "\n");
772 res = 1;
773 break;
774 }
775
776 return res;
777}
778
779static int startup()
780{
781 LOG(VB_GENERAL, LOG_INFO, "Mythshutdown: --startup");
782
783 int res = 0;
784 QDateTime startupTime = QDateTime();
785 QString s = getGlobalSetting("MythshutdownWakeupTime", "");
786 if (!s.isEmpty())
787 startupTime = MythDate::fromString(s);
788
789 // if we don't have a valid startup time assume we were started manually
790 if (!startupTime.isValid())
791 res = 1;
792 else
793 {
794 // if we started within 15mins of the saved wakeup time assume we started
795 // automatically to record or for a daily wakeup/shutdown period
796 auto delta = MythDate::secsInPast(startupTime);
797 delta = std::chrono::abs(delta);
798
799 if (delta < 15min)
800 res = 0;
801 else
802 res = 1;
803 }
804
805 if (res)
806 {
807 LOG(VB_GENERAL, LOG_INFO,
808 QString("looks like we were started manually: %1").arg(res));
809 }
810 else
811 {
812 LOG(VB_GENERAL, LOG_INFO,
813 QString("looks like we were started automatically: %1").arg(res));
814 }
815
816
817 LOG(VB_GENERAL, LOG_INFO,
818 QString("Mythshutdown: --startup returned: %1").arg(res));
819
820 return res;
821}
822
823int main(int argc, char **argv)
824{
826 if (!cmdline.Parse(argc, argv))
827 {
830 }
831
832 if (cmdline.toBool("showhelp"))
833 {
835 return GENERIC_EXIT_OK;
836 }
837
838 if (cmdline.toBool("showversion"))
839 {
841 return GENERIC_EXIT_OK;
842 }
843
844 QCoreApplication a(argc, argv);
845 QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHSHUTDOWN);
846
847 int retval = cmdline.ConfigureLogging("none");
848 if (retval != GENERIC_EXIT_OK)
849 return retval;
850
851 MythContext context {MYTH_BINARY_VERSION};
852 if (!context.Init(false))
853 {
854 LOG(VB_STDIO|VB_FLUSH, LOG_ERR, "Error: "
855 "Could not initialize MythContext. Exiting.\n");
857 }
858
859 int res = 0;
860
861 if (cmdline.toBool("lock"))
862 res = lockShutdown();
863 else if (cmdline.toBool("unlock"))
864 res = unlockShutdown();
865 else if (cmdline.toBool("check"))
866 res = checkOKShutdown(cmdline.toInt("check") == 1);
867 else if (cmdline.toBool("setschedwakeup"))
869 else if (cmdline.toBool("startup"))
870 res = startup();
871 else if (cmdline.toBool("shutdown"))
872 res = shutdown();
873 else if (cmdline.toBool("status"))
874 res = getStatus(cmdline.toInt("status") == 1);
875 else if (cmdline.toBool("setwakeup"))
876 {
877 // only one of --utc or --localtime can be passed per
878 // CommandLineArg::AllowOneOf() in commandlineparser.cpp
879 bool utc = cmdline.toBool("utc");
880 QString tmp = cmdline.toString("setwakeup");
881
882 QDateTime wakeuptime = (utc) ?
885
886 if (!wakeuptime.isValid())
887 {
888 LOG(VB_STDIO|VB_FLUSH, LOG_ERR,
889 QObject::tr("Error: "
890 "--setwakeup invalid date format (%1)\n\t\t\t"
891 "must be yyyy-MM-ddThh:mm:ss", "mythshutdown")
892 .arg(tmp) + "\n");
893 res = 1;
894 }
895 else
896 {
897 setWakeupTime(wakeuptime);
898 }
899 }
900 else if (cmdline.toBool("safeshutdown"))
901 {
902 res = checkOKShutdown(true);
903 if (res == 0)
904 {
905 // Nothing to stop a shutdown (eg. recording in progress).
907 res = shutdown();
908 }
909
910 }
911 else
912 {
914 }
915
916 return res;
917}
static GlobalSpinBoxSetting * idleWaitForRecordingTime()
static bool HasRunningOrPendingJobs(std::chrono::minutes startingWithinMins=0min)
Definition: jobqueue.cpp:1230
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:837
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:137
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
int ConfigureLogging(const QString &mask="general", bool progress=false)
Read in logging options and initialize the logging interface.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
static void PrintVersion(void)
Print application version information.
void PrintHelp(void) const
Print command line option help.
Startup context for MythTV.
Definition: mythcontext.h:20
bool IsConnectedToMaster(void)
QString GetSetting(const QString &key, const QString &defaultval="")
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
void SendMessage(const QString &message)
int GetNumSetting(const QString &key, int defaultval=0)
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
@ GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:16
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:18
static guint32 * tmp
Definition: goom_core.cpp:26
static constexpr const char * MYTH_APPNAME_MYTHSHUTDOWN
Definition: mythappname.h:14
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static bool isRecording()
static int checkOKShutdown(bool bWantRecStatus)
static int setScheduledWakeupTime()
static void setGlobalSetting(const QString &key, const QString &v)
int main(int argc, char **argv)
static int getStatus(bool bWantRecStatus)
static void setWakeupTime(const QDateTime &wakeupTime)
static bool isRunning(const char *program)
Returns true if a program containing the specified string is running on this machine.
static int lockShutdown()
static QString getGlobalSetting(const QString &key, const QString &defaultval)
static int shutdown()
static int startup()
static int unlockShutdown()
static QDateTime getDailyWakeupTime(const QString &sPeriod)
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
std::chrono::seconds secsInPast(const QDateTime &past)
Definition: mythdate.cpp:212
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kDateTimeShort
Default local time.
Definition: mythdate.h:24
@ ISODate
Default UTC.
Definition: mythdate.h:17
@ kTime
Default local time.
Definition: mythdate.h:22
@ kDatabase
Default UTC, database format.
Definition: mythdate.h:27
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
bool GetNextRecordingList(QDateTime &nextRecordingStart, bool *hasConflicts, std::vector< ProgramInfo > *list)
bool RemoteGetRecordingStatus(std::vector< TunerStatus > *tunerList, bool list_inactive)