From 5fea19aa88231e54e9684941f3c775b71bfca9d3 Mon Sep 17 00:00:00 2001 From: Andrey Prygunkov Date: Thu, 25 Sep 2008 21:19:24 +0000 Subject: [PATCH] added scheduler; new options , , --- Makefile.am | 6 +- Makefile.in | 13 +-- Options.cpp | 175 +++++++++++++++++++++++++++++++++++- Options.h | 3 + PrePostProcessor.cpp | 104 +++++++++++++++++---- PrePostProcessor.h | 7 +- Scheduler.cpp | 210 +++++++++++++++++++++++++++++++++++++++++++ Scheduler.h | 86 ++++++++++++++++++ ScriptController.cpp | 12 --- nzbget.conf.example | 33 +++++++ nzbget.cpp | 11 +++ nzbget.h | 1 + nzbget.vcproj | 8 ++ 13 files changed, 631 insertions(+), 38 deletions(-) create mode 100644 Scheduler.cpp create mode 100644 Scheduler.h diff --git a/Makefile.am b/Makefile.am index 0542b3a8..e2bc1419 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,9 +8,9 @@ nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \ Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h PostInfo.cpp \ PostInfo.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \ QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \ - RemoteServer.cpp RemoteServer.h ScriptController.cpp ScriptController.h ServerPool.cpp \ - ServerPool.h TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp Util.h XmlRpc.cpp XmlRpc.h \ - nzbget.cpp nzbget.h + RemoteServer.cpp RemoteServer.h Scheduler.cpp Scheduler.h ScriptController.cpp \ + ScriptController.h ServerPool.cpp ServerPool.h TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp \ + Util.h XmlRpc.cpp XmlRpc.h nzbget.cpp nzbget.h EXTRA_DIST = nzbget.conf.example postprocess-example.sh \ win32.h NTService.cpp NTService.h \ diff --git a/Makefile.in b/Makefile.in index 51ffa7b0..0b6c63e9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -60,9 +60,9 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \ PostInfo.$(OBJEXT) PrePostProcessor.$(OBJEXT) \ QueueCoordinator.$(OBJEXT) QueueEditor.$(OBJEXT) \ RemoteClient.$(OBJEXT) RemoteServer.$(OBJEXT) \ - ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) TLS.$(OBJEXT) \ - Thread.$(OBJEXT) Util.$(OBJEXT) XmlRpc.$(OBJEXT) \ - nzbget.$(OBJEXT) + Scheduler.$(OBJEXT) ScriptController.$(OBJEXT) \ + ServerPool.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \ + Util.$(OBJEXT) XmlRpc.$(OBJEXT) nzbget.$(OBJEXT) nzbget_OBJECTS = $(am_nzbget_OBJECTS) nzbget_LDADD = $(LDADD) DEFAULT_INCLUDES = -I.@am__isrc@ @@ -213,9 +213,9 @@ nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \ Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h PostInfo.cpp \ PostInfo.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \ QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \ - RemoteServer.cpp RemoteServer.h ScriptController.cpp ScriptController.h ServerPool.cpp \ - ServerPool.h TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp Util.h XmlRpc.cpp XmlRpc.h \ - nzbget.cpp nzbget.h + RemoteServer.cpp RemoteServer.h Scheduler.cpp Scheduler.h ScriptController.cpp \ + ScriptController.h ServerPool.cpp ServerPool.h TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp \ + Util.h XmlRpc.cpp XmlRpc.h nzbget.cpp nzbget.h EXTRA_DIST = nzbget.conf.example postprocess-example.sh \ win32.h NTService.cpp NTService.h \ @@ -334,6 +334,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueEditor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteClient.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scheduler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ScriptController.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@ diff --git a/Options.cpp b/Options.cpp index daebc4c3..da8c8b04 100644 --- a/Options.cpp +++ b/Options.cpp @@ -51,8 +51,10 @@ #include "ServerPool.h" #include "NewsServer.h" #include "MessageBase.h" +#include "Scheduler.h" extern ServerPool* g_pServerPool; +extern Scheduler* g_pScheduler; #ifdef HAVE_GETOPT_LONG static struct option long_options[] = @@ -301,6 +303,7 @@ Options::Options(int argc, char* argv[]) InitFileArg(argc, argv); InitServers(); + InitScheduler(); CheckOptions(); } @@ -1173,6 +1176,166 @@ void Options::InitServers() g_pServerPool->InitConnections(); } +void Options::InitScheduler() +{ + int n = 1; + while (true) + { + char optname[128]; + + sprintf(optname, "task%i.time", n); + const char* szTime = GetOption(optname); + + sprintf(optname, "task%i.weekdays", n); + const char* szWeekDays = GetOption(optname); + + sprintf(optname, "task%i.command", n); + const char* szCommand = GetOption(optname); + + bool definition = szTime || szWeekDays || szCommand; + bool completed = szTime && szCommand; + + if (!definition) + { + break; + } + + if (definition && !completed) + { + abort("FATAL ERROR: Task definition not complete for Task%i\n", n); + } + + int iHours, iMinutes; + if (!ParseTime(szTime, &iHours, &iMinutes)) + { + abort("FATAL ERROR: Invalid value for option Task%i.Time\n", n); + } + + int iWeekDays = 0; + if (szWeekDays && !ParseWeekDays(szWeekDays, &iWeekDays)) + { + abort("FATAL ERROR: Invalid value for option Task%i.WeekDays\n", n); + } + + Scheduler::ECommand eCommand; + int iDownloadRate=0; + if (!strcasecmp(szCommand, "pause")) + { + eCommand = Scheduler::scPause; + } + else if (!strcasecmp(szCommand, "unpause")) + { + eCommand = Scheduler::scUnpause; + } + else + { + eCommand = Scheduler::scDownloadRate; + char* szErr; + iDownloadRate = strtol(szCommand, &szErr, 10); + if (!szErr || *szErr != '\0' || iDownloadRate < 0) + { + abort("FATAL ERROR: Invalid value for option Task%i.Command\n", n); + } + } + + Scheduler::Task* pTask = new Scheduler::Task(iHours, iMinutes, iWeekDays, eCommand, iDownloadRate); + g_pScheduler->AddTask(pTask); + + n++; + } +} + +bool Options::ParseTime(const char* szTime, int* pHours, int* pMinutes) +{ + int iColons = 0; + const char* p = szTime; + while (*p) + { + if (!strchr("0123456789:", *p)) + { + return false; + } + if (*p == ':') + { + iColons++; + } + p++; + } + + if (iColons != 1) + { + return false; + } + + const char* szColon = strchr(szTime, ':'); + if (!szColon) + { + return false; + } + *pHours = atoi(szTime); + if (*pHours < 0 || *pHours > 23) + { + return false; + } + *pMinutes = atoi(szColon + 1); + if (*pMinutes < 0 || *pMinutes > 59) + { + return false; + } + return true; +} + +bool Options::ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits) +{ + *pWeekDaysBits = 0; + const char* p = szWeekDays; + int iFirstDay = 0; + bool bRange = false; + while (*p) + { + if (strchr("1234567", *p)) + { + int iDay = *p - '0'; + if (bRange) + { + if (iDay <= iFirstDay || iFirstDay == 0) + { + return false; + } + for (int i = iFirstDay; i <= iDay; i++) + { + *pWeekDaysBits |= 1 << (i - 1); + } + iFirstDay = 0; + } + else + { + *pWeekDaysBits |= 1 << (iDay - 1); + iFirstDay = iDay; + } + bRange = false; + } + else if (*p == ',') + { + bRange = false; + } + else if (*p == '-') + { + bRange = true; + } + else if (*p == ' ') + { + // skip spaces + } + else + { + return false; + } + p++; + } + return true; +} + void Options::LoadConfig(const char * configfile) { FILE* infile = fopen(configfile, "r"); @@ -1279,7 +1442,17 @@ bool Options::ValidateOptionName(const char * optname) return true; } } - + + if (!strncasecmp(optname, "task", 4)) + { + char* p = (char*)optname + 4; + while (*p >= '0' && *p <= '9') p++; + if (p && (!strcasecmp(p, ".time") || !strcasecmp(p, ".weekdays") || !strcasecmp(p, ".command"))) + { + return true; + } + } + return false; } diff --git a/Options.h b/Options.h index 8e4932ea..1dd8fa5e 100644 --- a/Options.h +++ b/Options.h @@ -181,6 +181,7 @@ private: void InitOptions(); void InitFileArg(int argc, char* argv[]); void InitServers(); + void InitScheduler(); void CheckOptions(); void PrintUsage(char* com); void Dump(); @@ -193,6 +194,8 @@ private: void LoadConfig(const char* configfile); void CheckDir(char** dir, const char* szOptionName); void ParseFileIDList(int argc, char* argv[], int optind); + bool ParseTime(const char* szTime, int* pHours, int* pMinutes); + bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits); public: Options(int argc, char* argv[]); diff --git a/PrePostProcessor.cpp b/PrePostProcessor.cpp index b22f3048..76a7577a 100644 --- a/PrePostProcessor.cpp +++ b/PrePostProcessor.cpp @@ -51,10 +51,12 @@ #include "ScriptController.h" #include "DiskState.h" #include "Util.h" +#include "Scheduler.h" extern QueueCoordinator* g_pQueueCoordinator; extern Options* g_pOptions; extern DiskState* g_pDiskState; +extern Scheduler* g_pScheduler; static const int PARSTATUS_NOT_CHECKED = 0; static const int PARSTATUS_FAILED = 1; @@ -78,6 +80,7 @@ PrePostProcessor::PrePostProcessor() debug("Creating PrePostProcessor"); m_bHasMoreJobs = false; + m_bPostPause = false; m_QueueCoordinatorObserver.owner = this; g_pQueueCoordinator->Attach(&m_QueueCoordinatorObserver); @@ -138,8 +141,12 @@ void PrePostProcessor::Run() } } + g_pScheduler->FirstCheck(); + ApplySchedulerState(); + int iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000; int iDiskSpaceInterval = 1000; + int iSchedulerInterval = 1000; while (!IsStopped()) { if (g_pOptions->GetNzbDir() && g_pOptions->GetNzbDirInterval() > 0 && @@ -163,6 +170,15 @@ void PrePostProcessor::Run() // check post-queue every 200 msec CheckPostQueue(); + if (iSchedulerInterval >= 1000) + { + // check scheduler tasks every 1 second + g_pScheduler->IntervalCheck(); + ApplySchedulerState(); + iSchedulerInterval = 0; + } + iSchedulerInterval += 200; + usleep(200 * 1000); } @@ -424,6 +440,14 @@ void PrePostProcessor::CheckPostQueue() } } #endif + if (m_bPostScript && g_pOptions->GetPostPauseQueue()) + { + if (UnpauseDownload()) + { + info("Unpausing queue after post-process-script"); + } + } + JobCompleted(pDownloadQueue, pPostInfo); } else @@ -483,18 +507,12 @@ void PrePostProcessor::SanitisePostQueue() } } -bool PrePostProcessor::PostProcessEnabled() -{ - const char* szScript = g_pOptions->GetPostProcess(); - return szScript && strlen(szScript) > 0; -} - /** * Mutex "m_mutexQueue" must be locked prior to call of this function. */ void PrePostProcessor::StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo) { - if (!PostProcessEnabled()) + if (!m_bPostScript) { pPostInfo->SetStage(PostInfo::ptFinished); return; @@ -520,6 +538,14 @@ void PrePostProcessor::StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* p bool bHasFailedParJobs = false; #endif + if (g_pOptions->GetPostPauseQueue()) + { + if (PauseDownload()) + { + info("Pausing queue before post-process-script"); + } + } + ScriptController::StartScriptJob(pPostInfo, g_pOptions->GetPostProcess(), bNZBFileCompleted, bHasFailedParJobs); } @@ -714,10 +740,12 @@ bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, const c */ void PrePostProcessor::StartParJob(PostInfo* pPostInfo) { - if (g_pOptions->GetParPauseQueue() && !g_pOptions->GetPause()) + if (g_pOptions->GetParPauseQueue()) { - info("Pausing queue before par-check"); - g_pOptions->SetPause(true); + if (PauseDownload()) + { + info("Pausing queue before par-check"); + } } info("Checking pars for %s", pPostInfo->GetInfoName()); @@ -836,7 +864,7 @@ bool PrePostProcessor::AddPar(FileInfo * pFileInfo, bool bDeleted) if (g_pOptions->GetParPauseQueue()) { - g_pOptions->SetPause(true); + PauseDownload(); } } else @@ -929,10 +957,12 @@ void PrePostProcessor::ParCheckerUpdate(Subject* Caller, void* Aspect) m_mutexQueue.Unlock(); - if (g_pOptions->GetParPauseQueue() && !(g_pOptions->GetPostPauseQueue() && PostProcessEnabled())) + if (g_pOptions->GetParPauseQueue() && !(g_pOptions->GetPostPauseQueue() && m_bPostScript)) { - info("Unpausing queue after par-check"); - g_pOptions->SetPause(false); + if (UnpauseDownload()) + { + info("Unpausing queue after par-check"); + } } } } @@ -1097,7 +1127,7 @@ bool PrePostProcessor::RequestMorePars(const char* szNZBFilename, const char* sz if (bOK && g_pOptions->GetParPauseQueue()) { - g_pOptions->SetPause(false); + UnpauseDownload(); } return bOK; @@ -1255,3 +1285,47 @@ void PrePostProcessor::UpdateParProgress() } #endif + +void PrePostProcessor::ApplySchedulerState() +{ + if (g_pScheduler->GetDownloadRateChanged()) + { + info("Scheduler: set download rate to %i KB/s", g_pScheduler->GetDownloadRate()); + g_pOptions->SetDownloadRate((float)g_pScheduler->GetDownloadRate()); + } + + if (g_pScheduler->GetPauseChanged()) + { + info("Scheduler: %s download queue", g_pScheduler->GetPause() ? "pause" : "unpause"); + m_bSchedulerPauseChanged = true; + m_bSchedulerPause = g_pScheduler->GetPause(); + if (!m_bPostPause) + { + g_pOptions->SetPause(m_bSchedulerPause); + } + } +} + +bool PrePostProcessor::PauseDownload() +{ + debug("PrePostProcessor::PauseDownload()"); + + m_bPostPause = !g_pOptions->GetPause(); + m_bSchedulerPauseChanged = false; + g_pOptions->SetPause(m_bPostPause); + return m_bPostPause; +} + +bool PrePostProcessor::UnpauseDownload() +{ + debug("PrePostProcessor::UnpauseDownload()"); + + bool bPause = true; + if (m_bPostPause) + { + m_bPostPause = false; + bPause = m_bSchedulerPauseChanged && m_bSchedulerPause; + g_pOptions->SetPause(bPause); + } + return !bPause; +} diff --git a/PrePostProcessor.h b/PrePostProcessor.h index c8356390..bf46102a 100644 --- a/PrePostProcessor.h +++ b/PrePostProcessor.h @@ -81,6 +81,9 @@ private: QueueCoordinatorObserver m_QueueCoordinatorObserver; bool m_bHasMoreJobs; bool m_bPostScript; + bool m_bSchedulerPauseChanged; + bool m_bSchedulerPause; + bool m_bPostPause; void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename); void CheckIncomingNZBs(const char* szDirectory, const char* szCategory); @@ -95,8 +98,10 @@ private: void SavePostQueue(); void SanitisePostQueue(); void CheckDiskSpace(); - bool PostProcessEnabled(); void AddFileToQueue(const char* szFilename, const char* szCategory); + void ApplySchedulerState(); + bool PauseDownload(); + bool UnpauseDownload(); Mutex m_mutexQueue; PostQueue m_PostQueue; diff --git a/Scheduler.cpp b/Scheduler.cpp new file mode 100644 index 00000000..9260c30b --- /dev/null +++ b/Scheduler.cpp @@ -0,0 +1,210 @@ +/* + * This file if part of nzbget + * + * Copyright (C) 2008 Andrei Prygounkov + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Revision$ + * $Date$ + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef WIN32 +#include "win32.h" +#endif + +#include +#include + +#include "nzbget.h" +#include "Scheduler.h" +#include "Options.h" +#include "Log.h" + +extern Options* g_pOptions; + +Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, int iDownloadRate) +{ + m_iHours = iHours; + m_iMinutes = iMinutes; + m_iWeekDaysBits = iWeekDaysBits; + m_eCommand = eCommand; + m_iDownloadRate = iDownloadRate; + m_tLastExecuted = 0; +} + +Scheduler::Scheduler() +{ + debug("Creating Scheduler"); + + m_tLastCheck = 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* pTask) +{ + m_mutexTaskList.Lock(); + m_TaskList.push_back(pTask); + m_mutexTaskList.Unlock(); +} + +bool Scheduler::CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2) +{ + return (pTask1->m_iHours < pTask2->m_iHours) || + ((pTask1->m_iHours == pTask2->m_iHours) && (pTask1->m_iMinutes < pTask2->m_iMinutes)); +} + +void Scheduler::FirstCheck() +{ + m_mutexTaskList.Lock(); + m_TaskList.sort(CompareTasks); + m_mutexTaskList.Unlock(); + + // check all tasks for the last week + time_t tCurrent = time(NULL); + m_tLastCheck = tCurrent - 60*60*24*7; + m_bDetectClockChanges = false; + m_bDownloadRateChanged = false; + m_bPauseChanged = false; + CheckTasks(); +} + +void Scheduler::IntervalCheck() +{ + m_bDetectClockChanges = true; + m_bDownloadRateChanged = false; + m_bPauseChanged = false; + CheckTasks(); +} + +void Scheduler::CheckTasks() +{ + m_mutexTaskList.Lock(); + + time_t tCurrent = time(NULL); + struct tm tmCurrent; + localtime_r(&tCurrent, &tmCurrent); + + struct tm tmLastCheck; + + if (m_bDetectClockChanges) + { + // Detect large step changes of system time + time_t tDiff = tCurrent - m_tLastCheck; + if (tDiff > 60*90 || tDiff < -60*90) + { + debug("Reset scheduled tasks (detected clock adjustment greater than 90 minutes)"); + m_tLastCheck = tCurrent; + + for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++) + { + Task* pTask = *it; + pTask->m_tLastExecuted = 0; + } + } + } + + localtime_r(&m_tLastCheck, &tmLastCheck); + + struct 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 tLoop = mktime(&tmLoop); + + while (tLoop <= tCurrent) + { + for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++) + { + Task* pTask = *it; + if (pTask->m_tLastExecuted != tLoop) + { + struct tm tmAppoint; + memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop)); + tmAppoint.tm_hour = pTask->m_iHours; + tmAppoint.tm_min = pTask->m_iMinutes; + tmAppoint.tm_sec = 0; + + time_t tAppoint = mktime(&tmAppoint); + int iWeekDay = tmAppoint.tm_wday; + if (iWeekDay == 0) + { + iWeekDay = 7; + } + + bool bWeekDayOK = pTask->m_iWeekDaysBits == 0 || (pTask->m_iWeekDaysBits & (1 << (iWeekDay - 1))); + bool bDoTask = bWeekDayOK && m_tLastCheck < tAppoint && tAppoint <= tCurrent; + + //debug("TEMP: 1) m_tLastCheck=%i, tCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask); + + if (bDoTask) + { + ExecuteTask(pTask); + pTask->m_tLastExecuted = tLoop; + } + } + } + tLoop += 60*60*24; // inc day + localtime_r(&tLoop, &tmLoop); + } + + m_tLastCheck = tCurrent; + + m_mutexTaskList.Unlock(); +} + +void Scheduler::ExecuteTask(Task* pTask) +{ + if (pTask->m_eCommand == scDownloadRate) + { + debug("Executing scheduled command: Set download rate to %i", pTask->m_iDownloadRate); + } + else + { + const char* szCommandName[] = { "Pause", "Unpause", "Set download rate" }; + debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]); + } + + switch (pTask->m_eCommand) + { + case scDownloadRate: + m_iDownloadRate = pTask->m_iDownloadRate; + m_bDownloadRateChanged = true; + break; + + case scPause: + case scUnpause: + m_bPause = pTask->m_eCommand == scPause; + m_bPauseChanged = true; + break; + } +} diff --git a/Scheduler.h b/Scheduler.h new file mode 100644 index 00000000..09dd8009 --- /dev/null +++ b/Scheduler.h @@ -0,0 +1,86 @@ +/* + * This file if part of nzbget + * + * Copyright (C) 2008 Andrei Prygounkov + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Revision$ + * $Date$ + * + */ + + +#ifndef SCHEDULER_H +#define SCHEDULER_H + +#include + +#include "Thread.h" + +class Scheduler +{ +public: + enum ECommand + { + scPause, + scUnpause, + scDownloadRate + }; + + class Task + { + private: + int m_iHours; + int m_iMinutes; + int m_iWeekDaysBits; + ECommand m_eCommand; + int m_iDownloadRate; + time_t m_tLastExecuted; + + public: + Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, int iDownloadRate); + friend class Scheduler; + }; + +private: + + typedef std::list TaskList; + + TaskList m_TaskList; + Mutex m_mutexTaskList; + time_t m_tLastCheck; + bool m_bDetectClockChanges; + bool m_bDownloadRateChanged; + int m_iDownloadRate; + bool m_bPauseChanged; + bool m_bPause; + void ExecuteTask(Task* pTask); + void CheckTasks(); + static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2); + +public: + Scheduler(); + ~Scheduler(); + void AddTask(Task* pTask); + void FirstCheck(); + void IntervalCheck(); + bool GetDownloadRateChanged() { return m_bDownloadRateChanged; } + int GetDownloadRate() { return m_iDownloadRate; } + bool GetPauseChanged() { return m_bPauseChanged; } + bool GetPause() { return m_bPause; } +}; + +#endif diff --git a/ScriptController.cpp b/ScriptController.cpp index 99260d3a..1f9ad066 100644 --- a/ScriptController.cpp +++ b/ScriptController.cpp @@ -54,12 +54,6 @@ extern Options* g_pOptions; void ScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs) { - if (g_pOptions->GetPostPauseQueue() && !g_pOptions->GetPause()) - { - info("Pausing queue before post-process-script"); - g_pOptions->SetPause(true); - } - info("Executing post-process-script for %s", pPostInfo->GetInfoName()); ScriptController* pScriptController = new ScriptController(); @@ -266,12 +260,6 @@ void ScriptController::Finished() { m_pPostInfo->SetStage(PostInfo::ptFinished); m_pPostInfo->SetWorking(false); - - if (g_pOptions->GetPostPauseQueue()) - { - info("Unpausing queue after post-process-script"); - g_pOptions->SetPause(false); - } } void ScriptController::AddMessage(char* szText) diff --git a/nzbget.conf.example b/nzbget.conf.example index 03ef2b1a..105a48ab 100644 --- a/nzbget.conf.example +++ b/nzbget.conf.example @@ -520,6 +520,39 @@ PostLogKind=Detail # NOTE: See also option . PostPauseQueue=no + +############################################################################## +### SCHEDULER ### + +# This section defines scheduler commands. +# For each command create a pair of options "TaskX.At" and "TaskX.Command". +# The following example shows how to throttle downloads in the daytime +# by 100 KB/s and download at full speed overnights: + +# Time to execute the command (HH:MM) +#Task1.Time=08:00 + +# Week days to execute the command (1, 2, 3, 4, 5, 6, 7) +# Comma separated list of week days numbers. +# 1 is Monday. +# Character '-' may be used to define ranges. +# Examples: "1-7", "1-5", "5,6", "1-5, 7". +# NOTE: if the command must be executed every day (1-7), the option "WeekDays" +# can be omitted. +#Task1.WeekDays=1-7 + +# Command to be executed (Pause, Unpause, 0..9999999) +# Possible commands: +# Pause - pauses download +# Unpause - resumes download +# 0..9999999 - sets download rate in KB/s, 0 - no limit +#Task1.Command=100 + +#Task2.Time=20:00 +#Task2.WeekDays=1-7 +#Task2.Command=0 + + ############################################################################## ### PERFORMANCE ### diff --git a/nzbget.cpp b/nzbget.cpp index 5b4f7851..d4850a20 100644 --- a/nzbget.cpp +++ b/nzbget.cpp @@ -70,6 +70,7 @@ #include "DiskState.h" #include "PrePostProcessor.h" #include "ParChecker.h" +#include "Scheduler.h" #ifdef WIN32 #include "NTService.h" #endif @@ -101,6 +102,7 @@ NZBInfoLocker* g_pNZBInfoLocker = NULL; Log* g_pLog = NULL; PrePostProcessor* g_pPrePostProcessor = NULL; DiskState* g_pDiskState = NULL; +Scheduler* g_pScheduler = NULL; /* @@ -132,6 +134,7 @@ int main(int argc, char *argv[]) // Init options & get the name of the .nzb file g_pLog = new Log(); g_pServerPool = new ServerPool(); + g_pScheduler = new Scheduler(); debug("Options parsing"); g_pOptions = new Options(argc, argv); @@ -595,6 +598,14 @@ void Cleanup() } debug("ServerPool deleted"); + debug("Deleting Scheduler"); + if (g_pScheduler) + { + delete g_pScheduler; + g_pScheduler = NULL; + } + debug("Scheduler deleted"); + Thread::Final(); Connection::Final(); diff --git a/nzbget.h b/nzbget.h index 444dfca3..9fb5e541 100644 --- a/nzbget.h +++ b/nzbget.h @@ -36,6 +36,7 @@ #endif #define fdopen _fdopen #define ctime_r(timep, buf, bufsize) ctime_s(buf, bufsize, timep) +#define localtime_r(time, tm) localtime_s(tm, time) #define int32_t __int32 #define mkdir(dir, flags) _mkdir(dir) #define rmdir _rmdir diff --git a/nzbget.vcproj b/nzbget.vcproj index 6df2216b..2dac8580 100644 --- a/nzbget.vcproj +++ b/nzbget.vcproj @@ -455,6 +455,14 @@ RelativePath=".\RemoteServer.h" > + + + +