1 /*
2  *  This file is part of nzbget. See <http://nzbget.net>.
3  *
4  *  Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
5  *  Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
6  *
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.
11  *
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.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include "nzbget.h"
23 #include "ServerPool.h"
24 #include "Log.h"
25 #include "NzbFile.h"
26 #include "Options.h"
27 #include "WorkState.h"
28 #include "CommandLineParser.h"
29 #include "ScriptConfig.h"
30 #include "Thread.h"
31 #include "ColoredFrontend.h"
32 #include "NCursesFrontend.h"
33 #include "QueueCoordinator.h"
34 #include "UrlCoordinator.h"
35 #include "RemoteServer.h"
36 #include "WebServer.h"
37 #include "RemoteClient.h"
38 #include "MessageBase.h"
39 #include "DiskState.h"
40 #include "PrePostProcessor.h"
41 #include "HistoryCoordinator.h"
42 #include "DupeCoordinator.h"
43 #include "Scheduler.h"
44 #include "Scanner.h"
45 #include "FeedCoordinator.h"
46 #include "Service.h"
47 #include "DiskService.h"
48 #include "Maintenance.h"
49 #include "ArticleWriter.h"
50 #include "StatMeter.h"
51 #include "QueueScript.h"
52 #include "Util.h"
53 #include "FileSystem.h"
54 #include "StackTrace.h"
55 #include "CommandScript.h"
56 #include "YEncode.h"
57 #ifdef WIN32
58 #include "WinService.h"
59 #include "WinConsole.h"
60 #include "WebDownloader.h"
61 #endif
62 #ifdef ENABLE_TESTS
63 #include "TestMain.h"
64 #endif
65 #ifndef DISABLE_NSERV
66 #include "NServMain.h"
67 #endif
68 
69 // Prototypes
70 void RunMain();
71 
72 // Globals
73 Log* g_Log;
74 Options* g_Options;
75 WorkState* g_WorkState;
76 ServerPool* g_ServerPool;
77 QueueCoordinator* g_QueueCoordinator;
78 UrlCoordinator* g_UrlCoordinator;
79 StatMeter* g_StatMeter;
80 PrePostProcessor* g_PrePostProcessor;
81 HistoryCoordinator* g_HistoryCoordinator;
82 DupeCoordinator* g_DupeCoordinator;
83 DiskState* g_DiskState;
84 Scanner* g_Scanner;
85 FeedCoordinator* g_FeedCoordinator;
86 Maintenance* g_Maintenance;
87 ArticleCache* g_ArticleCache;
88 QueueScriptCoordinator* g_QueueScriptCoordinator;
89 ServiceCoordinator* g_ServiceCoordinator;
90 ScriptConfig* g_ScriptConfig;
91 CommandScriptLog* g_CommandScriptLog;
92 #ifdef WIN32
93 WinConsole* g_WinConsole;
94 #endif
95 int g_ArgumentCount;
96 char* (*g_EnvironmentVariables)[] = nullptr;
97 char* (*g_Arguments)[] = nullptr;
98 
99 
100 /*
101  * Main entry point
102  */
main(int argc,char * argv[],char * argp[])103 int main(int argc, char *argv[], char *argp[])
104 {
105 #ifdef WIN32
106 #ifdef _DEBUG
107 	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
108 	_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
109 	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF
110 #ifdef DEBUG_CRTMEMLEAKS
111 		| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
112 #endif
113 		);
114 #endif
115 #endif
116 
117 	Util::Init();
118 	YEncode::init();
119 
120 	g_ArgumentCount = argc;
121 	g_Arguments = (char*(*)[])argv;
122 	g_EnvironmentVariables = (char*(*)[])argp;
123 
124 	if (argc > 1 && (!strcmp(argv[1], "-tests") || !strcmp(argv[1], "--tests")))
125 	{
126 #ifdef ENABLE_TESTS
127 		return TestMain(argc, argv);
128 #else
129 		printf("ERROR: Could not start tests, the program was compiled without tests\n");
130 		return 1;
131 #endif
132 	}
133 
134 #ifdef ENABLE_TESTS
135 	TestCleanup();
136 #endif
137 
138 	if (argc > 1 && (!strcmp(argv[1], "--nserv")))
139 	{
140 #ifndef DISABLE_NSERV
141 		return NServMain(argc, argv);
142 #else
143 		printf("ERROR: Could not start NServ, the program was compiled without NServ\n");
144 		return 1;
145 #endif
146 	}
147 
148 #ifdef WIN32
149 	InstallUninstallServiceCheck(argc, argv);
150 #endif
151 
152 	srand((unsigned int)Util::CurrentTime());
153 
154 #ifdef WIN32
155 	for (int i=0; i < argc; i++)
156 	{
157 		if (!strcmp(argv[i], "-D"))
158 		{
159 			AllocConsole(); // needed for sending CTRL+BREAK signal to child processes
160 			StartService(RunMain);
161 			return 0;
162 		}
163 	}
164 #endif
165 
166 	RunMain();
167 
168 	return 0;
169 }
170 
171 
172 class NZBGet : public Options::Extender
173 {
174 public:
175 	~NZBGet();
176 	void Run(bool reload);
177 	void Stop(bool reload);
GetReloading()178 	bool GetReloading() { return m_reloading; }
179 
180 	// Options::Extender
181 	virtual void AddNewsServer(int id, bool active, const char* name, const char* host,
182 		int port, int ipVersion, const char* user, const char* pass, bool joinGroup,
183 		bool tls, const char* cipher, int maxConnections, int retention,
184 		int level, int group, bool optional);
185 	virtual void AddFeed(int id, const char* name, const char* url, int interval,
186 		const char* filter, bool backlog, bool pauseNzb, const char* category,
187 		int priority, const char* feedScript);
188 	virtual void AddTask(int id, int hours, int minutes, int weekDaysBits,
189 		Options::ESchedulerCommand command, const char* param);
190 #ifdef WIN32
191 	virtual void SetupFirstStart();
192 #endif
193 
194 private:
195 	// globals
196 	std::unique_ptr<Log> m_log;
197 	std::unique_ptr<Options> m_options;
198 	std::unique_ptr<WorkState> m_workState;
199 	std::unique_ptr<ServerPool> m_serverPool;
200 	std::unique_ptr<QueueCoordinator> m_queueCoordinator;
201 	std::unique_ptr<UrlCoordinator> m_urlCoordinator;
202 	std::unique_ptr<StatMeter> m_statMeter;
203 	std::unique_ptr<PrePostProcessor> m_prePostProcessor;
204 	std::unique_ptr<HistoryCoordinator> m_historyCoordinator;
205 	std::unique_ptr<DupeCoordinator> m_dupeCoordinator;
206 	std::unique_ptr<DiskState> m_diskState;
207 	std::unique_ptr<Scanner> m_scanner;
208 	std::unique_ptr<FeedCoordinator> m_feedCoordinator;
209 	std::unique_ptr<Maintenance> m_maintenance;
210 	std::unique_ptr<ArticleCache> m_articleCache;
211 	std::unique_ptr<QueueScriptCoordinator> m_queueScriptCoordinator;
212 	std::unique_ptr<ServiceCoordinator> m_serviceCoordinator;
213 	std::unique_ptr<ScriptConfig> m_scriptConfig;
214 	std::unique_ptr<CommandScriptLog> m_commandScriptLog;
215 #ifdef WIN32
216 	std::unique_ptr<WinConsole> m_winConsole;
217 #endif
218 
219 	// non-globals
220 	std::unique_ptr<Thread> m_frontend;
221 	std::unique_ptr<RemoteServer> m_remoteServer;
222 	std::unique_ptr<RemoteServer> m_remoteSecureServer;
223 	std::unique_ptr<DiskService> m_diskService;
224 	std::unique_ptr<Scheduler> m_scheduler;
225 	std::unique_ptr<CommandLineParser> m_commandLineParser;
226 
227 	bool m_reloading = false;
228 	bool m_daemonized = false;
229 	bool m_stopped = false;
230 	Mutex m_waitMutex;
231 	ConditionVar m_waitCond;
232 
233 	void Init();
234 	void Final();
235 	void BootConfig();
236 	void CreateGlobals();
237 	void Cleanup();
238 	void PrintOptions();
239 	void ProcessDirect();
240 	void ProcessClientRequest();
241 	void ProcessWebGet();
242 	void ProcessSigVerify();
243 	void StartRemoteServer();
244 	void StopRemoteServer();
245 	void StartFrontend();
246 	void StopFrontend();
247 	void ProcessStandalone();
248 	void DoMainLoop();
249 #ifndef WIN32
250 	void Daemonize();
251 #endif
252 };
253 
254 std::unique_ptr<NZBGet> g_NZBGet;
255 
~NZBGet()256 NZBGet::~NZBGet()
257 {
258 	Cleanup();
259 }
260 
Init()261 void NZBGet::Init()
262 {
263 	m_log = std::make_unique<Log>();
264 
265 	debug("nzbget %s", Util::VersionRevision());
266 
267 	if (!m_reloading)
268 	{
269 		Thread::Init();
270 		Connection::Init();
271 #ifndef DISABLE_TLS
272 		TlsSocket::Init();
273 #endif
274 	}
275 
276 	CreateGlobals();
277 
278 #ifdef WIN32
279 	m_winConsole->InitAppMode();
280 #endif
281 
282 	BootConfig();
283 
284 #ifndef WIN32
285 	if (m_options->GetUMask() < 01000)
286 	{
287 		/* set newly created file permissions */
288 		umask(m_options->GetUMask());
289 	}
290 #endif
291 
292 	m_scanner->InitOptions();
293 	m_queueScriptCoordinator->InitOptions();
294 #ifndef DISABLE_TLS
295 	TlsSocket::InitOptions(g_Options->GetCertCheck() ? g_Options->GetCertStore() : nullptr);
296 #endif
297 
298 	if (m_commandLineParser->GetDaemonMode())
299 	{
300 #ifdef WIN32
301 		info("nzbget %s service-mode", Util::VersionRevision());
302 #else
303 		if (!m_reloading)
304 		{
305 			Daemonize();
306 		}
307 		info("nzbget %s daemon-mode", Util::VersionRevision());
308 #endif
309 	}
310 	else if (m_options->GetServerMode())
311 	{
312 		info("nzbget %s server-mode", Util::VersionRevision());
313 	}
314 	else if (m_commandLineParser->GetRemoteClientMode())
315 	{
316 		info("nzbget %s remote-mode", Util::VersionRevision());
317 	}
318 
319 	m_reloading = false;
320 
321 	if (!m_commandLineParser->GetRemoteClientMode())
322 	{
323 		m_serverPool->InitConnections();
324 		m_statMeter->Init();
325 	}
326 
327 	InstallErrorHandler();
328 }
329 
Final()330 void NZBGet::Final()
331 {
332 	if (!m_reloading)
333 	{
334 #ifndef DISABLE_TLS
335 		TlsSocket::Final();
336 #endif
337 		Connection::Final();
338 	}
339 }
340 
CreateGlobals()341 void NZBGet::CreateGlobals()
342 {
343 #ifdef WIN32
344 	m_winConsole = std::make_unique<WinConsole>();
345 	g_WinConsole = m_winConsole.get();
346 #endif
347 
348 	m_workState = std::make_unique<WorkState>();
349 	g_WorkState = m_workState.get();
350 
351 	m_serviceCoordinator = std::make_unique<ServiceCoordinator>();
352 	g_ServiceCoordinator = m_serviceCoordinator.get();
353 
354 	m_serverPool = std::make_unique<ServerPool>();
355 	g_ServerPool = m_serverPool.get();
356 
357 	m_queueCoordinator = std::make_unique<QueueCoordinator>();
358 	g_QueueCoordinator = m_queueCoordinator.get();
359 
360 	m_statMeter = std::make_unique<StatMeter>();
361 	g_StatMeter = m_statMeter.get();
362 
363 	m_scanner = std::make_unique<Scanner>();
364 	g_Scanner = m_scanner.get();
365 
366 	m_prePostProcessor = std::make_unique<PrePostProcessor>();
367 	g_PrePostProcessor = m_prePostProcessor.get();
368 
369 	m_historyCoordinator = std::make_unique<HistoryCoordinator>();
370 	g_HistoryCoordinator = m_historyCoordinator.get();
371 
372 	m_dupeCoordinator = std::make_unique<DupeCoordinator>();
373 	g_DupeCoordinator = m_dupeCoordinator.get();
374 
375 	m_urlCoordinator = std::make_unique<UrlCoordinator>();
376 	g_UrlCoordinator = m_urlCoordinator.get();
377 
378 	m_feedCoordinator = std::make_unique<FeedCoordinator>();
379 	g_FeedCoordinator = m_feedCoordinator.get();
380 
381 	m_articleCache = std::make_unique<ArticleCache>();
382 	g_ArticleCache = m_articleCache.get();
383 
384 	m_maintenance = std::make_unique<Maintenance>();
385 	g_Maintenance = m_maintenance.get();
386 
387 	m_queueScriptCoordinator = std::make_unique<QueueScriptCoordinator>();
388 	g_QueueScriptCoordinator = m_queueScriptCoordinator.get();
389 
390 	m_diskState = std::make_unique<DiskState>();
391 	g_DiskState = m_diskState.get();
392 
393 	m_scriptConfig = std::make_unique<ScriptConfig>();
394 	g_ScriptConfig = m_scriptConfig.get();
395 
396 	m_commandScriptLog = std::make_unique<CommandScriptLog>();
397 	g_CommandScriptLog = m_commandScriptLog.get();
398 
399 	m_scheduler = std::make_unique<Scheduler>();
400 
401 	m_diskService = std::make_unique<DiskService>();
402 }
403 
BootConfig()404 void NZBGet::BootConfig()
405 {
406 	debug("Parsing command line");
407 	m_commandLineParser = std::make_unique<CommandLineParser>(g_ArgumentCount, (const char**)(*g_Arguments));
408 	if (m_commandLineParser->GetPrintVersion())
409 	{
410 		printf("nzbget version: %s\n", Util::VersionRevision());
411 		exit(0);
412 	}
413 	if (m_commandLineParser->GetPrintUsage() || m_commandLineParser->GetErrors() || g_ArgumentCount <= 1)
414 	{
415 		m_commandLineParser->PrintUsage(((const char**)(*g_Arguments))[0]);
416 		exit(m_commandLineParser->GetPrintUsage() ? 0 : 1);
417 	}
418 
419 	debug("Reading options");
420 	m_options = std::make_unique<Options>((*g_Arguments)[0], m_commandLineParser->GetConfigFilename(),
421 		m_commandLineParser->GetNoConfig(), (Options::CmdOptList*)m_commandLineParser->GetOptionList(), this);
422 	m_options->SetRemoteClientMode(m_commandLineParser->GetRemoteClientMode());
423 	m_options->SetServerMode(m_commandLineParser->GetServerMode());
424 	m_workState->SetPauseDownload(m_commandLineParser->GetPauseDownload());
425 	m_workState->SetSpeedLimit(g_Options->GetDownloadRate());
426 
427 	m_log->InitOptions();
428 
429 	if (m_options->GetFatalError())
430 	{
431 		exit(1);
432 	}
433 	else if (m_options->GetConfigErrors() &&
434 		m_commandLineParser->GetClientOperation() == CommandLineParser::opClientNoOperation)
435 	{
436 		info("Pausing all activities due to errors in configuration");
437 		m_workState->SetPauseDownload(true);
438 		m_workState->SetPausePostProcess(true);
439 		m_workState->SetPauseScan(true);
440 	}
441 
442 	m_serverPool->SetTimeout(m_options->GetArticleTimeout());
443 	m_serverPool->SetRetryInterval(m_options->GetArticleInterval());
444 
445 	m_scriptConfig->InitOptions();
446 }
447 
Cleanup()448 void NZBGet::Cleanup()
449 {
450 	debug("Cleaning up global objects");
451 
452 	if (m_options && m_commandLineParser->GetDaemonMode() && !m_reloading && m_daemonized)
453 	{
454 		info("Deleting lock file");
455 		FileSystem::DeleteFile(m_options->GetLockFile());
456 	}
457 
458 	g_UrlCoordinator = nullptr;
459 	g_PrePostProcessor = nullptr;
460 	g_Scanner = nullptr;
461 	g_HistoryCoordinator = nullptr;
462 	g_DupeCoordinator = nullptr;
463 	g_QueueCoordinator = nullptr;
464 	g_DiskState = nullptr;
465 	g_ScriptConfig = nullptr;
466 	g_ServerPool = nullptr;
467 	g_FeedCoordinator = nullptr;
468 	g_ArticleCache = nullptr;
469 	g_QueueScriptCoordinator = nullptr;
470 	g_Maintenance = nullptr;
471 	g_StatMeter = nullptr;
472 	g_CommandScriptLog = nullptr;
473 #ifdef WIN32
474 	g_WinConsole = nullptr;
475 #endif
476 }
477 
ProcessDirect()478 void NZBGet::ProcessDirect()
479 {
480 #ifdef DEBUG
481 	if (m_commandLineParser->GetTestBacktrace())
482 	{
483 		TestSegFault(); // never returns
484 	}
485 #endif
486 
487 	if (m_commandLineParser->GetWebGet())
488 	{
489 		ProcessWebGet(); // never returns
490 	}
491 
492 	if (m_commandLineParser->GetSigVerify())
493 	{
494 		ProcessSigVerify(); // never returns
495 	}
496 
497 	// client request
498 	if (m_commandLineParser->GetClientOperation() != CommandLineParser::opClientNoOperation)
499 	{
500 		ProcessClientRequest(); // never returns
501 	}
502 
503 	if (m_commandLineParser->GetPrintOptions())
504 	{
505 		PrintOptions(); // never returns
506 	}
507 }
508 
StartRemoteServer()509 void NZBGet::StartRemoteServer()
510 {
511 	if (!m_options->GetServerMode())
512 	{
513 		return;
514 	}
515 
516 	WebProcessor::Init();
517 	m_remoteServer = std::make_unique<RemoteServer>(false);
518 	m_remoteServer->Start();
519 
520 	if (m_options->GetSecureControl()
521 #ifndef WIN32
522 		&& !(m_options->GetControlIp() && m_options->GetControlIp()[0] == '/')
523 #endif
524 )
525 	{
526 		m_remoteSecureServer = std::make_unique<RemoteServer>(true);
527 		m_remoteSecureServer->Start();
528 	}
529 }
530 
StopRemoteServer()531 void NZBGet::StopRemoteServer()
532 {
533 	if (m_remoteServer)
534 	{
535 		debug("stopping RemoteServer");
536 		m_remoteServer->Stop();
537 	}
538 
539 	if (m_remoteSecureServer)
540 	{
541 		debug("stopping RemoteSecureServer");
542 		m_remoteSecureServer->Stop();
543 	}
544 
545 	int maxWaitMSec = 5000;
546 	while (((m_remoteServer && m_remoteServer->IsRunning()) ||
547 		(m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
548 		maxWaitMSec > 0)
549 	{
550 		Util::Sleep(100);
551 		maxWaitMSec -= 100;
552 	}
553 
554 	if (m_remoteServer && m_remoteServer->IsRunning())
555 	{
556 		m_remoteServer->ForceStop();
557 	}
558 
559 	if (m_remoteSecureServer && m_remoteSecureServer->IsRunning())
560 	{
561 		m_remoteSecureServer->ForceStop();
562 	}
563 
564 	maxWaitMSec = 5000;
565 	while (((m_remoteServer && m_remoteServer->IsRunning()) ||
566 		(m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
567 		maxWaitMSec > 0)
568 	{
569 		Util::Sleep(100);
570 		maxWaitMSec -= 100;
571 	}
572 
573 	if (m_remoteServer && m_remoteServer->IsRunning())
574 	{
575 		debug("Killing RemoteServer");
576 		m_remoteServer->Kill();
577 	}
578 
579 	if (m_remoteSecureServer && m_remoteSecureServer->IsRunning())
580 	{
581 		debug("Killing RemoteSecureServer");
582 		m_remoteSecureServer->Kill();
583 	}
584 
585 	debug("RemoteServer stopped");
586 }
587 
StartFrontend()588 void NZBGet::StartFrontend()
589 {
590 	if (!m_commandLineParser->GetDaemonMode())
591 	{
592 		switch (m_options->GetOutputMode())
593 		{
594 		case Options::omNCurses:
595 #ifndef DISABLE_CURSES
596 			m_frontend = std::make_unique<NCursesFrontend>();
597 			break;
598 #endif
599 
600 		case Options::omColored:
601 			m_frontend = std::make_unique<ColoredFrontend>();
602 			break;
603 
604 		case Options::omLoggable:
605 			m_frontend = std::make_unique<LoggableFrontend>();
606 			break;
607 		}
608 	}
609 
610 	if (m_frontend)
611 	{
612 		m_frontend->Start();
613 	}
614 }
615 
StopFrontend()616 void NZBGet::StopFrontend()
617 {
618 	if (m_frontend)
619 	{
620 		if (!m_commandLineParser->GetRemoteClientMode())
621 		{
622 			debug("Stopping Frontend");
623 			m_frontend->Stop();
624 		}
625 		while (m_frontend->IsRunning())
626 		{
627 			Util::Sleep(50);
628 		}
629 		debug("Frontend stopped");
630 	}
631 }
632 
ProcessStandalone()633 void NZBGet::ProcessStandalone()
634 {
635 	const char* category = m_commandLineParser->GetAddCategory() ? m_commandLineParser->GetAddCategory() : "";
636 	NzbFile nzbFile(m_commandLineParser->GetArgFilename(), category);
637 	if (!nzbFile.Parse())
638 	{
639 		printf("Parsing NZB-document %s failed\n\n",
640 			m_commandLineParser->GetArgFilename() ? m_commandLineParser->GetArgFilename() : "N/A");
641 		return;
642 	}
643 	std::unique_ptr<NzbInfo> nzbInfo = nzbFile.DetachNzbInfo();
644 	m_scanner->InitPPParameters(category, nzbInfo->GetParameters(), false);
645 	m_queueCoordinator->AddNzbFileToQueue(std::move(nzbInfo), nullptr, false);
646 }
647 
DoMainLoop()648 void NZBGet::DoMainLoop()
649 {
650 	debug("Entering main program loop");
651 
652 #ifdef WIN32
653 	m_winConsole->Start();
654 #endif
655 	m_queueCoordinator->Start();
656 	m_urlCoordinator->Start();
657 	m_prePostProcessor->Start();
658 	m_feedCoordinator->Start();
659 	m_serviceCoordinator->Start();
660 	if (m_options->GetArticleCache() > 0)
661 	{
662 		m_articleCache->Start();
663 	}
664 
665 	// enter main program-loop
666 	while (m_queueCoordinator->IsRunning() ||
667 		m_urlCoordinator->IsRunning() ||
668 		m_prePostProcessor->IsRunning() ||
669 		m_feedCoordinator->IsRunning() ||
670 		m_serviceCoordinator->IsRunning() ||
671 #ifdef WIN32
672 		m_winConsole->IsRunning() ||
673 #endif
674 		m_articleCache->IsRunning())
675 	{
676 		if (!m_options->GetServerMode() &&
677 			!m_queueCoordinator->HasMoreJobs() &&
678 			!m_urlCoordinator->HasMoreJobs() &&
679 			!m_prePostProcessor->HasMoreJobs())
680 		{
681 			// Standalone-mode: download completed
682 			if (!m_queueCoordinator->IsStopped())
683 			{
684 				m_queueCoordinator->Stop();
685 			}
686 			if (!m_urlCoordinator->IsStopped())
687 			{
688 				m_urlCoordinator->Stop();
689 			}
690 			if (!m_prePostProcessor->IsStopped())
691 			{
692 				m_prePostProcessor->Stop();
693 			}
694 			if (!m_feedCoordinator->IsStopped())
695 			{
696 				m_feedCoordinator->Stop();
697 			}
698 			if (!m_articleCache->IsStopped())
699 			{
700 				m_articleCache->Stop();
701 			}
702 			if (!m_serviceCoordinator->IsStopped())
703 			{
704 				m_serviceCoordinator->Stop();
705 			}
706 		}
707 		Util::Sleep(100);
708 
709 		if (m_options->GetServerMode() && !m_stopped)
710 		{
711 			// wait for stop signal
712 			Guard guard(m_waitMutex);
713 			m_waitCond.Wait(m_waitMutex, [&]{ return m_stopped; });
714 		}
715 	}
716 
717 	debug("Main program loop terminated");
718 }
719 
Run(bool reload)720 void NZBGet::Run(bool reload)
721 {
722 	m_reloading = reload;
723 
724 	Init();
725 
726 	ProcessDirect();
727 
728 	StartRemoteServer();
729 	StartFrontend();
730 
731 	if (!m_commandLineParser->GetRemoteClientMode())
732 	{
733 		if (!m_commandLineParser->GetServerMode())
734 		{
735 			ProcessStandalone();
736 		}
737 
738 		DoMainLoop();
739 	}
740 
741 	ScriptController::TerminateAll();
742 
743 	StopRemoteServer();
744 	StopFrontend();
745 
746 	Final();
747 }
748 
ProcessClientRequest()749 void NZBGet::ProcessClientRequest()
750 {
751 	RemoteClient Client;
752 	bool ok = false;
753 
754 	switch (m_commandLineParser->GetClientOperation())
755 	{
756 		case CommandLineParser::opClientRequestListFiles:
757 			ok = Client.RequestServerList(true, false, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
758 			break;
759 
760 		case CommandLineParser::opClientRequestListGroups:
761 			ok = Client.RequestServerList(false, true, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
762 			break;
763 
764 		case CommandLineParser::opClientRequestListStatus:
765 			ok = Client.RequestServerList(false, false, nullptr);
766 			break;
767 
768 		case CommandLineParser::opClientRequestDownloadPause:
769 			ok = Client.RequestServerPauseUnpause(true, rpDownload);
770 			break;
771 
772 		case CommandLineParser::opClientRequestDownloadUnpause:
773 			ok = Client.RequestServerPauseUnpause(false, rpDownload);
774 			break;
775 
776 		case CommandLineParser::opClientRequestSetRate:
777 			ok = Client.RequestServerSetDownloadRate(m_commandLineParser->GetSetRate());
778 			break;
779 
780 		case CommandLineParser::opClientRequestDumpDebug:
781 			ok = Client.RequestServerDumpDebug();
782 			break;
783 
784 		case CommandLineParser::opClientRequestEditQueue:
785 			ok = Client.RequestServerEditQueue((DownloadQueue::EEditAction)m_commandLineParser->GetEditQueueAction(),
786 				m_commandLineParser->GetEditQueueOffset(), m_commandLineParser->GetEditQueueText(),
787 				m_commandLineParser->GetEditQueueIdList(), m_commandLineParser->GetEditQueueNameList(),
788 				(ERemoteMatchMode)m_commandLineParser->GetMatchMode());
789 			break;
790 
791 		case CommandLineParser::opClientRequestLog:
792 			ok = Client.RequestServerLog(m_commandLineParser->GetLogLines());
793 			break;
794 
795 		case CommandLineParser::opClientRequestShutdown:
796 			ok = Client.RequestServerShutdown();
797 			break;
798 
799 		case CommandLineParser::opClientRequestReload:
800 			ok = Client.RequestServerReload();
801 			break;
802 
803 		case CommandLineParser::opClientRequestDownload:
804 			ok = Client.RequestServerDownload(m_commandLineParser->GetAddNzbFilename(), m_commandLineParser->GetArgFilename(),
805 				m_commandLineParser->GetAddCategory(), m_commandLineParser->GetAddTop(), m_commandLineParser->GetAddPaused(), m_commandLineParser->GetAddPriority(),
806 				m_commandLineParser->GetAddDupeKey(), m_commandLineParser->GetAddDupeMode(), m_commandLineParser->GetAddDupeScore());
807 			break;
808 
809 		case CommandLineParser::opClientRequestVersion:
810 			ok = Client.RequestServerVersion();
811 			break;
812 
813 		case CommandLineParser::opClientRequestPostQueue:
814 			ok = Client.RequestPostQueue();
815 			break;
816 
817 		case CommandLineParser::opClientRequestWriteLog:
818 			ok = Client.RequestWriteLog(m_commandLineParser->GetWriteLogKind(), m_commandLineParser->GetLastArg());
819 			break;
820 
821 		case CommandLineParser::opClientRequestScanAsync:
822 			ok = Client.RequestScan(false);
823 			break;
824 
825 		case CommandLineParser::opClientRequestScanSync:
826 			ok = Client.RequestScan(true);
827 			break;
828 
829 		case CommandLineParser::opClientRequestPostPause:
830 			ok = Client.RequestServerPauseUnpause(true, rpPostProcess);
831 			break;
832 
833 		case CommandLineParser::opClientRequestPostUnpause:
834 			ok = Client.RequestServerPauseUnpause(false, rpPostProcess);
835 			break;
836 
837 		case CommandLineParser::opClientRequestScanPause:
838 			ok = Client.RequestServerPauseUnpause(true, rpScan);
839 			break;
840 
841 		case CommandLineParser::opClientRequestScanUnpause:
842 			ok = Client.RequestServerPauseUnpause(false, rpScan);
843 			break;
844 
845 		case CommandLineParser::opClientRequestHistory:
846 		case CommandLineParser::opClientRequestHistoryAll:
847 			ok = Client.RequestHistory(m_commandLineParser->GetClientOperation() == CommandLineParser::opClientRequestHistoryAll);
848 			break;
849 
850 		case CommandLineParser::opClientNoOperation:
851 			return;
852 	}
853 
854 	exit(ok ? 0 : 1);
855 }
856 
ProcessWebGet()857 void NZBGet::ProcessWebGet()
858 {
859 	WebDownloader downloader;
860 	downloader.SetUrl(m_commandLineParser->GetLastArg());
861 	downloader.SetForce(true);
862 	downloader.SetRetry(false);
863 	downloader.SetOutputFilename(m_commandLineParser->GetWebGetFilename());
864 	downloader.SetInfoName("WebGet");
865 
866 	WebDownloader::EStatus status = downloader.DownloadWithRedirects(5);
867 	bool ok = status == WebDownloader::adFinished;
868 
869 	exit(ok ? 0 : 1);
870 }
871 
ProcessSigVerify()872 void NZBGet::ProcessSigVerify()
873 {
874 #ifdef HAVE_OPENSSL
875 	bool ok = Maintenance::VerifySignature(m_commandLineParser->GetLastArg(),
876 		m_commandLineParser->GetSigFilename(), m_commandLineParser->GetPubKeyFilename());
877 	exit(ok ? 93 : 1);
878 #else
879 	printf("ERROR: Could not verify signature, the program was compiled without OpenSSL support\n");
880 	exit(1);
881 #endif
882 }
883 
Stop(bool reload)884 void NZBGet::Stop(bool reload)
885 {
886 	m_reloading = reload;
887 
888 	if (!m_reloading)
889 	{
890 		info("Stopping, please wait...");
891 	}
892 	if (m_commandLineParser->GetRemoteClientMode())
893 	{
894 		if (m_frontend)
895 		{
896 			debug("Stopping Frontend");
897 			m_frontend->Stop();
898 		}
899 	}
900 	else
901 	{
902 		if (m_queueCoordinator)
903 		{
904 			debug("Stopping QueueCoordinator");
905 			m_serviceCoordinator->Stop();
906 			m_queueCoordinator->Stop();
907 			m_urlCoordinator->Stop();
908 			m_prePostProcessor->Stop();
909 			m_feedCoordinator->Stop();
910 			m_articleCache->Stop();
911 			m_queueScriptCoordinator->Stop();
912 #ifdef WIN32
913 			m_winConsole->Stop();
914 #endif
915 		}
916 	}
917 
918 	// trigger stop/reload signal
919 	Guard guard(m_waitMutex);
920 	m_stopped = true;
921 	m_waitCond.NotifyAll();
922 }
923 
PrintOptions()924 void NZBGet::PrintOptions()
925 {
926 	for (Options::OptEntry& optEntry : g_Options->GuardOptEntries())
927 	{
928 		printf("%s = \"%s\"\n", optEntry.GetName(), optEntry.GetValue());
929 	}
930 	exit(0);
931 }
932 
933 #ifndef WIN32
Daemonize()934 void NZBGet::Daemonize()
935 {
936 	int f = fork();
937 	if (f < 0) exit(1); /* fork error */
938 	if (f > 0) exit(0); /* parent exits */
939 
940 	/* child (daemon) continues */
941 	m_daemonized = true;
942 
943 	// obtain a new process group
944 	setsid();
945 
946 	// handle standart I/O
947 	int d = open("/dev/null", O_RDWR);
948 	dup2(d, 0);
949 	dup2(d, 1);
950 	dup2(d, 2);
951 	close(d);
952 
953 	// set up lock-file
954 	int lfp = -1;
955 	if (!Util::EmptyStr(m_options->GetLockFile()))
956 	{
957 		lfp = open(m_options->GetLockFile(), O_RDWR | O_CREAT, 0640);
958 		if (lfp < 0)
959 		{
960 			error("Starting daemon failed: could not create lock-file %s", m_options->GetLockFile());
961 			exit(1);
962 		}
963 
964 #ifdef HAVE_LOCKF
965 		if (lockf(lfp, F_TLOCK, 0) < 0)
966 #else
967 		if (flock(lfp, LOCK_EX) < 0)
968 #endif
969 		{
970 			error("Starting daemon failed: could not acquire lock on lock-file %s", m_options->GetLockFile());
971 			exit(1);
972 		}
973 	}
974 
975 	/* Drop user if there is one, and we were run as root */
976 	if (getuid() == 0 || geteuid() == 0)
977 	{
978 		struct passwd *pw = getpwnam(m_options->GetDaemonUsername());
979 		if (pw)
980 		{
981 			// Change owner of lock file
982 			fchown(lfp, pw->pw_uid, pw->pw_gid);
983 			// Set aux groups to null.
984 			setgroups(0, (const gid_t*)0);
985 			// Set primary group.
986 			setgid(pw->pw_gid);
987 			// Try setting aux groups correctly - not critical if this fails.
988 			initgroups(m_options->GetDaemonUsername(), pw->pw_gid);
989 			// Finally, set uid.
990 			setuid(pw->pw_uid);
991 		}
992 	}
993 
994 	// record pid to lockfile
995 	if (lfp > -1)
996 	{
997 		BString<100> str("%d\n", getpid());
998 		write(lfp, str, strlen(str));
999 	}
1000 
1001 	// ignore unwanted signals
1002 	signal(SIGCHLD, SIG_IGN);
1003 	signal(SIGTSTP, SIG_IGN);
1004 	signal(SIGTTOU, SIG_IGN);
1005 	signal(SIGTTIN, SIG_IGN);
1006 }
1007 #endif
1008 
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)1009 void NZBGet::AddNewsServer(int id, bool active, const char* name, const char* host,
1010 	int port, int ipVersion, const char* user, const char* pass, bool joinGroup, bool tls,
1011 	const char* cipher, int maxConnections, int retention, int level, int group, bool optional)
1012 {
1013 	m_serverPool->AddServer(std::make_unique<NewsServer>(id, active, name, host, port, ipVersion, user, pass, joinGroup,
1014 		tls, cipher, maxConnections, retention, level, group, optional));
1015 }
1016 
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)1017 void NZBGet::AddFeed(int id, const char* name, const char* url, int interval, const char* filter,
1018 	bool backlog, bool pauseNzb, const char* category, int priority, const char* feedScript)
1019 {
1020 	m_feedCoordinator->AddFeed(std::make_unique<FeedInfo>(id, name, url, backlog, interval, filter,
1021 		pauseNzb, category, priority, feedScript));
1022 }
1023 
AddTask(int id,int hours,int minutes,int weekDaysBits,Options::ESchedulerCommand command,const char * param)1024 void NZBGet::AddTask(int id, int hours, int minutes, int weekDaysBits,
1025 	Options::ESchedulerCommand command, const char* param)
1026 {
1027 	m_scheduler->AddTask(std::make_unique<Scheduler::Task>(id, hours, minutes, weekDaysBits,
1028 		(Scheduler::ECommand)command, param));
1029 }
1030 
1031 #ifdef WIN32
SetupFirstStart()1032 void NZBGet::SetupFirstStart()
1033 {
1034 	m_winConsole->SetupFirstStart();
1035 }
1036 #endif
1037 
RunMain()1038 void RunMain()
1039 {
1040 	bool reload = false;
1041 	while (!g_NZBGet || g_NZBGet->GetReloading())
1042 	{
1043 		g_NZBGet = std::make_unique<NZBGet>();
1044 		g_NZBGet->Run(reload);
1045 		reload = true;
1046 	}
1047 
1048 	g_NZBGet.reset();
1049 }
1050 
Reload()1051 void Reload()
1052 {
1053 	info("Reloading...");
1054 	g_NZBGet->Stop(true);
1055 }
1056 
ExitProc()1057 void ExitProc()
1058 {
1059 	g_NZBGet->Stop(false);
1060 }
1061