mirror of
https://github.com/nzbget/nzbget.git
synced 2026-04-21 05:16:53 -04:00
364 lines
8.8 KiB
C++
364 lines
8.8 KiB
C++
/*
|
|
* This file is part of nzbget
|
|
*
|
|
* Copyright (C) 2008-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $Revision$
|
|
* $Date$
|
|
*
|
|
*/
|
|
|
|
|
|
#include "nzbget.h"
|
|
#include "Scheduler.h"
|
|
#include "Options.h"
|
|
#include "Log.h"
|
|
#include "NewsServer.h"
|
|
#include "ServerPool.h"
|
|
#include "FeedInfo.h"
|
|
#include "FeedCoordinator.h"
|
|
#include "SchedulerScript.h"
|
|
|
|
Scheduler::Task::Task(int id, int hours, int minutes, int weekDaysBits, ECommand command, const char* param)
|
|
{
|
|
m_id = id;
|
|
m_hours = hours;
|
|
m_minutes = minutes;
|
|
m_weekDaysBits = weekDaysBits;
|
|
m_command = command;
|
|
m_param = param ? strdup(param) : NULL;
|
|
m_lastExecuted = 0;
|
|
}
|
|
|
|
Scheduler::Task::~Task()
|
|
{
|
|
free(m_param);
|
|
}
|
|
|
|
|
|
Scheduler::Scheduler()
|
|
{
|
|
debug("Creating Scheduler");
|
|
|
|
m_firstChecked = false;
|
|
m_lastCheck = 0;
|
|
m_taskList.clear();
|
|
}
|
|
|
|
Scheduler::~Scheduler()
|
|
{
|
|
debug("Destroying Scheduler");
|
|
|
|
for (TaskList::iterator it = m_taskList.begin(); it != m_taskList.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
|
|
void Scheduler::AddTask(Task* task)
|
|
{
|
|
m_taskListMutex.Lock();
|
|
m_taskList.push_back(task);
|
|
m_taskListMutex.Unlock();
|
|
}
|
|
|
|
bool Scheduler::CompareTasks(Scheduler::Task* task1, Scheduler::Task* task2)
|
|
{
|
|
return (task1->m_hours < task2->m_hours) ||
|
|
((task1->m_hours == task2->m_hours) && (task1->m_minutes < task2->m_minutes));
|
|
}
|
|
|
|
void Scheduler::FirstCheck()
|
|
{
|
|
m_taskListMutex.Lock();
|
|
m_taskList.sort(CompareTasks);
|
|
m_taskListMutex.Unlock();
|
|
|
|
// check all tasks for the last week
|
|
CheckTasks();
|
|
}
|
|
|
|
void Scheduler::ServiceWork()
|
|
{
|
|
if (!DownloadQueue::IsLoaded())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_firstChecked)
|
|
{
|
|
FirstCheck();
|
|
m_firstChecked = true;
|
|
return;
|
|
}
|
|
|
|
m_executeProcess = true;
|
|
CheckTasks();
|
|
CheckScheduledResume();
|
|
}
|
|
|
|
void Scheduler::CheckTasks()
|
|
{
|
|
PrepareLog();
|
|
|
|
m_taskListMutex.Lock();
|
|
|
|
time_t current = time(NULL);
|
|
|
|
if (!m_taskList.empty())
|
|
{
|
|
// Detect large step changes of system time
|
|
time_t diff = current - m_lastCheck;
|
|
if (diff > 60*90 || diff < 0)
|
|
{
|
|
debug("Reset scheduled tasks (detected clock change greater than 90 minutes or negative)");
|
|
|
|
// check all tasks for the last week
|
|
m_lastCheck = current - 60*60*24*7;
|
|
m_executeProcess = false;
|
|
|
|
for (TaskList::iterator it = m_taskList.begin(); it != m_taskList.end(); it++)
|
|
{
|
|
Task* task = *it;
|
|
task->m_lastExecuted = 0;
|
|
}
|
|
}
|
|
|
|
time_t localCurrent = current + g_Options->GetLocalTimeOffset();
|
|
time_t localLastCheck = m_lastCheck + g_Options->GetLocalTimeOffset();
|
|
|
|
tm tmCurrent;
|
|
gmtime_r(&localCurrent, &tmCurrent);
|
|
tm tmLastCheck;
|
|
gmtime_r(&localLastCheck, &tmLastCheck);
|
|
|
|
tm tmLoop;
|
|
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
|
|
tmLoop.tm_hour = tmCurrent.tm_hour;
|
|
tmLoop.tm_min = tmCurrent.tm_min;
|
|
tmLoop.tm_sec = tmCurrent.tm_sec;
|
|
time_t loop = Util::Timegm(&tmLoop);
|
|
|
|
while (loop <= localCurrent)
|
|
{
|
|
for (TaskList::iterator it = m_taskList.begin(); it != m_taskList.end(); it++)
|
|
{
|
|
Task* task = *it;
|
|
if (task->m_lastExecuted != loop)
|
|
{
|
|
tm tmAppoint;
|
|
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
|
|
tmAppoint.tm_hour = task->m_hours;
|
|
tmAppoint.tm_min = task->m_minutes;
|
|
tmAppoint.tm_sec = 0;
|
|
|
|
time_t appoint = Util::Timegm(&tmAppoint);
|
|
|
|
int weekDay = tmAppoint.tm_wday;
|
|
if (weekDay == 0)
|
|
{
|
|
weekDay = 7;
|
|
}
|
|
|
|
bool weekDayOK = task->m_weekDaysBits == 0 || (task->m_weekDaysBits & (1 << (weekDay - 1)));
|
|
bool doTask = weekDayOK && localLastCheck < appoint && appoint <= localCurrent;
|
|
|
|
//debug("TEMP: 1) m_tLastCheck=%i, tLocalCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tLocalCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
|
|
|
|
if (doTask)
|
|
{
|
|
ExecuteTask(task);
|
|
task->m_lastExecuted = loop;
|
|
}
|
|
}
|
|
}
|
|
loop += 60*60*24; // inc day
|
|
gmtime_r(&loop, &tmLoop);
|
|
}
|
|
}
|
|
|
|
m_lastCheck = current;
|
|
|
|
m_taskListMutex.Unlock();
|
|
|
|
PrintLog();
|
|
}
|
|
|
|
void Scheduler::ExecuteTask(Task* task)
|
|
{
|
|
const char* commandName[] = { "Pause", "Unpause", "Pause Post-processing", "Unpause Post-processing",
|
|
"Set download rate", "Execute process", "Execute script",
|
|
"Pause Scan", "Unpause Scan", "Enable Server", "Disable Server", "Fetch Feed" };
|
|
debug("Executing scheduled command: %s", commandName[task->m_command]);
|
|
|
|
switch (task->m_command)
|
|
{
|
|
case scDownloadRate:
|
|
if (!Util::EmptyStr(task->m_param))
|
|
{
|
|
g_Options->SetDownloadRate(atoi(task->m_param) * 1024);
|
|
m_downloadRateChanged = true;
|
|
}
|
|
break;
|
|
|
|
case scPauseDownload:
|
|
case scUnpauseDownload:
|
|
g_Options->SetPauseDownload(task->m_command == scPauseDownload);
|
|
m_pauseDownloadChanged = true;
|
|
break;
|
|
|
|
case scPausePostProcess:
|
|
case scUnpausePostProcess:
|
|
g_Options->SetPausePostProcess(task->m_command == scPausePostProcess);
|
|
m_pausePostProcessChanged = true;
|
|
break;
|
|
|
|
case scPauseScan:
|
|
case scUnpauseScan:
|
|
g_Options->SetPauseScan(task->m_command == scPauseScan);
|
|
m_pauseScanChanged = true;
|
|
break;
|
|
|
|
case scScript:
|
|
case scProcess:
|
|
if (m_executeProcess)
|
|
{
|
|
SchedulerScriptController::StartScript(task->m_param, task->m_command == scProcess, task->m_id);
|
|
}
|
|
break;
|
|
|
|
case scActivateServer:
|
|
case scDeactivateServer:
|
|
EditServer(task->m_command == scActivateServer, task->m_param);
|
|
break;
|
|
|
|
case scFetchFeed:
|
|
if (m_executeProcess)
|
|
{
|
|
FetchFeed(task->m_param);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Scheduler::PrepareLog()
|
|
{
|
|
m_downloadRateChanged = false;
|
|
m_pauseDownloadChanged = false;
|
|
m_pausePostProcessChanged = false;
|
|
m_pauseScanChanged = false;
|
|
m_serverChanged = false;
|
|
}
|
|
|
|
void Scheduler::PrintLog()
|
|
{
|
|
if (m_downloadRateChanged)
|
|
{
|
|
info("Scheduler: setting download rate to %i KB/s", g_Options->GetDownloadRate() / 1024);
|
|
}
|
|
if (m_pauseDownloadChanged)
|
|
{
|
|
info("Scheduler: %s download", g_Options->GetPauseDownload() ? "pausing" : "unpausing");
|
|
}
|
|
if (m_pausePostProcessChanged)
|
|
{
|
|
info("Scheduler: %s post-processing", g_Options->GetPausePostProcess() ? "pausing" : "unpausing");
|
|
}
|
|
if (m_pauseScanChanged)
|
|
{
|
|
info("Scheduler: %s scan", g_Options->GetPauseScan() ? "pausing" : "unpausing");
|
|
}
|
|
if (m_serverChanged)
|
|
{
|
|
int index = 0;
|
|
for (Servers::iterator it = g_ServerPool->GetServers()->begin(); it != g_ServerPool->GetServers()->end(); it++, index++)
|
|
{
|
|
NewsServer* server = *it;
|
|
if (server->GetActive() != m_serverStatusList[index])
|
|
{
|
|
info("Scheduler: %s %s", server->GetActive() ? "activating" : "deactivating", server->GetName());
|
|
}
|
|
}
|
|
g_ServerPool->Changed();
|
|
}
|
|
}
|
|
|
|
void Scheduler::EditServer(bool active, const char* serverList)
|
|
{
|
|
Tokenizer tok(serverList, ",;");
|
|
while (const char* serverRef = tok.Next())
|
|
{
|
|
int id = atoi(serverRef);
|
|
for (Servers::iterator it = g_ServerPool->GetServers()->begin(); it != g_ServerPool->GetServers()->end(); it++)
|
|
{
|
|
NewsServer* server = *it;
|
|
if ((id > 0 && server->GetId() == id) ||
|
|
!strcasecmp(server->GetName(), serverRef))
|
|
{
|
|
if (!m_serverChanged)
|
|
{
|
|
// store old server status for logging
|
|
m_serverStatusList.clear();
|
|
m_serverStatusList.reserve(g_ServerPool->GetServers()->size());
|
|
for (Servers::iterator it2 = g_ServerPool->GetServers()->begin(); it2 != g_ServerPool->GetServers()->end(); it2++)
|
|
{
|
|
NewsServer* server2 = *it2;
|
|
m_serverStatusList.push_back(server2->GetActive());
|
|
}
|
|
}
|
|
m_serverChanged = true;
|
|
server->SetActive(active);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Scheduler::FetchFeed(const char* feedList)
|
|
{
|
|
Tokenizer tok(feedList, ",;");
|
|
while (const char* feedRef = tok.Next())
|
|
{
|
|
int id = atoi(feedRef);
|
|
for (Feeds::iterator it = g_FeedCoordinator->GetFeeds()->begin(); it != g_FeedCoordinator->GetFeeds()->end(); it++)
|
|
{
|
|
FeedInfo* feed = *it;
|
|
if (feed->GetId() == id ||
|
|
!strcasecmp(feed->GetName(), feedRef) ||
|
|
!strcasecmp("0", feedRef))
|
|
{
|
|
g_FeedCoordinator->FetchFeed(!strcasecmp("0", feedRef) ? 0 : feed->GetId());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Scheduler::CheckScheduledResume()
|
|
{
|
|
time_t resumeTime = g_Options->GetResumeTime();
|
|
time_t currentTime = time(NULL);
|
|
if (resumeTime > 0 && currentTime >= resumeTime)
|
|
{
|
|
info("Autoresume");
|
|
g_Options->SetResumeTime(0);
|
|
g_Options->SetPauseDownload(false);
|
|
g_Options->SetPausePostProcess(false);
|
|
g_Options->SetPauseScan(false);
|
|
}
|
|
}
|