]> granicus.if.org Git - icinga2/blob - lib/base/application.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / application.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/application.hpp"
4 #include "base/application-ti.cpp"
5 #include "base/stacktrace.hpp"
6 #include "base/timer.hpp"
7 #include "base/logger.hpp"
8 #include "base/exception.hpp"
9 #include "base/objectlock.hpp"
10 #include "base/utility.hpp"
11 #include "base/loader.hpp"
12 #include "base/debug.hpp"
13 #include "base/type.hpp"
14 #include "base/convert.hpp"
15 #include "base/scriptglobal.hpp"
16 #include "base/process.hpp"
17 #include <boost/algorithm/string/trim.hpp>
18 #include <boost/exception/errinfo_api_function.hpp>
19 #include <boost/exception/errinfo_errno.hpp>
20 #include <boost/exception/errinfo_file_name.hpp>
21 #include <sstream>
22 #include <iostream>
23 #include <fstream>
24 #include <thread>
25 #ifdef __linux__
26 #include <sys/prctl.h>
27 #endif /* __linux__ */
28 #ifdef _WIN32
29 #include <windows.h>
30 #endif /* _WIN32 */
31 #ifdef HAVE_SYSTEMD
32 #include <systemd/sd-daemon.h>
33 #endif /* HAVE_SYSTEMD */
34
35 using namespace icinga;
36
37 REGISTER_TYPE(Application);
38
39 boost::signals2::signal<void ()> Application::OnReopenLogs;
40 Application::Ptr Application::m_Instance = nullptr;
41 bool Application::m_ShuttingDown = false;
42 bool Application::m_RequestRestart = false;
43 bool Application::m_RequestReopenLogs = false;
44 pid_t Application::m_ReloadProcess = 0;
45 static bool l_Restarting = false;
46 static bool l_InExceptionHandler = false;
47 int Application::m_ArgC;
48 char **Application::m_ArgV;
49 double Application::m_StartTime;
50 double Application::m_MainTime;
51 bool Application::m_ScriptDebuggerEnabled = false;
52 double Application::m_LastReloadFailed;
53
54 /**
55  * Constructor for the Application class.
56  */
57 void Application::OnConfigLoaded()
58 {
59         m_PidFile = nullptr;
60
61         ASSERT(m_Instance == nullptr);
62         m_Instance = this;
63 }
64
65 /**
66  * Destructor for the application class.
67  */
68 void Application::Stop(bool runtimeRemoved)
69 {
70         m_ShuttingDown = true;
71
72 #ifdef _WIN32
73         WSACleanup();
74 #endif /* _WIN32 */
75
76         ClosePidFile(true);
77
78         ObjectImpl<Application>::Stop(runtimeRemoved);
79 }
80
81 Application::~Application()
82 {
83         m_Instance = nullptr;
84 }
85
86 void Application::Exit(int rc)
87 {
88         std::cout.flush();
89         std::cerr.flush();
90
91         for (const Logger::Ptr& logger : Logger::GetLoggers()) {
92                 logger->Flush();
93         }
94
95         UninitializeBase();
96 #ifdef I2_DEBUG
97         exit(rc);
98 #else /* I2_DEBUG */
99         _exit(rc); // Yay, our static destructors are pretty much beyond repair at this point.
100 #endif /* I2_DEBUG */
101 }
102
103 void Application::InitializeBase()
104 {
105 #ifdef _WIN32
106         /* disable GUI-based error messages for LoadLibrary() */
107         SetErrorMode(SEM_FAILCRITICALERRORS);
108
109         WSADATA wsaData;
110         if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
111                 BOOST_THROW_EXCEPTION(win32_error()
112                         << boost::errinfo_api_function("WSAStartup")
113                         << errinfo_win32_error(WSAGetLastError()));
114         }
115 #else /* _WIN32 */
116         struct sigaction sa;
117         memset(&sa, 0, sizeof(sa));
118         sa.sa_handler = SIG_IGN;
119         sigaction(SIGPIPE, &sa, nullptr);
120 #endif /* _WIN32 */
121
122         Loader::ExecuteDeferredInitializers();
123
124         /* Make sure the thread pool gets initialized. */
125         GetTP().Start();
126
127         /* Make sure the timer thread gets initialized. */
128         Timer::Initialize();
129 }
130
131 void Application::UninitializeBase()
132 {
133         Timer::Uninitialize();
134
135         GetTP().Stop();
136 }
137
138 /**
139  * Retrieves a pointer to the application singleton object.
140  *
141  * @returns The application object.
142  */
143 Application::Ptr Application::GetInstance()
144 {
145         return m_Instance;
146 }
147
148 void Application::SetResourceLimits()
149 {
150 #ifdef __linux__
151         rlimit rl;
152
153 #       ifdef RLIMIT_NOFILE
154         rlim_t fileLimit = Configuration::RLimitFiles;
155
156         if (fileLimit != 0) {
157                 if (fileLimit < (rlim_t)GetDefaultRLimitFiles()) {
158                         Log(LogWarning, "Application")
159                                 << "The user-specified value for RLimitFiles cannot be smaller than the default value (" << GetDefaultRLimitFiles() << "). Using the default value instead.";
160                         fileLimit = GetDefaultRLimitFiles();
161                 }
162
163                 rl.rlim_cur = fileLimit;
164                 rl.rlim_max = rl.rlim_cur;
165
166                 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
167                         Log(LogWarning, "Application")
168                             << "Failed to adjust resource limit for open file handles (RLIMIT_NOFILE) with error \"" << strerror(errno) << "\"";
169 #       else /* RLIMIT_NOFILE */
170                 Log(LogNotice, "Application", "System does not support adjusting the resource limit for open file handles (RLIMIT_NOFILE)");
171 #       endif /* RLIMIT_NOFILE */
172         }
173
174 #       ifdef RLIMIT_NPROC
175         rlim_t processLimit = Configuration::RLimitProcesses;
176
177         if (processLimit != 0) {
178                 if (processLimit < (rlim_t)GetDefaultRLimitProcesses()) {
179                         Log(LogWarning, "Application")
180                                 << "The user-specified value for RLimitProcesses cannot be smaller than the default value (" << GetDefaultRLimitProcesses() << "). Using the default value instead.";
181                         processLimit = GetDefaultRLimitProcesses();
182                 }
183
184                 rl.rlim_cur = processLimit;
185                 rl.rlim_max = rl.rlim_cur;
186
187                 if (setrlimit(RLIMIT_NPROC, &rl) < 0)
188                         Log(LogWarning, "Application")
189                             << "Failed adjust resource limit for number of processes (RLIMIT_NPROC) with error \"" << strerror(errno) << "\"";
190 #       else /* RLIMIT_NPROC */
191                 Log(LogNotice, "Application", "System does not support adjusting the resource limit for number of processes (RLIMIT_NPROC)");
192 #       endif /* RLIMIT_NPROC */
193         }
194
195 #       ifdef RLIMIT_STACK
196         int argc = Application::GetArgC();
197         char **argv = Application::GetArgV();
198         bool set_stack_rlimit = true;
199
200         for (int i = 0; i < argc; i++) {
201                 if (strcmp(argv[i], "--no-stack-rlimit") == 0) {
202                         set_stack_rlimit = false;
203                         break;
204                 }
205         }
206
207         if (getrlimit(RLIMIT_STACK, &rl) < 0) {
208                 Log(LogWarning, "Application", "Could not determine resource limit for stack size (RLIMIT_STACK)");
209                 rl.rlim_max = RLIM_INFINITY;
210         }
211
212         rlim_t stackLimit;
213
214         stackLimit = Configuration::RLimitStack;
215
216         if (stackLimit != 0) {
217                 if (stackLimit < (rlim_t)GetDefaultRLimitStack()) {
218                         Log(LogWarning, "Application")
219                                 << "The user-specified value for RLimitStack cannot be smaller than the default value (" << GetDefaultRLimitStack() << "). Using the default value instead.";
220                         stackLimit = GetDefaultRLimitStack();
221                 }
222
223                 if (set_stack_rlimit)
224                         rl.rlim_cur = stackLimit;
225                 else
226                         rl.rlim_cur = rl.rlim_max;
227
228                 if (setrlimit(RLIMIT_STACK, &rl) < 0)
229                         Log(LogWarning, "Application")
230                             << "Failed adjust resource limit for stack size (RLIMIT_STACK) with error \"" << strerror(errno) << "\"";
231                 else if (set_stack_rlimit) {
232                         char **new_argv = static_cast<char **>(malloc(sizeof(char *) * (argc + 2)));
233
234                         if (!new_argv) {
235                                 perror("malloc");
236                                 Exit(EXIT_FAILURE);
237                         }
238
239                         new_argv[0] = argv[0];
240                         new_argv[1] = strdup("--no-stack-rlimit");
241
242                         if (!new_argv[1]) {
243                                 perror("strdup");
244                                 exit(1);
245                         }
246
247                         for (int i = 1; i < argc; i++)
248                                 new_argv[i + 1] = argv[i];
249
250                         new_argv[argc + 1] = nullptr;
251
252                         (void) execvp(new_argv[0], new_argv);
253                         perror("execvp");
254                         _exit(EXIT_FAILURE);
255                 }
256 #       else /* RLIMIT_STACK */
257                 Log(LogNotice, "Application", "System does not support adjusting the resource limit for stack size (RLIMIT_STACK)");
258 #       endif /* RLIMIT_STACK */
259         }
260 #endif /* __linux__ */
261 }
262
263 int Application::GetArgC()
264 {
265         return m_ArgC;
266 }
267
268 void Application::SetArgC(int argc)
269 {
270         m_ArgC = argc;
271 }
272
273 char **Application::GetArgV()
274 {
275         return m_ArgV;
276 }
277
278 void Application::SetArgV(char **argv)
279 {
280         m_ArgV = argv;
281 }
282
283 /**
284  * Processes events for registered sockets and timers and calls whatever
285  * handlers have been set up for these events.
286  */
287 void Application::RunEventLoop()
288 {
289 #ifdef HAVE_SYSTEMD
290         sd_notify(0, "READY=1");
291 #endif /* HAVE_SYSTEMD */
292
293         double lastLoop = Utility::GetTime();
294
295         while (!m_ShuttingDown) {
296                 if (m_RequestRestart) {
297                         m_RequestRestart = false;         // we are now handling the request, once is enough
298
299 #ifdef HAVE_SYSTEMD
300                         sd_notify(0, "RELOADING=1");
301 #endif /* HAVE_SYSTEMD */
302
303                         // are we already restarting? ignore request if we already are
304                         if (!l_Restarting) {
305                                 l_Restarting = true;
306                                 m_ReloadProcess = StartReloadProcess();
307                         }
308                 } else {
309                         /* Watches for changes to the system time. Adjusts timers if necessary. */
310                         Utility::Sleep(2.5);
311
312                         if (m_RequestReopenLogs) {
313                                 Log(LogNotice, "Application", "Reopening log files");
314                                 m_RequestReopenLogs = false;
315                                 OnReopenLogs();
316                         }
317
318                         double now = Utility::GetTime();
319                         double timeDiff = lastLoop - now;
320
321 #ifdef HAVE_SYSTEMD
322                         sd_notify(0, "WATCHDOG=1");
323 #endif /* HAVE_SYSTEMD */
324
325                         if (std::fabs(timeDiff) > 15) {
326                                 /* We made a significant jump in time. */
327                                 Log(LogInformation, "Application")
328                                         << "We jumped "
329                                         << (timeDiff < 0 ? "forward" : "backward")
330                                         << " in time: " << std::fabs(timeDiff) << " seconds";
331
332                                 Timer::AdjustTimers(-timeDiff);
333                         }
334
335                         lastLoop = now;
336                 }
337         }
338
339 #ifdef HAVE_SYSTEMD
340         sd_notify(0, "STOPPING=1");
341 #endif /* HAVE_SYSTEMD */
342
343         Log(LogInformation, "Application", "Shutting down...");
344
345         ConfigObject::StopObjects();
346         Application::GetInstance()->OnShutdown();
347
348         UninitializeBase();
349 }
350
351 bool Application::IsShuttingDown()
352 {
353         return m_ShuttingDown;
354 }
355
356 bool Application::IsRestarting()
357 {
358         return l_Restarting;
359 }
360
361 void Application::OnShutdown()
362 {
363         /* Nothing to do here. */
364 }
365
366 static void ReloadProcessCallbackInternal(const ProcessResult& pr)
367 {
368         if (pr.ExitStatus != 0) {
369                 Application::SetLastReloadFailed(Utility::GetTime());
370                 Log(LogCritical, "Application", "Found error in config: reloading aborted");
371         }
372 #ifdef _WIN32
373         else
374                 Application::Exit(7); /* keep this exit code in sync with icinga-app */
375 #endif /* _WIN32 */
376 }
377
378 static void ReloadProcessCallback(const ProcessResult& pr)
379 {
380         l_Restarting = false;
381
382         std::thread t(std::bind(&ReloadProcessCallbackInternal, pr));
383         t.detach();
384 }
385
386 pid_t Application::StartReloadProcess()
387 {
388         // prepare arguments
389         ArrayData args;
390         args.push_back(GetExePath(m_ArgV[0]));
391
392         for (int i=1; i < Application::GetArgC(); i++) {
393                 if (std::string(Application::GetArgV()[i]) != "--reload-internal")
394                         args.push_back(Application::GetArgV()[i]);
395                 else
396                         i++;     // the next parameter after --reload-internal is the pid, remove that too
397         }
398
399 #ifndef _WIN32
400         args.push_back("--reload-internal");
401         args.push_back(Convert::ToString(Utility::GetPid()));
402 #else /* _WIN32 */
403         args.push_back("--validate");
404 #endif /* _WIN32 */
405
406         double reloadTimeout = Application::GetReloadTimeout();
407
408         Process::Ptr process = new Process(Process::PrepareCommand(new Array(std::move(args))));
409         process->SetTimeout(reloadTimeout);
410         process->Run(&ReloadProcessCallback);
411
412         Log(LogInformation, "Application")
413                 << "Got reload command: Started new instance with PID '"
414                 << (unsigned long)(process->GetPID()) << "' (timeout is "
415                 << reloadTimeout << "s).";
416
417         return process->GetPID();
418 }
419
420 /**
421  * Signals the application to shut down during the next
422  * execution of the event loop.
423  */
424 void Application::RequestShutdown()
425 {
426         Log(LogInformation, "Application", "Received request to shut down.");
427
428         m_ShuttingDown = true;
429 }
430
431 /**
432  * Signals the application to restart during the next
433  * execution of the event loop.
434  */
435 void Application::RequestRestart()
436 {
437         m_RequestRestart = true;
438 }
439
440 /**
441  * Signals the application to reopen log files during the
442  * next execution of the event loop.
443  */
444 void Application::RequestReopenLogs()
445 {
446         m_RequestReopenLogs = true;
447 }
448
449 /**
450  * Retrieves the full path of the executable.
451  *
452  * @param argv0 The first command-line argument.
453  * @returns The path.
454  */
455 String Application::GetExePath(const String& argv0)
456 {
457         String executablePath;
458
459 #ifndef _WIN32
460         char buffer[MAXPATHLEN];
461         if (!getcwd(buffer, sizeof(buffer))) {
462                 BOOST_THROW_EXCEPTION(posix_error()
463                         << boost::errinfo_api_function("getcwd")
464                         << boost::errinfo_errno(errno));
465         }
466
467         String workingDirectory = buffer;
468
469         if (argv0[0] != '/')
470                 executablePath = workingDirectory + "/" + argv0;
471         else
472                 executablePath = argv0;
473
474         bool foundSlash = false;
475         for (size_t i = 0; i < argv0.GetLength(); i++) {
476                 if (argv0[i] == '/') {
477                         foundSlash = true;
478                         break;
479                 }
480         }
481
482         if (!foundSlash) {
483                 String pathEnv = Utility::GetFromEnvironment("PATH");
484                 if (!pathEnv.IsEmpty()) {
485                         std::vector<String> paths = String(pathEnv).Split(":");
486
487                         bool foundPath = false;
488                         for (const String& path : paths) {
489                                 String pathTest = path + "/" + argv0;
490
491                                 if (access(pathTest.CStr(), X_OK) == 0) {
492                                         executablePath = pathTest;
493                                         foundPath = true;
494                                         break;
495                                 }
496                         }
497
498                         if (!foundPath) {
499                                 executablePath.Clear();
500                                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not determine executable path."));
501                         }
502                 }
503         }
504
505         if (!realpath(executablePath.CStr(), buffer)) {
506                 BOOST_THROW_EXCEPTION(posix_error()
507                         << boost::errinfo_api_function("realpath")
508                         << boost::errinfo_errno(errno)
509                         << boost::errinfo_file_name(executablePath));
510         }
511
512         return buffer;
513 #else /* _WIN32 */
514         char FullExePath[MAXPATHLEN];
515
516         if (!GetModuleFileName(nullptr, FullExePath, sizeof(FullExePath)))
517                 BOOST_THROW_EXCEPTION(win32_error()
518                         << boost::errinfo_api_function("GetModuleFileName")
519                         << errinfo_win32_error(GetLastError()));
520
521         return FullExePath;
522 #endif /* _WIN32 */
523 }
524
525 /**
526  * Display version and path information.
527  */
528 void Application::DisplayInfoMessage(std::ostream& os, bool skipVersion)
529 {
530         /* icinga-app prints its own version information, stack traces need it here. */
531         if (!skipVersion)
532                 os << "  Application version: " << GetAppVersion() << "\n\n";
533
534         os << "System information:\n"
535                 << "  Platform: " << Utility::GetPlatformName() << "\n"
536                 << "  Platform version: " << Utility::GetPlatformVersion() << "\n"
537                 << "  Kernel: " << Utility::GetPlatformKernel() << "\n"
538                 << "  Kernel version: " << Utility::GetPlatformKernelVersion() << "\n"
539                 << "  Architecture: " << Utility::GetPlatformArchitecture() << "\n";
540
541         Namespace::Ptr systemNS = ScriptGlobal::Get("System");
542
543         os << "\nBuild information:\n"
544                 << "  Compiler: " << systemNS->Get("BuildCompilerName") << " " << systemNS->Get("BuildCompilerVersion") << "\n"
545                 << "  Build host: " << systemNS->Get("BuildHostName") << "\n";
546
547         os << "\nApplication information:\n"
548                 << "\nGeneral paths:\n"
549                 << "  Config directory: " << Configuration::ConfigDir << "\n"
550                 << "  Data directory: " << Configuration::DataDir << "\n"
551                 << "  Log directory: " << Configuration::LogDir << "\n"
552                 << "  Cache directory: " << Configuration::CacheDir << "\n"
553                 << "  Spool directory: " << Configuration::SpoolDir << "\n"
554                 << "  Run directory: " << Configuration::InitRunDir << "\n"
555                 << "\nOld paths (deprecated):\n"
556                 << "  Installation root: " << Configuration::PrefixDir << "\n"
557                 << "  Sysconf directory: " << Configuration::SysconfDir << "\n"
558                 << "  Run directory (base): " << Configuration::RunDir << "\n"
559                 << "  Local state directory: " << Configuration::LocalStateDir << "\n"
560                 << "\nInternal paths:\n"
561                 << "  Package data directory: " << Configuration::PkgDataDir << "\n"
562                 << "  State path: " << Configuration::StatePath << "\n"
563                 << "  Modified attributes path: " << Configuration::ModAttrPath << "\n"
564                 << "  Objects path: " << Configuration::ObjectsPath << "\n"
565                 << "  Vars path: " << Configuration::VarsPath << "\n"
566                 << "  PID path: " << Configuration::PidPath << "\n";
567
568 }
569
570 /**
571  * Displays a message that tells users what to do when they encounter a bug.
572  */
573 void Application::DisplayBugMessage(std::ostream& os)
574 {
575         os << "***" << "\n"
576                 << "* This would indicate a runtime problem or configuration error. If you believe this is a bug in Icinga 2" << "\n"
577                 << "* please submit a bug report at https://github.com/Icinga/icinga2 and include this stack trace as well as any other" << "\n"
578                 << "* information that might be useful in order to reproduce this problem." << "\n"
579                 << "***" << "\n";
580 }
581
582 String Application::GetCrashReportFilename()
583 {
584         return Configuration::LogDir + "/crash/report." + Convert::ToString(Utility::GetTime());
585 }
586
587
588 void Application::AttachDebugger(const String& filename, bool interactive)
589 {
590 #ifndef _WIN32
591 #ifdef __linux__
592         prctl(PR_SET_DUMPABLE, 1);
593 #endif /* __linux __ */
594
595         String my_pid = Convert::ToString(Utility::GetPid());
596
597         pid_t pid = fork();
598
599         if (pid < 0) {
600                 BOOST_THROW_EXCEPTION(posix_error()
601                         << boost::errinfo_api_function("fork")
602                         << boost::errinfo_errno(errno));
603         }
604
605         if (pid == 0) {
606                 if (!interactive) {
607                         int fd = open(filename.CStr(), O_CREAT | O_RDWR | O_APPEND, 0600);
608
609                         if (fd < 0) {
610                                 BOOST_THROW_EXCEPTION(posix_error()
611                                         << boost::errinfo_api_function("open")
612                                         << boost::errinfo_errno(errno)
613                                         << boost::errinfo_file_name(filename));
614                         }
615
616                         if (fd != 1) {
617                                 /* redirect stdout to the file */
618                                 dup2(fd, 1);
619                                 close(fd);
620                         }
621
622                         /* redirect stderr to stdout */
623                         if (fd != 2)
624                                 close(2);
625
626                         dup2(1, 2);
627                 }
628
629                 char **argv;
630                 char *my_pid_str = strdup(my_pid.CStr());
631
632                 if (interactive) {
633                         const char *uargv[] = {
634                                 "gdb",
635                                 "-p",
636                                 my_pid_str,
637                                 nullptr
638                         };
639
640                         argv = const_cast<char **>(uargv);
641
642                         (void) execvp(argv[0], argv);
643                 } else {
644                         const char *uargv[] = {
645                                 "gdb",
646                                 "--batch",
647                                 "-p",
648                                 my_pid_str,
649                                 "-ex",
650                                 "thread apply all bt full",
651                                 "-ex",
652                                 "detach",
653                                 "-ex",
654                                 "quit",
655                                 nullptr
656                         };
657
658                         argv = const_cast<char **>(uargv);
659
660                         (void) execvp(argv[0], argv);
661                 }
662
663                 perror("Failed to launch GDB");
664                 free(my_pid_str);
665                 _exit(0);
666         }
667
668         int status;
669         if (waitpid(pid, &status, 0) < 0) {
670                 BOOST_THROW_EXCEPTION(posix_error()
671                         << boost::errinfo_api_function("waitpid")
672                         << boost::errinfo_errno(errno));
673         }
674
675 #ifdef __linux__
676         prctl(PR_SET_DUMPABLE, 0);
677 #endif /* __linux __ */
678 #else /* _WIN32 */
679         DebugBreak();
680 #endif /* _WIN32 */
681 }
682
683 #ifndef _WIN32
684 /**
685  * Signal handler for SIGINT and SIGTERM. Prepares the application for cleanly
686  * shutting down during the next execution of the event loop.
687  *
688  * @param - The signal number.
689  */
690 void Application::SigIntTermHandler(int signum)
691 {
692         struct sigaction sa;
693         memset(&sa, 0, sizeof(sa));
694         sa.sa_handler = SIG_DFL;
695         sigaction(signum, &sa, nullptr);
696
697         Application::Ptr instance = Application::GetInstance();
698
699         if (!instance)
700                 return;
701
702         instance->RequestShutdown();
703 }
704 #endif /* _WIN32 */
705
706 /**
707  * Signal handler for SIGUSR1. This signal causes Icinga to re-open
708  * its log files and is mainly for use by logrotate.
709  *
710  * @param - The signal number.
711  */
712 void Application::SigUsr1Handler(int)
713 {
714         Log(LogInformation, "Application")
715                 << "Received USR1 signal, reopening application logs.";
716
717         RequestReopenLogs();
718 }
719
720 /**
721  * Signal handler for SIGUSR2. Hands over PID to child and commits suicide
722  *
723  * @param - The signal number.
724  */
725 void Application::SigUsr2Handler(int)
726 {
727         Log(LogInformation, "Application", "Reload requested, letting new process take over.");
728 #ifdef HAVE_SYSTEMD
729         sd_notifyf(0, "MAINPID=%lu", (unsigned long) m_ReloadProcess);
730 #endif /* HAVE_SYSTEMD */
731
732         /* Write the PID of the new process to the pidfile before this
733          * process exits to keep systemd happy.
734          */
735         Application::Ptr instance = GetInstance();
736         try {
737                 instance->UpdatePidFile(Configuration::PidPath, m_ReloadProcess);
738         } catch (const std::exception&) {
739                 /* abort restart */
740                 Log(LogCritical, "Application", "Cannot update PID file. Aborting restart operation.");
741                 return;
742         }
743
744         instance->ClosePidFile(false);
745
746         /* Ensure to dump the program state on reload. */
747         ConfigObject::StopObjects();
748         instance->OnShutdown();
749
750         Log(LogInformation, "Application")
751                 << "Reload done, parent process shutting down. Child process with PID '" << m_ReloadProcess << "' is taking over.";
752
753         Exit(0);
754 }
755
756 /**
757  * Signal handler for SIGABRT. Helps with debugging ASSERT()s.
758  *
759  * @param - The signal number.
760  */
761 void Application::SigAbrtHandler(int)
762 {
763 #ifndef _WIN32
764         struct sigaction sa;
765         memset(&sa, 0, sizeof(sa));
766         sa.sa_handler = SIG_DFL;
767         sigaction(SIGABRT, &sa, nullptr);
768 #endif /* _WIN32 */
769
770         std::cerr << "Caught SIGABRT." << std::endl
771                 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << std::endl
772                 << std::endl;
773
774         String fname = GetCrashReportFilename();
775         String dirName = Utility::DirName(fname);
776
777         if (!Utility::PathExists(dirName)) {
778 #ifndef _WIN32
779                 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
780 #else /*_ WIN32 */
781                 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
782 #endif /* _WIN32 */
783                         std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
784                 }
785         }
786
787         bool interactive_debugger = Configuration::AttachDebugger;
788
789         if (!interactive_debugger) {
790                 std::ofstream ofs;
791                 ofs.open(fname.CStr());
792
793                 Log(LogCritical, "Application")
794                         << "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'" << "\n";
795
796                 DisplayInfoMessage(ofs);
797
798                 StackTrace trace;
799                 ofs << "Stacktrace:" << "\n";
800                 trace.Print(ofs, 1);
801
802                 DisplayBugMessage(ofs);
803
804                 ofs << "\n";
805                 ofs.close();
806         } else {
807                 Log(LogCritical, "Application", "Icinga 2 has terminated unexpectedly. Attaching debugger...");
808         }
809
810         AttachDebugger(fname, interactive_debugger);
811 }
812
813 #ifdef _WIN32
814 /**
815  * Console control handler. Prepares the application for cleanly
816  * shutting down during the next execution of the event loop.
817  */
818 BOOL WINAPI Application::CtrlHandler(DWORD type)
819 {
820         Application::Ptr instance = Application::GetInstance();
821
822         if (!instance)
823                 return TRUE;
824
825         instance->RequestShutdown();
826
827         SetConsoleCtrlHandler(nullptr, FALSE);
828         return TRUE;
829 }
830
831 bool Application::IsProcessElevated() {
832         BOOL fIsElevated = FALSE;
833         DWORD dwError = ERROR_SUCCESS;
834         HANDLE hToken = nullptr;
835
836         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
837                 dwError = GetLastError();
838         else {
839                 TOKEN_ELEVATION elevation;
840                 DWORD dwSize;
841
842                 if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
843                         dwError = GetLastError();
844                 else
845                         fIsElevated = elevation.TokenIsElevated;
846         }
847
848         if (hToken) {
849                 CloseHandle(hToken);
850                 hToken = nullptr;
851         }
852
853         if (ERROR_SUCCESS != dwError) {
854                 LPSTR mBuf = nullptr;
855                 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
856                         nullptr, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), mBuf, 0, nullptr))
857                         BOOST_THROW_EXCEPTION(std::runtime_error("Failed to format error message, last error was: " + dwError));
858                 else
859                         BOOST_THROW_EXCEPTION(std::runtime_error(mBuf));
860         }
861
862         return fIsElevated;
863 }
864 #endif /* _WIN32 */
865
866 /**
867  * Handler for unhandled exceptions.
868  */
869 void Application::ExceptionHandler()
870 {
871         if (l_InExceptionHandler)
872                 for (;;)
873                         Utility::Sleep(5);
874
875         l_InExceptionHandler = true;
876
877 #ifndef _WIN32
878         struct sigaction sa;
879         memset(&sa, 0, sizeof(sa));
880         sa.sa_handler = SIG_DFL;
881         sigaction(SIGABRT, &sa, nullptr);
882 #endif /* _WIN32 */
883
884         String fname = GetCrashReportFilename();
885         String dirName = Utility::DirName(fname);
886
887         if (!Utility::PathExists(dirName)) {
888 #ifndef _WIN32
889                 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
890 #else /*_ WIN32 */
891                 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
892 #endif /* _WIN32 */
893                         std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
894                 }
895         }
896
897         bool interactive_debugger = Configuration::AttachDebugger;
898
899         if (!interactive_debugger) {
900                 std::ofstream ofs;
901                 ofs.open(fname.CStr());
902
903                 ofs << "Caught unhandled exception." << "\n"
904                         << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
905                         << "\n";
906
907                 DisplayInfoMessage(ofs);
908
909                 try {
910                         RethrowUncaughtException();
911                 } catch (const std::exception& ex) {
912                         Log(LogCritical, "Application")
913                                 << DiagnosticInformation(ex, false) << "\n"
914                                 << "\n"
915                                 << "Additional information is available in '" << fname << "'" << "\n";
916
917                         ofs << "\n"
918                                 << DiagnosticInformation(ex)
919                                 << "\n";
920                 }
921
922                 DisplayBugMessage(ofs);
923
924                 ofs.close();
925         }
926
927         AttachDebugger(fname, interactive_debugger);
928
929         abort();
930 }
931
932 #ifdef _WIN32
933 LONG CALLBACK Application::SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi)
934 {
935         if (l_InExceptionHandler)
936                 return EXCEPTION_CONTINUE_SEARCH;
937
938         l_InExceptionHandler = true;
939
940         String fname = GetCrashReportFilename();
941         String dirName = Utility::DirName(fname);
942
943         if (!Utility::PathExists(dirName)) {
944 #ifndef _WIN32
945                 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
946 #else /*_ WIN32 */
947                 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
948 #endif /* _WIN32 */
949                         std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
950                 }
951         }
952
953         std::ofstream ofs;
954         ofs.open(fname.CStr());
955
956         Log(LogCritical, "Application")
957                 << "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'";
958
959         DisplayInfoMessage(ofs);
960
961         ofs << "Caught unhandled SEH exception." << "\n"
962                 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
963                 << "\n";
964
965         StackTrace trace(exi);
966         ofs << "Stacktrace:" << "\n";
967         trace.Print(ofs, 1);
968
969         DisplayBugMessage(ofs);
970
971         return EXCEPTION_CONTINUE_SEARCH;
972 }
973 #endif /* _WIN32 */
974
975 /**
976  * Installs the exception handlers.
977  */
978 void Application::InstallExceptionHandlers()
979 {
980         std::set_terminate(&Application::ExceptionHandler);
981
982 #ifndef _WIN32
983         struct sigaction sa;
984         memset(&sa, 0, sizeof(sa));
985         sa.sa_handler = &Application::SigAbrtHandler;
986         sigaction(SIGABRT, &sa, nullptr);
987 #else /* _WIN32 */
988         SetUnhandledExceptionFilter(&Application::SEHUnhandledExceptionFilter);
989 #endif /* _WIN32 */
990 }
991
992 /**
993  * Runs the application.
994  *
995  * @returns The application's exit code.
996  */
997 int Application::Run()
998 {
999 #ifndef _WIN32
1000         struct sigaction sa;
1001         memset(&sa, 0, sizeof(sa));
1002         sa.sa_handler = &Application::SigIntTermHandler;
1003         sigaction(SIGINT, &sa, nullptr);
1004         sigaction(SIGTERM, &sa, nullptr);
1005
1006         sa.sa_handler = &Application::SigUsr1Handler;
1007         sigaction(SIGUSR1, &sa, nullptr);
1008
1009         sa.sa_handler = &Application::SigUsr2Handler;
1010         sigaction(SIGUSR2, &sa, nullptr);
1011 #else /* _WIN32 */
1012         SetConsoleCtrlHandler(&Application::CtrlHandler, TRUE);
1013 #endif /* _WIN32 */
1014
1015         try {
1016                 UpdatePidFile(Configuration::PidPath);
1017         } catch (const std::exception&) {
1018                 Log(LogCritical, "Application")
1019                         << "Cannot update PID file '" << Configuration::PidPath << "'. Aborting.";
1020                 return EXIT_FAILURE;
1021         }
1022
1023         SetMainTime(Utility::GetTime());
1024
1025         return Main();
1026 }
1027
1028 void Application::UpdatePidFile(const String& filename)
1029 {
1030         UpdatePidFile(filename, Utility::GetPid());
1031 }
1032
1033 /**
1034  * Grabs the PID file lock and updates the PID. Terminates the application
1035  * if the PID file is already locked by another instance of the application.
1036  *
1037  * @param filename The name of the PID file.
1038  * @param pid The PID to write; default is the current PID
1039  */
1040 void Application::UpdatePidFile(const String& filename, pid_t pid)
1041 {
1042         ObjectLock olock(this);
1043
1044         if (m_PidFile)
1045                 fclose(m_PidFile);
1046
1047         /* There's just no sane way of getting a file descriptor for a
1048          * C++ ofstream which is why we're using FILEs here. */
1049         m_PidFile = fopen(filename.CStr(), "r+");
1050
1051         if (!m_PidFile)
1052                 m_PidFile = fopen(filename.CStr(), "w");
1053
1054         if (!m_PidFile) {
1055                 Log(LogCritical, "Application")
1056                         << "Could not open PID file '" << filename << "'.";
1057                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open PID file '" + filename + "'"));
1058         }
1059
1060 #ifndef _WIN32
1061         int fd = fileno(m_PidFile);
1062
1063         Utility::SetCloExec(fd);
1064
1065         struct flock lock;
1066
1067         lock.l_start = 0;
1068         lock.l_len = 0;
1069         lock.l_type = F_WRLCK;
1070         lock.l_whence = SEEK_SET;
1071
1072         if (fcntl(fd, F_SETLK, &lock) < 0) {
1073                 Log(LogCritical, "Application", "Could not lock PID file. Make sure that only one instance of the application is running.");
1074
1075                 Application::Exit(EXIT_FAILURE);
1076         }
1077
1078         if (ftruncate(fd, 0) < 0) {
1079                 Log(LogCritical, "Application")
1080                         << "ftruncate() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
1081
1082                 BOOST_THROW_EXCEPTION(posix_error()
1083                         << boost::errinfo_api_function("ftruncate")
1084                         << boost::errinfo_errno(errno));
1085         }
1086 #endif /* _WIN32 */
1087
1088         fprintf(m_PidFile, "%lu\n", (unsigned long)pid);
1089         fflush(m_PidFile);
1090 }
1091
1092 /**
1093  * Closes the PID file. Does nothing if the PID file is not currently open.
1094  */
1095 void Application::ClosePidFile(bool unlink)
1096 {
1097         ObjectLock olock(this);
1098
1099         if (m_PidFile) {
1100                 if (unlink) {
1101                         String pidpath = Configuration::PidPath;
1102                         ::unlink(pidpath.CStr());
1103                 }
1104
1105                 fclose(m_PidFile);
1106         }
1107
1108         m_PidFile = nullptr;
1109 }
1110
1111 /**
1112  * Checks if another process currently owns the pidfile and read it
1113  *
1114  * @param filename The name of the PID file.
1115  * @returns 0: no process owning the pidfile, pid of the process otherwise
1116  */
1117 pid_t Application::ReadPidFile(const String& filename)
1118 {
1119         FILE *pidfile = fopen(filename.CStr(), "r");
1120
1121         if (!pidfile)
1122                 return 0;
1123
1124 #ifndef _WIN32
1125         int fd = fileno(pidfile);
1126
1127         struct flock lock;
1128
1129         lock.l_start = 0;
1130         lock.l_len = 0;
1131         lock.l_type = F_WRLCK;
1132         lock.l_whence = SEEK_SET;
1133
1134         if (fcntl(fd, F_GETLK, &lock) < 0) {
1135                 int error = errno;
1136                 fclose(pidfile);
1137                 BOOST_THROW_EXCEPTION(posix_error()
1138                         << boost::errinfo_api_function("fcntl")
1139                         << boost::errinfo_errno(error));
1140         }
1141
1142         if (lock.l_type == F_UNLCK) {
1143                 // nobody has locked the file: no icinga running
1144                 fclose(pidfile);
1145                 return -1;
1146         }
1147 #endif /* _WIN32 */
1148
1149         pid_t runningpid;
1150         int res = fscanf(pidfile, "%d", &runningpid);
1151         fclose(pidfile);
1152
1153         // bogus result?
1154         if (res != 1)
1155                 return 0;
1156
1157 #ifdef _WIN32
1158         HANDLE hProcess = OpenProcess(0, FALSE, runningpid);
1159
1160         if (!hProcess)
1161                 return 0;
1162
1163         CloseHandle(hProcess);
1164 #endif /* _WIN32 */
1165
1166         return runningpid;
1167 }
1168
1169 int Application::GetDefaultRLimitFiles()
1170 {
1171         return 16 * 1024;
1172 }
1173
1174 int Application::GetDefaultRLimitProcesses()
1175 {
1176         return 16 * 1024;
1177 }
1178
1179 int Application::GetDefaultRLimitStack()
1180 {
1181         return 256 * 1024;
1182 }
1183
1184 double Application::GetReloadTimeout()
1185 {
1186         return ScriptGlobal::Get("ReloadTimeout");
1187 }
1188
1189 /**
1190  * Returns the global thread pool.
1191  *
1192  * @returns The global thread pool.
1193  */
1194 ThreadPool& Application::GetTP()
1195 {
1196         static ThreadPool tp;
1197         return tp;
1198 }
1199
1200 double Application::GetStartTime()
1201 {
1202         return m_StartTime;
1203 }
1204
1205 void Application::SetStartTime(double ts)
1206 {
1207         m_StartTime = ts;
1208 }
1209
1210 double Application::GetMainTime()
1211 {
1212         return m_MainTime;
1213 }
1214
1215 void Application::SetMainTime(double ts)
1216 {
1217         m_MainTime = ts;
1218 }
1219
1220 bool Application::GetScriptDebuggerEnabled()
1221 {
1222         return m_ScriptDebuggerEnabled;
1223 }
1224
1225 void Application::SetScriptDebuggerEnabled(bool enabled)
1226 {
1227         m_ScriptDebuggerEnabled = enabled;
1228 }
1229
1230 double Application::GetLastReloadFailed()
1231 {
1232         return m_LastReloadFailed;
1233 }
1234
1235 void Application::SetLastReloadFailed(double ts)
1236 {
1237         m_LastReloadFailed = ts;
1238 }
1239
1240 void Application::ValidateName(const Lazy<String>& lvalue, const ValidationUtils& utils)
1241 {
1242         ObjectImpl<Application>::ValidateName(lvalue, utils);
1243
1244         if (lvalue() != "app")
1245                 BOOST_THROW_EXCEPTION(ValidationError(this, { "name" }, "Application object must be named 'app'."));
1246 }