Files
nzbget/daemon/main/nzbget.cpp
Andrey Prygunkov ec17d119a1 #115: put all external headers together
into “nzbget.h”
2015-11-19 23:51:02 +01:00

972 lines
23 KiB
C++

/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-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 "ServerPool.h"
#include "Log.h"
#include "NzbFile.h"
#include "Options.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 "ParChecker.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 "StackTrace.h"
#ifdef WIN32
#include "WinService.h"
#include "WinConsole.h"
#include "WebDownloader.h"
#endif
#ifdef ENABLE_TESTS
#include "TestMain.h"
#endif
// Prototypes
void RunMain();
void Run(bool reload);
void Reload();
void Cleanup();
void ProcessClientRequest();
void ProcessWebGet();
void ProcessSigVerify();
#ifndef WIN32
void Daemonize();
#endif
#ifndef DISABLE_PARCHECK
void DisableCout();
#endif
void BootConfig();
Thread* g_Frontend = NULL;
CommandLineParser* g_CommandLineParser = NULL;
ServerPool* g_ServerPool = NULL;
QueueCoordinator* g_QueueCoordinator = NULL;
UrlCoordinator* g_UrlCoordinator = NULL;
RemoteServer* g_RemoteServer = NULL;
RemoteServer* g_RemoteSecureServer = NULL;
StatMeter* g_StatMeter = NULL;
PrePostProcessor* g_PrePostProcessor = NULL;
HistoryCoordinator* g_HistoryCoordinator = NULL;
DupeCoordinator* g_DupeCoordinator = NULL;
DiskState* g_DiskState = NULL;
Scheduler* g_Scheduler = NULL;
Scanner* g_Scanner = NULL;
FeedCoordinator* g_FeedCoordinator = NULL;
Maintenance* g_Maintenance = NULL;
ArticleCache* g_ArticleCache = NULL;
QueueScriptCoordinator* g_QueueScriptCoordinator = NULL;
ServiceCoordinator* g_ServiceCoordinator = NULL;
DiskService* g_DiskService = NULL;
int g_ArgumentCount;
char* (*g_EnvironmentVariables)[] = NULL;
char* (*g_Arguments)[] = NULL;
bool g_Reloading = true;
#ifdef WIN32
WinConsole* g_WinConsole = NULL;
#endif
/*
* Main loop
*/
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();
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
#ifdef WIN32
InstallUninstallServiceCheck(argc, argv);
#endif
#ifndef DISABLE_PARCHECK
DisableCout();
#endif
srand(time(NULL));
#ifdef WIN32
for (int i=0; i < argc; i++)
{
if (!strcmp(argv[i], "-D"))
{
StartService(RunMain);
return 0;
}
}
#endif
RunMain();
return 0;
}
void RunMain()
{
// we need to save and later restore current directory each time
// the program is reloaded (RPC-Method "reload") in order for
// config to properly load in a case relative paths are used
// in command line
char curDir[MAX_PATH + 1];
Util::GetCurrentDirectory(curDir, sizeof(curDir));
bool reload = false;
while (g_Reloading)
{
g_Reloading = false;
Util::SetCurrentDirectory(curDir);
Run(reload);
reload = true;
}
}
void Run(bool reload)
{
Log::Init();
debug("nzbget %s", Util::VersionRevision());
if (!reload)
{
Thread::Init();
}
#ifdef WIN32
g_WinConsole = new WinConsole();
g_WinConsole->InitAppMode();
#endif
g_ServiceCoordinator = new ServiceCoordinator();
g_ServerPool = new ServerPool();
g_Scheduler = new Scheduler();
g_QueueCoordinator = new QueueCoordinator();
g_StatMeter = new StatMeter();
g_Scanner = new Scanner();
g_PrePostProcessor = new PrePostProcessor();
g_HistoryCoordinator = new HistoryCoordinator();
g_DupeCoordinator = new DupeCoordinator();
g_UrlCoordinator = new UrlCoordinator();
g_FeedCoordinator = new FeedCoordinator();
g_ArticleCache = new ArticleCache();
g_Maintenance = new Maintenance();
g_QueueScriptCoordinator = new QueueScriptCoordinator();
g_DiskService = new DiskService();
BootConfig();
#ifndef WIN32
if (g_Options->GetUMask() < 01000)
{
/* set newly created file permissions */
umask(g_Options->GetUMask());
}
#endif
g_Scanner->InitOptions();
g_QueueScriptCoordinator->InitOptions();
if (g_CommandLineParser->GetDaemonMode())
{
#ifdef WIN32
info("nzbget %s service-mode", Util::VersionRevision());
#else
if (!reload)
{
Daemonize();
}
info("nzbget %s daemon-mode", Util::VersionRevision());
#endif
}
else if (g_Options->GetServerMode())
{
info("nzbget %s server-mode", Util::VersionRevision());
}
else if (g_CommandLineParser->GetRemoteClientMode())
{
info("nzbget %s remote-mode", Util::VersionRevision());
}
if (!reload)
{
Connection::Init();
}
if (!g_CommandLineParser->GetRemoteClientMode())
{
g_ServerPool->InitConnections();
g_StatMeter->Init();
}
InstallErrorHandler();
#ifdef DEBUG
if (g_CommandLineParser->GetTestBacktrace())
{
TestSegFault();
}
#endif
if (g_CommandLineParser->GetWebGet())
{
ProcessWebGet();
return;
}
if (g_CommandLineParser->GetSigVerify())
{
ProcessSigVerify();
return;
}
// client request
if (g_CommandLineParser->GetClientOperation() != CommandLineParser::opClientNoOperation)
{
ProcessClientRequest();
Cleanup();
return;
}
// Setup the network-server
if (g_Options->GetServerMode())
{
WebProcessor::Init();
g_RemoteServer = new RemoteServer(false);
g_RemoteServer->Start();
if (g_Options->GetSecureControl())
{
g_RemoteSecureServer = new RemoteServer(true);
g_RemoteSecureServer->Start();
}
}
// Create the frontend
if (!g_CommandLineParser->GetDaemonMode())
{
switch (g_Options->GetOutputMode())
{
case Options::omNCurses:
#ifndef DISABLE_CURSES
g_Frontend = new NCursesFrontend();
break;
#endif
case Options::omColored:
g_Frontend = new ColoredFrontend();
break;
case Options::omLoggable:
g_Frontend = new LoggableFrontend();
break;
}
}
// Starting a thread with the frontend
if (g_Frontend)
{
g_Frontend->Start();
}
// Starting QueueCoordinator and PrePostProcessor
if (!g_CommandLineParser->GetRemoteClientMode())
{
// Standalone-mode
if (!g_CommandLineParser->GetServerMode())
{
const char* category = g_CommandLineParser->GetAddCategory() ? g_CommandLineParser->GetAddCategory() : "";
NzbFile* nzbFile = new NzbFile(g_CommandLineParser->GetArgFilename(), category);
if (!nzbFile->Parse())
{
printf("Parsing NZB-document %s failed\n\n", g_CommandLineParser->GetArgFilename() ? g_CommandLineParser->GetArgFilename() : "N/A");
delete nzbFile;
return;
}
g_Scanner->InitPPParameters(category, nzbFile->GetNzbInfo()->GetParameters(), false);
g_QueueCoordinator->AddNzbFileToQueue(nzbFile, NULL, false);
delete nzbFile;
}
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
{
g_DiskState = new DiskState();
}
#ifdef WIN32
g_WinConsole->Start();
#endif
g_QueueCoordinator->Start();
g_UrlCoordinator->Start();
g_PrePostProcessor->Start();
g_FeedCoordinator->Start();
g_ServiceCoordinator->Start();
if (g_Options->GetArticleCache() > 0)
{
g_ArticleCache->Start();
}
// enter main program-loop
while (g_QueueCoordinator->IsRunning() ||
g_UrlCoordinator->IsRunning() ||
g_PrePostProcessor->IsRunning() ||
g_FeedCoordinator->IsRunning() ||
g_ServiceCoordinator->IsRunning() ||
#ifdef WIN32
g_WinConsole->IsRunning() ||
#endif
g_ArticleCache->IsRunning())
{
if (!g_Options->GetServerMode() &&
!g_QueueCoordinator->HasMoreJobs() &&
!g_UrlCoordinator->HasMoreJobs() &&
!g_PrePostProcessor->HasMoreJobs())
{
// Standalone-mode: download completed
if (!g_QueueCoordinator->IsStopped())
{
g_QueueCoordinator->Stop();
}
if (!g_UrlCoordinator->IsStopped())
{
g_UrlCoordinator->Stop();
}
if (!g_PrePostProcessor->IsStopped())
{
g_PrePostProcessor->Stop();
}
if (!g_FeedCoordinator->IsStopped())
{
g_FeedCoordinator->Stop();
}
if (!g_ArticleCache->IsStopped())
{
g_ArticleCache->Stop();
}
if (!g_ServiceCoordinator->IsStopped())
{
g_ServiceCoordinator->Stop();
}
}
usleep(100 * 1000);
}
// main program-loop is terminated
debug("QueueCoordinator stopped");
debug("UrlCoordinator stopped");
debug("PrePostProcessor stopped");
debug("FeedCoordinator stopped");
debug("ServiceCoordinator stopped");
debug("ArticleCache stopped");
}
ScriptController::TerminateAll();
// Stop network-server
if (g_RemoteServer)
{
debug("stopping RemoteServer");
g_RemoteServer->Stop();
int maxWaitMSec = 1000;
while (g_RemoteServer->IsRunning() && maxWaitMSec > 0)
{
usleep(100 * 1000);
maxWaitMSec -= 100;
}
if (g_RemoteServer->IsRunning())
{
debug("Killing RemoteServer");
g_RemoteServer->Kill();
}
debug("RemoteServer stopped");
}
if (g_RemoteSecureServer)
{
debug("stopping RemoteSecureServer");
g_RemoteSecureServer->Stop();
int maxWaitMSec = 1000;
while (g_RemoteSecureServer->IsRunning() && maxWaitMSec > 0)
{
usleep(100 * 1000);
maxWaitMSec -= 100;
}
if (g_RemoteSecureServer->IsRunning())
{
debug("Killing RemoteSecureServer");
g_RemoteSecureServer->Kill();
}
debug("RemoteSecureServer stopped");
}
// Stop Frontend
if (g_Frontend)
{
if (!g_CommandLineParser->GetRemoteClientMode())
{
debug("Stopping Frontend");
g_Frontend->Stop();
}
while (g_Frontend->IsRunning())
{
usleep(50 * 1000);
}
debug("Frontend stopped");
}
Cleanup();
}
class OptionsExtender : public Options::Extender
{
protected:
#ifdef WIN32
virtual void SetupFirstStart()
{
g_WinConsole->SetupFirstStart();
}
#endif
virtual void AddNewsServer(int id, bool active, const char* name, const char* host,
int port, const char* user, const char* pass, bool joinGroup,
bool tls, const char* cipher, int maxConnections, int retention,
int level, int group)
{
g_ServerPool->AddServer(new NewsServer(id, active, name, host, port, user, pass, joinGroup,
tls, cipher, maxConnections, retention, level, group));
}
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)
{
g_FeedCoordinator->AddFeed(new FeedInfo(id, name, url, backlog, interval, filter, pauseNzb, category, priority, feedScript));
}
virtual void AddTask(int id, int hours, int minutes, int weekDaysBits,
Options::ESchedulerCommand command, const char* param)
{
g_Scheduler->AddTask(new Scheduler::Task(id, hours, minutes, weekDaysBits, (Scheduler::ECommand)command, param));
}
} g_OptionsExtender;
void BootConfig()
{
debug("Parsing command line");
g_CommandLineParser = new CommandLineParser(g_ArgumentCount, (const char**)(*g_Arguments));
if (g_CommandLineParser->GetPrintVersion())
{
printf("nzbget version: %s\n", Util::VersionRevision());
exit(0);
}
if (g_CommandLineParser->GetPrintUsage() || g_CommandLineParser->GetErrors() || g_ArgumentCount <= 1)
{
g_CommandLineParser->PrintUsage(((const char**)(*g_Arguments))[0]);
exit(0);
}
debug("Reading options");
g_Options = new Options((*g_Arguments)[0], g_CommandLineParser->GetConfigFilename(),
g_CommandLineParser->GetNoConfig(), (Options::CmdOptList*)g_CommandLineParser->GetOptionList(),
&g_OptionsExtender);
g_Options->SetRemoteClientMode(g_CommandLineParser->GetRemoteClientMode());
g_Options->SetServerMode(g_CommandLineParser->GetServerMode());
g_Options->SetPauseDownload(g_CommandLineParser->GetPauseDownload());
g_Log->InitOptions();
if (g_Options->GetFatalError())
{
exit(1);
}
else if (g_Options->GetConfigErrors() &&
g_CommandLineParser->GetClientOperation() == CommandLineParser::opClientNoOperation)
{
info("Pausing all activities due to errors in configuration");
g_Options->SetPauseDownload(true);
g_Options->SetPausePostProcess(true);
g_Options->SetPauseScan(true);
}
g_ServerPool->SetTimeout(g_Options->GetArticleTimeout());
g_ServerPool->SetRetryInterval(g_Options->GetRetryInterval());
g_ScriptConfig = new ScriptConfig();
}
void ProcessClientRequest()
{
RemoteClient* Client = new RemoteClient();
switch (g_CommandLineParser->GetClientOperation())
{
case CommandLineParser::opClientRequestListFiles:
Client->RequestServerList(true, false, g_CommandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? g_CommandLineParser->GetEditQueueText() : NULL);
break;
case CommandLineParser::opClientRequestListGroups:
Client->RequestServerList(false, true, g_CommandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? g_CommandLineParser->GetEditQueueText() : NULL);
break;
case CommandLineParser::opClientRequestListStatus:
Client->RequestServerList(false, false, NULL);
break;
case CommandLineParser::opClientRequestDownloadPause:
Client->RequestServerPauseUnpause(true, rpDownload);
break;
case CommandLineParser::opClientRequestDownloadUnpause:
Client->RequestServerPauseUnpause(false, rpDownload);
break;
case CommandLineParser::opClientRequestSetRate:
Client->RequestServerSetDownloadRate(g_CommandLineParser->GetSetRate());
break;
case CommandLineParser::opClientRequestDumpDebug:
Client->RequestServerDumpDebug();
break;
case CommandLineParser::opClientRequestEditQueue:
Client->RequestServerEditQueue((DownloadQueue::EEditAction)g_CommandLineParser->GetEditQueueAction(),
g_CommandLineParser->GetEditQueueOffset(), g_CommandLineParser->GetEditQueueText(),
g_CommandLineParser->GetEditQueueIdList(), g_CommandLineParser->GetEditQueueIdCount(),
g_CommandLineParser->GetEditQueueNameList(), (ERemoteMatchMode)g_CommandLineParser->GetMatchMode());
break;
case CommandLineParser::opClientRequestLog:
Client->RequestServerLog(g_CommandLineParser->GetLogLines());
break;
case CommandLineParser::opClientRequestShutdown:
Client->RequestServerShutdown();
break;
case CommandLineParser::opClientRequestReload:
Client->RequestServerReload();
break;
case CommandLineParser::opClientRequestDownload:
Client->RequestServerDownload(g_CommandLineParser->GetAddNzbFilename(), g_CommandLineParser->GetArgFilename(),
g_CommandLineParser->GetAddCategory(), g_CommandLineParser->GetAddTop(), g_CommandLineParser->GetAddPaused(), g_CommandLineParser->GetAddPriority(),
g_CommandLineParser->GetAddDupeKey(), g_CommandLineParser->GetAddDupeMode(), g_CommandLineParser->GetAddDupeScore());
break;
case CommandLineParser::opClientRequestVersion:
Client->RequestServerVersion();
break;
case CommandLineParser::opClientRequestPostQueue:
Client->RequestPostQueue();
break;
case CommandLineParser::opClientRequestWriteLog:
Client->RequestWriteLog(g_CommandLineParser->GetWriteLogKind(), g_CommandLineParser->GetLastArg());
break;
case CommandLineParser::opClientRequestScanAsync:
Client->RequestScan(false);
break;
case CommandLineParser::opClientRequestScanSync:
Client->RequestScan(true);
break;
case CommandLineParser::opClientRequestPostPause:
Client->RequestServerPauseUnpause(true, rpPostProcess);
break;
case CommandLineParser::opClientRequestPostUnpause:
Client->RequestServerPauseUnpause(false, rpPostProcess);
break;
case CommandLineParser::opClientRequestScanPause:
Client->RequestServerPauseUnpause(true, rpScan);
break;
case CommandLineParser::opClientRequestScanUnpause:
Client->RequestServerPauseUnpause(false, rpScan);
break;
case CommandLineParser::opClientRequestHistory:
case CommandLineParser::opClientRequestHistoryAll:
Client->RequestHistory(g_CommandLineParser->GetClientOperation() == CommandLineParser::opClientRequestHistoryAll);
break;
case CommandLineParser::opClientNoOperation:
break;
}
delete Client;
}
void ProcessWebGet()
{
WebDownloader downloader;
downloader.SetUrl(g_CommandLineParser->GetLastArg());
downloader.SetForce(true);
downloader.SetRetry(false);
downloader.SetOutputFilename(g_CommandLineParser->GetWebGetFilename());
downloader.SetInfoName("WebGet");
WebDownloader::EStatus status = downloader.DownloadWithRedirects(5);
bool ok = status == WebDownloader::adFinished;
exit(ok ? 0 : 1);
}
void ProcessSigVerify()
{
#ifdef HAVE_OPENSSL
bool ok = Maintenance::VerifySignature(g_CommandLineParser->GetLastArg(),
g_CommandLineParser->GetSigFilename(), g_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 ExitProc()
{
if (!g_Reloading)
{
info("Stopping, please wait...");
}
if (g_CommandLineParser->GetRemoteClientMode())
{
if (g_Frontend)
{
debug("Stopping Frontend");
g_Frontend->Stop();
}
}
else
{
if (g_QueueCoordinator)
{
debug("Stopping QueueCoordinator");
g_ServiceCoordinator->Stop();
g_QueueCoordinator->Stop();
g_UrlCoordinator->Stop();
g_PrePostProcessor->Stop();
g_FeedCoordinator->Stop();
g_ArticleCache->Stop();
g_QueueScriptCoordinator->Stop();
#ifdef WIN32
g_WinConsole->Stop();
#endif
}
}
}
void Reload()
{
g_Reloading = true;
info("Reloading...");
ExitProc();
}
void Cleanup()
{
debug("Cleaning up global objects");
debug("Deleting UrlCoordinator");
delete g_UrlCoordinator;
g_UrlCoordinator = NULL;
debug("UrlCoordinator deleted");
debug("Deleting RemoteServer");
delete g_RemoteServer;
g_RemoteServer = NULL;
debug("RemoteServer deleted");
debug("Deleting RemoteSecureServer");
delete g_RemoteSecureServer;
g_RemoteSecureServer = NULL;
debug("RemoteSecureServer deleted");
debug("Deleting PrePostProcessor");
delete g_PrePostProcessor;
g_PrePostProcessor = NULL;
delete g_Scanner;
g_Scanner = NULL;
debug("PrePostProcessor deleted");
debug("Deleting HistoryCoordinator");
delete g_HistoryCoordinator;
g_HistoryCoordinator = NULL;
debug("HistoryCoordinator deleted");
debug("Deleting DupeCoordinator");
delete g_DupeCoordinator;
g_DupeCoordinator = NULL;
debug("DupeCoordinator deleted");
debug("Deleting Frontend");
delete g_Frontend;
g_Frontend = NULL;
debug("Frontend deleted");
debug("Deleting QueueCoordinator");
delete g_QueueCoordinator;
g_QueueCoordinator = NULL;
debug("QueueCoordinator deleted");
debug("Deleting DiskState");
delete g_DiskState;
g_DiskState = NULL;
debug("DiskState deleted");
debug("Deleting Options");
if (g_Options)
{
if (g_CommandLineParser->GetDaemonMode() && !g_Reloading)
{
info("Deleting lock file");
remove(g_Options->GetLockFile());
}
delete g_Options;
}
debug("Options deleted");
debug("Deleting CommandLineParser");
if (g_CommandLineParser)
{
delete g_CommandLineParser;
g_CommandLineParser = NULL;
}
debug("CommandLineParser deleted");
debug("Deleting ScriptConfig");
if (g_ScriptConfig)
{
delete g_ScriptConfig;
g_ScriptConfig = NULL;
}
debug("ScriptConfig deleted");
debug("Deleting ServerPool");
delete g_ServerPool;
g_ServerPool = NULL;
debug("ServerPool deleted");
debug("Deleting Scheduler");
delete g_Scheduler;
g_Scheduler = NULL;
debug("Scheduler deleted");
debug("Deleting FeedCoordinator");
delete g_FeedCoordinator;
g_FeedCoordinator = NULL;
debug("FeedCoordinator deleted");
debug("Deleting ArticleCache");
delete g_ArticleCache;
g_ArticleCache = NULL;
debug("ArticleCache deleted");
debug("Deleting QueueScriptCoordinator");
delete g_QueueScriptCoordinator;
g_QueueScriptCoordinator = NULL;
debug("QueueScriptCoordinator deleted");
debug("Deleting Maintenance");
delete g_Maintenance;
g_Maintenance = NULL;
debug("Maintenance deleted");
debug("Deleting StatMeter");
delete g_StatMeter;
g_StatMeter = NULL;
debug("StatMeter deleted");
debug("Deleting ServiceCoordinator");
delete g_ServiceCoordinator;
g_ServiceCoordinator = NULL;
debug("ServiceCoordinator deleted");
debug("Deleting DiskService");
delete g_DiskService;
g_DiskService = NULL;
debug("DiskService deleted");
if (!g_Reloading)
{
Connection::Final();
Thread::Final();
}
#ifdef WIN32
delete g_WinConsole;
g_WinConsole = NULL;
#endif
debug("Global objects cleaned up");
Log::Final();
}
#ifndef WIN32
void Daemonize()
{
int f = fork();
if (f < 0) exit(1); /* fork error */
if (f > 0) exit(0); /* parent exits */
/* child (daemon) continues */
// obtain a new process group
setsid();
// close all descriptors
for (int i = getdtablesize(); i >= 0; --i)
{
close(i);
}
// handle standart I/O
int d = open("/dev/null", O_RDWR);
dup(d);
dup(d);
// change running directory
chdir(g_Options->GetDestDir());
// set up lock-file
int lfp = -1;
if (!Util::EmptyStr(g_Options->GetLockFile()))
{
lfp = open(g_Options->GetLockFile(), O_RDWR | O_CREAT, 0640);
if (lfp < 0)
{
error("Starting daemon failed: could not create lock-file %s", g_Options->GetLockFile());
exit(1);
}
if (lockf(lfp, F_TLOCK, 0) < 0)
{
error("Starting daemon failed: could not acquire lock on lock-file %s", g_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(g_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(g_Options->GetDaemonUsername(), pw->pw_gid);
// Finally, set uid.
setuid(pw->pw_uid);
}
}
// record pid to lockfile
if (lfp > -1)
{
char str[10];
sprintf(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
#ifndef DISABLE_PARCHECK
class NullStreamBuf : public std::streambuf
{
public:
int sputc ( char c ) { return (int) c; }
} NullStreamBufInstance;
void DisableCout()
{
// libpar2 prints messages to c++ standard output stream (std::cout).
// However we do not want these messages to be printed.
// Since we do not use std::cout in nzbget we just disable it.
std::cout.rdbuf(&NullStreamBufInstance);
}
#endif