diff --git a/HISTORY b/HISTORY index 9b99af91..02a43624 100644 --- a/HISTORY +++ b/HISTORY @@ -409,3 +409,7 @@ Video Disk Recorder Revision History that every timer may use the primary interface. - The 'active' field of a timer will now be explicitly set to '1' if the user modifies an active timer (see FORMATS for details). +- The new command line option -w can be used to activate a watchdog that makes + VDR exit in case the main program loop does not respond for more than the + given number of seconds. This is mainly useful in combination with the new + 'runvdr' script that restarts VDR in case is has exited. diff --git a/INSTALL b/INSTALL index b15e7b92..c334ed6d 100644 --- a/INSTALL +++ b/INSTALL @@ -15,7 +15,7 @@ If you have the DVB driver source in a different location you will have to change the definition of DVBDIR in the Makefile. -This program requires the card driver version 0.8.1 or higher +This program requires the card driver version 0.8.2 or higher to work properly. You need to load the dvb.o module *without* option 'outstream=0' (previous versions of VDR required this option to have the driver supply the data in AV_PES format; as of version 0.70 VDR @@ -71,6 +71,13 @@ If the program shall run as a daemon, use the --daemon option. This will completely detach it from the terminal and will continue as a background process. +Automatic restart in case of hangups: +------------------------------------- + +If you run VDR using the 'runvdr' shell script it will use the built-in +watchdog timer to restart the program in case something happens that +causes a program hangup. + Command line options: --------------------- diff --git a/runvdr b/runvdr new file mode 100755 index 00000000..83b2ea62 --- /dev/null +++ b/runvdr @@ -0,0 +1,13 @@ +#!/bin/sh + +DVBDIR='../DVB/driver' +VDRCMD='./vdr -w 60' + +while test 1; do +# (cd $DVBDIR; make reload) +# sleep 3 + if $VDRCMD; then exit; fi + date + echo "restarting VDR" + sleep 10 + done diff --git a/vdr.c b/vdr.c index 4fb33a9c..938afcf0 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/people/kls/vdr * - * $Id: vdr.c 1.53 2001/02/11 14:51:44 kls Exp $ + * $Id: vdr.c 1.54 2001/02/24 16:18:43 kls Exp $ */ #include @@ -55,30 +55,41 @@ static void SignalHandler(int signum) signal(signum, SignalHandler); } +static void Watchdog(int signum) +{ + // Something terrible must have happened that prevented the 'alarm()' from + // being called in time, so let's get out of here: + esyslog(LOG_ERR, "PANIC: watchdog timer expired - exiting!"); + exit(1); +} + int main(int argc, char *argv[]) { // Command line options: #define DEFAULTSVDRPPORT 2001 +#define DEFAULTWATCHDOG 0 // seconds int SVDRPport = DEFAULTSVDRPPORT; const char *ConfigDirectory = NULL; bool DaemonMode = false; + int WatchdogTimeout = DEFAULTWATCHDOG; static struct option long_options[] = { - { "config", required_argument, NULL, 'c' }, - { "daemon", no_argument, NULL, 'd' }, - { "device", required_argument, NULL, 'D' }, - { "help", no_argument, NULL, 'h' }, - { "log", required_argument, NULL, 'l' }, - { "port", required_argument, NULL, 'p' }, - { "video", required_argument, NULL, 'v' }, + { "config", required_argument, NULL, 'c' }, + { "daemon", no_argument, NULL, 'd' }, + { "device", required_argument, NULL, 'D' }, + { "help", no_argument, NULL, 'h' }, + { "log", required_argument, NULL, 'l' }, + { "port", required_argument, NULL, 'p' }, + { "video", required_argument, NULL, 'v' }, + { "watchdog", required_argument, NULL, 'w' }, { 0 } }; int c; int option_index = 0; - while ((c = getopt_long(argc, argv, "c:dD:hl:p:v:", long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "c:dD:hl:p:v:w:", long_options, &option_index)) != -1) { switch (c) { case 'c': ConfigDirectory = optarg; break; @@ -94,23 +105,26 @@ int main(int argc, char *argv[]) abort(); break; case 'h': printf("Usage: vdr [OPTION]\n\n" // for easier orientation, this is column 80| - " -c DIR, --config=DIR read config files from DIR (default is to read them\n" - " from the video directory)\n" - " -h, --help display this help and exit\n" - " -d, --daemon run in daemon mode\n" - " -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n" - " there may be several -D options (default: all DVB\n" - " devices will be used)\n" - " -l LEVEL, --log=LEVEL set log level (default: 3)\n" - " 0 = no logging, 1 = errors only,\n" - " 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" - " -v DIR, --video=DIR use DIR as video directory (default is %s)\n" + " -c DIR, --config=DIR read config files from DIR (default is to read them\n" + " from the video directory)\n" + " -h, --help display this help and exit\n" + " -d, --daemon run in daemon mode\n" + " -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n" + " there may be several -D options (default: all DVB\n" + " devices will be used)\n" + " -l LEVEL, --log=LEVEL set log level (default: 3)\n" + " 0 = no logging, 1 = errors only,\n" + " 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" + " -v DIR, --video=DIR use DIR as video directory (default is %s)\n" + " -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n" + " seconds (default: %d); '0' disables the watchdog\n" "\n" "Report bugs to \n", DEFAULTSVDRPPORT, - VideoDirectory + VideoDirectory, + DEFAULTWATCHDOG ); return 0; break; @@ -135,6 +149,16 @@ int main(int argc, char *argv[]) while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/') optarg[strlen(optarg) - 1] = 0; break; + case 'w': if (isnumber(optarg)) { + int t = atoi(optarg); + if (t >= 0) { + WatchdogTimeout = t; + break; + } + } + fprintf(stderr, "vdr: invalid watchdog timeout: %s\n", optarg); + abort(); + break; default: abort(); } } @@ -213,6 +237,8 @@ int main(int argc, char *argv[]) if (signal(SIGINT, SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN); if (signal(SIGPIPE, SignalHandler) == SIG_IGN) signal(SIGPIPE, SIG_IGN); + if (WatchdogTimeout > 0) + if (signal(SIGALRM, Watchdog) == SIG_IGN) signal(SIGALRM, SIG_IGN); // Main program loop: @@ -221,8 +247,22 @@ int main(int argc, char *argv[]) int LastChannel = -1; int PreviousChannel = cDvbApi::CurrentChannel(); time_t LastActivity = time(NULL); + int MaxLatencyTime = 0; + + if (WatchdogTimeout > 0) { + dsyslog(LOG_INFO, "setting watchdog timer to %d seconds", WatchdogTimeout); + alarm(WatchdogTimeout); // Initial watchdog timer start + } while (!Interrupted) { + // Restart the Watchdog timer: + if (WatchdogTimeout > 0) { + int LatencyTime = WatchdogTimeout - alarm(WatchdogTimeout); + if (LatencyTime > MaxLatencyTime) { + MaxLatencyTime = LatencyTime; + dsyslog(LOG_INFO, "max. latency time %d seconds", MaxLatencyTime); + } + } // Channel display: if (!EITScanner.Active() && cDvbApi::CurrentChannel() != LastChannel) { if (!Menu) @@ -344,6 +384,8 @@ int main(int argc, char *argv[]) delete ReplayControl; delete Interface; cDvbApi::Cleanup(); + if (WatchdogTimeout > 0) + dsyslog(LOG_INFO, "max. latency time %d seconds", MaxLatencyTime); isyslog(LOG_INFO, "exiting"); if (SysLogLevel > 0) closelog();