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