2
* This file is part of nzbget
4
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
5
* Copyright (C) 2007-2010 Andrei Prygounkov <hugbug@users.sourceforge.net>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
* $Date: 2010-01-30 15:43:58 +0100 (Sat, 30 Jan 2010) $
43
#include <sys/resource.h>
44
#ifdef HAVE_SYS_PRCTL_H
45
#include <sys/prctl.h>
49
#include <sys/types.h>
53
#ifndef DISABLE_PARCHECK
61
#include "ServerPool.h"
66
#include "ColoredFrontend.h"
67
#include "NCursesFrontend.h"
68
#include "QueueCoordinator.h"
69
#include "RemoteServer.h"
70
#include "RemoteClient.h"
71
#include "MessageBase.h"
72
#include "DiskState.h"
73
#include "PrePostProcessor.h"
74
#include "ParChecker.h"
75
#include "Scheduler.h"
78
#include "NTService.h"
84
void ProcessClientRequest();
86
void InstallSignalHandlers();
88
void PrintBacktrace();
89
#ifdef HAVE_SYS_PRCTL_H
90
void EnableDumpCore();
96
#ifndef DISABLE_PARCHECK
100
Thread* g_pFrontend = NULL;
101
Options* g_pOptions = NULL;
102
ServerPool* g_pServerPool = NULL;
103
QueueCoordinator* g_pQueueCoordinator = NULL;
104
RemoteServer* g_pRemoteServer = NULL;
105
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
106
DownloadQueueHolder* g_pDownloadQueueHolder = NULL;
108
PrePostProcessor* g_pPrePostProcessor = NULL;
109
DiskState* g_pDiskState = NULL;
110
Scheduler* g_pScheduler = NULL;
111
char* (*szEnvironmentVariables)[] = NULL;
116
int main(int argc, char *argv[], char *argp[])
120
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
121
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
122
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF
123
#ifdef DEBUG_CRTMEMLEAKS
124
| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
130
Util::InitVersionRevision();
133
InstallUninstallServiceCheck(argc, argv);
136
#ifndef DISABLE_PARCHECK
142
debug("nzbget %s", Util::VersionRevision());
144
g_pServerPool = new ServerPool();
145
g_pScheduler = new Scheduler();
148
debug("Reading options");
149
g_pOptions = new Options(argc, argv);
150
szEnvironmentVariables = (char*(*)[])argp;
153
if (g_pOptions->GetUMask() < 01000)
155
/* set newly created file permissions */
156
umask(g_pOptions->GetUMask());
160
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
162
debug("Deleting old log-file");
166
g_pLog->InitOptions();
168
if (g_pOptions->GetDaemonMode())
171
info("nzbget %s service-mode", Util::VersionRevision());
174
info("nzbget %s daemon-mode", Util::VersionRevision());
177
else if (g_pOptions->GetServerMode())
179
info("nzbget %s server-mode", Util::VersionRevision());
181
else if (g_pOptions->GetRemoteClientMode())
183
info("nzbget %s remote-mode", Util::VersionRevision());
186
if (!g_pOptions->GetRemoteClientMode())
188
g_pServerPool->InitConnections();
190
g_pServerPool->LogDebugInfo();
195
if (g_pOptions->GetDaemonMode())
201
#ifdef HAVE_SYS_PRCTL_H
202
if (g_pOptions->GetDumpCore())
213
_CrtDumpMemoryLeaks();
223
InstallSignalHandlers();
225
if (g_pOptions->GetTestBacktrace())
232
Connection::Init(g_pOptions->GetTLS() && !g_pOptions->GetRemoteClientMode() &&
233
(g_pOptions->GetClientOperation() == Options::opClientNoOperation));
236
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
238
ProcessClientRequest();
243
// Create the queue coordinator
244
if (!g_pOptions->GetRemoteClientMode())
246
g_pQueueCoordinator = new QueueCoordinator();
247
g_pDownloadSpeedMeter = g_pQueueCoordinator;
248
g_pDownloadQueueHolder = g_pQueueCoordinator;
251
// Setup the network-server
252
if (g_pOptions->GetServerMode())
254
g_pRemoteServer = new RemoteServer();
255
g_pRemoteServer->Start();
258
// Creating PrePostProcessor
259
if (!g_pOptions->GetRemoteClientMode())
261
g_pPrePostProcessor = new PrePostProcessor();
264
// Create the frontend
265
if (!g_pOptions->GetDaemonMode())
267
switch (g_pOptions->GetOutputMode())
269
case Options::omNCurses:
270
#ifndef DISABLE_CURSES
271
g_pFrontend = new NCursesFrontend();
274
case Options::omColored:
275
g_pFrontend = new ColoredFrontend();
277
case Options::omLoggable:
278
g_pFrontend = new LoggableFrontend();
283
// Starting a thread with the frontend
286
g_pFrontend->Start();
289
// Starting QueueCoordinator and PrePostProcessor
290
if (!g_pOptions->GetRemoteClientMode())
293
if (!g_pOptions->GetServerMode())
295
NZBFile* pNZBFile = NZBFile::CreateFromFile(g_pOptions->GetArgFilename(), g_pOptions->GetCategory() ? g_pOptions->GetCategory() : "");
298
abort("FATAL ERROR: Parsing NZB-document %s failed\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
301
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
305
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
307
g_pDiskState = new DiskState();
310
g_pQueueCoordinator->Start();
311
g_pPrePostProcessor->Start();
313
// enter main program-loop
314
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
316
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->HasMoreJobs() && !g_pPrePostProcessor->HasMoreJobs())
318
// Standalone-mode: download completed
319
if (!g_pQueueCoordinator->IsStopped())
321
g_pQueueCoordinator->Stop();
323
if (!g_pPrePostProcessor->IsStopped())
325
g_pPrePostProcessor->Stop();
331
// main program-loop is terminated
332
debug("QueueCoordinator stopped");
333
debug("PrePostProcessor stopped");
336
// Stop network-server
339
debug("stopping RemoteServer");
340
g_pRemoteServer->Stop();
341
int iMaxWaitMSec = 1000;
342
while (g_pRemoteServer->IsRunning() && iMaxWaitMSec > 0)
347
if (g_pRemoteServer->IsRunning())
349
debug("Killing RemoteServer");
350
g_pRemoteServer->Kill();
352
debug("RemoteServer stopped");
358
if (!g_pOptions->GetRemoteClientMode())
360
debug("Stopping Frontend");
363
while (g_pFrontend->IsRunning())
367
debug("Frontend stopped");
373
void ProcessClientRequest()
375
RemoteClient* Client = new RemoteClient();
377
switch (g_pOptions->GetClientOperation())
379
case Options::opClientRequestListFiles:
380
Client->RequestServerList(true, false);
383
case Options::opClientRequestListGroups:
384
Client->RequestServerList(false, true);
387
case Options::opClientRequestListStatus:
388
Client->RequestServerList(false, false);
391
case Options::opClientRequestDownloadPause:
392
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload);
395
case Options::opClientRequestDownloadUnpause:
396
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload);
399
case Options::opClientRequestDownload2Pause:
400
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload2);
403
case Options::opClientRequestDownload2Unpause:
404
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload2);
407
case Options::opClientRequestSetRate:
408
Client->RequestServerSetDownloadRate(g_pOptions->GetSetRate());
411
case Options::opClientRequestDumpDebug:
412
Client->RequestServerDumpDebug();
415
case Options::opClientRequestEditQueue:
416
Client->RequestServerEditQueue((eRemoteEditAction)g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
417
g_pOptions->GetEditQueueText(), g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(), true);
420
case Options::opClientRequestLog:
421
Client->RequestServerLog(g_pOptions->GetLogLines());
424
case Options::opClientRequestShutdown:
425
Client->RequestServerShutdown();
428
case Options::opClientRequestDownload:
429
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetCategory(), g_pOptions->GetAddTop());
432
case Options::opClientRequestVersion:
433
Client->RequestServerVersion();
436
case Options::opClientRequestPostQueue:
437
Client->RequestPostQueue();
440
case Options::opClientRequestWriteLog:
441
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
444
case Options::opClientRequestScan:
445
Client->RequestScan();
448
case Options::opClientRequestPostPause:
449
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionPostProcess);
452
case Options::opClientRequestPostUnpause:
453
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionPostProcess);
456
case Options::opClientRequestScanPause:
457
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionScan);
460
case Options::opClientRequestScanUnpause:
461
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionScan);
464
case Options::opClientRequestHistory:
465
Client->RequestHistory();
468
case Options::opClientNoOperation:
477
info("Stopping, please wait...");
478
if (g_pOptions->GetRemoteClientMode())
482
debug("Stopping Frontend");
488
if (g_pQueueCoordinator)
490
debug("Stopping QueueCoordinator");
491
g_pQueueCoordinator->Stop();
492
g_pPrePostProcessor->Stop();
499
typedef void(*sighandler)(int);
500
std::vector<sighandler> SignalProcList;
506
void SignalProc(int iSignal)
511
signal(SIGINT, SIG_DFL); // Reset the signal handler
516
signal(SIGTERM, SIG_DFL); // Reset the signal handler
530
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
535
// printf("Signal %i received\n", iSignal);
536
if (SignalProcList[iSignal - 1])
538
SignalProcList[iSignal - 1](iSignal);
545
void InstallSignalHandlers()
547
signal(SIGINT, SignalProc);
548
signal(SIGTERM, SignalProc);
549
signal(SIGCHLD, SignalProc);
550
signal(SIGPIPE, SIG_IGN);
552
SignalProcList.clear();
553
for (int i = 1; i <= 32; i++)
555
SignalProcList.push_back((sighandler)signal(i, SignalProc));
557
signal(SIGWINCH, SIG_DFL);
561
void PrintBacktrace()
563
#ifdef HAVE_BACKTRACE
564
printf("Segmentation fault, tracing...\n");
571
size = backtrace(array, 100);
572
strings = backtrace_symbols(array, size);
574
// first trace to screen
575
printf("Obtained %zd stack frames\n", size);
576
for (i = 0; i < size; i++)
578
printf("%s\n", strings[i]);
582
error("Segmentation fault, tracing...");
583
error("Obtained %zd stack frames", size);
584
for (i = 0; i < size; i++)
586
error("%s", strings[i]);
591
error("Segmentation fault");
603
#ifdef HAVE_SYS_PRCTL_H
605
* activates the creation of core-files
607
void EnableDumpCore()
610
rlim.rlim_cur= RLIM_INFINITY;
611
rlim.rlim_max= RLIM_INFINITY;
612
setrlimit(RLIMIT_CORE, &rlim);
613
prctl(PR_SET_DUMPABLE, 1);
620
debug("Cleaning up global objects");
622
debug("Deleting QueueCoordinator");
623
if (g_pQueueCoordinator)
625
delete g_pQueueCoordinator;
626
g_pQueueCoordinator = NULL;
628
debug("QueueCoordinator deleted");
630
debug("Deleting RemoteServer");
633
delete g_pRemoteServer;
634
g_pRemoteServer = NULL;
636
debug("RemoteServer deleted");
638
debug("Deleting PrePostProcessor");
639
if (g_pPrePostProcessor)
641
delete g_pPrePostProcessor;
642
g_pPrePostProcessor = NULL;
644
debug("PrePostProcessor deleted");
646
debug("Deleting Frontend");
652
debug("Frontend deleted");
654
debug("Deleting DiskState");
660
debug("DiskState deleted");
662
debug("Deleting Options");
665
if (g_pOptions->GetDaemonMode())
667
info("Deleting lock file");
668
remove(g_pOptions->GetLockFile());
673
debug("Options deleted");
675
debug("Deleting ServerPool");
678
delete g_pServerPool;
679
g_pServerPool = NULL;
681
debug("ServerPool deleted");
683
debug("Deleting Scheduler");
689
debug("Scheduler deleted");
695
debug("Global objects cleaned up");
709
if (getppid() == 1) return; /* already a daemon */
711
if (i < 0) exit(1); /* fork error */
712
if (i > 0) exit(0); /* parent exits */
713
/* child (daemon) continues */
714
setsid(); /* obtain a new process group */
715
for (i = getdtablesize();i >= 0;--i) close(i); /* close all descriptors */
716
i = open("/dev/null", O_RDWR); dup(i); dup(i); /* handle standart I/O */
717
chdir(g_pOptions->GetDestDir()); /* change running directory */
718
lfp = open(g_pOptions->GetLockFile(), O_RDWR | O_CREAT, 0640);
719
if (lfp < 0) exit(1); /* can not open */
720
if (lockf(lfp, F_TLOCK, 0) < 0) exit(0); /* can not lock */
722
/* Drop user if there is one, and we were run as root */
723
if ( getuid() == 0 || geteuid() == 0 )
725
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUserName());
728
fchown(lfp, pw->pw_uid, pw->pw_gid); /* change owner of lock file */
729
setgroups( 0, (const gid_t*) 0 ); /* Set aux groups to null. */
730
setgid(pw->pw_gid); /* Set primary group. */
731
/* Try setting aux groups correctly - not critical if this fails. */
732
initgroups( g_pOptions->GetDaemonUserName(),pw->pw_gid);
733
/* Finally, set uid. */
738
/* first instance continues */
739
sprintf(str, "%d\n", getpid());
740
write(lfp, str, strlen(str)); /* record pid to lockfile */
741
signal(SIGCHLD, SIG_IGN); /* ignore child */
742
signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
743
signal(SIGTTOU, SIG_IGN);
744
signal(SIGTTIN, SIG_IGN);
748
#ifndef DISABLE_PARCHECK
749
class NullStreamBuf : public std::streambuf
752
int sputc ( char c ) { return (int) c; }
753
} NullStreamBufInstance;
757
// libpar2 prints messages to c++ standard output stream (std::cout).
758
// However we do not want these messages to be printed.
759
// Since we do not use std::cout in nzbget we just disable it.
760
std::cout.rdbuf(&NullStreamBufInstance);