1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Rewrite of shutdown handling; implemented cPlugin::WakeupTime(); SIGHUP forces reload; cThread::EmergencyExit() replaced by ShutdownHandler.RequestEmergencyExit()

This commit is contained in:
Klaus Schmidinger 2007-02-25 10:56:29 +01:00
parent 50b14be807
commit ddb7f33567
23 changed files with 770 additions and 189 deletions

View File

@ -1485,6 +1485,10 @@ Udo Richter <udo_richter@gmx.de>
for some hints on how to improve handling cPluginManager::Active() for some hints on how to improve handling cPluginManager::Active()
for fixing a possible segfault if VDR gets terminated while a message is displayed for fixing a possible segfault if VDR gets terminated while a message is displayed
for reporting an error in the INSTALL section on retrying shutdown later for reporting an error in the INSTALL section on retrying shutdown later
for rewriting shutdown handling
for implementing cPlugin::WakeupTime() to allow plugins to request VDR to wake
up at a particular time
for making the HUP signal force a restart of VDR
Sven Kreiensen <svenk@kammer.uni-hannover.de> Sven Kreiensen <svenk@kammer.uni-hannover.de>
for his help in keeping 'channels.conf.terr' up to date for his help in keeping 'channels.conf.terr' up to date

View File

@ -5099,7 +5099,7 @@ Video Disk Recorder Revision History
- Fixed handling error status in cDvbTuner::GetFrontendStatus() (thanks to - Fixed handling error status in cDvbTuner::GetFrontendStatus() (thanks to
Reinhard Nissl). Reinhard Nissl).
2007-02-03: Version 1.5.1 2007-02-25: Version 1.5.1
- Added cDevice::HasCi() so that devices with Common Interface can be avoided - Added cDevice::HasCi() so that devices with Common Interface can be avoided
when tuning to an FTA channel, thus preserving the CAM resources even on budget when tuning to an FTA channel, thus preserving the CAM resources even on budget
@ -5110,3 +5110,8 @@ Video Disk Recorder Revision History
Nissl). Nissl).
- Removed 'assert(0)' from cDvbSpuDecoder::setTime() (thanks to Marco Schlüßler). - Removed 'assert(0)' from cDvbSpuDecoder::setTime() (thanks to Marco Schlüßler).
- Adapted 'libsi' to DVB-S2 (thanks to Marco Schlüßler). - Adapted 'libsi' to DVB-S2 (thanks to Marco Schlüßler).
- Shutdown handling has been rewritten (thanks to Udo Richter).
- Plugins can now implement the new function WakeupTime() to request VDR to wake
up at a particular time (thanks to Udo Richter).
- The HUP signal now forces a restart of VDR (thanks to Udo Richter).
- cThread::EmergencyExit() has been replaced by ShutdownHandler.RequestEmergencyExit().

47
INSTALL
View File

@ -160,30 +160,33 @@ and the next timer event is at least MinEventTimeout minutes in the future
(see the Setup parameters in MANUAL). (see the Setup parameters in MANUAL).
The command given in the '-s' option will be called with five parameters. The command given in the '-s' option will be called with five 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 until 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 and will call the shutdown
program again after another five minutes.
If there are currently no timers active, both parameters will be '0'. The first one is the time (in UTC) of the next timer event or plugin wakeup
In that case the program shall not set the hardware for automatic restart time (as a time_t type number), and the second one is the number of
and only perform the system shutdown. A program that uses the second parameter seconds from the current time until the next timer event. Your program can
to set the hardware for restart must therefore also check whether the first choose which one to use for programming some sort of hardware device that
parameter is '0'. 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. VDR will not automatically exit after calling the shutdown
program, but will rather continue normally until 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 and will call the shutdown program again after a
while. The command will be started in a separate background session, so it
can continue to run even after VDR has terminated.
The third parameter contains the number of the channel that will be recorded If there are currently no timers active and there is no plugin wakeup
by the next timer (or 0 if no timer is present), and the fourth parameter time, both parameters will be '0'. In that case the program shall not set
contains the file name of the recording as defined in the timer (or an empty the hardware for automatic restart and only perform the system shutdown.
string if no timer is present). These can be used by the shutdown program to A program that uses the second parameter to set the hardware for restart
show that information on some display interface etc. must therefore also check whether the first parameter is '0'.
If the wakeup time is given by a timer, the third parameter will be the
number of the channel that will be recorded, otherwise it will be 0. The
fourth parameter contains the file name of the recording as defined in the
timer, the name of the plugin that requested the wakeup time, or an empty
string if no wakeup time is present. These can be used by the shutdown
program to show that information on some display interface etc.
The fifth parameter indicates the reason why the shutdown was requested. The fifth parameter indicates the reason why the shutdown was requested.
'0' means this is an automatic shutdown due to some timeout, while '1' means '0' means this is an automatic shutdown due to some timeout, while '1' means

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: Makefile 1.96 2007/01/07 14:38:00 kls Exp $ # $Id: Makefile 1.97 2007/02/24 13:23:12 kls Exp $
.DELETE_ON_ERROR: .DELETE_ON_ERROR:
@ -35,7 +35,7 @@ SILIB = $(LSIDIR)/libsi.a
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\ OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\ dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\ lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\ receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\ skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
timers.o tools.o transfer.o vdr.o videodir.o timers.o tools.o transfer.o vdr.o videodir.o

View File

@ -6,7 +6,7 @@
<center><h1>The VDR Plugin System</h1></center> <center><h1>The VDR Plugin System</h1></center>
<center><b>Version 1.5.0</b></center> <center><b>Version 1.5.1</b></center>
<p> <p>
<center> <center>
Copyright &copy; 2006 Klaus Schmidinger<br> Copyright &copy; 2006 Klaus Schmidinger<br>
@ -14,9 +14,12 @@ Copyright &copy; 2006 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a> <a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center> </center>
<p> <p>
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%> <!--X1.5.0--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.5.0 are marked like this. Important modifications introduced in version 1.5.0 are marked like this.
<!--X1.5.0--></td></tr></table> <!--X1.5.0--></td></tr></table>
<!--X1.5.1--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.5.1 are marked like this.
<!--X1.5.1--></td></tr></table>
<p> <p>
VDR provides an easy to use plugin interface that allows additional functionality VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file. to be added to the program by implementing a dynamically loadable library file.
@ -55,6 +58,9 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Housekeeping">Housekeeping</a> <li><a href="#Housekeeping">Housekeeping</a>
<li><a href="#Main thread hook">Main thread hook</a> <li><a href="#Main thread hook">Main thread hook</a>
<li><a href="#Activity">Activity</a> <li><a href="#Activity">Activity</a>
<!--X1.5.1--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<li><a href="#Wakeup">Wakeup</a>
<!--X1.5.1--></td></tr></table>
<li><a href="#Setup parameters">Setup parameters</a> <li><a href="#Setup parameters">Setup parameters</a>
<li><a href="#The Setup menu">The Setup menu</a> <li><a href="#The Setup menu">The Setup menu</a>
<li><a href="#Configuration files">Configuration files</a> <li><a href="#Configuration files">Configuration files</a>
@ -76,7 +82,7 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Devices">Devices</a> <li><a href="#Devices">Devices</a>
<li><a href="#Audio">Audio</a> <li><a href="#Audio">Audio</a>
<li><a href="#Remote Control">Remote Control</a> <li><a href="#Remote Control">Remote Control</a>
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%> <!--X1.5.0--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<li><a href="#Conditional Access">Conditional Access</a> <li><a href="#Conditional Access">Conditional Access</a>
<!--X1.5.0--></td></tr></table> <!--X1.5.0--></td></tr></table>
</ul> </ul>
@ -675,6 +681,41 @@ be queried, and further prompts may show up. If all prompts have been confirmed,
the shutdown will take place. As soon as one prompt is not confirmed, no the shutdown will take place. As soon as one prompt is not confirmed, no
further plugins will be queried and no shutdown will be done. further plugins will be queried and no shutdown will be done.
<!--X1.5.1--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<a name="Wakeup"><hr><h2>Wakeup</h2>
<center><i><b>Wake me up before you go-go</b></i></center><p>
If a plugin wants to schedule activity for a later time, or wants to perform
periodic activity at a certain time at night, and if VDR shall wake up from
shutdown at that time, the plugin can implement the function
<p><table><tr><td bgcolor=#F0F0F0><pre>
virtual time_t WakeupTime(void);
</pre></td></tr></table><p>
which shall return the time of the next custom wakeup time, or 0 if no wakeup
is planned. VDR will pass the most recent wakeup time of all plugins, or the next
timer time, whichever comes first, to the shutdown script. The following sample
will wake up VDR every night at 1:00:
<p><table><tr><td bgcolor=#F0F0F0><pre>
time_t MyPlugin::WakeupTime(void)
{
time_t Now = time(NULL);
time_t Time = cTimer::SetTime(Now, cTimer::TimeToInt(100));
if (Time &lt;= Now)
Time = cTimer::IncDay(Time, 1);
return Time;
}
</pre></td></tr></table><p>
After wakeup, the plugin shall continue to return the wakeup time and shall
return a string when <tt>Active()</tt> is called at that time, otherwise VDR may shut down
again instantly. If <tt>WakeupTime()</tt> returns a time that is not in
the future, the time will be ignored.
<!--X1.5.1--></td></tr></table>
<a name="Setup parameters"><hr><h2>Setup parameters</h2> <a name="Setup parameters"><hr><h2>Setup parameters</h2>
<center><i><b>Remember me...</b></i></center><p> <center><i><b>Remember me...</b></i></center><p>
@ -2046,7 +2087,7 @@ Put(uint64 Code, bool Repeat = false, bool Release = false);
The other parameters have the same meaning as in the first version of this function. The other parameters have the same meaning as in the first version of this function.
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%> <!--X1.5.0--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<a name="Conditional Access"><hr><h2>Conditional Access</h2> <a name="Conditional Access"><hr><h2>Conditional Access</h2>
<center><i><b>Members only!</b></i></center><p> <center><i><b>Members only!</b></i></center><p>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.c 1.147 2007/01/26 13:32:19 kls Exp $ * $Id: config.c 1.148 2007/02/24 13:29:52 kls Exp $
*/ */
#include "config.h" #include "config.h"
@ -267,6 +267,7 @@ cSetup::cSetup(void)
SplitEditedFiles = 0; SplitEditedFiles = 0;
MinEventTimeout = 30; MinEventTimeout = 30;
MinUserInactivity = 300; MinUserInactivity = 300;
NextWakeupTime = 0;
MultiSpeedMode = 0; MultiSpeedMode = 0;
ShowReplayMode = 0; ShowReplayMode = 0;
ResumeID = 0; ResumeID = 0;
@ -428,6 +429,7 @@ bool cSetup::Parse(const char *Name, const char *Value)
else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value); else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value); else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value); else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
else if (!strcasecmp(Name, "NextWakeupTime")) NextWakeupTime = atoi(Value);
else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value); else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value); else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value); else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value);
@ -496,6 +498,7 @@ bool cSetup::Save(void)
Store("SplitEditedFiles", SplitEditedFiles); Store("SplitEditedFiles", SplitEditedFiles);
Store("MinEventTimeout", MinEventTimeout); Store("MinEventTimeout", MinEventTimeout);
Store("MinUserInactivity", MinUserInactivity); Store("MinUserInactivity", MinUserInactivity);
Store("NextWakeupTime", NextWakeupTime);
Store("MultiSpeedMode", MultiSpeedMode); Store("MultiSpeedMode", MultiSpeedMode);
Store("ShowReplayMode", ShowReplayMode); Store("ShowReplayMode", ShowReplayMode);
Store("ResumeID", ResumeID); Store("ResumeID", ResumeID);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.h 1.284 2007/01/13 11:42:43 kls Exp $ * $Id: config.h 1.285 2007/02/24 13:23:12 kls Exp $
*/ */
#ifndef __CONFIG_H #ifndef __CONFIG_H
@ -244,6 +244,7 @@ public:
int MaxVideoFileSize; int MaxVideoFileSize;
int SplitEditedFiles; int SplitEditedFiles;
int MinEventTimeout, MinUserInactivity; int MinEventTimeout, MinUserInactivity;
time_t NextWakeupTime;
int MultiSpeedMode; int MultiSpeedMode;
int ShowReplayMode; int ShowReplayMode;
int ResumeID; int ResumeID;

134
i18n.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: i18n.c 1.287 2007/01/26 13:26:05 kls Exp $ * $Id: i18n.c 1.288 2007/02/25 10:11:26 kls Exp $
* *
* Translations provided by: * Translations provided by:
* *
@ -1341,6 +1341,28 @@ const tI18nPhrase Phrases[] = {
"Optagelse igang - genstart alligevel?", "Optagelse igang - genstart alligevel?",
"Systém je zaneprázdnìn - pøesto restartovat?", "Systém je zaneprázdnìn - pøesto restartovat?",
}, },
{ "Editing - restart anyway?",
"Schnitt läuft - trotzdem neu starten?",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "Recording - shut down anyway?", { "Recording - shut down anyway?",
"Aufnahme läuft - trotzdem ausschalten?", "Aufnahme läuft - trotzdem ausschalten?",
"Snemanje - zares izklopi?", "Snemanje - zares izklopi?",
@ -1407,6 +1429,116 @@ const tI18nPhrase Phrases[] = {
"Tryk vilkårlig tast for at annullere sluk", "Tryk vilkårlig tast for at annullere sluk",
"Jakákoliv klávesa zru¹í vypnutí", "Jakákoliv klávesa zru¹í vypnutí",
}, },
{ "Press any key to cancel restart",
"Taste drücken, um Neustart abzubrechen",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "VDR will shut down later - press Power to force",
"VDR schaltet später aus - Power zum erzwingen",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"VDR sammuu myöhemmin - pakota virtakytkimellä",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "VDR will shut down in %s minutes",
"VDR wird in %s Minuten ausschalten",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"VDR sammuu %s minuutin kuluttua",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "Editing - shut down anyway?",
"Schnitt läuft - trotzdem ausschalten?",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"Leikkaus kesken - sammutetaanko?",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "Plugin %s wakes up in %ld min, continue?",
"Plugin %s wacht in %ld Min auf, weiter?",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"Laajennos %s herää %ld minuutin kuluttua - sammutetaanko?",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
// Channel parameters: // Channel parameters:
{ "Name", { "Name",
"Name", "Name",

3
keys.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: keys.h 1.10 2006/10/14 10:41:20 kls Exp $ * $Id: keys.h 1.11 2007/02/25 10:49:35 kls Exp $
*/ */
#ifndef __KEYS_H #ifndef __KEYS_H
@ -74,6 +74,7 @@ enum eKeys { // "Up" and "Down" must be the first two keys!
#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0) #define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
#define NORMALKEY(k) (eKeys((k) & ~k_Repeat)) #define NORMALKEY(k) (eKeys((k) & ~k_Repeat))
#define ISMODELESSKEY(k) (RAWKEY(k) > k9) #define ISMODELESSKEY(k) (RAWKEY(k) > k9)
#define ISREALKEY(k) (k != kNone && k != k_Plugin)
#define BASICKEY(k) (eKeys((k) & 0xFFFF)) #define BASICKEY(k) (eKeys((k) & 0xFFFF))
#define KBDKEY(k) (eKeys(((k) << 16) | kKbd)) #define KBDKEY(k) (eKeys(((k) << 16) | kKbd))

11
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.c 1.447 2007/01/07 14:19:48 kls Exp $ * $Id: menu.c 1.448 2007/02/25 09:51:53 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
@ -22,6 +22,7 @@
#include "plugin.h" #include "plugin.h"
#include "recording.h" #include "recording.h"
#include "remote.h" #include "remote.h"
#include "shutdown.h"
#include "sources.h" #include "sources.h"
#include "status.h" #include "status.h"
#include "themes.h" #include "themes.h"
@ -2770,10 +2771,8 @@ void cMenuSetup::Set(void)
eOSState cMenuSetup::Restart(void) eOSState cMenuSetup::Restart(void)
{ {
if (Interface->Confirm(tr("Really restart?")) if (Interface->Confirm(tr("Really restart?")) && ShutdownHandler.ConfirmRestart(true)) {
&& (!cRecordControls::Active() || Interface->Confirm(tr("Recording - restart anyway?"))) ShutdownHandler.Exit(1);
&& !cPluginManager::Active(tr("restart anyway?"))) {
cThread::EmergencyExit(true);
return osEnd; return osEnd;
} }
return osContinue; return osContinue;
@ -3699,7 +3698,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
if (device) { if (device) {
dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number()); dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
if (!device->SwitchChannel(channel, false)) { if (!device->SwitchChannel(channel, false)) {
cThread::EmergencyExit(true); ShutdownHandler.RequestEmergencyExit();
return false; return false;
} }
if (!Timer || Timer->Matches()) { if (!Timer || Timer->Matches()) {

View File

@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: newplugin 1.30 2006/09/09 12:38:35 kls Exp $ # $Id: newplugin 1.31 2007/02/24 13:23:08 kls Exp $
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n"; $PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
@ -169,6 +169,7 @@ public:
virtual void Housekeeping(void); virtual void Housekeeping(void);
virtual void MainThreadHook(void); virtual void MainThreadHook(void);
virtual cString Active(void); virtual cString Active(void);
virtual time_t WakeupTime(void);
virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; } virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
virtual cOsdObject *MainMenuAction(void); virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void); virtual cMenuSetupPage *SetupMenu(void);
@ -236,6 +237,12 @@ cString cPlugin${PLUGIN_CLASS}::Active(void)
return NULL; return NULL;
} }
time_t cPlugin${PLUGIN_CLASS}::WakeupTime(void)
{
// Return custom wakeup time for shutdown script
return 0;
}
cOsdObject *cPlugin${PLUGIN_CLASS}::MainMenuAction(void) cOsdObject *cPlugin${PLUGIN_CLASS}::MainMenuAction(void)
{ {
// Perform the action when selected from the main VDR menu. // Perform the action when selected from the main VDR menu.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: plugin.c 1.24 2006/10/14 09:49:16 kls Exp $ * $Id: plugin.c 1.25 2007/02/24 13:44:23 kls Exp $
*/ */
#include "plugin.h" #include "plugin.h"
@ -80,6 +80,11 @@ cString cPlugin::Active(void)
return NULL; return NULL;
} }
time_t cPlugin::WakeupTime(void)
{
return 0;
}
const char *cPlugin::MainMenuEntry(void) const char *cPlugin::MainMenuEntry(void)
{ {
return NULL; return NULL;
@ -403,6 +408,26 @@ bool cPluginManager::Active(const char *Prompt)
return false; return false;
} }
cPlugin *cPluginManager::GetNextWakeupPlugin(void)
{
cPlugin *NextPlugin = NULL;
if (pluginManager) {
time_t Now = time(NULL);
time_t Next = 0;
for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
cPlugin *p = dll->Plugin();
if (p) {
time_t t = p->WakeupTime();
if (t > Now && (!Next || t < Next)) {
Next = t;
NextPlugin = p;
}
}
}
}
return NextPlugin;
}
bool cPluginManager::HasPlugins(void) bool cPluginManager::HasPlugins(void)
{ {
return pluginManager && pluginManager->dlls.Count(); return pluginManager && pluginManager->dlls.Count();

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: plugin.h 1.13 2006/04/17 09:18:16 kls Exp $ * $Id: plugin.h 1.14 2007/02/24 13:45:28 kls Exp $
*/ */
#ifndef __PLUGIN_H #ifndef __PLUGIN_H
@ -41,6 +41,7 @@ public:
virtual void Housekeeping(void); virtual void Housekeeping(void);
virtual void MainThreadHook(void); virtual void MainThreadHook(void);
virtual cString Active(void); virtual cString Active(void);
virtual time_t WakeupTime(void);
virtual const char *MainMenuEntry(void); virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void); virtual cOsdObject *MainMenuAction(void);
@ -93,6 +94,7 @@ public:
void Housekeeping(void); void Housekeeping(void);
void MainThreadHook(void); void MainThreadHook(void);
static bool Active(const char *Prompt = NULL); static bool Active(const char *Prompt = NULL);
static cPlugin *GetNextWakeupPlugin(void);
static bool HasPlugins(void); static bool HasPlugins(void);
static cPlugin *GetPlugin(int Index); static cPlugin *GetPlugin(int Index);
static cPlugin *GetPlugin(const char *Name); static cPlugin *GetPlugin(const char *Name);

View File

@ -4,13 +4,14 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recorder.c 1.18 2007/01/07 14:43:09 kls Exp $ * $Id: recorder.c 1.19 2007/02/24 16:36:24 kls Exp $
*/ */
#include "recorder.h" #include "recorder.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include "shutdown.h"
#define RECORDERBUFSIZE MEGABYTE(5) #define RECORDERBUFSIZE MEGABYTE(5)
@ -117,7 +118,7 @@ void cFileWriter::Action(void)
} }
else if (time(NULL) - t > MAXBROKENTIMEOUT) { else if (time(NULL) - t > MAXBROKENTIMEOUT) {
esyslog("ERROR: video data stream broken"); esyslog("ERROR: video data stream broken");
cThread::EmergencyExit(true); ShutdownHandler.RequestEmergencyExit();
t = time(NULL); t = time(NULL);
} }
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remote.c 1.55 2006/12/02 11:12:42 kls Exp $ * $Id: remote.c 1.56 2007/02/24 13:23:12 kls Exp $
*/ */
#include "remote.h" #include "remote.h"
@ -31,6 +31,7 @@ cMutex cRemote::mutex;
cCondVar cRemote::keyPressed; cCondVar cRemote::keyPressed;
const char *cRemote::keyMacroPlugin = NULL; const char *cRemote::keyMacroPlugin = NULL;
const char *cRemote::callPlugin = NULL; const char *cRemote::callPlugin = NULL;
time_t cRemote::lastActivity = 0;
cRemote::cRemote(const char *Name) cRemote::cRemote(const char *Name)
{ {
@ -183,6 +184,7 @@ eKeys cRemote::Get(int WaitMs, char **UnknownCode)
out = 0; out = 0;
if ((k & k_Repeat) != 0) if ((k & k_Repeat) != 0)
repeatTimeout.Set(REPEATTIMEOUT); repeatTimeout.Set(REPEATTIMEOUT);
lastActivity = time(NULL);
return k; return k;
} }
else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs) && repeatTimeout.TimedOut()) else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs) && repeatTimeout.TimedOut())

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remote.h 1.38 2006/12/02 11:12:49 kls Exp $ * $Id: remote.h 1.39 2007/02/24 15:53:00 kls Exp $
*/ */
#ifndef __REMOTE_H #ifndef __REMOTE_H
@ -28,6 +28,7 @@ private:
static char *unknownCode; static char *unknownCode;
static cMutex mutex; static cMutex mutex;
static cCondVar keyPressed; static cCondVar keyPressed;
static time_t lastActivity;
static const char *keyMacroPlugin; static const char *keyMacroPlugin;
static const char *callPlugin; static const char *callPlugin;
char *name; char *name;
@ -61,6 +62,8 @@ public:
///< plugin name will be reset to NULL by this call. ///< plugin name will be reset to NULL by this call.
static bool HasKeys(void); static bool HasKeys(void);
static eKeys Get(int WaitMs = 1000, char **UnknownCode = NULL); static eKeys Get(int WaitMs = 1000, char **UnknownCode = NULL);
static time_t LastActivity(void) { return lastActivity; }
///< Absolute time when last key was delivered by Get().
}; };
class cRemotes : public cList<cRemote> {}; class cRemotes : public cList<cRemote> {};

View File

@ -11,13 +11,13 @@
* The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>, * The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>,
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de. * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
* *
* $Id: remux.c 1.57 2006/12/01 14:46:25 kls Exp $ * $Id: remux.c 1.58 2007/02/24 16:36:10 kls Exp $
*/ */
#include "remux.h" #include "remux.h"
#include <stdlib.h> #include <stdlib.h>
#include "channels.h" #include "channels.h"
#include "thread.h" #include "shutdown.h"
#include "tools.h" #include "tools.h"
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader) ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
@ -2011,7 +2011,7 @@ int cRemux::Put(const uchar *Data, int Count)
esyslog("ERROR: no useful data seen within %d byte of video stream", skipped); esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
skipped = -1; skipped = -1;
if (exitOnFailure) if (exitOnFailure)
cThread::EmergencyExit(true); ShutdownHandler.RequestEmergencyExit();
} }
else else
skipped += used; skipped += used;
@ -2059,7 +2059,7 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
if (pt < I_FRAME || B_FRAME < pt) { if (pt < I_FRAME || B_FRAME < pt) {
esyslog("ERROR: unknown picture type '%d'", pt); esyslog("ERROR: unknown picture type '%d'", pt);
if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure) if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
cThread::EmergencyExit(true); ShutdownHandler.RequestEmergencyExit();
} }
else if (!synced) { else if (!synced) {
if (pt == I_FRAME) { if (pt == I_FRAME) {

250
shutdown.c Normal file
View File

@ -0,0 +1,250 @@
/*
* 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 <udo_richter@gmx.de>.
*
* $Id: shutdown.c 1.1 2007/02/24 17:24:11 kls Exp $
*/
#include "shutdown.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "channels.h"
#include "config.h"
#include "cutter.h"
#include "i18n.h"
#include "interface.h"
#include "menu.h"
#include "plugin.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;
char time[10];
snprintf(time, sizeof(time), "%d:%d0", counter > 0 ? counter / 6 : 0, counter > 0 ? counter % 6 : 0);
cString Message = cString::sprintf(message, time);
Skins.Message(mtStatus, Message);
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)
{
esyslog("initiating emergency exit");
emergencyExitRequested = true;
Exit(1);
}
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
SetUserInactive();
}
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 %ld %ld %d \"%s\" %d", shutdownCommand, WakeupTime, Delta, Channel, *strescape(File, "\"$"), UserShutdown);
isyslog("executing '%s'", *cmd);
if (SystemExec(cmd, true) == 0)
Setup.NextWakeupTime = WakeupTime; // Remember this wakeup time for comparison on reboot
}
void cShutdownHandler::SetUserInactiveTimeout(int Seconds, bool Force)
{
if (!Setup.MinUserInactivity && !Force) {
activeTimeout = 0;
return;
}
if (Seconds < 0)
Seconds = Setup.MinUserInactivity * 60;
activeTimeout = time(NULL) + Seconds;
}
bool cShutdownHandler::ConfirmShutdown(bool Interactive)
{
if (!shutdownCommand) {
if (Interactive)
Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
return false;
}
if (cCutter::Active()) {
if (!Interactive || !Interface->Confirm(tr("Editing - shut down anyway?")))
return false;
}
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 %ld minutes, shut down anyway?"), 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 %ld min, continue?"), Plugin->Name(), Delta / 60);
if (!Interface->Confirm(buf))
return false;
}
return true;
}
bool cShutdownHandler::ConfirmRestart(bool Interactive)
{
if (cCutter::Active()) {
if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?")))
return false;
}
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)
{
time_t Now = time(NULL);
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;
}

112
shutdown.h Normal file
View File

@ -0,0 +1,112 @@
/*
* shutdown.h: 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 <udo_richter@gmx.de>.
*
* $Id: shutdown.h 1.1 2007/02/24 17:23:59 kls Exp $
*/
#ifndef __SHUTDOWN_H
#define __SHUTDOWN_H
#include <time.h>
class cCountdown {
private:
time_t timeout; ///< 5-minute countdown timer
int counter; ///< last shown time in 10s units
bool timedOut; ///< countdown did run down to 0 and was not canceled
const char *message; ///< message to display, %s is placeholder for time
public:
cCountdown(void);
void Start(const char *Message, int Seconds);
///< Start the 5 minute shutdown warning countdown.
void Cancel(void);
///< Cancel the 5 minute shutdown warning countdown.
bool Done(void);
///< Check if countdown timer has run out without canceling.
operator bool(void) const { return timeout != 0; }
///< Check if countdown is running.
bool Update(void);
///< Update status display of the countdown.
///< Returns true on actual update.
};
class cShutdownHandler {
private:
time_t activeTimeout;
///< Time when VDR will become non-interactive. 0 means never.
time_t retry;
///< Time for retrying the shutdown.
char *shutdownCommand;
///< Command for shutting down VDR.
int exitCode;
///< Exit code, if VDR exit was requested, or -1 if not requested.
bool emergencyExitRequested;
///< The requested exit is an emergency exit.
public:
cCountdown countdown;
cShutdownHandler(void);
~cShutdownHandler();
void Exit(int ExitCode) { exitCode = ExitCode; }
///< Set VDR exit code and initiate end of VDR main loop.
///< This will exit VDR without any confirmation.
bool DoExit(void) { return exitCode >= 0; }
///< Check if an exit code was set, and VDR should exit.
int GetExitCode(void) { return exitCode >= 0 ? exitCode : 0; }
///< Get the currently set exit code of VDR.
bool EmergencyExitRequested(void) { return emergencyExitRequested; }
///< Returns true if an emergency exit was requested.
void RequestEmergencyExit(void);
///< Requests an emergency exit of the VDR main loop.
void CheckManualStart(int ManualStart);
///< Check whether the next timer is in ManualStart time window.
///< If yes, assume non-interactive use.
void SetShutdownCommand(const char *ShutdownCommand);
///< Set the command string for shutdown command.
void CallShutdownCommand(time_t WakeupTime, int Channel, const char *File, bool UserShutdown);
///< Call the shutdown command with the given parameters.
bool IsUserInactive(time_t AtTime = 0) { return activeTimeout && activeTimeout <= (AtTime ? AtTime : time(NULL)); }
///< Check whether VDR is in interactive mode or non-interactive mode (waiting for shutdown).
///< AtTime checks whether VDR will probably be inactive at that time.
time_t GetUserInactiveTime(void) { return activeTimeout; }
///< Time when user will become non-inactive, or 0 if never.
void SetUserInactiveTimeout(int Seconds = -1, bool Force = false);
///< Set the time when VDR will switch into non-interactive mode or power down.
///< -1 means Setup.MinUserInactivity in the future.
///< Otherwise, seconds in the future.
///< If MinUserInactivity = 0 and Force = false, Seconds is ignored and VDR will
///< stay interactive forever.
void SetUserInactive(void) { SetUserInactiveTimeout(0, true); }
///< Set VDR manually into non-interactive mode.
bool Retry(time_t AtTime = 0) { return retry <= (AtTime ? AtTime : time(NULL)); }
///< Check whether its time to re-try the shutdown.
///< AtTime checks whether VDR will probably be inactive at that time.
time_t GetRetry(void) { return retry; }
///< Time when shutdown retry block ends.
void SetRetry(int Seconds) { retry = time(NULL) + Seconds; }
///< Set shutdown retry so that VDR will not try to automatically shut down
///< within Seconds.
bool ConfirmShutdown(bool Ask);
///< Check for background activity that blocks shutdown.
///< Returns immediately and without user interaction if Ask = false.
///< Asks for confirmation if Ask = true.
///< Returns true if ready for shutdown.
bool ConfirmRestart(bool Ask);
///< Check for background activity that blocks restart.
///< Returns immediately and without user interaction if Ask = false.
///< Asks for confirmation if Ask = true.
///< Returns true if ready for restart.
bool DoShutdown(bool Force);
///< Call the shutdown script with data of the next pending timer.
///< Fails if Force = false and a timer is running or within MinEventTimeout.
///< Always calls shutdown on Force = true.
///< Returns true on success.
};
extern cShutdownHandler ShutdownHandler;
#endif

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: thread.c 1.59 2007/01/07 14:44:22 kls Exp $ * $Id: thread.c 1.60 2007/02/24 16:13:33 kls Exp $
*/ */
#include "thread.h" #include "thread.h"
@ -200,7 +200,6 @@ void cMutex::Unlock(void)
// --- cThread --------------------------------------------------------------- // --- cThread ---------------------------------------------------------------
tThreadId cThread::mainThreadId = 0; tThreadId cThread::mainThreadId = 0;
bool cThread::emergencyExitRequested = false;
cThread::cThread(const char *Description) cThread::cThread(const char *Description)
{ {
@ -320,14 +319,6 @@ void cThread::Cancel(int WaitSeconds)
} }
} }
bool cThread::EmergencyExit(bool Request)
{
if (!Request)
return emergencyExitRequested;
esyslog("initiating emergency exit");
return emergencyExitRequested = true; // yes, it's an assignment, not a comparison!
}
tThreadId cThread::ThreadId(void) tThreadId cThread::ThreadId(void)
{ {
return syscall(__NR_gettid); return syscall(__NR_gettid);
@ -505,7 +496,7 @@ int cPipe::Close(void)
// --- SystemExec ------------------------------------------------------------ // --- SystemExec ------------------------------------------------------------
int SystemExec(const char *Command) int SystemExec(const char *Command, bool Detached)
{ {
pid_t pid; pid_t pid;
@ -515,14 +506,24 @@ int SystemExec(const char *Command)
} }
if (pid > 0) { // parent process if (pid > 0) { // parent process
int status; int status = 0;
if (waitpid(pid, &status, 0) < 0) { if (!Detached && waitpid(pid, &status, 0) < 0) {
LOG_ERROR; LOG_ERROR;
return -1; return -1;
} }
return status; return status;
} }
else { // child process else { // child process
if (Detached) {
// Start a new session
pid_t sid = setsid();
if (sid < 0)
LOG_ERROR;
// close STDIN and re-open as /dev/null
int devnull = open("/dev/null", O_RDONLY);
if (devnull < 0 || dup2(devnull, 0) < 0)
LOG_ERROR;
}
int MaxPossibleFileDescriptors = getdtablesize(); int MaxPossibleFileDescriptors = getdtablesize();
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++) for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
close(i); //close all dup'ed filedescriptors close(i); //close all dup'ed filedescriptors

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: thread.h 1.38 2007/01/07 14:44:38 kls Exp $ * $Id: thread.h 1.39 2007/02/24 16:13:28 kls Exp $
*/ */
#ifndef __THREAD_H #ifndef __THREAD_H
@ -84,7 +84,6 @@ private:
cMutex mutex; cMutex mutex;
char *description; char *description;
static tThreadId mainThreadId; static tThreadId mainThreadId;
static bool emergencyExitRequested;
static void *StartThread(cThread *Thread); static void *StartThread(cThread *Thread);
protected: protected:
void SetPriority(int Priority); void SetPriority(int Priority);
@ -118,7 +117,6 @@ public:
///< If the thread is already running, nothing happens. ///< If the thread is already running, nothing happens.
bool Active(void); bool Active(void);
///< Checks whether the thread is still alive. ///< Checks whether the thread is still alive.
static bool EmergencyExit(bool Request = false);
static tThreadId ThreadId(void); static tThreadId ThreadId(void);
static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; } static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
static void SetMainThreadId(void); static void SetMainThreadId(void);
@ -175,7 +173,9 @@ public:
// SystemExec() implements a 'system()' call that closes all unnecessary file // SystemExec() implements a 'system()' call that closes all unnecessary file
// descriptors in the child process. // descriptors in the child process.
// With Detached=true, calls command in background and in a separate session,
// with stdin connected to /dev/null.
int SystemExec(const char *Command); int SystemExec(const char *Command, bool Detached = false);
#endif //__THREAD_H #endif //__THREAD_H

10
vdr.1
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.1 1.28 2007/01/07 14:03:56 kls Exp $ .\" $Id: vdr.1 1.29 2007/02/24 17:40:20 kls Exp $
.\" .\"
.TH vdr 1 "07 Jan 2007" "1.4.5" "Video Disk Recorder" .TH vdr 1 "07 Jan 2007" "1.4.5" "Video Disk Recorder"
.SH NAME .SH NAME
@ -153,6 +153,14 @@ Print version information and exit.
.BI \-w\ sec ,\ \-\-watchdog= sec .BI \-w\ sec ,\ \-\-watchdog= sec
Activate the watchdog timer with a timeout of \fIsec\fR seconds. Activate the watchdog timer with a timeout of \fIsec\fR seconds.
A value of \fB0\fR (default) disables the watchdog. A value of \fB0\fR (default) disables the watchdog.
.SH SIGNALS
.TP
.B SIGINT, SIGTERM
Program exits with status 0.
.TP
.B SIGHUP
Program exits with status 1. This can be used to force a reload, for example
if an update has been installed.
.SH EXIT STATUS .SH EXIT STATUS
.TP .TP
.B 0 .B 0

201
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.cadsoft.de/vdr * The project's page is at http://www.cadsoft.de/vdr
* *
* $Id: vdr.c 1.283 2007/01/07 14:46:14 kls Exp $ * $Id: vdr.c 1.284 2007/02/25 10:56:29 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -54,6 +54,7 @@
#include "plugin.h" #include "plugin.h"
#include "rcu.h" #include "rcu.h"
#include "recording.h" #include "recording.h"
#include "shutdown.h"
#include "skinclassic.h" #include "skinclassic.h"
#include "skinsttng.h" #include "skinsttng.h"
#include "sources.h" #include "sources.h"
@ -66,20 +67,23 @@
#define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings #define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
#define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown #define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown
#define SHUTDOWNRETRY 360 // seconds before trying again to shut down
#define SHUTDOWNFORCEPROMPT 5 // seconds to wait in user prompt to allow forcing shutdown
#define SHUTDOWNCANCELROMPT 5 // seconds to wait in user prompt to allow canceling shutdown
#define RESTARTCANCELPROMPT 5 // seconds to wait in user prompt before restarting on SIGHUP
#define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start #define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start
#define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications #define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
#define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready #define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
#define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed #define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
#define SHUTDOWNRETRY 300 // seconds before trying again to shut down
#define TIMERCHECKDELTA 10 // seconds between checks for timers that need to see their channel #define TIMERCHECKDELTA 10 // seconds between checks for timers that need to see their channel
#define TIMERDEVICETIMEOUT 8 // seconds before a device used for timer check may be reused #define TIMERDEVICETIMEOUT 8 // seconds before a device used for timer check may be reused
#define TIMERLOOKAHEADTIME 60 // seconds before a non-VPS timer starts and the channel is switched if possible #define TIMERLOOKAHEADTIME 60 // seconds before a non-VPS timer starts and the channel is switched if possible
#define VPSLOOKAHEADTIME 24 // hours within which VPS timers will make sure their events are up to date #define VPSLOOKAHEADTIME 24 // hours within which VPS timers will make sure their events are up to date
#define VPSUPTODATETIME 3600 // seconds before the event or schedule of a VPS timer needs to be refreshed #define VPSUPTODATETIME 3600 // seconds before the event or schedule of a VPS timer needs to be refreshed
#define EXIT(v) { ExitCode = (v); goto Exit; } #define EXIT(v) { ShutdownHandler.Exit(v); goto Exit; }
static int Interrupted = 0; static int LastSignal = 0;
static bool SetUser(const char *UserName) static bool SetUser(const char *UserName)
{ {
@ -138,9 +142,17 @@ static bool SetKeepCaps(bool On)
static void SignalHandler(int signum) static void SignalHandler(int signum)
{ {
if (signum != SIGPIPE) { isyslog("caught signal %d", signum);
Interrupted = signum; switch (signum) {
case SIGPIPE:
break;
case SIGHUP:
LastSignal = signum;
break;
default:
LastSignal = signum;
Interface->Interrupt(); Interface->Interrupt();
ShutdownHandler.Exit(0);
} }
signal(signum, SignalHandler); signal(signum, SignalHandler);
} }
@ -184,7 +196,6 @@ int main(int argc, char *argv[])
bool MuteAudio = false; bool MuteAudio = false;
int WatchdogTimeout = DEFAULTWATCHDOG; int WatchdogTimeout = DEFAULTWATCHDOG;
const char *Terminal = NULL; const char *Terminal = NULL;
const char *Shutdown = NULL;
bool UseKbd = true; bool UseKbd = true;
const char *LircDevice = NULL; const char *LircDevice = NULL;
@ -205,7 +216,6 @@ int main(int argc, char *argv[])
#endif #endif
cPluginManager PluginManager(DEFAULTPLUGINDIR); cPluginManager PluginManager(DEFAULTPLUGINDIR);
int ExitCode = 0;
static struct option long_options[] = { static struct option long_options[] = {
{ "audio", required_argument, NULL, 'a' }, { "audio", required_argument, NULL, 'a' },
@ -313,7 +323,7 @@ int main(int argc, char *argv[])
break; break;
case 'r': cRecordingUserCommand::SetCommand(optarg); case 'r': cRecordingUserCommand::SetCommand(optarg);
break; break;
case 's': Shutdown = optarg; case 's': ShutdownHandler.SetShutdownCommand(optarg);
break; break;
case 't': Terminal = optarg; case 't': Terminal = optarg;
if (access(Terminal, R_OK | W_OK) < 0) { if (access(Terminal, R_OK | W_OK) < 0) {
@ -498,10 +508,7 @@ int main(int argc, char *argv[])
int PreviousChannel[2] = { 1, 1 }; int PreviousChannel[2] = { 1, 1 };
int PreviousChannelIndex = 0; int PreviousChannelIndex = 0;
time_t LastChannelChanged = time(NULL); time_t LastChannelChanged = time(NULL);
time_t LastActivity = 0;
int MaxLatencyTime = 0; int MaxLatencyTime = 0;
bool ForceShutdown = false;
bool UserShutdown = false;
bool InhibitEpgScan = false; bool InhibitEpgScan = false;
bool IsInfoMenu = false; bool IsInfoMenu = false;
cSkin *CurrentSkin = NULL; cSkin *CurrentSkin = NULL;
@ -596,6 +603,10 @@ int main(int argc, char *argv[])
} }
} }
// Check for timers in automatic start time window:
ShutdownHandler.CheckManualStart(MANUALSTART);
// User interface: // User interface:
Interface = new cInterface(SVDRPport); Interface = new cInterface(SVDRPport);
@ -668,12 +679,7 @@ int main(int argc, char *argv[])
#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL) #define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
while (!Interrupted) { while (!ShutdownHandler.DoExit()) {
// Handle emergency exits:
if (cThread::EmergencyExit()) {
esyslog("emergency exit requested - shutting down");
break;
}
#ifdef DEBUGRINGBUFFERS #ifdef DEBUGRINGBUFFERS
cRingBufferLinear::PrintDebugRBL(); cRingBufferLinear::PrintDebugRBL();
#endif #endif
@ -856,9 +862,13 @@ int main(int argc, char *argv[])
// User Input: // User Input:
cOsdObject *Interact = Menu ? Menu : cControl::Control(); cOsdObject *Interact = Menu ? Menu : cControl::Control();
eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse()); eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
if (NORMALKEY(key) != kNone) { if (ISREALKEY(key)) {
EITScanner.Activity(); EITScanner.Activity();
LastActivity = time(NULL); // Cancel shutdown countdown:
if (ShutdownHandler.countdown)
ShutdownHandler.countdown.Cancel();
// Set user active for MinUserInactivity time in the future:
ShutdownHandler.SetUserInactiveTimeout();
} }
// Keys that must work independent of any interactive mode: // Keys that must work independent of any interactive mode:
switch (key) { switch (key) {
@ -996,37 +1006,32 @@ int main(int argc, char *argv[])
} }
break; break;
// Power off: // Power off:
case kPower: { case kPower:
isyslog("Power button pressed"); isyslog("Power button pressed");
DELETE_MENU; DELETE_MENU;
if (!Shutdown) { // Check for activity, request power button again if active:
Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!")); if (!ShutdownHandler.ConfirmShutdown(false) && Skins.Message(mtWarning, tr("VDR will shut down later - press Power to force"), SHUTDOWNFORCEPROMPT) != kPower) {
// Not pressed power - set VDR to be non-interactive and power down later:
ShutdownHandler.SetUserInactive();
break; break;
} }
LastActivity = 1; // not 0, see below! // No activity or power button pressed twice - ask for confirmation:
UserShutdown = true; if (!ShutdownHandler.ConfirmShutdown(true)) {
if (cRecordControls::Active()) { // Non-confirmed background activity - set VDR to be non-interactive and power down later:
if (!Interface->Confirm(tr("Recording - shut down anyway?"))) ShutdownHandler.SetUserInactive();
break; break;
} }
if (cPluginManager::Active(tr("shut down anyway?"))) // Ask the final question:
if (!Interface->Confirm(tr("Press any key to cancel shutdown"), SHUTDOWNCANCELROMPT, true))
// If final question was canceled, continue to be active:
break; break;
if (!cRecordControls::Active()) { // Ok, now call the shutdown script:
cTimer *timer = Timers.GetNextActiveTimer(); ShutdownHandler.DoShutdown(true);
time_t Next = timer ? timer->StartTime() : 0; // Set VDR to be non-interactive and power down again later:
time_t Delta = timer ? Next - time(NULL) : 0; ShutdownHandler.SetUserInactive();
if (Next && Delta <= Setup.MinEventTimeout * 60) { // Do not attempt to automatically shut down for a while:
char *buf; ShutdownHandler.SetRetry(SHUTDOWNRETRY);
asprintf(&buf, tr("Recording in %ld minutes, shut down anyway?"), Delta / 60);
bool confirm = Interface->Confirm(buf);
free(buf);
if (!confirm)
break; break;
}
}
ForceShutdown = true;
break;
}
default: break; default: break;
} }
Interact = Menu ? Menu : cControl::Control(); // might have been closed in the mean time Interact = Menu ? Menu : cControl::Control(); // might have been closed in the mean time
@ -1041,7 +1046,7 @@ int main(int argc, char *argv[])
continue; continue;
} }
} }
else if (time(NULL) - LastActivity > MENUTIMEOUT) else if (time(NULL) - cRemote::LastActivity() > MENUTIMEOUT)
state = osEnd; state = osEnd;
} }
switch (state) { switch (state) {
@ -1143,77 +1148,55 @@ int main(int argc, char *argv[])
Skins.Message(mtInfo, tr("Editing process finished")); Skins.Message(mtInfo, tr("Editing process finished"));
} }
} }
if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) {
time_t Now = time(NULL); // SIGHUP shall cause a restart:
if (Now - LastActivity > ACTIVITYTIMEOUT) { if (LastSignal == SIGHUP) {
if (ShutdownHandler.ConfirmRestart(true) && Interface->Confirm(tr("Press any key to cancel restart"), RESTARTCANCELPROMPT, true))
EXIT(1);
LastSignal = 0;
}
// Update the shutdown countdown:
if (ShutdownHandler.countdown && ShutdownHandler.countdown.Update()) {
if (!ShutdownHandler.ConfirmShutdown(false))
ShutdownHandler.countdown.Cancel();
}
if (!Interact && !cRecordControls::Active() && !cCutter::Active() && !Interface->HasSVDRPConnection() && cRemote::LastActivity() > ACTIVITYTIMEOUT) {
// Handle housekeeping tasks
// Shutdown: // Shutdown:
if (Shutdown && (Setup.MinUserInactivity || LastActivity == 1) && Now - LastActivity > Setup.MinUserInactivity * 60) { // Check whether VDR will be ready for shutdown in SHUTDOWNWAIT seconds:
cTimer *timer = Timers.GetNextActiveTimer(); time_t Soon = time(NULL) + SHUTDOWNWAIT;
time_t Next = timer ? timer->StartTime() : 0; if (ShutdownHandler.IsUserInactive(Soon) && ShutdownHandler.Retry(Soon) && !ShutdownHandler.countdown) {
time_t Delta = timer ? Next - Now : 0; if (ShutdownHandler.ConfirmShutdown(false))
if (!LastActivity) { // Time to shut down - start final countdown:
if (!timer || Delta > MANUALSTART) { ShutdownHandler.countdown.Start(tr("VDR will shut down in %s minutes"), SHUTDOWNWAIT); // the placeholder is really %s!
// Apparently the user started VDR manually // Dont try to shut down again for a while:
dsyslog("assuming manual start of VDR"); ShutdownHandler.SetRetry(SHUTDOWNRETRY);
LastActivity = Now;
continue; // don't run into the actual shutdown procedure below
}
else
LastActivity = 1;
}
if (timer && Delta < Setup.MinEventTimeout * 60 && ForceShutdown) {
Delta = Setup.MinEventTimeout * 60;
Next = Now + Delta;
timer = NULL;
dsyslog("reboot at %s", *TimeToString(Next));
}
if (!ForceShutdown && cPluginManager::Active()) {
LastActivity = Now - Setup.MinUserInactivity * 60 + SHUTDOWNRETRY; // try again later
continue;
}
if (!Next || Delta > Setup.MinEventTimeout * 60 || ForceShutdown) {
ForceShutdown = false;
if (timer)
dsyslog("next timer event at %s", *TimeToString(Next));
if (WatchdogTimeout > 0)
signal(SIGALRM, SIG_IGN);
if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) {
cControl::Shutdown();
int Channel = timer ? timer->Channel()->Number() : 0;
const char *File = timer ? timer->File() : "";
if (timer)
Delta = Next - time(NULL); // compensates for Confirm() timeout
char *cmd;
asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, *strescape(File, "\"$"), UserShutdown);
isyslog("executing '%s'", cmd);
SystemExec(cmd);
free(cmd);
LastActivity = time(NULL) - Setup.MinUserInactivity * 60 + SHUTDOWNRETRY; // try again later
}
else {
LastActivity = Now;
if (WatchdogTimeout > 0) {
alarm(WatchdogTimeout);
if (signal(SIGALRM, Watchdog) == SIG_IGN)
signal(SIGALRM, SIG_IGN);
}
}
UserShutdown = false;
continue; // skip the rest of the housekeeping for now
} }
// Countdown run down to 0?
if (ShutdownHandler.countdown.Done()) {
// Timed out, now do a final check:
if (ShutdownHandler.IsUserInactive() && ShutdownHandler.ConfirmShutdown(false))
ShutdownHandler.DoShutdown(false);
// Do this again a bit later:
ShutdownHandler.SetRetry(SHUTDOWNRETRY);
} }
// Disk housekeeping: // Disk housekeeping:
RemoveDeletedRecordings(); RemoveDeletedRecordings();
cSchedules::Cleanup(); cSchedules::Cleanup();
// Plugins housekeeping: // Plugins housekeeping:
PluginManager.Housekeeping(); PluginManager.Housekeeping();
} }
}
// Main thread hooks of plugins: // Main thread hooks of plugins:
PluginManager.MainThreadHook(); PluginManager.MainThreadHook();
} }
if (Interrupted)
isyslog("caught signal %d", Interrupted); if (ShutdownHandler.EmergencyExitRequested())
esyslog("emergency exit requested - shutting down");
Exit: Exit:
@ -1227,7 +1210,7 @@ Exit:
Remotes.Clear(); Remotes.Clear();
Audios.Clear(); Audios.Clear();
Skins.Clear(); Skins.Clear();
if (ExitCode != 2) { if (ShutdownHandler.GetExitCode() != 2) {
Setup.CurrentChannel = cDevice::CurrentChannel(); Setup.CurrentChannel = cDevice::CurrentChannel();
Setup.CurrentVolume = cDevice::CurrentVolume(); Setup.CurrentVolume = cDevice::CurrentVolume();
Setup.Save(); Setup.Save();
@ -1238,14 +1221,12 @@ Exit:
ReportEpgBugFixStats(); ReportEpgBugFixStats();
if (WatchdogTimeout > 0) if (WatchdogTimeout > 0)
dsyslog("max. latency time %d seconds", MaxLatencyTime); dsyslog("max. latency time %d seconds", MaxLatencyTime);
isyslog("exiting"); isyslog("exiting, exit code %d", ShutdownHandler.GetExitCode());
if (ShutdownHandler.EmergencyExitRequested())
esyslog("emergency exit!");
if (SysLogLevel > 0) if (SysLogLevel > 0)
closelog(); closelog();
if (HasStdin) if (HasStdin)
tcsetattr(STDIN_FILENO, TCSANOW, &savedTm); tcsetattr(STDIN_FILENO, TCSANOW, &savedTm);
if (cThread::EmergencyExit()) { return ShutdownHandler.GetExitCode();
esyslog("emergency exit!");
return 1;
}
return ExitCode;
} }