/* * shutdown.c: Handling of shutdown and inactivity * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * Original version written by Udo Richter . * * $Id: shutdown.c 5.1 2022/11/22 14:33:48 kls Exp $ */ #include "shutdown.h" #include #include #include #include #include "channels.h" #include "config.h" #include "i18n.h" #include "interface.h" #include "menu.h" #include "plugin.h" #include "recording.h" #include "timers.h" #include "tools.h" cShutdownHandler ShutdownHandler; cCountdown::cCountdown(void) { timeout = 0; counter = 0; timedOut = false; message = NULL; } void cCountdown::Start(const char *Message, int Seconds) { timeout = time(NULL) + Seconds; counter = -1; timedOut = false; message = Message; Update(); } void cCountdown::Cancel(void) { if (timeout) { timeout = 0; timedOut = false; Skins.Message(mtStatus, NULL); } } bool cCountdown::Done(void) { if (timedOut) { Cancel(); return true; } return false; } bool cCountdown::Update(void) { if (timeout) { int NewCounter = (timeout - time(NULL) + 9) / 10; if (NewCounter <= 0) timedOut = true; if (counter != NewCounter) { counter = NewCounter; Skins.Message(mtStatus, cString::sprintf(message, *cString::sprintf("%d:%d0", counter > 0 ? counter / 6 : 0, counter > 0 ? counter % 6 : 0))); return true; } } return false; } cShutdownHandler::cShutdownHandler(void) { activeTimeout = 0; retry = 0; shutdownCommand = NULL; exitCode = -1; emergencyExitRequested = false; } cShutdownHandler::~cShutdownHandler() { free(shutdownCommand); } void cShutdownHandler::RequestEmergencyExit(void) { if (Setup.EmergencyExit) { esyslog("initiating emergency exit"); emergencyExitRequested = true; Exit(1); } else dsyslog("emergency exit request ignored according to setup"); } void cShutdownHandler::CheckManualStart(int ManualStart) { time_t Delta = Setup.NextWakeupTime ? Setup.NextWakeupTime - time(NULL) : 0; if (!Setup.NextWakeupTime || abs(Delta) > ManualStart) { // Apparently the user started VDR manually dsyslog("assuming manual start of VDR"); // Set inactive after MinUserInactivity SetUserInactiveTimeout(); } else { // Set inactive from now on dsyslog("scheduled wakeup time in %jd minutes, assuming automatic start of VDR", intmax_t(Delta / 60)); SetUserInactiveTimeout(-3, true); } } void cShutdownHandler::SetShutdownCommand(const char *ShutdownCommand) { free(shutdownCommand); shutdownCommand = ShutdownCommand ? strdup(ShutdownCommand) : NULL; } void cShutdownHandler::CallShutdownCommand(time_t WakeupTime, int Channel, const char *File, bool UserShutdown) { time_t Delta = WakeupTime ? WakeupTime - time(NULL) : 0; cString cmd = cString::sprintf("%s %jd %jd %d \"%s\" %d", shutdownCommand, intmax_t(WakeupTime), intmax_t(Delta), Channel, *strescape(File, "\\\"$"), UserShutdown); isyslog("executing '%s'", *cmd); int Status = SystemExec(cmd, true); if (!WIFEXITED(Status) || WEXITSTATUS(Status)) esyslog("SystemExec() failed with status %d", Status); else { Setup.NextWakeupTime = WakeupTime; // Remember this wakeup time for comparison on reboot Setup.Save(); } } void cShutdownHandler::SetUserInactiveTimeout(int Seconds, bool Force) { if (!Setup.MinUserInactivity && !Force) { activeTimeout = 0; return; } if (Seconds >= 0) activeTimeout = time(NULL) + Seconds; else if (Seconds == -1) activeTimeout = time(NULL) + Setup.MinUserInactivity * 60; else if (Seconds == -2) activeTimeout = 0; else if (Seconds == -3) activeTimeout = 1; } bool cShutdownHandler::ConfirmShutdown(bool Interactive) { if (!Interactive && !cRemote::Enabled()) return false; if (!shutdownCommand) { if (Interactive) Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!")); return false; } if (RecordingsHandler.Active()) { if (!Interactive || !Interface->Confirm(tr("Editing - shut down anyway?"))) return false; } LOCK_TIMERS_READ; const cTimer *Timer = Timers->GetNextActiveTimer(); time_t Next = Timer ? Timer->StartTime() : 0; time_t Delta = Timer ? Next - time(NULL) : 0; if (cRecordControls::Active() || (Next && Delta <= 0)) { // VPS recordings in timer end margin may cause Delta <= 0 if (!Interactive || !Interface->Confirm(tr("Recording - shut down anyway?"))) return false; } else if (Next && Delta <= Setup.MinEventTimeout * 60) { // Timer within Min Event Timeout if (!Interactive) return false; cString buf = cString::sprintf(tr("Recording in %jd minutes, shut down anyway?"), intmax_t(Delta / 60)); if (!Interface->Confirm(buf)) return false; } if (cPluginManager::Active(Interactive ? tr("shut down anyway?") : NULL)) return false; cPlugin *Plugin = cPluginManager::GetNextWakeupPlugin(); Next = Plugin ? Plugin->WakeupTime() : 0; Delta = Next ? Next - time(NULL) : 0; if (Next && Delta <= Setup.MinEventTimeout * 60) { // Plugin wakeup within Min Event Timeout if (!Interactive) return false; cString buf = cString::sprintf(tr("Plugin %s wakes up in %jd min, continue?"), Plugin->Name(), intmax_t(Delta / 60)); if (!Interface->Confirm(buf)) return false; } return true; } bool cShutdownHandler::ConfirmRestart(bool Interactive) { if (RecordingsHandler.Active()) { if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?"))) return false; } LOCK_TIMERS_READ; const cTimer *Timer = Timers->GetNextActiveTimer(); time_t Next = Timer ? Timer->StartTime() : 0; time_t Delta = Timer ? Next - time(NULL) : 0; if (cRecordControls::Active() || (Next && Delta <= 0)) { // VPS recordings in timer end margin may cause Delta <= 0 if (!Interactive || !Interface->Confirm(tr("Recording - restart anyway?"))) return false; } if (cPluginManager::Active(Interactive ? tr("restart anyway?") : NULL)) return false; return true; } bool cShutdownHandler::DoShutdown(bool Force) { LOCK_TIMERS_READ; time_t Now = time(NULL); const cTimer *Timer = Timers->GetNextActiveTimer(); cPlugin *Plugin = cPluginManager::GetNextWakeupPlugin(); time_t Next = Timer ? Timer->StartTime() : 0; time_t NextPlugin = Plugin ? Plugin->WakeupTime() : 0; if (NextPlugin && (!Next || Next > NextPlugin)) { Next = NextPlugin; Timer = NULL; } time_t Delta = Next ? Next - Now : 0; if (Next && Delta < Setup.MinEventTimeout * 60) { if (!Force) return false; Delta = Setup.MinEventTimeout * 60; Next = Now + Delta; Timer = NULL; dsyslog("reboot at %s", *TimeToString(Next)); } if (Next && Timer) { dsyslog("next timer event at %s", *TimeToString(Next)); CallShutdownCommand(Next, Timer->Channel()->Number(), Timer->File(), Force); } else if (Next && Plugin) { CallShutdownCommand(Next, 0, Plugin->Name(), Force); dsyslog("next plugin wakeup at %s", *TimeToString(Next)); } else CallShutdownCommand(Next, 0, "", Force); // Next should always be 0 here. Just for safety, pass it. return true; }