vdr  1.7.27
shutdown.c
Go to the documentation of this file.
00001 /*
00002  * shutdown.c: Handling of shutdown and inactivity
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * Original version written by Udo Richter <udo_richter@gmx.de>.
00008  *
00009  * $Id: shutdown.c 2.0 2008/02/24 10:29:00 kls Exp $
00010  */
00011 
00012 #include "shutdown.h"
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <sys/types.h>
00016 #include <sys/wait.h>
00017 #include "channels.h"
00018 #include "config.h"
00019 #include "cutter.h"
00020 #include "filetransfer.h"
00021 #include "i18n.h"
00022 #include "interface.h"
00023 #include "menu.h"
00024 #include "plugin.h"
00025 #include "timers.h"
00026 #include "tools.h"
00027 
00028 cShutdownHandler ShutdownHandler;
00029 
00030 cCountdown::cCountdown(void)
00031 {
00032   timeout = 0;
00033   counter = 0;
00034   timedOut = false;
00035   message = NULL;
00036 }
00037 
00038 void cCountdown::Start(const char *Message, int Seconds)
00039 {
00040   timeout = time(NULL) + Seconds;
00041   counter = -1;
00042   timedOut = false;
00043   message = Message;
00044   Update();
00045 }
00046 
00047 void cCountdown::Cancel(void)
00048 {
00049   if (timeout) {
00050      timeout = 0;
00051      timedOut = false;
00052      Skins.Message(mtStatus, NULL);
00053      }
00054 }
00055 
00056 bool cCountdown::Done(void)
00057 {
00058   if (timedOut) {
00059      Cancel();
00060      return true;
00061      }
00062   return false;
00063 }
00064 
00065 bool cCountdown::Update(void)
00066 {
00067   if (timeout) {
00068      int NewCounter = (timeout - time(NULL) + 9) / 10;
00069      if (NewCounter <= 0)
00070         timedOut = true;
00071      if (counter != NewCounter) {
00072         counter = NewCounter;
00073         char time[10];
00074         snprintf(time, sizeof(time), "%d:%d0", counter > 0 ? counter / 6 : 0, counter > 0 ? counter % 6 : 0);
00075         cString Message = cString::sprintf(message, time);
00076         Skins.Message(mtStatus, Message);
00077         return true;
00078         }
00079      }
00080   return false;
00081 }
00082 
00083 cShutdownHandler::cShutdownHandler(void)
00084 {
00085   activeTimeout = 0;
00086   retry = 0;
00087   shutdownCommand = NULL;
00088   exitCode = -1;
00089   emergencyExitRequested = false;
00090 }
00091 
00092 cShutdownHandler::~cShutdownHandler()
00093 {
00094   free(shutdownCommand);
00095 }
00096 
00097 void cShutdownHandler::RequestEmergencyExit(void)
00098 {
00099   if (Setup.EmergencyExit) {
00100      esyslog("initiating emergency exit");
00101      emergencyExitRequested = true;
00102      Exit(1);
00103      }
00104   else
00105      dsyslog("emergency exit request ignored according to setup");
00106 }
00107 
00108 void cShutdownHandler::CheckManualStart(int ManualStart)
00109 {
00110   time_t Delta = Setup.NextWakeupTime ? Setup.NextWakeupTime - time(NULL) : 0;
00111 
00112   if (!Setup.NextWakeupTime || abs(Delta) > ManualStart) {
00113      // Apparently the user started VDR manually
00114      dsyslog("assuming manual start of VDR");
00115      // Set inactive after MinUserInactivity
00116      SetUserInactiveTimeout();
00117      }
00118   else {
00119      // Set inactive from now on
00120      dsyslog("scheduled wakeup time in %ld minutes, assuming automatic start of VDR", Delta / 60);
00121      SetUserInactive();
00122      }
00123 }
00124 
00125 void cShutdownHandler::SetShutdownCommand(const char *ShutdownCommand)
00126 {
00127   free(shutdownCommand);
00128   shutdownCommand = ShutdownCommand ? strdup(ShutdownCommand) : NULL;
00129 }
00130 
00131 void cShutdownHandler::CallShutdownCommand(time_t WakeupTime, int Channel, const char *File, bool UserShutdown)
00132 {
00133   time_t Delta = WakeupTime ? WakeupTime - time(NULL) : 0;
00134   cString cmd = cString::sprintf("%s %ld %ld %d \"%s\" %d", shutdownCommand, WakeupTime, Delta, Channel, *strescape(File, "\\\"$"), UserShutdown);
00135   isyslog("executing '%s'", *cmd);
00136   int Status = SystemExec(cmd, true);
00137   if (!WIFEXITED(Status) || WEXITSTATUS(Status))
00138      esyslog("SystemExec() failed with status %d", Status);
00139   else {
00140      Setup.NextWakeupTime = WakeupTime; // Remember this wakeup time for comparison on reboot
00141      Setup.Save();
00142      }
00143 }
00144 
00145 void cShutdownHandler::SetUserInactiveTimeout(int Seconds, bool Force)
00146 {
00147   if (!Setup.MinUserInactivity && !Force) {
00148      activeTimeout = 0;
00149      return;
00150      }
00151   if (Seconds < 0)
00152      Seconds = Setup.MinUserInactivity * 60;
00153   activeTimeout = time(NULL) + Seconds;
00154 }
00155 
00156 bool cShutdownHandler::ConfirmShutdown(bool Interactive)
00157 {
00158   if (!Interactive && !cRemote::Enabled())
00159      return false;
00160 
00161   if (!shutdownCommand) {
00162      if (Interactive)
00163         Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
00164      return false;
00165      }
00166   if (cCutter::Active()) {
00167      if (!Interactive || !Interface->Confirm(tr("Editing - shut down anyway?")))
00168         return false;
00169      }
00170   if (cFileTransfer::Active()) {
00171      if (!Interactive || !Interface->Confirm(tr("Transfering file - shut down anyway?")))
00172         return false;
00173      }
00174 
00175   cTimer *timer = Timers.GetNextActiveTimer();
00176   time_t Next = timer ? timer->StartTime() : 0;
00177   time_t Delta = timer ? Next - time(NULL) : 0;
00178 
00179   if (cRecordControls::Active() || (Next && Delta <= 0)) {
00180      // VPS recordings in timer end margin may cause Delta <= 0
00181      if (!Interactive || !Interface->Confirm(tr("Recording - shut down anyway?")))
00182         return false;
00183      }
00184   else if (Next && Delta <= Setup.MinEventTimeout * 60) {
00185      // Timer within Min Event Timeout
00186      if (!Interactive)
00187         return false;
00188      cString buf = cString::sprintf(tr("Recording in %ld minutes, shut down anyway?"), Delta / 60);
00189      if (!Interface->Confirm(buf))
00190         return false;
00191      }
00192 
00193   if (cPluginManager::Active(Interactive ? tr("shut down anyway?") : NULL))
00194      return false;
00195 
00196   cPlugin *Plugin = cPluginManager::GetNextWakeupPlugin();
00197   Next = Plugin ? Plugin->WakeupTime() : 0;
00198   Delta = Next ? Next - time(NULL) : 0;
00199   if (Next && Delta <= Setup.MinEventTimeout * 60) {
00200      // Plugin wakeup within Min Event Timeout
00201      if (!Interactive)
00202         return false;
00203      cString buf = cString::sprintf(tr("Plugin %s wakes up in %ld min, continue?"), Plugin->Name(), Delta / 60);
00204      if (!Interface->Confirm(buf))
00205         return false;
00206      }
00207 
00208   return true;
00209 }
00210 
00211 bool cShutdownHandler::ConfirmRestart(bool Interactive)
00212 {
00213   if (cCutter::Active()) {
00214      if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?")))
00215         return false;
00216      }
00217   if (cFileTransfer::Active()) {
00218      if (!Interactive || !Interface->Confirm(tr("Transfering file - restart anyway?")))
00219         return false;
00220      }
00221 
00222   cTimer *timer = Timers.GetNextActiveTimer();
00223   time_t Next  = timer ? timer->StartTime() : 0;
00224   time_t Delta = timer ? Next - time(NULL) : 0;
00225 
00226   if (cRecordControls::Active() || (Next && Delta <= 0)) {
00227      // VPS recordings in timer end margin may cause Delta <= 0
00228      if (!Interactive || !Interface->Confirm(tr("Recording - restart anyway?")))
00229         return false;
00230      }
00231 
00232   if (cPluginManager::Active(Interactive ? tr("restart anyway?") : NULL))
00233      return false;
00234 
00235   return true;
00236 }
00237 
00238 bool cShutdownHandler::DoShutdown(bool Force)
00239 {
00240   time_t Now = time(NULL);
00241   cTimer *timer = Timers.GetNextActiveTimer();
00242   cPlugin *Plugin = cPluginManager::GetNextWakeupPlugin();
00243 
00244   time_t Next = timer ? timer->StartTime() : 0;
00245   time_t NextPlugin = Plugin ? Plugin->WakeupTime() : 0;
00246   if (NextPlugin && (!Next || Next > NextPlugin)) {
00247      Next = NextPlugin;
00248      timer = NULL;
00249      }
00250   time_t Delta = Next ? Next - Now : 0;
00251 
00252   if (Next && Delta < Setup.MinEventTimeout * 60) {
00253      if (!Force)
00254         return false;
00255      Delta = Setup.MinEventTimeout * 60;
00256      Next = Now + Delta;
00257      timer = NULL;
00258      dsyslog("reboot at %s", *TimeToString(Next));
00259      }
00260 
00261   if (Next && timer) {
00262      dsyslog("next timer event at %s", *TimeToString(Next));
00263      CallShutdownCommand(Next, timer->Channel()->Number(), timer->File(), Force);
00264      }
00265   else if (Next && Plugin) {
00266      CallShutdownCommand(Next, 0, Plugin->Name(), Force);
00267      dsyslog("next plugin wakeup at %s", *TimeToString(Next));
00268      }
00269   else
00270      CallShutdownCommand(Next, 0, "", Force); // Next should always be 0 here. Just for safety, pass it.
00271 
00272   return true;
00273 }