2007-02-25 10:56:29 +01:00
|
|
|
/*
|
|
|
|
* 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>.
|
|
|
|
*
|
2015-09-01 11:14:27 +02:00
|
|
|
* $Id: shutdown.c 4.1 2015/07/18 11:29:26 kls Exp $
|
2007-02-25 10:56:29 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "shutdown.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include "channels.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "i18n.h"
|
|
|
|
#include "interface.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "plugin.h"
|
2013-10-10 13:13:30 +02:00
|
|
|
#include "recording.h"
|
2007-02-25 10:56:29 +01:00
|
|
|
#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)
|
|
|
|
{
|
2007-11-25 14:20:03 +01:00
|
|
|
if (Setup.EmergencyExit) {
|
|
|
|
esyslog("initiating emergency exit");
|
|
|
|
emergencyExitRequested = true;
|
|
|
|
Exit(1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
dsyslog("emergency exit request ignored according to setup");
|
2007-02-25 10:56:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2007-10-19 14:37:03 +02:00
|
|
|
else {
|
2007-02-25 10:56:29 +01:00
|
|
|
// Set inactive from now on
|
2007-10-19 14:37:03 +02:00
|
|
|
dsyslog("scheduled wakeup time in %ld minutes, assuming automatic start of VDR", Delta / 60);
|
2013-02-18 10:41:43 +01:00
|
|
|
SetUserInactiveTimeout(-3, true);
|
2007-10-19 14:37:03 +02:00
|
|
|
}
|
2007-02-25 10:56:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2008-02-24 10:30:49 +01:00
|
|
|
cString cmd = cString::sprintf("%s %ld %ld %d \"%s\" %d", shutdownCommand, WakeupTime, Delta, Channel, *strescape(File, "\\\"$"), UserShutdown);
|
2007-02-25 10:56:29 +01:00
|
|
|
isyslog("executing '%s'", *cmd);
|
2007-10-19 14:37:03 +02:00
|
|
|
int Status = SystemExec(cmd, true);
|
|
|
|
if (!WIFEXITED(Status) || WEXITSTATUS(Status))
|
|
|
|
esyslog("SystemExec() failed with status %d", Status);
|
|
|
|
else {
|
2007-02-25 10:56:29 +01:00
|
|
|
Setup.NextWakeupTime = WakeupTime; // Remember this wakeup time for comparison on reboot
|
2007-10-19 14:37:03 +02:00
|
|
|
Setup.Save();
|
|
|
|
}
|
2007-02-25 10:56:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void cShutdownHandler::SetUserInactiveTimeout(int Seconds, bool Force)
|
|
|
|
{
|
|
|
|
if (!Setup.MinUserInactivity && !Force) {
|
|
|
|
activeTimeout = 0;
|
|
|
|
return;
|
|
|
|
}
|
2013-02-18 10:41:43 +01:00
|
|
|
if (Seconds >= 0)
|
|
|
|
activeTimeout = time(NULL) + Seconds;
|
|
|
|
else if (Seconds == -1)
|
|
|
|
activeTimeout = time(NULL) + Setup.MinUserInactivity * 60;
|
|
|
|
else if (Seconds == -2)
|
|
|
|
activeTimeout = 0;
|
|
|
|
else if (Seconds == -3)
|
|
|
|
activeTimeout = 1;
|
2007-02-25 10:56:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cShutdownHandler::ConfirmShutdown(bool Interactive)
|
|
|
|
{
|
2008-01-13 14:31:47 +01:00
|
|
|
if (!Interactive && !cRemote::Enabled())
|
|
|
|
return false;
|
|
|
|
|
2007-02-25 10:56:29 +01:00
|
|
|
if (!shutdownCommand) {
|
|
|
|
if (Interactive)
|
|
|
|
Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
|
|
|
|
return false;
|
|
|
|
}
|
2013-10-10 13:13:30 +02:00
|
|
|
if (RecordingsHandler.Active()) {
|
2007-02-25 10:56:29 +01:00
|
|
|
if (!Interactive || !Interface->Confirm(tr("Editing - shut down anyway?")))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
LOCK_TIMERS_READ;
|
|
|
|
const cTimer *Timer = Timers->GetNextActiveTimer();
|
|
|
|
time_t Next = Timer ? Timer->StartTime() : 0;
|
|
|
|
time_t Delta = Timer ? Next - time(NULL) : 0;
|
2007-02-25 10:56:29 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2013-10-10 13:13:30 +02:00
|
|
|
if (RecordingsHandler.Active()) {
|
2007-02-25 10:56:29 +01:00
|
|
|
if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?")))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
LOCK_TIMERS_READ;
|
|
|
|
const cTimer *Timer = Timers->GetNextActiveTimer();
|
|
|
|
time_t Next = Timer ? Timer->StartTime() : 0;
|
|
|
|
time_t Delta = Timer ? Next - time(NULL) : 0;
|
2007-02-25 10:56:29 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2015-09-01 11:14:27 +02:00
|
|
|
LOCK_TIMERS_READ;
|
2007-02-25 10:56:29 +01:00
|
|
|
time_t Now = time(NULL);
|
2015-09-01 11:14:27 +02:00
|
|
|
const cTimer *Timer = Timers->GetNextActiveTimer();
|
2007-02-25 10:56:29 +01:00
|
|
|
cPlugin *Plugin = cPluginManager::GetNextWakeupPlugin();
|
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
time_t Next = Timer ? Timer->StartTime() : 0;
|
2007-02-25 10:56:29 +01:00
|
|
|
time_t NextPlugin = Plugin ? Plugin->WakeupTime() : 0;
|
|
|
|
if (NextPlugin && (!Next || Next > NextPlugin)) {
|
|
|
|
Next = NextPlugin;
|
2015-09-01 11:14:27 +02:00
|
|
|
Timer = NULL;
|
2007-02-25 10:56:29 +01:00
|
|
|
}
|
|
|
|
time_t Delta = Next ? Next - Now : 0;
|
|
|
|
|
|
|
|
if (Next && Delta < Setup.MinEventTimeout * 60) {
|
|
|
|
if (!Force)
|
|
|
|
return false;
|
|
|
|
Delta = Setup.MinEventTimeout * 60;
|
|
|
|
Next = Now + Delta;
|
2015-09-01 11:14:27 +02:00
|
|
|
Timer = NULL;
|
2007-02-25 10:56:29 +01:00
|
|
|
dsyslog("reboot at %s", *TimeToString(Next));
|
|
|
|
}
|
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
if (Next && Timer) {
|
2007-02-25 10:56:29 +01:00
|
|
|
dsyslog("next timer event at %s", *TimeToString(Next));
|
2015-09-01 11:14:27 +02:00
|
|
|
CallShutdownCommand(Next, Timer->Channel()->Number(), Timer->File(), Force);
|
2007-02-25 10:56:29 +01:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|