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:
parent
50b14be807
commit
ddb7f33567
@ -1485,6 +1485,10 @@ Udo Richter <udo_richter@gmx.de>
|
||||
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 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>
|
||||
for his help in keeping 'channels.conf.terr' up to date
|
||||
|
7
HISTORY
7
HISTORY
@ -5099,7 +5099,7 @@ Video Disk Recorder Revision History
|
||||
- Fixed handling error status in cDvbTuner::GetFrontendStatus() (thanks to
|
||||
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
|
||||
when tuning to an FTA channel, thus preserving the CAM resources even on budget
|
||||
@ -5110,3 +5110,8 @@ Video Disk Recorder Revision History
|
||||
Nissl).
|
||||
- Removed 'assert(0)' from cDvbSpuDecoder::setTime() (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
47
INSTALL
@ -160,30 +160,33 @@ 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 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'.
|
||||
In that case the program shall not set the hardware for automatic restart
|
||||
and only perform the system shutdown. A program that uses the second parameter
|
||||
to set the hardware for restart must therefore also check whether the first
|
||||
parameter is '0'.
|
||||
The first one is the time (in UTC) of the next timer event or plugin wakeup
|
||||
time (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. 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
|
||||
by the next timer (or 0 if no timer is present), and the fourth parameter
|
||||
contains the file name of the recording as defined in the timer (or an empty
|
||||
string if no timer is present). These can be used by the shutdown program to
|
||||
show that information on some display interface etc.
|
||||
If there are currently no timers active and there is no plugin wakeup
|
||||
time, both parameters will be '0'. In that case the program shall not set
|
||||
the hardware for automatic restart and only perform the system shutdown.
|
||||
A program that uses the second parameter to set the hardware for restart
|
||||
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.
|
||||
'0' means this is an automatic shutdown due to some timeout, while '1' means
|
||||
|
4
Makefile
4
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# 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:
|
||||
|
||||
@ -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\
|
||||
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\
|
||||
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\
|
||||
timers.o tools.o transfer.o vdr.o videodir.o
|
||||
|
||||
|
49
PLUGINS.html
49
PLUGINS.html
@ -6,7 +6,7 @@
|
||||
|
||||
<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>
|
||||
<center>
|
||||
Copyright © 2006 Klaus Schmidinger<br>
|
||||
@ -14,9 +14,12 @@ Copyright © 2006 Klaus Schmidinger<br>
|
||||
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
|
||||
</center>
|
||||
<p>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%>
|
||||
Important modifications introduced in version 1.5.0 are marked like this.
|
||||
<!--X1.5.0--></td></tr></table>
|
||||
<!--X1.5.1--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
Important modifications introduced in version 1.5.1 are marked like this.
|
||||
<!--X1.5.1--></td></tr></table>
|
||||
<p>
|
||||
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.
|
||||
@ -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="#Main thread hook">Main thread hook</a>
|
||||
<li><a href="#Activity">Activity</a>
|
||||
<!--X1.5.1--><table width=100%><tr><td bgcolor=#FF0000> </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="#The Setup menu">The Setup menu</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="#Audio">Audio</a>
|
||||
<li><a href="#Remote Control">Remote Control</a>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%>
|
||||
<li><a href="#Conditional Access">Conditional Access</a>
|
||||
<!--X1.5.0--></td></tr></table>
|
||||
</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
|
||||
further plugins will be queried and no shutdown will be done.
|
||||
|
||||
<!--X1.5.1--><table width=100%><tr><td bgcolor=#FF0000> </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 <= 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>
|
||||
|
||||
<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.
|
||||
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||
<!--X1.5.0--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%>
|
||||
<a name="Conditional Access"><hr><h2>Conditional Access</h2>
|
||||
|
||||
<center><i><b>Members only!</b></i></center><p>
|
||||
|
5
config.c
5
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.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"
|
||||
@ -267,6 +267,7 @@ cSetup::cSetup(void)
|
||||
SplitEditedFiles = 0;
|
||||
MinEventTimeout = 30;
|
||||
MinUserInactivity = 300;
|
||||
NextWakeupTime = 0;
|
||||
MultiSpeedMode = 0;
|
||||
ShowReplayMode = 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, "MinEventTimeout")) MinEventTimeout = 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, "ShowReplayMode")) ShowReplayMode = atoi(Value);
|
||||
else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value);
|
||||
@ -496,6 +498,7 @@ bool cSetup::Save(void)
|
||||
Store("SplitEditedFiles", SplitEditedFiles);
|
||||
Store("MinEventTimeout", MinEventTimeout);
|
||||
Store("MinUserInactivity", MinUserInactivity);
|
||||
Store("NextWakeupTime", NextWakeupTime);
|
||||
Store("MultiSpeedMode", MultiSpeedMode);
|
||||
Store("ShowReplayMode", ShowReplayMode);
|
||||
Store("ResumeID", ResumeID);
|
||||
|
3
config.h
3
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.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
|
||||
@ -244,6 +244,7 @@ public:
|
||||
int MaxVideoFileSize;
|
||||
int SplitEditedFiles;
|
||||
int MinEventTimeout, MinUserInactivity;
|
||||
time_t NextWakeupTime;
|
||||
int MultiSpeedMode;
|
||||
int ShowReplayMode;
|
||||
int ResumeID;
|
||||
|
134
i18n.c
134
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.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:
|
||||
*
|
||||
@ -1341,6 +1341,28 @@ const tI18nPhrase Phrases[] = {
|
||||
"Optagelse igang - genstart alligevel?",
|
||||
"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?",
|
||||
"Aufnahme läuft - trotzdem ausschalten?",
|
||||
"Snemanje - zares izklopi?",
|
||||
@ -1407,6 +1429,116 @@ const tI18nPhrase Phrases[] = {
|
||||
"Tryk vilkårlig tast for at annullere sluk",
|
||||
"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:
|
||||
{ "Name",
|
||||
"Name",
|
||||
|
3
keys.h
3
keys.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -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 NORMALKEY(k) (eKeys((k) & ~k_Repeat))
|
||||
#define ISMODELESSKEY(k) (RAWKEY(k) > k9)
|
||||
#define ISREALKEY(k) (k != kNone && k != k_Plugin)
|
||||
|
||||
#define BASICKEY(k) (eKeys((k) & 0xFFFF))
|
||||
#define KBDKEY(k) (eKeys(((k) << 16) | kKbd))
|
||||
|
11
menu.c
11
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.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"
|
||||
@ -22,6 +22,7 @@
|
||||
#include "plugin.h"
|
||||
#include "recording.h"
|
||||
#include "remote.h"
|
||||
#include "shutdown.h"
|
||||
#include "sources.h"
|
||||
#include "status.h"
|
||||
#include "themes.h"
|
||||
@ -2770,10 +2771,8 @@ void cMenuSetup::Set(void)
|
||||
|
||||
eOSState cMenuSetup::Restart(void)
|
||||
{
|
||||
if (Interface->Confirm(tr("Really restart?"))
|
||||
&& (!cRecordControls::Active() || Interface->Confirm(tr("Recording - restart anyway?")))
|
||||
&& !cPluginManager::Active(tr("restart anyway?"))) {
|
||||
cThread::EmergencyExit(true);
|
||||
if (Interface->Confirm(tr("Really restart?")) && ShutdownHandler.ConfirmRestart(true)) {
|
||||
ShutdownHandler.Exit(1);
|
||||
return osEnd;
|
||||
}
|
||||
return osContinue;
|
||||
@ -3699,7 +3698,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
|
||||
if (device) {
|
||||
dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
|
||||
if (!device->SwitchChannel(channel, false)) {
|
||||
cThread::EmergencyExit(true);
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
return false;
|
||||
}
|
||||
if (!Timer || Timer->Matches()) {
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# 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";
|
||||
|
||||
@ -169,6 +169,7 @@ public:
|
||||
virtual void Housekeeping(void);
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cString Active(void);
|
||||
virtual time_t WakeupTime(void);
|
||||
virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
|
||||
virtual cOsdObject *MainMenuAction(void);
|
||||
virtual cMenuSetupPage *SetupMenu(void);
|
||||
@ -236,6 +237,12 @@ cString cPlugin${PLUGIN_CLASS}::Active(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
time_t cPlugin${PLUGIN_CLASS}::WakeupTime(void)
|
||||
{
|
||||
// Return custom wakeup time for shutdown script
|
||||
return 0;
|
||||
}
|
||||
|
||||
cOsdObject *cPlugin${PLUGIN_CLASS}::MainMenuAction(void)
|
||||
{
|
||||
// Perform the action when selected from the main VDR menu.
|
||||
|
27
plugin.c
27
plugin.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -80,6 +80,11 @@ cString cPlugin::Active(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
time_t cPlugin::WakeupTime(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *cPlugin::MainMenuEntry(void)
|
||||
{
|
||||
return NULL;
|
||||
@ -403,6 +408,26 @@ bool cPluginManager::Active(const char *Prompt)
|
||||
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)
|
||||
{
|
||||
return pluginManager && pluginManager->dlls.Count();
|
||||
|
4
plugin.h
4
plugin.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -41,6 +41,7 @@ public:
|
||||
virtual void Housekeeping(void);
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cString Active(void);
|
||||
virtual time_t WakeupTime(void);
|
||||
|
||||
virtual const char *MainMenuEntry(void);
|
||||
virtual cOsdObject *MainMenuAction(void);
|
||||
@ -93,6 +94,7 @@ public:
|
||||
void Housekeeping(void);
|
||||
void MainThreadHook(void);
|
||||
static bool Active(const char *Prompt = NULL);
|
||||
static cPlugin *GetNextWakeupPlugin(void);
|
||||
static bool HasPlugins(void);
|
||||
static cPlugin *GetPlugin(int Index);
|
||||
static cPlugin *GetPlugin(const char *Name);
|
||||
|
@ -4,13 +4,14 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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 <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "shutdown.h"
|
||||
|
||||
#define RECORDERBUFSIZE MEGABYTE(5)
|
||||
|
||||
@ -117,7 +118,7 @@ void cFileWriter::Action(void)
|
||||
}
|
||||
else if (time(NULL) - t > MAXBROKENTIMEOUT) {
|
||||
esyslog("ERROR: video data stream broken");
|
||||
cThread::EmergencyExit(true);
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
t = time(NULL);
|
||||
}
|
||||
}
|
||||
|
4
remote.c
4
remote.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -31,6 +31,7 @@ cMutex cRemote::mutex;
|
||||
cCondVar cRemote::keyPressed;
|
||||
const char *cRemote::keyMacroPlugin = NULL;
|
||||
const char *cRemote::callPlugin = NULL;
|
||||
time_t cRemote::lastActivity = 0;
|
||||
|
||||
cRemote::cRemote(const char *Name)
|
||||
{
|
||||
@ -183,6 +184,7 @@ eKeys cRemote::Get(int WaitMs, char **UnknownCode)
|
||||
out = 0;
|
||||
if ((k & k_Repeat) != 0)
|
||||
repeatTimeout.Set(REPEATTIMEOUT);
|
||||
lastActivity = time(NULL);
|
||||
return k;
|
||||
}
|
||||
else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs) && repeatTimeout.TimedOut())
|
||||
|
5
remote.h
5
remote.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -28,6 +28,7 @@ private:
|
||||
static char *unknownCode;
|
||||
static cMutex mutex;
|
||||
static cCondVar keyPressed;
|
||||
static time_t lastActivity;
|
||||
static const char *keyMacroPlugin;
|
||||
static const char *callPlugin;
|
||||
char *name;
|
||||
@ -61,6 +62,8 @@ public:
|
||||
///< plugin name will be reset to NULL by this call.
|
||||
static bool HasKeys(void);
|
||||
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> {};
|
||||
|
8
remux.c
8
remux.c
@ -11,13 +11,13 @@
|
||||
* 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.
|
||||
*
|
||||
* $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 <stdlib.h>
|
||||
#include "channels.h"
|
||||
#include "thread.h"
|
||||
#include "shutdown.h"
|
||||
#include "tools.h"
|
||||
|
||||
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);
|
||||
skipped = -1;
|
||||
if (exitOnFailure)
|
||||
cThread::EmergencyExit(true);
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
}
|
||||
else
|
||||
skipped += used;
|
||||
@ -2059,7 +2059,7 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
|
||||
if (pt < I_FRAME || B_FRAME < pt) {
|
||||
esyslog("ERROR: unknown picture type '%d'", pt);
|
||||
if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
|
||||
cThread::EmergencyExit(true);
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
}
|
||||
else if (!synced) {
|
||||
if (pt == I_FRAME) {
|
||||
|
250
shutdown.c
Normal file
250
shutdown.c
Normal 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
112
shutdown.h
Normal 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
|
27
thread.c
27
thread.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -200,7 +200,6 @@ void cMutex::Unlock(void)
|
||||
// --- cThread ---------------------------------------------------------------
|
||||
|
||||
tThreadId cThread::mainThreadId = 0;
|
||||
bool cThread::emergencyExitRequested = false;
|
||||
|
||||
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)
|
||||
{
|
||||
return syscall(__NR_gettid);
|
||||
@ -505,7 +496,7 @@ int cPipe::Close(void)
|
||||
|
||||
// --- SystemExec ------------------------------------------------------------
|
||||
|
||||
int SystemExec(const char *Command)
|
||||
int SystemExec(const char *Command, bool Detached)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
@ -515,14 +506,24 @@ int SystemExec(const char *Command)
|
||||
}
|
||||
|
||||
if (pid > 0) { // parent process
|
||||
int status;
|
||||
if (waitpid(pid, &status, 0) < 0) {
|
||||
int status = 0;
|
||||
if (!Detached && waitpid(pid, &status, 0) < 0) {
|
||||
LOG_ERROR;
|
||||
return -1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
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();
|
||||
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
||||
close(i); //close all dup'ed filedescriptors
|
||||
|
8
thread.h
8
thread.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -84,7 +84,6 @@ private:
|
||||
cMutex mutex;
|
||||
char *description;
|
||||
static tThreadId mainThreadId;
|
||||
static bool emergencyExitRequested;
|
||||
static void *StartThread(cThread *Thread);
|
||||
protected:
|
||||
void SetPriority(int Priority);
|
||||
@ -118,7 +117,6 @@ public:
|
||||
///< If the thread is already running, nothing happens.
|
||||
bool Active(void);
|
||||
///< Checks whether the thread is still alive.
|
||||
static bool EmergencyExit(bool Request = false);
|
||||
static tThreadId ThreadId(void);
|
||||
static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
|
||||
static void SetMainThreadId(void);
|
||||
@ -175,7 +173,9 @@ public:
|
||||
|
||||
// SystemExec() implements a 'system()' call that closes all unnecessary file
|
||||
// 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
|
||||
|
10
vdr.1
10
vdr.1
@ -8,7 +8,7 @@
|
||||
.\" License as specified in the file COPYING that comes with the
|
||||
.\" 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"
|
||||
.SH NAME
|
||||
@ -153,6 +153,14 @@ Print version information and exit.
|
||||
.BI \-w\ sec ,\ \-\-watchdog= sec
|
||||
Activate the watchdog timer with a timeout of \fIsec\fR seconds.
|
||||
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
|
||||
.TP
|
||||
.B 0
|
||||
|
201
vdr.c
201
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* 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>
|
||||
@ -54,6 +54,7 @@
|
||||
#include "plugin.h"
|
||||
#include "rcu.h"
|
||||
#include "recording.h"
|
||||
#include "shutdown.h"
|
||||
#include "skinclassic.h"
|
||||
#include "skinsttng.h"
|
||||
#include "sources.h"
|
||||
@ -66,20 +67,23 @@
|
||||
#define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
|
||||
#define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
|
||||
#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 CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
|
||||
#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 SHUTDOWNRETRY 300 // seconds before trying again to shut down
|
||||
#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 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 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)
|
||||
{
|
||||
@ -138,9 +142,17 @@ static bool SetKeepCaps(bool On)
|
||||
|
||||
static void SignalHandler(int signum)
|
||||
{
|
||||
if (signum != SIGPIPE) {
|
||||
Interrupted = signum;
|
||||
isyslog("caught signal %d", signum);
|
||||
switch (signum) {
|
||||
case SIGPIPE:
|
||||
break;
|
||||
case SIGHUP:
|
||||
LastSignal = signum;
|
||||
break;
|
||||
default:
|
||||
LastSignal = signum;
|
||||
Interface->Interrupt();
|
||||
ShutdownHandler.Exit(0);
|
||||
}
|
||||
signal(signum, SignalHandler);
|
||||
}
|
||||
@ -184,7 +196,6 @@ int main(int argc, char *argv[])
|
||||
bool MuteAudio = false;
|
||||
int WatchdogTimeout = DEFAULTWATCHDOG;
|
||||
const char *Terminal = NULL;
|
||||
const char *Shutdown = NULL;
|
||||
|
||||
bool UseKbd = true;
|
||||
const char *LircDevice = NULL;
|
||||
@ -205,7 +216,6 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
cPluginManager PluginManager(DEFAULTPLUGINDIR);
|
||||
int ExitCode = 0;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{ "audio", required_argument, NULL, 'a' },
|
||||
@ -313,7 +323,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
case 'r': cRecordingUserCommand::SetCommand(optarg);
|
||||
break;
|
||||
case 's': Shutdown = optarg;
|
||||
case 's': ShutdownHandler.SetShutdownCommand(optarg);
|
||||
break;
|
||||
case 't': Terminal = optarg;
|
||||
if (access(Terminal, R_OK | W_OK) < 0) {
|
||||
@ -498,10 +508,7 @@ int main(int argc, char *argv[])
|
||||
int PreviousChannel[2] = { 1, 1 };
|
||||
int PreviousChannelIndex = 0;
|
||||
time_t LastChannelChanged = time(NULL);
|
||||
time_t LastActivity = 0;
|
||||
int MaxLatencyTime = 0;
|
||||
bool ForceShutdown = false;
|
||||
bool UserShutdown = false;
|
||||
bool InhibitEpgScan = false;
|
||||
bool IsInfoMenu = false;
|
||||
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:
|
||||
|
||||
Interface = new cInterface(SVDRPport);
|
||||
@ -668,12 +679,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
|
||||
|
||||
while (!Interrupted) {
|
||||
// Handle emergency exits:
|
||||
if (cThread::EmergencyExit()) {
|
||||
esyslog("emergency exit requested - shutting down");
|
||||
break;
|
||||
}
|
||||
while (!ShutdownHandler.DoExit()) {
|
||||
#ifdef DEBUGRINGBUFFERS
|
||||
cRingBufferLinear::PrintDebugRBL();
|
||||
#endif
|
||||
@ -856,9 +862,13 @@ int main(int argc, char *argv[])
|
||||
// User Input:
|
||||
cOsdObject *Interact = Menu ? Menu : cControl::Control();
|
||||
eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
|
||||
if (NORMALKEY(key) != kNone) {
|
||||
if (ISREALKEY(key)) {
|
||||
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:
|
||||
switch (key) {
|
||||
@ -996,37 +1006,32 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
// Power off:
|
||||
case kPower: {
|
||||
case kPower:
|
||||
isyslog("Power button pressed");
|
||||
DELETE_MENU;
|
||||
if (!Shutdown) {
|
||||
Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
|
||||
// Check for activity, request power button again if active:
|
||||
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;
|
||||
}
|
||||
LastActivity = 1; // not 0, see below!
|
||||
UserShutdown = true;
|
||||
if (cRecordControls::Active()) {
|
||||
if (!Interface->Confirm(tr("Recording - shut down anyway?")))
|
||||
// No activity or power button pressed twice - ask for confirmation:
|
||||
if (!ShutdownHandler.ConfirmShutdown(true)) {
|
||||
// Non-confirmed background activity - set VDR to be non-interactive and power down later:
|
||||
ShutdownHandler.SetUserInactive();
|
||||
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;
|
||||
if (!cRecordControls::Active()) {
|
||||
cTimer *timer = Timers.GetNextActiveTimer();
|
||||
time_t Next = timer ? timer->StartTime() : 0;
|
||||
time_t Delta = timer ? Next - time(NULL) : 0;
|
||||
if (Next && Delta <= Setup.MinEventTimeout * 60) {
|
||||
char *buf;
|
||||
asprintf(&buf, tr("Recording in %ld minutes, shut down anyway?"), Delta / 60);
|
||||
bool confirm = Interface->Confirm(buf);
|
||||
free(buf);
|
||||
if (!confirm)
|
||||
// Ok, now call the shutdown script:
|
||||
ShutdownHandler.DoShutdown(true);
|
||||
// Set VDR to be non-interactive and power down again later:
|
||||
ShutdownHandler.SetUserInactive();
|
||||
// Do not attempt to automatically shut down for a while:
|
||||
ShutdownHandler.SetRetry(SHUTDOWNRETRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ForceShutdown = true;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
Interact = Menu ? Menu : cControl::Control(); // might have been closed in the mean time
|
||||
@ -1041,7 +1046,7 @@ int main(int argc, char *argv[])
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (time(NULL) - LastActivity > MENUTIMEOUT)
|
||||
else if (time(NULL) - cRemote::LastActivity() > MENUTIMEOUT)
|
||||
state = osEnd;
|
||||
}
|
||||
switch (state) {
|
||||
@ -1143,77 +1148,55 @@ int main(int argc, char *argv[])
|
||||
Skins.Message(mtInfo, tr("Editing process finished"));
|
||||
}
|
||||
}
|
||||
if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) {
|
||||
time_t Now = time(NULL);
|
||||
if (Now - LastActivity > ACTIVITYTIMEOUT) {
|
||||
|
||||
// SIGHUP shall cause a restart:
|
||||
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:
|
||||
if (Shutdown && (Setup.MinUserInactivity || LastActivity == 1) && Now - LastActivity > Setup.MinUserInactivity * 60) {
|
||||
cTimer *timer = Timers.GetNextActiveTimer();
|
||||
time_t Next = timer ? timer->StartTime() : 0;
|
||||
time_t Delta = timer ? Next - Now : 0;
|
||||
if (!LastActivity) {
|
||||
if (!timer || Delta > MANUALSTART) {
|
||||
// Apparently the user started VDR manually
|
||||
dsyslog("assuming manual start of VDR");
|
||||
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
|
||||
// Check whether VDR will be ready for shutdown in SHUTDOWNWAIT seconds:
|
||||
time_t Soon = time(NULL) + SHUTDOWNWAIT;
|
||||
if (ShutdownHandler.IsUserInactive(Soon) && ShutdownHandler.Retry(Soon) && !ShutdownHandler.countdown) {
|
||||
if (ShutdownHandler.ConfirmShutdown(false))
|
||||
// Time to shut down - start final countdown:
|
||||
ShutdownHandler.countdown.Start(tr("VDR will shut down in %s minutes"), SHUTDOWNWAIT); // the placeholder is really %s!
|
||||
// Dont try to shut down again for a while:
|
||||
ShutdownHandler.SetRetry(SHUTDOWNRETRY);
|
||||
}
|
||||
// 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:
|
||||
RemoveDeletedRecordings();
|
||||
cSchedules::Cleanup();
|
||||
// Plugins housekeeping:
|
||||
PluginManager.Housekeeping();
|
||||
}
|
||||
}
|
||||
|
||||
// Main thread hooks of plugins:
|
||||
PluginManager.MainThreadHook();
|
||||
}
|
||||
if (Interrupted)
|
||||
isyslog("caught signal %d", Interrupted);
|
||||
|
||||
if (ShutdownHandler.EmergencyExitRequested())
|
||||
esyslog("emergency exit requested - shutting down");
|
||||
|
||||
Exit:
|
||||
|
||||
@ -1227,7 +1210,7 @@ Exit:
|
||||
Remotes.Clear();
|
||||
Audios.Clear();
|
||||
Skins.Clear();
|
||||
if (ExitCode != 2) {
|
||||
if (ShutdownHandler.GetExitCode() != 2) {
|
||||
Setup.CurrentChannel = cDevice::CurrentChannel();
|
||||
Setup.CurrentVolume = cDevice::CurrentVolume();
|
||||
Setup.Save();
|
||||
@ -1238,14 +1221,12 @@ Exit:
|
||||
ReportEpgBugFixStats();
|
||||
if (WatchdogTimeout > 0)
|
||||
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)
|
||||
closelog();
|
||||
if (HasStdin)
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &savedTm);
|
||||
if (cThread::EmergencyExit()) {
|
||||
esyslog("emergency exit!");
|
||||
return 1;
|
||||
}
|
||||
return ExitCode;
|
||||
return ShutdownHandler.GetExitCode();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user