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