From a8f8de9dc9fda5fc94e5bc3d98dac5785e72d7da Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sat, 1 Sep 2001 09:04:37 +0200 Subject: [PATCH] Implemented automatic shutdown --- HISTORY | 6 +++++ INSTALL | 42 +++++++++++++++++++++++++++++++++++ MANUAL | 9 ++++++++ config.c | 8 ++++++- config.h | 5 +++-- i18n.c | 29 +++++++++++++++++++++++- interface.c | 11 +++++---- interface.h | 6 +++-- menu.c | 4 +++- vdr.c | 64 ++++++++++++++++++++++++++++++++++++++++++++--------- 10 files changed, 162 insertions(+), 22 deletions(-) diff --git a/HISTORY b/HISTORY index 250d8572..1de01112 100644 --- a/HISTORY +++ b/HISTORY @@ -676,3 +676,9 @@ Video Disk Recorder Revision History - Timers are now sorted in the "Timers" menu, showing the sequence in which they will be recording. This can be disabled in the "Setup" menu. Note that the "Mark" button doesn't work if timers are displayed sorted. + +2001-09-01: Version 0.9.4 + +- Changed version number notation. +- Implemented automatic shutdown (see INSTALL and MANUAL for details). + diff --git a/INSTALL b/INSTALL index 946e06de..cca0f21a 100644 --- a/INSTALL +++ b/INSTALL @@ -98,6 +98,48 @@ call to the VDR program, be sure to NOT use the '-d' option! Otherwise VDR will go into 'deamon' mode and the initial program call will return immediately! +Automatic shutdown: +------------------- + +If you define a shutdown command via the '-s' command line option, VDR +will call the given command if there is currently no recording or replay +active, the user has been inactive for at least MinUserInactivity minutes +and the next timer event is at least MinEventTimeout minutes in the future +(see the Setup parameters in MANUAL). + +The command given in the '-s' option will be called with two parameters. +The first one is the time (in UTC) of the next timer event (as a time_t +type number), and the second one is the number of seconds from the current +time until the next timer event. Your program can choose which one to use +for programming some sort of hardware device that makes sure the computer +will be restarted in time before the next timer event. Your program must +also initiate the actual shutdown procedure of the computer. After this +your program should return to VDR. VDR will not automatically exit after +calling the shutdown program, but will rather continue normally untit it +receives a SIGTERM when the computer is actually shut down. So in case +the shutdown fails, or the shutdown program for some reason decides not to +perform a shutdown, VDR will stay up and running. + +Before the shutdown program is called, the user will be prompted to inform +him that the system is about to shut down. If any remote control key is +pressed while this prompt is visible, the shutdown will be cancelled (and +tried again after another MinUserInactivity minutes). The shutdown prompt +will be displayed for 5 minutes, which should be enough time for the user +to react. + +A sample shell script to be used with the '-s' option might look like this: + +#!/bin/sh +setRTCwakeup $(($1 - 300)) +sudo halt + +Here 'setRTCwakeup' would be some program that uses the first parameter +(which is the absolute time of the next timer event) to set the Real Time +Clock so that it wakes up the computer 5 minutes (i.e. 300 seconds) before +that event. The 'sudo halt' command then shuts down the computer. +You will have to substitute both commands with whatever applies to your +particular hard- and software environment. + Command line options: --------------------- diff --git a/MANUAL b/MANUAL index ae93099f..1983c1b9 100644 --- a/MANUAL +++ b/MANUAL @@ -417,6 +417,15 @@ Video Disk Recorder User's Manual you may want to use smaller values if you are planning on archiving a recording to CD. + MinEventTimeout=120 If the command line option '-s' has been set, VDR will + MinUserInactivity=120 automatically shutdown the computer if the next timer + event is at least MinEventTimeout minutes in the future, + and the user has been inactive for at least + MinUserInactivity minutes. Setting MinUserInactivity + to 0 disables the automatic shutdown, while still + retaining the possibility to manually shutdown the + computer. + * Executing system commands The "Main" menu option "Commands" allows you to execute any system commands diff --git a/config.c b/config.c index ad4b9dad..788f044e 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.59 2001/08/26 14:46:43 kls Exp $ + * $Id: config.c 1.60 2001/08/31 13:46:26 kls Exp $ */ #include "config.h" @@ -820,6 +820,8 @@ cSetup::cSetup(void) OSDwidth = 52; OSDheight = 18; MaxVideoFileSize = MAXVIDEOFILESIZE; + MinEventTimeout = 120; + MinUserInactivity = 120; CurrentChannel = -1; } @@ -853,6 +855,8 @@ bool cSetup::Parse(char *s) else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value); else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value); else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value); + else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value); + else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value); else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value); else return false; @@ -921,6 +925,8 @@ bool cSetup::Save(const char *FileName) fprintf(f, "OSDwidth = %d\n", OSDwidth); fprintf(f, "OSDheight = %d\n", OSDheight); fprintf(f, "MaxVideoFileSize = %d\n", MaxVideoFileSize); + fprintf(f, "MinEventTimeout = %d\n", MinEventTimeout); + fprintf(f, "MinUserInactivity = %d\n", MinUserInactivity); fprintf(f, "CurrentChannel = %d\n", CurrentChannel); f.Close(); isyslog(LOG_INFO, "saved setup to %s", FileName); diff --git a/config.h b/config.h index 70f35740..78502e1b 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.66 2001/08/26 14:46:53 kls Exp $ + * $Id: config.h 1.67 2001/09/01 07:15:26 kls Exp $ */ #ifndef __CONFIG_H @@ -19,7 +19,7 @@ #include "eit.h" #include "tools.h" -#define VDRVERSION "0.93" +#define VDRVERSION "0.9.4" #define MaxBuffer 10000 @@ -295,6 +295,7 @@ public: int ChannelInfoPos; int OSDwidth, OSDheight; int MaxVideoFileSize; + int MinEventTimeout, MinUserInactivity; int CurrentChannel; cSetup(void); bool Load(const char *FileName); diff --git a/i18n.c b/i18n.c index 35a6bb1e..f991b25e 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.35 2001/08/26 13:45:10 kls Exp $ + * $Id: i18n.c 1.36 2001/08/31 15:37:05 kls Exp $ * * Slovenian translations provided by Miha Setina * Italian translations provided by Alberto Carraro @@ -385,6 +385,15 @@ const tPhrase Phrases[] = { "Annuler les modifications?", "Avbryte redigering", }, + { "Press any key to cancel shutdown", + "Taste drücken um Shutdown abzubrechen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, // Channel parameters: { "Name", "Name", @@ -875,6 +884,24 @@ const tPhrase Phrases[] = { "", // TODO "", // TODO }, + { "MinEventTimeout", + "Mindest Event Pause", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "MinUserInactivity", + "Mindest User Inaktivität", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, // The days of the week: { "MTWTFSS", "MDMDFSS", diff --git a/interface.c b/interface.c index 1e06f4af..b7cb457c 100644 --- a/interface.c +++ b/interface.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.c 1.41 2001/08/25 13:15:00 kls Exp $ + * $Id: interface.c 1.42 2001/09/01 07:30:37 kls Exp $ */ #include "interface.h" @@ -20,6 +20,7 @@ cInterface::cInterface(int SVDRPport) cols[0] = 0; width = height = 0; keyFromWait = kNone; + interrupted = false; rcIo = NULL; SVDRP = NULL; #if defined(REMOTE_RCU) @@ -110,11 +111,12 @@ eKeys cInterface::Wait(int Seconds, bool KeepChar) time_t timeout = time(NULL) + Seconds; for (;;) { Key = GetKey(); - if ((Key != kNone && (RAWKEY(Key) != kOk || RAWKEY(Key) == Key)) || time(NULL) > timeout) + if ((Key != kNone && (RAWKEY(Key) != kOk || RAWKEY(Key) == Key)) || time(NULL) > timeout || interrupted) break; } if (KeepChar && ISRAWKEY(Key)) keyFromWait = Key; + interrupted = false; return Key; } @@ -312,12 +314,13 @@ void cInterface::Error(const char *s) Close(); } -bool cInterface::Confirm(const char *s) +bool cInterface::Confirm(const char *s, int Seconds, bool WaitForTimeout) { Open(); isyslog(LOG_INFO, "confirm: %s", s); Status(s, clrBlack, clrYellow); - bool result = Wait(10) == kOk; + eKeys k = Wait(Seconds); + bool result = WaitForTimeout ? k == kNone : k == kOk; Status(NULL); Close(); isyslog(LOG_INFO, "%sconfirmed", result ? "" : "not "); diff --git a/interface.h b/interface.h index 2b0e2f1a..925c33e6 100644 --- a/interface.h +++ b/interface.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.h 1.22 2001/07/27 11:38:01 kls Exp $ + * $Id: interface.h 1.23 2001/09/01 07:29:24 kls Exp $ */ #ifndef __INTERFACE_H @@ -23,6 +23,7 @@ private: int open; int cols[MaxCols]; eKeys keyFromWait; + bool interrupted; cSVDRP *SVDRP; cRcIoBase *rcIo; unsigned int GetCh(bool Wait = true, bool *Repeat = NULL, bool *Release = NULL); @@ -34,6 +35,7 @@ public: ~cInterface(); void Open(int NumCols = 0, int NumLines = 0); void Close(void); + void Interrupt(void) { interrupted = true; } int Width(void) { return width; } int Height(void) { return height; } eKeys GetKey(bool Wait = true); @@ -52,7 +54,7 @@ public: void Status(const char *s, eDvbColor FgColor = clrBlack, eDvbColor BgColor = clrCyan); void Info(const char *s); void Error(const char *s); - bool Confirm(const char *s); + bool Confirm(const char *s, int Seconds = 10, bool WaitForTimeout = false); void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); void LearnKeys(void); void DisplayChannelNumber(int Number); diff --git a/menu.c b/menu.c index 6d6eae8e..1507b676 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.109 2001/08/26 14:03:27 kls Exp $ + * $Id: menu.c 1.110 2001/08/31 13:47:28 kls Exp $ */ #include "menu.h" @@ -1731,6 +1731,8 @@ void cMenuSetup::Set(void) Add(new cMenuEditIntItem( tr("OSDwidth"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH)); Add(new cMenuEditIntItem( tr("OSDheight"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT)); Add(new cMenuEditIntItem( tr("MaxVideoFileSize"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZE)); + Add(new cMenuEditIntItem( tr("MinEventTimeout"), &data.MinEventTimeout)); + Add(new cMenuEditIntItem( tr("MinUserInactivity"), &data.MinUserInactivity)); } eOSState cMenuSetup::ProcessKey(eKeys Key) diff --git a/vdr.c b/vdr.c index 3f8f8da8..322b50cb 100644 --- a/vdr.c +++ b/vdr.c @@ -22,9 +22,10 @@ * * The project's page is at http://www.cadsoft.de/people/kls/vdr * - * $Id: vdr.c 1.64 2001/08/26 15:02:00 kls Exp $ + * $Id: vdr.c 1.65 2001/09/01 08:57:11 kls Exp $ */ +#define _GNU_SOURCE #include #include #include @@ -48,13 +49,16 @@ #endif #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping +#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown static int Interrupted = 0; static void SignalHandler(int signum) { - if (signum != SIGPIPE) + if (signum != SIGPIPE) { Interrupted = signum; + Interface->Interrupt(); + } signal(signum, SignalHandler); } @@ -77,7 +81,8 @@ int main(int argc, char *argv[]) const char *ConfigDirectory = NULL; bool DaemonMode = false; int WatchdogTimeout = DEFAULTWATCHDOG; - char *Terminal = NULL; + const char *Terminal = NULL; + const char *Shutdown = NULL; static struct option long_options[] = { { "audio", required_argument, NULL, 'a' }, @@ -88,16 +93,17 @@ int main(int argc, char *argv[]) { "help", no_argument, NULL, 'h' }, { "log", required_argument, NULL, 'l' }, { "port", required_argument, NULL, 'p' }, + { "shutdown", required_argument, NULL, 's' }, + { "terminal", required_argument, NULL, 't' }, { "video", required_argument, NULL, 'v' }, { "dvd", required_argument, NULL, 'V' }, { "watchdog", required_argument, NULL, 'w' }, - { "terminal", required_argument, NULL, 't' }, { NULL } }; int c; int option_index = 0; - while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:p:t:v:V:w:", long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:p:s:t:v:V:w:", long_options, &option_index)) != -1) { switch (c) { case 'a': cDvbApi::SetAudioCommand(optarg); break; @@ -134,6 +140,7 @@ int main(int argc, char *argv[]) " 2 = errors and info, 3 = errors, info and debug\n" " -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n" " 0 turns off SVDRP\n" + " -s CMD, --shutdown=CMD call CMD to shutdown the computer\n" " -t TTY, --terminal=TTY controlling tty\n" " -v DIR, --video=DIR use DIR as video directory (default: %s)\n" " -V DEV, --dvd=DEV use DEV as the DVD device (default: %s)\n" @@ -170,6 +177,8 @@ int main(int argc, char *argv[]) return 2; } break; + case 's': Shutdown = optarg; + break; case 't': Terminal = optarg; break; case 'v': VideoDirectory = optarg; @@ -292,7 +301,7 @@ int main(int argc, char *argv[]) cReplayControl *ReplayControl = NULL; int LastChannel = -1; int PreviousChannel = cDvbApi::CurrentChannel(); - time_t LastActivity = time(NULL); + time_t LastActivity = 0; int MaxLatencyTime = 0; if (WatchdogTimeout > 0) { @@ -333,8 +342,10 @@ int main(int argc, char *argv[]) // User Input: cOsdBase **Interact = Menu ? &Menu : (cOsdBase **)&ReplayControl; eKeys key = Interface->GetKey(!*Interact || !(*Interact)->NeedsFastResponse()); - if (NORMALKEY(key) != kNone) + if (NORMALKEY(key) != kNone) { EITScanner.Activity(); + LastActivity = time(NULL); + } if (*Interact) { switch ((*Interact)->ProcessKey(key)) { case osMenu: DELETENULL(Menu); @@ -424,13 +435,44 @@ int main(int argc, char *argv[]) cVideoCutter::Active(); } if (!*Interact && !cRecordControls::Active()) { - if (time(NULL) - LastActivity > ACTIVITYTIMEOUT) { + time_t Now = time(NULL); + if (Now - LastActivity > ACTIVITYTIMEOUT) { + // Shutdown: + if (Shutdown && Setup.MinUserInactivity && Now - LastActivity > Setup.MinUserInactivity * 60) { + cTimer *timer = Timers.GetNextActiveTimer(); + if (timer) { + time_t Next = timer->StartTime(); + time_t Delta = Next - Now; + dsyslog(LOG_INFO, "next timer event at %s", ctime(&Next)); + if (Delta > Setup.MinEventTimeout * 60) { + if (!LastActivity) { + // Apparently the user started VDR manually + dsyslog(LOG_INFO, "assuming manual start of VDR"); + LastActivity = Now; + continue; + } + if (WatchdogTimeout > 0) + signal(SIGALRM, SIG_IGN); + if (Interface->Confirm(tr("Press any key to cancel shutdown"), SHUTDOWNWAIT, true)) { + char *cmd; + asprintf(&cmd, "%s %ld %ld", Shutdown, Next, Delta); + isyslog(LOG_INFO, "executing '%s'", cmd); + system(cmd); + delete cmd; + } + else if (WatchdogTimeout > 0) { + alarm(WatchdogTimeout); + if (signal(SIGALRM, Watchdog) == SIG_IGN) + signal(SIGALRM, SIG_IGN); + } + LastActivity = Now; // don't try again too soon + } + } + } + // Disk housekeeping: RemoveDeletedRecordings(); - LastActivity = time(NULL); } } - else - LastActivity = time(NULL); } if (Interrupted) isyslog(LOG_INFO, "caught signal %d", Interrupted);