Files
nzbget/daemon/main/nzbget.cpp
2019-01-26 18:16:17 +01:00

1061 lines
26 KiB
C++

/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2019 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "ServerPool.h"
#include "Log.h"
#include "NzbFile.h"
#include "Options.h"
#include "WorkState.h"
#include "CommandLineParser.h"
#include "ScriptConfig.h"
#include "Thread.h"
#include "ColoredFrontend.h"
#include "NCursesFrontend.h"
#include "QueueCoordinator.h"
#include "UrlCoordinator.h"
#include "RemoteServer.h"
#include "WebServer.h"
#include "RemoteClient.h"
#include "MessageBase.h"
#include "DiskState.h"
#include "PrePostProcessor.h"
#include "HistoryCoordinator.h"
#include "DupeCoordinator.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "FeedCoordinator.h"
#include "Service.h"
#include "DiskService.h"
#include "Maintenance.h"
#include "ArticleWriter.h"
#include "StatMeter.h"
#include "QueueScript.h"
#include "Util.h"
#include "FileSystem.h"
#include "StackTrace.h"
#include "CommandScript.h"
#include "YEncode.h"
#ifdef WIN32
#include "WinService.h"
#include "WinConsole.h"
#include "WebDownloader.h"
#endif
#ifdef ENABLE_TESTS
#include "TestMain.h"
#endif
#ifndef DISABLE_NSERV
#include "NServMain.h"
#endif
// Prototypes
void RunMain();
// Globals
Log* g_Log;
Options* g_Options;
WorkState* g_WorkState;
ServerPool* g_ServerPool;
QueueCoordinator* g_QueueCoordinator;
UrlCoordinator* g_UrlCoordinator;
StatMeter* g_StatMeter;
PrePostProcessor* g_PrePostProcessor;
HistoryCoordinator* g_HistoryCoordinator;
DupeCoordinator* g_DupeCoordinator;
DiskState* g_DiskState;
Scanner* g_Scanner;
FeedCoordinator* g_FeedCoordinator;
Maintenance* g_Maintenance;
ArticleCache* g_ArticleCache;
QueueScriptCoordinator* g_QueueScriptCoordinator;
ServiceCoordinator* g_ServiceCoordinator;
ScriptConfig* g_ScriptConfig;
CommandScriptLog* g_CommandScriptLog;
#ifdef WIN32
WinConsole* g_WinConsole;
#endif
int g_ArgumentCount;
char* (*g_EnvironmentVariables)[] = nullptr;
char* (*g_Arguments)[] = nullptr;
/*
* Main entry point
*/
int main(int argc, char *argv[], char *argp[])
{
#ifdef WIN32
#ifdef _DEBUG
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF
#ifdef DEBUG_CRTMEMLEAKS
| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
#endif
);
#endif
#endif
Util::Init();
YEncode::init();
g_ArgumentCount = argc;
g_Arguments = (char*(*)[])argv;
g_EnvironmentVariables = (char*(*)[])argp;
if (argc > 1 && (!strcmp(argv[1], "-tests") || !strcmp(argv[1], "--tests")))
{
#ifdef ENABLE_TESTS
return TestMain(argc, argv);
#else
printf("ERROR: Could not start tests, the program was compiled without tests\n");
return 1;
#endif
}
#ifdef ENABLE_TESTS
TestCleanup();
#endif
if (argc > 1 && (!strcmp(argv[1], "--nserv")))
{
#ifndef DISABLE_NSERV
return NServMain(argc, argv);
#else
printf("ERROR: Could not start NServ, the program was compiled without NServ\n");
return 1;
#endif
}
#ifdef WIN32
InstallUninstallServiceCheck(argc, argv);
#endif
srand((unsigned int)Util::CurrentTime());
#ifdef WIN32
for (int i=0; i < argc; i++)
{
if (!strcmp(argv[i], "-D"))
{
AllocConsole(); // needed for sending CTRL+BREAK signal to child processes
StartService(RunMain);
return 0;
}
}
#endif
RunMain();
return 0;
}
class NZBGet : public Options::Extender
{
public:
~NZBGet();
void Run(bool reload);
void Stop(bool reload);
bool GetReloading() { return m_reloading; }
// Options::Extender
virtual void AddNewsServer(int id, bool active, const char* name, const char* host,
int port, int ipVersion, const char* user, const char* pass, bool joinGroup,
bool tls, const char* cipher, int maxConnections, int retention,
int level, int group, bool optional);
virtual void AddFeed(int id, const char* name, const char* url, int interval,
const char* filter, bool backlog, bool pauseNzb, const char* category,
int priority, const char* feedScript);
virtual void AddTask(int id, int hours, int minutes, int weekDaysBits,
Options::ESchedulerCommand command, const char* param);
#ifdef WIN32
virtual void SetupFirstStart();
#endif
private:
// globals
std::unique_ptr<Log> m_log;
std::unique_ptr<Options> m_options;
std::unique_ptr<WorkState> m_workState;
std::unique_ptr<ServerPool> m_serverPool;
std::unique_ptr<QueueCoordinator> m_queueCoordinator;
std::unique_ptr<UrlCoordinator> m_urlCoordinator;
std::unique_ptr<StatMeter> m_statMeter;
std::unique_ptr<PrePostProcessor> m_prePostProcessor;
std::unique_ptr<HistoryCoordinator> m_historyCoordinator;
std::unique_ptr<DupeCoordinator> m_dupeCoordinator;
std::unique_ptr<DiskState> m_diskState;
std::unique_ptr<Scanner> m_scanner;
std::unique_ptr<FeedCoordinator> m_feedCoordinator;
std::unique_ptr<Maintenance> m_maintenance;
std::unique_ptr<ArticleCache> m_articleCache;
std::unique_ptr<QueueScriptCoordinator> m_queueScriptCoordinator;
std::unique_ptr<ServiceCoordinator> m_serviceCoordinator;
std::unique_ptr<ScriptConfig> m_scriptConfig;
std::unique_ptr<CommandScriptLog> m_commandScriptLog;
#ifdef WIN32
std::unique_ptr<WinConsole> m_winConsole;
#endif
// non-globals
std::unique_ptr<Thread> m_frontend;
std::unique_ptr<RemoteServer> m_remoteServer;
std::unique_ptr<RemoteServer> m_remoteSecureServer;
std::unique_ptr<DiskService> m_diskService;
std::unique_ptr<Scheduler> m_scheduler;
std::unique_ptr<CommandLineParser> m_commandLineParser;
bool m_reloading = false;
bool m_daemonized = false;
bool m_stopped = false;
Mutex m_waitMutex;
ConditionVar m_waitCond;
void Init();
void Final();
void BootConfig();
void CreateGlobals();
void Cleanup();
void PrintOptions();
void ProcessDirect();
void ProcessClientRequest();
void ProcessWebGet();
void ProcessSigVerify();
void StartRemoteServer();
void StopRemoteServer();
void StartFrontend();
void StopFrontend();
void ProcessStandalone();
void DoMainLoop();
#ifndef WIN32
void Daemonize();
#endif
};
std::unique_ptr<NZBGet> g_NZBGet;
NZBGet::~NZBGet()
{
Cleanup();
}
void NZBGet::Init()
{
m_log = std::make_unique<Log>();
debug("nzbget %s", Util::VersionRevision());
if (!m_reloading)
{
Thread::Init();
Connection::Init();
#ifndef DISABLE_TLS
TlsSocket::Init();
#endif
}
CreateGlobals();
#ifdef WIN32
m_winConsole->InitAppMode();
#endif
BootConfig();
#ifndef WIN32
if (m_options->GetUMask() < 01000)
{
/* set newly created file permissions */
umask(m_options->GetUMask());
}
#endif
m_scanner->InitOptions();
m_queueScriptCoordinator->InitOptions();
#ifndef DISABLE_TLS
TlsSocket::InitOptions(g_Options->GetCertCheck() ? g_Options->GetCertStore() : nullptr);
#endif
if (m_commandLineParser->GetDaemonMode())
{
#ifdef WIN32
info("nzbget %s service-mode", Util::VersionRevision());
#else
if (!m_reloading)
{
Daemonize();
}
info("nzbget %s daemon-mode", Util::VersionRevision());
#endif
}
else if (m_options->GetServerMode())
{
info("nzbget %s server-mode", Util::VersionRevision());
}
else if (m_commandLineParser->GetRemoteClientMode())
{
info("nzbget %s remote-mode", Util::VersionRevision());
}
m_reloading = false;
if (!m_commandLineParser->GetRemoteClientMode())
{
m_serverPool->InitConnections();
m_statMeter->Init();
}
InstallErrorHandler();
}
void NZBGet::Final()
{
if (!m_reloading)
{
#ifndef DISABLE_TLS
TlsSocket::Final();
#endif
Connection::Final();
}
}
void NZBGet::CreateGlobals()
{
#ifdef WIN32
m_winConsole = std::make_unique<WinConsole>();
g_WinConsole = m_winConsole.get();
#endif
m_workState = std::make_unique<WorkState>();
g_WorkState = m_workState.get();
m_serviceCoordinator = std::make_unique<ServiceCoordinator>();
g_ServiceCoordinator = m_serviceCoordinator.get();
m_serverPool = std::make_unique<ServerPool>();
g_ServerPool = m_serverPool.get();
m_queueCoordinator = std::make_unique<QueueCoordinator>();
g_QueueCoordinator = m_queueCoordinator.get();
m_statMeter = std::make_unique<StatMeter>();
g_StatMeter = m_statMeter.get();
m_scanner = std::make_unique<Scanner>();
g_Scanner = m_scanner.get();
m_prePostProcessor = std::make_unique<PrePostProcessor>();
g_PrePostProcessor = m_prePostProcessor.get();
m_historyCoordinator = std::make_unique<HistoryCoordinator>();
g_HistoryCoordinator = m_historyCoordinator.get();
m_dupeCoordinator = std::make_unique<DupeCoordinator>();
g_DupeCoordinator = m_dupeCoordinator.get();
m_urlCoordinator = std::make_unique<UrlCoordinator>();
g_UrlCoordinator = m_urlCoordinator.get();
m_feedCoordinator = std::make_unique<FeedCoordinator>();
g_FeedCoordinator = m_feedCoordinator.get();
m_articleCache = std::make_unique<ArticleCache>();
g_ArticleCache = m_articleCache.get();
m_maintenance = std::make_unique<Maintenance>();
g_Maintenance = m_maintenance.get();
m_queueScriptCoordinator = std::make_unique<QueueScriptCoordinator>();
g_QueueScriptCoordinator = m_queueScriptCoordinator.get();
m_diskState = std::make_unique<DiskState>();
g_DiskState = m_diskState.get();
m_scriptConfig = std::make_unique<ScriptConfig>();
g_ScriptConfig = m_scriptConfig.get();
m_commandScriptLog = std::make_unique<CommandScriptLog>();
g_CommandScriptLog = m_commandScriptLog.get();
m_scheduler = std::make_unique<Scheduler>();
m_diskService = std::make_unique<DiskService>();
}
void NZBGet::BootConfig()
{
debug("Parsing command line");
m_commandLineParser = std::make_unique<CommandLineParser>(g_ArgumentCount, (const char**)(*g_Arguments));
if (m_commandLineParser->GetPrintVersion())
{
printf("nzbget version: %s\n", Util::VersionRevision());
exit(0);
}
if (m_commandLineParser->GetPrintUsage() || m_commandLineParser->GetErrors() || g_ArgumentCount <= 1)
{
m_commandLineParser->PrintUsage(((const char**)(*g_Arguments))[0]);
exit(m_commandLineParser->GetPrintUsage() ? 0 : 1);
}
debug("Reading options");
m_options = std::make_unique<Options>((*g_Arguments)[0], m_commandLineParser->GetConfigFilename(),
m_commandLineParser->GetNoConfig(), (Options::CmdOptList*)m_commandLineParser->GetOptionList(), this);
m_options->SetRemoteClientMode(m_commandLineParser->GetRemoteClientMode());
m_options->SetServerMode(m_commandLineParser->GetServerMode());
m_workState->SetPauseDownload(m_commandLineParser->GetPauseDownload());
m_workState->SetSpeedLimit(g_Options->GetDownloadRate());
m_log->InitOptions();
if (m_options->GetFatalError())
{
exit(1);
}
else if (m_options->GetConfigErrors() &&
m_commandLineParser->GetClientOperation() == CommandLineParser::opClientNoOperation)
{
info("Pausing all activities due to errors in configuration");
m_workState->SetPauseDownload(true);
m_workState->SetPausePostProcess(true);
m_workState->SetPauseScan(true);
}
m_serverPool->SetTimeout(m_options->GetArticleTimeout());
m_serverPool->SetRetryInterval(m_options->GetArticleInterval());
m_scriptConfig->InitOptions();
}
void NZBGet::Cleanup()
{
debug("Cleaning up global objects");
if (m_options && m_commandLineParser->GetDaemonMode() && !m_reloading && m_daemonized)
{
info("Deleting lock file");
FileSystem::DeleteFile(m_options->GetLockFile());
}
g_UrlCoordinator = nullptr;
g_PrePostProcessor = nullptr;
g_Scanner = nullptr;
g_HistoryCoordinator = nullptr;
g_DupeCoordinator = nullptr;
g_QueueCoordinator = nullptr;
g_DiskState = nullptr;
g_ScriptConfig = nullptr;
g_ServerPool = nullptr;
g_FeedCoordinator = nullptr;
g_ArticleCache = nullptr;
g_QueueScriptCoordinator = nullptr;
g_Maintenance = nullptr;
g_StatMeter = nullptr;
g_CommandScriptLog = nullptr;
#ifdef WIN32
g_WinConsole = nullptr;
#endif
}
void NZBGet::ProcessDirect()
{
#ifdef DEBUG
if (m_commandLineParser->GetTestBacktrace())
{
TestSegFault(); // never returns
}
#endif
if (m_commandLineParser->GetWebGet())
{
ProcessWebGet(); // never returns
}
if (m_commandLineParser->GetSigVerify())
{
ProcessSigVerify(); // never returns
}
// client request
if (m_commandLineParser->GetClientOperation() != CommandLineParser::opClientNoOperation)
{
ProcessClientRequest(); // never returns
}
if (m_commandLineParser->GetPrintOptions())
{
PrintOptions(); // never returns
}
}
void NZBGet::StartRemoteServer()
{
if (!m_options->GetServerMode())
{
return;
}
WebProcessor::Init();
m_remoteServer = std::make_unique<RemoteServer>(false);
m_remoteServer->Start();
if (m_options->GetSecureControl()
#ifndef WIN32
&& !(m_options->GetControlIp() && m_options->GetControlIp()[0] == '/')
#endif
)
{
m_remoteSecureServer = std::make_unique<RemoteServer>(true);
m_remoteSecureServer->Start();
}
}
void NZBGet::StopRemoteServer()
{
if (m_remoteServer)
{
debug("stopping RemoteServer");
m_remoteServer->Stop();
}
if (m_remoteSecureServer)
{
debug("stopping RemoteSecureServer");
m_remoteSecureServer->Stop();
}
int maxWaitMSec = 5000;
while (((m_remoteServer && m_remoteServer->IsRunning()) ||
(m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
maxWaitMSec > 0)
{
Util::Sleep(100);
maxWaitMSec -= 100;
}
if (m_remoteServer && m_remoteServer->IsRunning())
{
m_remoteServer->ForceStop();
}
if (m_remoteSecureServer && m_remoteSecureServer->IsRunning())
{
m_remoteSecureServer->ForceStop();
}
maxWaitMSec = 5000;
while (((m_remoteServer && m_remoteServer->IsRunning()) ||
(m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
maxWaitMSec > 0)
{
Util::Sleep(100);
maxWaitMSec -= 100;
}
if (m_remoteServer && m_remoteServer->IsRunning())
{
debug("Killing RemoteServer");
m_remoteServer->Kill();
}
if (m_remoteSecureServer && m_remoteSecureServer->IsRunning())
{
debug("Killing RemoteSecureServer");
m_remoteSecureServer->Kill();
}
debug("RemoteServer stopped");
}
void NZBGet::StartFrontend()
{
if (!m_commandLineParser->GetDaemonMode())
{
switch (m_options->GetOutputMode())
{
case Options::omNCurses:
#ifndef DISABLE_CURSES
m_frontend = std::make_unique<NCursesFrontend>();
break;
#endif
case Options::omColored:
m_frontend = std::make_unique<ColoredFrontend>();
break;
case Options::omLoggable:
m_frontend = std::make_unique<LoggableFrontend>();
break;
}
}
if (m_frontend)
{
m_frontend->Start();
}
}
void NZBGet::StopFrontend()
{
if (m_frontend)
{
if (!m_commandLineParser->GetRemoteClientMode())
{
debug("Stopping Frontend");
m_frontend->Stop();
}
while (m_frontend->IsRunning())
{
Util::Sleep(50);
}
debug("Frontend stopped");
}
}
void NZBGet::ProcessStandalone()
{
const char* category = m_commandLineParser->GetAddCategory() ? m_commandLineParser->GetAddCategory() : "";
NzbFile nzbFile(m_commandLineParser->GetArgFilename(), category);
if (!nzbFile.Parse())
{
printf("Parsing NZB-document %s failed\n\n",
m_commandLineParser->GetArgFilename() ? m_commandLineParser->GetArgFilename() : "N/A");
return;
}
std::unique_ptr<NzbInfo> nzbInfo = nzbFile.DetachNzbInfo();
m_scanner->InitPPParameters(category, nzbInfo->GetParameters(), false);
m_queueCoordinator->AddNzbFileToQueue(std::move(nzbInfo), nullptr, false);
}
void NZBGet::DoMainLoop()
{
debug("Entering main program loop");
#ifdef WIN32
m_winConsole->Start();
#endif
m_queueCoordinator->Start();
m_urlCoordinator->Start();
m_prePostProcessor->Start();
m_feedCoordinator->Start();
m_serviceCoordinator->Start();
if (m_options->GetArticleCache() > 0)
{
m_articleCache->Start();
}
// enter main program-loop
while (m_queueCoordinator->IsRunning() ||
m_urlCoordinator->IsRunning() ||
m_prePostProcessor->IsRunning() ||
m_feedCoordinator->IsRunning() ||
m_serviceCoordinator->IsRunning() ||
#ifdef WIN32
m_winConsole->IsRunning() ||
#endif
m_articleCache->IsRunning())
{
if (!m_options->GetServerMode() &&
!m_queueCoordinator->HasMoreJobs() &&
!m_urlCoordinator->HasMoreJobs() &&
!m_prePostProcessor->HasMoreJobs())
{
// Standalone-mode: download completed
if (!m_queueCoordinator->IsStopped())
{
m_queueCoordinator->Stop();
}
if (!m_urlCoordinator->IsStopped())
{
m_urlCoordinator->Stop();
}
if (!m_prePostProcessor->IsStopped())
{
m_prePostProcessor->Stop();
}
if (!m_feedCoordinator->IsStopped())
{
m_feedCoordinator->Stop();
}
if (!m_articleCache->IsStopped())
{
m_articleCache->Stop();
}
if (!m_serviceCoordinator->IsStopped())
{
m_serviceCoordinator->Stop();
}
}
Util::Sleep(100);
if (m_options->GetServerMode() && !m_stopped)
{
// wait for stop signal
Guard guard(m_waitMutex);
m_waitCond.Wait(m_waitMutex, [&]{ return m_stopped; });
}
}
debug("Main program loop terminated");
}
void NZBGet::Run(bool reload)
{
m_reloading = reload;
Init();
ProcessDirect();
StartRemoteServer();
StartFrontend();
if (!m_commandLineParser->GetRemoteClientMode())
{
if (!m_commandLineParser->GetServerMode())
{
ProcessStandalone();
}
DoMainLoop();
}
ScriptController::TerminateAll();
StopRemoteServer();
StopFrontend();
Final();
}
void NZBGet::ProcessClientRequest()
{
RemoteClient Client;
bool ok = false;
switch (m_commandLineParser->GetClientOperation())
{
case CommandLineParser::opClientRequestListFiles:
ok = Client.RequestServerList(true, false, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
break;
case CommandLineParser::opClientRequestListGroups:
ok = Client.RequestServerList(false, true, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
break;
case CommandLineParser::opClientRequestListStatus:
ok = Client.RequestServerList(false, false, nullptr);
break;
case CommandLineParser::opClientRequestDownloadPause:
ok = Client.RequestServerPauseUnpause(true, rpDownload);
break;
case CommandLineParser::opClientRequestDownloadUnpause:
ok = Client.RequestServerPauseUnpause(false, rpDownload);
break;
case CommandLineParser::opClientRequestSetRate:
ok = Client.RequestServerSetDownloadRate(m_commandLineParser->GetSetRate());
break;
case CommandLineParser::opClientRequestDumpDebug:
ok = Client.RequestServerDumpDebug();
break;
case CommandLineParser::opClientRequestEditQueue:
ok = Client.RequestServerEditQueue((DownloadQueue::EEditAction)m_commandLineParser->GetEditQueueAction(),
m_commandLineParser->GetEditQueueOffset(), m_commandLineParser->GetEditQueueText(),
m_commandLineParser->GetEditQueueIdList(), m_commandLineParser->GetEditQueueNameList(),
(ERemoteMatchMode)m_commandLineParser->GetMatchMode());
break;
case CommandLineParser::opClientRequestLog:
ok = Client.RequestServerLog(m_commandLineParser->GetLogLines());
break;
case CommandLineParser::opClientRequestShutdown:
ok = Client.RequestServerShutdown();
break;
case CommandLineParser::opClientRequestReload:
ok = Client.RequestServerReload();
break;
case CommandLineParser::opClientRequestDownload:
ok = Client.RequestServerDownload(m_commandLineParser->GetAddNzbFilename(), m_commandLineParser->GetArgFilename(),
m_commandLineParser->GetAddCategory(), m_commandLineParser->GetAddTop(), m_commandLineParser->GetAddPaused(), m_commandLineParser->GetAddPriority(),
m_commandLineParser->GetAddDupeKey(), m_commandLineParser->GetAddDupeMode(), m_commandLineParser->GetAddDupeScore());
break;
case CommandLineParser::opClientRequestVersion:
ok = Client.RequestServerVersion();
break;
case CommandLineParser::opClientRequestPostQueue:
ok = Client.RequestPostQueue();
break;
case CommandLineParser::opClientRequestWriteLog:
ok = Client.RequestWriteLog(m_commandLineParser->GetWriteLogKind(), m_commandLineParser->GetLastArg());
break;
case CommandLineParser::opClientRequestScanAsync:
ok = Client.RequestScan(false);
break;
case CommandLineParser::opClientRequestScanSync:
ok = Client.RequestScan(true);
break;
case CommandLineParser::opClientRequestPostPause:
ok = Client.RequestServerPauseUnpause(true, rpPostProcess);
break;
case CommandLineParser::opClientRequestPostUnpause:
ok = Client.RequestServerPauseUnpause(false, rpPostProcess);
break;
case CommandLineParser::opClientRequestScanPause:
ok = Client.RequestServerPauseUnpause(true, rpScan);
break;
case CommandLineParser::opClientRequestScanUnpause:
ok = Client.RequestServerPauseUnpause(false, rpScan);
break;
case CommandLineParser::opClientRequestHistory:
case CommandLineParser::opClientRequestHistoryAll:
ok = Client.RequestHistory(m_commandLineParser->GetClientOperation() == CommandLineParser::opClientRequestHistoryAll);
break;
case CommandLineParser::opClientNoOperation:
return;
}
exit(ok ? 0 : 1);
}
void NZBGet::ProcessWebGet()
{
WebDownloader downloader;
downloader.SetUrl(m_commandLineParser->GetLastArg());
downloader.SetForce(true);
downloader.SetRetry(false);
downloader.SetOutputFilename(m_commandLineParser->GetWebGetFilename());
downloader.SetInfoName("WebGet");
WebDownloader::EStatus status = downloader.DownloadWithRedirects(5);
bool ok = status == WebDownloader::adFinished;
exit(ok ? 0 : 1);
}
void NZBGet::ProcessSigVerify()
{
#ifdef HAVE_OPENSSL
bool ok = Maintenance::VerifySignature(m_commandLineParser->GetLastArg(),
m_commandLineParser->GetSigFilename(), m_commandLineParser->GetPubKeyFilename());
exit(ok ? 93 : 1);
#else
printf("ERROR: Could not verify signature, the program was compiled without OpenSSL support\n");
exit(1);
#endif
}
void NZBGet::Stop(bool reload)
{
m_reloading = reload;
if (!m_reloading)
{
info("Stopping, please wait...");
}
if (m_commandLineParser->GetRemoteClientMode())
{
if (m_frontend)
{
debug("Stopping Frontend");
m_frontend->Stop();
}
}
else
{
if (m_queueCoordinator)
{
debug("Stopping QueueCoordinator");
m_serviceCoordinator->Stop();
m_queueCoordinator->Stop();
m_urlCoordinator->Stop();
m_prePostProcessor->Stop();
m_feedCoordinator->Stop();
m_articleCache->Stop();
m_queueScriptCoordinator->Stop();
#ifdef WIN32
m_winConsole->Stop();
#endif
}
}
// trigger stop/reload signal
Guard guard(m_waitMutex);
m_stopped = true;
m_waitCond.NotifyAll();
}
void NZBGet::PrintOptions()
{
for (Options::OptEntry& optEntry : g_Options->GuardOptEntries())
{
printf("%s = \"%s\"\n", optEntry.GetName(), optEntry.GetValue());
}
exit(0);
}
#ifndef WIN32
void NZBGet::Daemonize()
{
int f = fork();
if (f < 0) exit(1); /* fork error */
if (f > 0) exit(0); /* parent exits */
/* child (daemon) continues */
m_daemonized = true;
// obtain a new process group
setsid();
// handle standart I/O
int d = open("/dev/null", O_RDWR);
dup2(d, 0);
dup2(d, 1);
dup2(d, 2);
close(d);
// set up lock-file
int lfp = -1;
if (!Util::EmptyStr(m_options->GetLockFile()))
{
lfp = open(m_options->GetLockFile(), O_RDWR | O_CREAT, 0640);
if (lfp < 0)
{
error("Starting daemon failed: could not create lock-file %s", m_options->GetLockFile());
exit(1);
}
#ifdef HAVE_LOCKF
if (lockf(lfp, F_TLOCK, 0) < 0)
#else
if (flock(lfp, LOCK_EX) < 0)
#endif
{
error("Starting daemon failed: could not acquire lock on lock-file %s", m_options->GetLockFile());
exit(1);
}
}
/* Drop user if there is one, and we were run as root */
if (getuid() == 0 || geteuid() == 0)
{
struct passwd *pw = getpwnam(m_options->GetDaemonUsername());
if (pw)
{
// Change owner of lock file
fchown(lfp, pw->pw_uid, pw->pw_gid);
// Set aux groups to null.
setgroups(0, (const gid_t*)0);
// Set primary group.
setgid(pw->pw_gid);
// Try setting aux groups correctly - not critical if this fails.
initgroups(m_options->GetDaemonUsername(), pw->pw_gid);
// Finally, set uid.
setuid(pw->pw_uid);
}
}
// record pid to lockfile
if (lfp > -1)
{
BString<100> str("%d\n", getpid());
write(lfp, str, strlen(str));
}
// ignore unwanted signals
signal(SIGCHLD, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
}
#endif
void NZBGet::AddNewsServer(int id, bool active, const char* name, const char* host,
int port, int ipVersion, const char* user, const char* pass, bool joinGroup, bool tls,
const char* cipher, int maxConnections, int retention, int level, int group, bool optional)
{
m_serverPool->AddServer(std::make_unique<NewsServer>(id, active, name, host, port, ipVersion, user, pass, joinGroup,
tls, cipher, maxConnections, retention, level, group, optional));
}
void NZBGet::AddFeed(int id, const char* name, const char* url, int interval, const char* filter,
bool backlog, bool pauseNzb, const char* category, int priority, const char* feedScript)
{
m_feedCoordinator->AddFeed(std::make_unique<FeedInfo>(id, name, url, backlog, interval, filter,
pauseNzb, category, priority, feedScript));
}
void NZBGet::AddTask(int id, int hours, int minutes, int weekDaysBits,
Options::ESchedulerCommand command, const char* param)
{
m_scheduler->AddTask(std::make_unique<Scheduler::Task>(id, hours, minutes, weekDaysBits,
(Scheduler::ECommand)command, param));
}
#ifdef WIN32
void NZBGet::SetupFirstStart()
{
m_winConsole->SetupFirstStart();
}
#endif
void RunMain()
{
bool reload = false;
while (!g_NZBGet || g_NZBGet->GetReloading())
{
g_NZBGet = std::make_unique<NZBGet>();
g_NZBGet->Run(reload);
reload = true;
}
g_NZBGet.reset();
}
void Reload()
{
info("Reloading...");
g_NZBGet->Stop(true);
}
void ExitProc()
{
g_NZBGet->Stop(false);
}