]> granicus.if.org Git - icinga2/blob - lib/base/application.cpp
Remove unused includes
[icinga2] / lib / base / application.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/application.hpp"
21 #include "base/application-ti.cpp"
22 #include "base/stacktrace.hpp"
23 #include "base/timer.hpp"
24 #include "base/logger.hpp"
25 #include "base/exception.hpp"
26 #include "base/objectlock.hpp"
27 #include "base/utility.hpp"
28 #include "base/loader.hpp"
29 #include "base/debug.hpp"
30 #include "base/type.hpp"
31 #include "base/convert.hpp"
32 #include "base/scriptglobal.hpp"
33 #include "base/process.hpp"
34 #include <boost/algorithm/string/trim.hpp>
35 #include <boost/exception/errinfo_api_function.hpp>
36 #include <boost/exception/errinfo_errno.hpp>
37 #include <boost/exception/errinfo_file_name.hpp>
38 #include <sstream>
39 #include <iostream>
40 #include <fstream>
41 #include <thread>
42 #ifdef __linux__
43 #include <sys/prctl.h>
44 #endif /* __linux__ */
45 #ifdef _WIN32
46 #include <windows.h>
47 #endif /* _WIN32 */
48 #ifdef HAVE_SYSTEMD
49 #include <systemd/sd-daemon.h>
50 #endif /* HAVE_SYSTEMD */
51
52 using namespace icinga;
53
54 REGISTER_TYPE(Application);
55
56 boost::signals2::signal<void ()> Application::OnReopenLogs;
57 Application::Ptr Application::m_Instance = nullptr;
58 bool Application::m_ShuttingDown = false;
59 bool Application::m_RequestRestart = false;
60 bool Application::m_RequestReopenLogs = false;
61 pid_t Application::m_ReloadProcess = 0;
62 static bool l_Restarting = false;
63 static bool l_InExceptionHandler = false;
64 int Application::m_ArgC;
65 char **Application::m_ArgV;
66 double Application::m_StartTime;
67 double Application::m_MainTime;
68 bool Application::m_ScriptDebuggerEnabled = false;
69 double Application::m_LastReloadFailed;
70
71 /**
72  * Constructor for the Application class.
73  */
74 void Application::OnConfigLoaded()
75 {
76         m_PidFile = nullptr;
77
78         ASSERT(m_Instance == nullptr);
79         m_Instance = this;
80 }
81
82 /**
83  * Destructor for the application class.
84  */
85 void Application::Stop(bool runtimeRemoved)
86 {
87         m_ShuttingDown = true;
88
89 #ifdef _WIN32
90         WSACleanup();
91 #endif /* _WIN32 */
92
93         // Getting a shutdown-signal when a restart is in progress usually
94         // means that the restart succeeded and the new process wants to take
95         // over. Write the PID of the new process to the pidfile before this
96         // process exits to keep systemd happy.
97         if (l_Restarting) {
98                 try {
99                         UpdatePidFile(GetPidPath(), m_ReloadProcess);
100                 } catch (const std::exception&) {
101                         /* abort restart */
102                         Log(LogCritical, "Application", "Cannot update PID file. Aborting restart operation.");
103                         return;
104                 }
105
106                 Log(LogDebug, "Application")
107                         << "Keeping pid  '" << m_ReloadProcess << "' open.";
108
109                 ClosePidFile(false);
110         } else
111                 ClosePidFile(true);
112
113         ObjectImpl<Application>::Stop(runtimeRemoved);
114 }
115
116 Application::~Application()
117 {
118         m_Instance = nullptr;
119 }
120
121 void Application::Exit(int rc)
122 {
123         std::cout.flush();
124         std::cerr.flush();
125
126         for (const Logger::Ptr& logger : Logger::GetLoggers()) {
127                 logger->Flush();
128         }
129
130         UninitializeBase();
131 #ifdef I2_DEBUG
132         exit(rc);
133 #else /* I2_DEBUG */
134         _exit(rc); // Yay, our static destructors are pretty much beyond repair at this point.
135 #endif /* I2_DEBUG */
136 }
137
138 void Application::InitializeBase()
139 {
140 #ifdef _WIN32
141         /* disable GUI-based error messages for LoadLibrary() */
142         SetErrorMode(SEM_FAILCRITICALERRORS);
143
144         WSADATA wsaData;
145         if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
146                 BOOST_THROW_EXCEPTION(win32_error()
147                         << boost::errinfo_api_function("WSAStartup")
148                         << errinfo_win32_error(WSAGetLastError()));
149         }
150 #else /* _WIN32 */
151         struct sigaction sa;
152         memset(&sa, 0, sizeof(sa));
153         sa.sa_handler = SIG_IGN;
154         sigaction(SIGPIPE, &sa, nullptr);
155 #endif /* _WIN32 */
156
157         Loader::ExecuteDeferredInitializers();
158
159         /* make sure the thread pool gets initialized */
160         GetTP().Start();
161 }
162
163 void Application::UninitializeBase()
164 {
165         Timer::Uninitialize();
166
167         GetTP().Stop();
168 }
169
170 /**
171  * Retrieves a pointer to the application singleton object.
172  *
173  * @returns The application object.
174  */
175 Application::Ptr Application::GetInstance()
176 {
177         return m_Instance;
178 }
179
180 void Application::SetResourceLimits()
181 {
182 #ifdef __linux__
183         rlimit rl;
184
185 #       ifdef RLIMIT_NOFILE
186         rlim_t fileLimit = GetRLimitFiles();
187
188         if (fileLimit != 0) {
189                 if (fileLimit < GetDefaultRLimitFiles()) {
190                         Log(LogWarning, "Application")
191                                 << "The user-specified value for RLimitFiles cannot be smaller than the default value (" << GetDefaultRLimitFiles() << "). Using the default value instead.";
192                         fileLimit = GetDefaultRLimitFiles();
193                 }
194
195                 rl.rlim_cur = fileLimit;
196                 rl.rlim_max = rl.rlim_cur;
197
198                 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
199                         Log(LogNotice, "Application", "Could not adjust resource limit for open file handles (RLIMIT_NOFILE)");
200 #       else /* RLIMIT_NOFILE */
201                 Log(LogNotice, "Application", "System does not support adjusting the resource limit for open file handles (RLIMIT_NOFILE)");
202 #       endif /* RLIMIT_NOFILE */
203         }
204
205 #       ifdef RLIMIT_NPROC
206         rlim_t processLimit = GetRLimitProcesses();
207
208         if (processLimit != 0) {
209                 if (processLimit < GetDefaultRLimitProcesses()) {
210                         Log(LogWarning, "Application")
211                                 << "The user-specified value for RLimitProcesses cannot be smaller than the default value (" << GetDefaultRLimitProcesses() << "). Using the default value instead.";
212                         processLimit = GetDefaultRLimitProcesses();
213                 }
214
215                 rl.rlim_cur = processLimit;
216                 rl.rlim_max = rl.rlim_cur;
217
218                 if (setrlimit(RLIMIT_NPROC, &rl) < 0)
219                         Log(LogNotice, "Application", "Could not adjust resource limit for number of processes (RLIMIT_NPROC)");
220 #       else /* RLIMIT_NPROC */
221                 Log(LogNotice, "Application", "System does not support adjusting the resource limit for number of processes (RLIMIT_NPROC)");
222 #       endif /* RLIMIT_NPROC */
223         }
224
225 #       ifdef RLIMIT_STACK
226         int argc = Application::GetArgC();
227         char **argv = Application::GetArgV();
228         bool set_stack_rlimit = true;
229
230         for (int i = 0; i < argc; i++) {
231                 if (strcmp(argv[i], "--no-stack-rlimit") == 0) {
232                         set_stack_rlimit = false;
233                         break;
234                 }
235         }
236
237         if (getrlimit(RLIMIT_STACK, &rl) < 0) {
238                 Log(LogWarning, "Application", "Could not determine resource limit for stack size (RLIMIT_STACK)");
239                 rl.rlim_max = RLIM_INFINITY;
240         }
241
242         rlim_t stackLimit;
243
244         stackLimit = GetRLimitStack();
245
246         if (stackLimit != 0) {
247                 if (stackLimit < GetDefaultRLimitStack()) {
248                         Log(LogWarning, "Application")
249                                 << "The user-specified value for RLimitStack cannot be smaller than the default value (" << GetDefaultRLimitStack() << "). Using the default value instead.";
250                         stackLimit = GetDefaultRLimitStack();
251                 }
252
253                 if (set_stack_rlimit)
254                         rl.rlim_cur = stackLimit;
255                 else
256                         rl.rlim_cur = rl.rlim_max;
257
258                 if (setrlimit(RLIMIT_STACK, &rl) < 0)
259                         Log(LogNotice, "Application", "Could not adjust resource limit for stack size (RLIMIT_STACK)");
260                 else if (set_stack_rlimit) {
261                         char **new_argv = static_cast<char **>(malloc(sizeof(char *) * (argc + 2)));
262
263                         if (!new_argv) {
264                                 perror("malloc");
265                                 Exit(EXIT_FAILURE);
266                         }
267
268                         new_argv[0] = argv[0];
269                         new_argv[1] = strdup("--no-stack-rlimit");
270
271                         if (!new_argv[1]) {
272                                 perror("strdup");
273                                 exit(1);
274                         }
275
276                         for (int i = 1; i < argc; i++)
277                                 new_argv[i + 1] = argv[i];
278
279                         new_argv[argc + 1] = nullptr;
280
281                         (void) execvp(new_argv[0], new_argv);
282                         perror("execvp");
283                         _exit(EXIT_FAILURE);
284                 }
285 #       else /* RLIMIT_STACK */
286                 Log(LogNotice, "Application", "System does not support adjusting the resource limit for stack size (RLIMIT_STACK)");
287 #       endif /* RLIMIT_STACK */
288         }
289 #endif /* __linux__ */
290 }
291
292 int Application::GetArgC()
293 {
294         return m_ArgC;
295 }
296
297 void Application::SetArgC(int argc)
298 {
299         m_ArgC = argc;
300 }
301
302 char **Application::GetArgV()
303 {
304         return m_ArgV;
305 }
306
307 void Application::SetArgV(char **argv)
308 {
309         m_ArgV = argv;
310 }
311
312 /**
313  * Processes events for registered sockets and timers and calls whatever
314  * handlers have been set up for these events.
315  */
316 void Application::RunEventLoop()
317 {
318
319 #ifdef HAVE_SYSTEMD
320         sd_notify(0, "READY=1");
321 #endif /* HAVE_SYSTEMD */
322
323         double lastLoop = Utility::GetTime();
324
325 mainloop:
326         while (!m_ShuttingDown && !m_RequestRestart) {
327                 /* Watches for changes to the system time. Adjusts timers if necessary. */
328                 Utility::Sleep(2.5);
329
330                 if (m_RequestReopenLogs) {
331                         Log(LogNotice, "Application", "Reopening log files");
332                         m_RequestReopenLogs = false;
333                         OnReopenLogs();
334                 }
335
336                 double now = Utility::GetTime();
337                 double timeDiff = lastLoop - now;
338
339 #ifdef HAVE_SYSTEMD
340                 sd_notify(0, "WATCHDOG=1");
341 #endif /* HAVE_SYSTEMD */
342
343                 if (std::fabs(timeDiff) > 15) {
344                         /* We made a significant jump in time. */
345                         Log(LogInformation, "Application")
346                                 << "We jumped "
347                                 << (timeDiff < 0 ? "forward" : "backward")
348                                 << " in time: " << std::fabs(timeDiff) << " seconds";
349
350                         Timer::AdjustTimers(-timeDiff);
351                 }
352
353                 lastLoop = now;
354         }
355
356         if (m_RequestRestart) {
357                 m_RequestRestart = false;         // we are now handling the request, once is enough
358
359 #ifdef HAVE_SYSTEMD
360                 sd_notify(0, "RELOADING=1");
361 #endif /* HAVE_SYSTEMD */
362
363                 // are we already restarting? ignore request if we already are
364                 if (l_Restarting)
365                         goto mainloop;
366
367                 l_Restarting = true;
368                 m_ReloadProcess = StartReloadProcess();
369
370                 goto mainloop;
371         }
372
373 #ifdef HAVE_SYSTEMD
374         sd_notify(0, "STOPPING=1");
375 #endif /* HAVE_SYSTEMD */
376
377         Log(LogInformation, "Application", "Shutting down...");
378
379         ConfigObject::StopObjects();
380         Application::GetInstance()->OnShutdown();
381
382         UninitializeBase();
383 }
384
385 bool Application::IsShuttingDown()
386 {
387         return m_ShuttingDown;
388 }
389
390 void Application::OnShutdown()
391 {
392         /* Nothing to do here. */
393 }
394
395 static void ReloadProcessCallbackInternal(const ProcessResult& pr)
396 {
397         if (pr.ExitStatus != 0) {
398                 Application::SetLastReloadFailed(Utility::GetTime());
399                 Log(LogCritical, "Application", "Found error in config: reloading aborted");
400         }
401 #ifdef _WIN32
402         else
403                 Application::Exit(7); /* keep this exit code in sync with icinga-app */
404 #endif /* _WIN32 */
405 }
406
407 static void ReloadProcessCallback(const ProcessResult& pr)
408 {
409         l_Restarting = false;
410
411         std::thread t(std::bind(&ReloadProcessCallbackInternal, pr));
412         t.detach();
413 }
414
415 pid_t Application::StartReloadProcess()
416 {
417         Log(LogInformation, "Application", "Got reload command: Starting new instance.");
418
419         // prepare arguments
420         ArrayData args;
421         args.push_back(GetExePath(m_ArgV[0]));
422
423         for (int i=1; i < Application::GetArgC(); i++) {
424                 if (std::string(Application::GetArgV()[i]) != "--reload-internal")
425                         args.push_back(Application::GetArgV()[i]);
426                 else
427                         i++;     // the next parameter after --reload-internal is the pid, remove that too
428         }
429
430 #ifndef _WIN32
431         args.push_back("--reload-internal");
432         args.push_back(Convert::ToString(Utility::GetPid()));
433 #else /* _WIN32 */
434         args.push_back("--validate");
435 #endif /* _WIN32 */
436
437         Process::Ptr process = new Process(Process::PrepareCommand(new Array(std::move(args))));
438         process->SetTimeout(300);
439         process->Run(&ReloadProcessCallback);
440
441         return process->GetPID();
442 }
443
444 /**
445  * Signals the application to shut down during the next
446  * execution of the event loop.
447  */
448 void Application::RequestShutdown()
449 {
450         Log(LogInformation, "Application", "Received request to shut down.");
451
452         m_ShuttingDown = true;
453 }
454
455 /**
456  * Signals the application to restart during the next
457  * execution of the event loop.
458  */
459 void Application::RequestRestart()
460 {
461         m_RequestRestart = true;
462 }
463
464 /**
465  * Signals the application to reopen log files during the
466  * next execution of the event loop.
467  */
468 void Application::RequestReopenLogs()
469 {
470         m_RequestReopenLogs = true;
471 }
472
473 /**
474  * Retrieves the full path of the executable.
475  *
476  * @param argv0 The first command-line argument.
477  * @returns The path.
478  */
479 String Application::GetExePath(const String& argv0)
480 {
481         String executablePath;
482
483 #ifndef _WIN32
484         char buffer[MAXPATHLEN];
485         if (!getcwd(buffer, sizeof(buffer))) {
486                 BOOST_THROW_EXCEPTION(posix_error()
487                         << boost::errinfo_api_function("getcwd")
488                         << boost::errinfo_errno(errno));
489         }
490
491         String workingDirectory = buffer;
492
493         if (argv0[0] != '/')
494                 executablePath = workingDirectory + "/" + argv0;
495         else
496                 executablePath = argv0;
497
498         bool foundSlash = false;
499         for (size_t i = 0; i < argv0.GetLength(); i++) {
500                 if (argv0[i] == '/') {
501                         foundSlash = true;
502                         break;
503                 }
504         }
505
506         if (!foundSlash) {
507                 const char *pathEnv = getenv("PATH");
508                 if (pathEnv) {
509                         std::vector<String> paths = String(pathEnv).Split(":");
510
511                         bool foundPath = false;
512                         for (const String& path : paths) {
513                                 String pathTest = path + "/" + argv0;
514
515                                 if (access(pathTest.CStr(), X_OK) == 0) {
516                                         executablePath = pathTest;
517                                         foundPath = true;
518                                         break;
519                                 }
520                         }
521
522                         if (!foundPath) {
523                                 executablePath.Clear();
524                                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not determine executable path."));
525                         }
526                 }
527         }
528
529         if (!realpath(executablePath.CStr(), buffer)) {
530                 BOOST_THROW_EXCEPTION(posix_error()
531                         << boost::errinfo_api_function("realpath")
532                         << boost::errinfo_errno(errno)
533                         << boost::errinfo_file_name(executablePath));
534         }
535
536         return buffer;
537 #else /* _WIN32 */
538         char FullExePath[MAXPATHLEN];
539
540         if (!GetModuleFileName(nullptr, FullExePath, sizeof(FullExePath)))
541                 BOOST_THROW_EXCEPTION(win32_error()
542                         << boost::errinfo_api_function("GetModuleFileName")
543                         << errinfo_win32_error(GetLastError()));
544
545         return FullExePath;
546 #endif /* _WIN32 */
547 }
548
549 /**
550  * Display version and path information.
551  */
552 void Application::DisplayInfoMessage(std::ostream& os, bool skipVersion)
553 {
554         os << "Application information:" << "\n";
555
556         if (!skipVersion)
557                 os << "  Application version: " << GetAppVersion() << "\n";
558
559         os << "  Installation root: " << GetPrefixDir() << "\n"
560                 << "  Sysconf directory: " << GetSysconfDir() << "\n"
561                 << "  Run directory: " << GetRunDir() << "\n"
562                 << "  Local state directory: " << GetLocalStateDir() << "\n"
563                 << "  Package data directory: " << GetPkgDataDir() << "\n"
564                 << "  State path: " << GetStatePath() << "\n"
565                 << "  Modified attributes path: " << GetModAttrPath() << "\n"
566                 << "  Objects path: " << GetObjectsPath() << "\n"
567                 << "  Vars path: " << GetVarsPath() << "\n"
568                 << "  PID path: " << GetPidPath() << "\n";
569
570         os << "\n"
571                 << "System information:" << "\n"
572                 << "  Platform: " << Utility::GetPlatformName() << "\n"
573                 << "  Platform version: " << Utility::GetPlatformVersion() << "\n"
574                 << "  Kernel: " << Utility::GetPlatformKernel() << "\n"
575                 << "  Kernel version: " << Utility::GetPlatformKernelVersion() << "\n"
576                 << "  Architecture: " << Utility::GetPlatformArchitecture() << "\n";
577
578         os << "\n"
579                 << "Build information:" << "\n"
580                 << "  Compiler: " << ScriptGlobal::Get("BuildCompilerName") << " " << ScriptGlobal::Get("BuildCompilerVersion") << "\n"
581                 << "  Build host: " << ScriptGlobal::Get("BuildHostName") << "\n";
582 }
583
584 /**
585  * Displays a message that tells users what to do when they encounter a bug.
586  */
587 void Application::DisplayBugMessage(std::ostream& os)
588 {
589         os << "***" << "\n"
590                 << "* This would indicate a runtime problem or configuration error. If you believe this is a bug in Icinga 2" << "\n"
591                 << "* please submit a bug report at https://github.com/Icinga/icinga2 and include this stack trace as well as any other" << "\n"
592                 << "* information that might be useful in order to reproduce this problem." << "\n"
593                 << "***" << "\n";
594 }
595
596 String Application::GetCrashReportFilename()
597 {
598         return GetLocalStateDir() + "/log/icinga2/crash/report." + Convert::ToString(Utility::GetTime());
599 }
600
601
602 void Application::AttachDebugger(const String& filename, bool interactive)
603 {
604 #ifndef _WIN32
605 #ifdef __linux__
606         prctl(PR_SET_DUMPABLE, 1);
607 #endif /* __linux __ */
608
609         String my_pid = Convert::ToString(Utility::GetPid());
610
611         pid_t pid = fork();
612
613         if (pid < 0) {
614                 BOOST_THROW_EXCEPTION(posix_error()
615                         << boost::errinfo_api_function("fork")
616                         << boost::errinfo_errno(errno));
617         }
618
619         if (pid == 0) {
620                 if (!interactive) {
621                         int fd = open(filename.CStr(), O_CREAT | O_RDWR | O_APPEND, 0600);
622
623                         if (fd < 0) {
624                                 BOOST_THROW_EXCEPTION(posix_error()
625                                         << boost::errinfo_api_function("open")
626                                         << boost::errinfo_errno(errno)
627                                         << boost::errinfo_file_name(filename));
628                         }
629
630                         if (fd != 1) {
631                                 /* redirect stdout to the file */
632                                 dup2(fd, 1);
633                                 close(fd);
634                         }
635
636                         /* redirect stderr to stdout */
637                         if (fd != 2)
638                                 close(2);
639
640                         dup2(1, 2);
641                 }
642
643                 char **argv;
644                 char *my_pid_str = strdup(my_pid.CStr());
645
646                 if (interactive) {
647                         const char *uargv[] = {
648                                 "gdb",
649                                 "-p",
650                                 my_pid_str,
651                                 nullptr
652                         };
653
654                         argv = const_cast<char **>(uargv);
655
656                         (void) execvp(argv[0], argv);
657                 } else {
658                         const char *uargv[] = {
659                                 "gdb",
660                                 "--batch",
661                                 "-p",
662                                 my_pid_str,
663                                 "-ex",
664                                 "thread apply all bt full",
665                                 "-ex",
666                                 "detach",
667                                 "-ex",
668                                 "quit",
669                                 nullptr
670                         };
671
672                         argv = const_cast<char **>(uargv);
673
674                         (void) execvp(argv[0], argv);
675                 }
676
677                 perror("Failed to launch GDB");
678                 free(my_pid_str);
679                 _exit(0);
680         }
681
682         int status;
683         if (waitpid(pid, &status, 0) < 0) {
684                 BOOST_THROW_EXCEPTION(posix_error()
685                         << boost::errinfo_api_function("waitpid")
686                         << boost::errinfo_errno(errno));
687         }
688
689 #ifdef __linux__
690         prctl(PR_SET_DUMPABLE, 0);
691 #endif /* __linux __ */
692 #else /* _WIN32 */
693         DebugBreak();
694 #endif /* _WIN32 */
695 }
696
697 #ifndef _WIN32
698 /**
699  * Signal handler for SIGINT and SIGTERM. Prepares the application for cleanly
700  * shutting down during the next execution of the event loop.
701  *
702  * @param - The signal number.
703  */
704 void Application::SigIntTermHandler(int signum)
705 {
706         struct sigaction sa;
707         memset(&sa, 0, sizeof(sa));
708         sa.sa_handler = SIG_DFL;
709         sigaction(signum, &sa, nullptr);
710
711         Application::Ptr instance = Application::GetInstance();
712
713         if (!instance)
714                 return;
715
716         instance->RequestShutdown();
717 }
718 #endif /* _WIN32 */
719
720 /**
721  * Signal handler for SIGUSR1. This signal causes Icinga to re-open
722  * its log files and is mainly for use by logrotate.
723  *
724  * @param - The signal number.
725  */
726 void Application::SigUsr1Handler(int)
727 {
728         RequestReopenLogs();
729 }
730
731 /**
732  * Signal handler for SIGUSR2. Hands over PID to child and commits suicide
733  *
734  * @param - The signal number.
735  */
736 void Application::SigUsr2Handler(int)
737 {
738         Log(LogInformation, "Application", "Reload requested, letting new process take over.");
739 #ifdef HAVE_SYSTEMD
740         sd_notifyf(0, "MAINPID=%lu", (unsigned long) m_ReloadProcess);
741 #endif /* HAVE_SYSTEMD */
742
743         Exit(0);
744 }
745
746 /**
747  * Signal handler for SIGABRT. Helps with debugging ASSERT()s.
748  *
749  * @param - The signal number.
750  */
751 void Application::SigAbrtHandler(int)
752 {
753 #ifndef _WIN32
754         struct sigaction sa;
755         memset(&sa, 0, sizeof(sa));
756         sa.sa_handler = SIG_DFL;
757         sigaction(SIGABRT, &sa, nullptr);
758 #endif /* _WIN32 */
759
760         std::cerr << "Caught SIGABRT." << std::endl
761                 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << std::endl
762                 << std::endl;
763
764         String fname = GetCrashReportFilename();
765         String dirName = Utility::DirName(fname);
766
767         if (!Utility::PathExists(dirName)) {
768 #ifndef _WIN32
769                 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
770 #else /*_ WIN32 */
771                 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
772 #endif /* _WIN32 */
773                         std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
774                 }
775         }
776
777         bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger"));
778
779         if (!interactive_debugger) {
780                 std::ofstream ofs;
781                 ofs.open(fname.CStr());
782
783                 Log(LogCritical, "Application")
784                         << "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'" << "\n";
785
786                 DisplayInfoMessage(ofs);
787
788                 StackTrace trace;
789                 ofs << "Stacktrace:" << "\n";
790                 trace.Print(ofs, 1);
791
792                 DisplayBugMessage(ofs);
793
794                 ofs << "\n";
795                 ofs.close();
796         } else {
797                 Log(LogCritical, "Application", "Icinga 2 has terminated unexpectedly. Attaching debugger...");
798         }
799
800         AttachDebugger(fname, interactive_debugger);
801 }
802
803 #ifdef _WIN32
804 /**
805  * Console control handler. Prepares the application for cleanly
806  * shutting down during the next execution of the event loop.
807  */
808 BOOL WINAPI Application::CtrlHandler(DWORD type)
809 {
810         Application::Ptr instance = Application::GetInstance();
811
812         if (!instance)
813                 return TRUE;
814
815         instance->RequestShutdown();
816
817         SetConsoleCtrlHandler(nullptr, FALSE);
818         return TRUE;
819 }
820
821 bool Application::IsProcessElevated() {
822         BOOL fIsElevated = FALSE;
823         DWORD dwError = ERROR_SUCCESS;
824         HANDLE hToken = nullptr;
825
826         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
827                 dwError = GetLastError();
828         else {
829                 TOKEN_ELEVATION elevation;
830                 DWORD dwSize;
831
832                 if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
833                         dwError = GetLastError();
834                 else
835                         fIsElevated = elevation.TokenIsElevated;
836         }
837
838         if (hToken) {
839                 CloseHandle(hToken);
840                 hToken = nullptr;
841         }
842
843         if (ERROR_SUCCESS != dwError) {
844                 LPSTR mBuf = nullptr;
845                 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
846                         nullptr, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), mBuf, 0, nullptr))
847                         BOOST_THROW_EXCEPTION(std::runtime_error("Failed to format error message, last error was: " + dwError));
848                 else
849                         BOOST_THROW_EXCEPTION(std::runtime_error(mBuf));
850         }
851
852         return fIsElevated;
853 }
854 #endif /* _WIN32 */
855
856 /**
857  * Handler for unhandled exceptions.
858  */
859 void Application::ExceptionHandler()
860 {
861         if (l_InExceptionHandler)
862                 for (;;)
863                         Utility::Sleep(5);
864
865         l_InExceptionHandler = true;
866
867 #ifndef _WIN32
868         struct sigaction sa;
869         memset(&sa, 0, sizeof(sa));
870         sa.sa_handler = SIG_DFL;
871         sigaction(SIGABRT, &sa, nullptr);
872 #endif /* _WIN32 */
873
874         String fname = GetCrashReportFilename();
875         String dirName = Utility::DirName(fname);
876
877         if (!Utility::PathExists(dirName)) {
878 #ifndef _WIN32
879                 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
880 #else /*_ WIN32 */
881                 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
882 #endif /* _WIN32 */
883                         std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
884                 }
885         }
886
887         bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger"));
888
889         if (!interactive_debugger) {
890                 std::ofstream ofs;
891                 ofs.open(fname.CStr());
892
893                 ofs << "Caught unhandled exception." << "\n"
894                         << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
895                         << "\n";
896
897                 DisplayInfoMessage(ofs);
898
899                 try {
900                         RethrowUncaughtException();
901                 } catch (const std::exception& ex) {
902                         Log(LogCritical, "Application")
903                                 << DiagnosticInformation(ex, false) << "\n"
904                                 << "\n"
905                                 << "Additional information is available in '" << fname << "'" << "\n";
906
907                         ofs << "\n"
908                                 << DiagnosticInformation(ex)
909                                 << "\n";
910                 }
911
912                 DisplayBugMessage(ofs);
913
914                 ofs.close();
915         }
916
917         AttachDebugger(fname, interactive_debugger);
918
919         abort();
920 }
921
922 #ifdef _WIN32
923 LONG CALLBACK Application::SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi)
924 {
925         if (l_InExceptionHandler)
926                 return EXCEPTION_CONTINUE_SEARCH;
927
928         l_InExceptionHandler = true;
929
930         String fname = GetCrashReportFilename();
931         String dirName = Utility::DirName(fname);
932
933         if (!Utility::PathExists(dirName)) {
934 #ifndef _WIN32
935                 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
936 #else /*_ WIN32 */
937                 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
938 #endif /* _WIN32 */
939                         std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
940                 }
941         }
942
943         std::ofstream ofs;
944         ofs.open(fname.CStr());
945
946         Log(LogCritical, "Application")
947                 << "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'";
948
949         DisplayInfoMessage(ofs);
950
951         ofs << "Caught unhandled SEH exception." << "\n"
952                 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
953                 << "\n";
954
955         StackTrace trace(exi);
956         ofs << "Stacktrace:" << "\n";
957         trace.Print(ofs, 1);
958
959         DisplayBugMessage(ofs);
960
961         return EXCEPTION_CONTINUE_SEARCH;
962 }
963 #endif /* _WIN32 */
964
965 /**
966  * Installs the exception handlers.
967  */
968 void Application::InstallExceptionHandlers()
969 {
970         std::set_terminate(&Application::ExceptionHandler);
971
972 #ifndef _WIN32
973         struct sigaction sa;
974         memset(&sa, 0, sizeof(sa));
975         sa.sa_handler = &Application::SigAbrtHandler;
976         sigaction(SIGABRT, &sa, nullptr);
977 #else /* _WIN32 */
978         SetUnhandledExceptionFilter(&Application::SEHUnhandledExceptionFilter);
979 #endif /* _WIN32 */
980 }
981
982 /**
983  * Runs the application.
984  *
985  * @returns The application's exit code.
986  */
987 int Application::Run()
988 {
989 #ifndef _WIN32
990         struct sigaction sa;
991         memset(&sa, 0, sizeof(sa));
992         sa.sa_handler = &Application::SigIntTermHandler;
993         sigaction(SIGINT, &sa, nullptr);
994         sigaction(SIGTERM, &sa, nullptr);
995
996         sa.sa_handler = &Application::SigUsr1Handler;
997         sigaction(SIGUSR1, &sa, nullptr);
998
999         sa.sa_handler = &Application::SigUsr2Handler;
1000         sigaction(SIGUSR2, &sa, nullptr);
1001 #else /* _WIN32 */
1002         SetConsoleCtrlHandler(&Application::CtrlHandler, TRUE);
1003 #endif /* _WIN32 */
1004
1005         try {
1006                 UpdatePidFile(GetPidPath());
1007         } catch (const std::exception&) {
1008                 Log(LogCritical, "Application")
1009                         << "Cannot update PID file '" << GetPidPath() << "'. Aborting.";
1010                 return EXIT_FAILURE;
1011         }
1012
1013         SetMainTime(Utility::GetTime());
1014
1015         return Main();
1016 }
1017
1018 void Application::UpdatePidFile(const String& filename)
1019 {
1020         UpdatePidFile(filename, Utility::GetPid());
1021 }
1022
1023 /**
1024  * Grabs the PID file lock and updates the PID. Terminates the application
1025  * if the PID file is already locked by another instance of the application.
1026  *
1027  * @param filename The name of the PID file.
1028  * @param pid The PID to write; default is the current PID
1029  */
1030 void Application::UpdatePidFile(const String& filename, pid_t pid)
1031 {
1032         ObjectLock olock(this);
1033
1034         if (m_PidFile)
1035                 fclose(m_PidFile);
1036
1037         /* There's just no sane way of getting a file descriptor for a
1038          * C++ ofstream which is why we're using FILEs here. */
1039         m_PidFile = fopen(filename.CStr(), "r+");
1040
1041         if (!m_PidFile)
1042                 m_PidFile = fopen(filename.CStr(), "w");
1043
1044         if (!m_PidFile) {
1045                 Log(LogCritical, "Application")
1046                         << "Could not open PID file '" << filename << "'.";
1047                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open PID file '" + filename + "'"));
1048         }
1049
1050 #ifndef _WIN32
1051         int fd = fileno(m_PidFile);
1052
1053         Utility::SetCloExec(fd);
1054
1055         struct flock lock;
1056
1057         lock.l_start = 0;
1058         lock.l_len = 0;
1059         lock.l_type = F_WRLCK;
1060         lock.l_whence = SEEK_SET;
1061
1062         if (fcntl(fd, F_SETLK, &lock) < 0) {
1063                 Log(LogCritical, "Application", "Could not lock PID file. Make sure that only one instance of the application is running.");
1064
1065                 Application::Exit(EXIT_FAILURE);
1066         }
1067
1068         if (ftruncate(fd, 0) < 0) {
1069                 Log(LogCritical, "Application")
1070                         << "ftruncate() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
1071
1072                 BOOST_THROW_EXCEPTION(posix_error()
1073                         << boost::errinfo_api_function("ftruncate")
1074                         << boost::errinfo_errno(errno));
1075         }
1076 #endif /* _WIN32 */
1077
1078         fprintf(m_PidFile, "%lu\n", (unsigned long)pid);
1079         fflush(m_PidFile);
1080 }
1081
1082 /**
1083  * Closes the PID file. Does nothing if the PID file is not currently open.
1084  */
1085 void Application::ClosePidFile(bool unlink)
1086 {
1087         ObjectLock olock(this);
1088
1089         if (m_PidFile) {
1090                 if (unlink) {
1091                         String pidpath = GetPidPath();
1092                         ::unlink(pidpath.CStr());
1093                 }
1094
1095                 fclose(m_PidFile);
1096         }
1097
1098         m_PidFile = nullptr;
1099 }
1100
1101 /**
1102  * Checks if another process currently owns the pidfile and read it
1103  *
1104  * @param filename The name of the PID file.
1105  * @returns 0: no process owning the pidfile, pid of the process otherwise
1106  */
1107 pid_t Application::ReadPidFile(const String& filename)
1108 {
1109         FILE *pidfile = fopen(filename.CStr(), "r");
1110
1111         if (!pidfile)
1112                 return 0;
1113
1114 #ifndef _WIN32
1115         int fd = fileno(pidfile);
1116
1117         struct flock lock;
1118
1119         lock.l_start = 0;
1120         lock.l_len = 0;
1121         lock.l_type = F_WRLCK;
1122         lock.l_whence = SEEK_SET;
1123
1124         if (fcntl(fd, F_GETLK, &lock) < 0) {
1125                 int error = errno;
1126                 fclose(pidfile);
1127                 BOOST_THROW_EXCEPTION(posix_error()
1128                         << boost::errinfo_api_function("fcntl")
1129                         << boost::errinfo_errno(error));
1130         }
1131
1132         if (lock.l_type == F_UNLCK) {
1133                 // nobody has locked the file: no icinga running
1134                 fclose(pidfile);
1135                 return -1;
1136         }
1137 #endif /* _WIN32 */
1138
1139         pid_t runningpid;
1140         int res = fscanf(pidfile, "%d", &runningpid);
1141         fclose(pidfile);
1142
1143         // bogus result?
1144         if (res != 1)
1145                 return 0;
1146
1147 #ifdef _WIN32
1148         HANDLE hProcess = OpenProcess(0, FALSE, runningpid);
1149
1150         if (!hProcess)
1151                 return 0;
1152
1153         CloseHandle(hProcess);
1154 #endif /* _WIN32 */
1155
1156         return runningpid;
1157 }
1158
1159
1160 /**
1161  * Retrieves the path of the installation prefix.
1162  *
1163  * @returns The path.
1164  */
1165 String Application::GetPrefixDir()
1166 {
1167         return ScriptGlobal::Get("PrefixDir");
1168 }
1169
1170 /**
1171  * Sets the path for the installation prefix.
1172  *
1173  * @param path The new path.
1174  */
1175 void Application::DeclarePrefixDir(const String& path)
1176 {
1177         if (!ScriptGlobal::Exists("PrefixDir"))
1178                 ScriptGlobal::Set("PrefixDir", path);
1179 }
1180
1181 /**
1182  * Retrives the path of the sysconf dir.
1183  *
1184  * @returns The path.
1185  */
1186 String Application::GetSysconfDir()
1187 {
1188         return ScriptGlobal::Get("SysconfDir");
1189 }
1190
1191 /**
1192  * Sets the path of the sysconf dir.
1193  *
1194  * @param path The new path.
1195  */
1196 void Application::DeclareSysconfDir(const String& path)
1197 {
1198         if (!ScriptGlobal::Exists("SysconfDir"))
1199                 ScriptGlobal::Set("SysconfDir", path);
1200 }
1201
1202 /**
1203  * Retrieves the path for the run dir.
1204  *
1205  * @returns The path.
1206  */
1207 String Application::GetRunDir()
1208 {
1209         return ScriptGlobal::Get("RunDir");
1210 }
1211
1212 /**
1213  * Sets the path of the run dir.
1214  *
1215  * @param path The new path.
1216  */
1217 void Application::DeclareRunDir(const String& path)
1218 {
1219         if (!ScriptGlobal::Exists("RunDir"))
1220                 ScriptGlobal::Set("RunDir", path);
1221 }
1222
1223 /**
1224  * Retrieves the path for the local state dir.
1225  *
1226  * @returns The path.
1227  */
1228 String Application::GetLocalStateDir()
1229 {
1230         return ScriptGlobal::Get("LocalStateDir");
1231 }
1232
1233 /**
1234  * Sets the path for the local state dir.
1235  *
1236  * @param path The new path.
1237  */
1238 void Application::DeclareLocalStateDir(const String& path)
1239 {
1240         if (!ScriptGlobal::Exists("LocalStateDir"))
1241                 ScriptGlobal::Set("LocalStateDir", path);
1242 }
1243
1244 /**
1245  * Retrieves the path for the local state dir.
1246  *
1247  * @returns The path.
1248  */
1249 String Application::GetZonesDir()
1250 {
1251         return ScriptGlobal::Get("ZonesDir", &Empty);
1252 }
1253
1254 /**
1255  * Sets the path of the zones dir.
1256  *
1257  * @param path The new path.
1258  */
1259 void Application::DeclareZonesDir(const String& path)
1260 {
1261         if (!ScriptGlobal::Exists("ZonesDir"))
1262                 ScriptGlobal::Set("ZonesDir", path);
1263 }
1264
1265 /**
1266  * Retrieves the path for the package data dir.
1267  *
1268  * @returns The path.
1269  */
1270 String Application::GetPkgDataDir()
1271 {
1272         String defaultValue = "";
1273         return ScriptGlobal::Get("PkgDataDir", &Empty);
1274 }
1275
1276 /**
1277  * Sets the path for the package data dir.
1278  *
1279  * @param path The new path.
1280  */
1281 void Application::DeclarePkgDataDir(const String& path)
1282 {
1283         if (!ScriptGlobal::Exists("PkgDataDir"))
1284                 ScriptGlobal::Set("PkgDataDir", path);
1285 }
1286
1287 /**
1288  * Retrieves the path for the include conf dir.
1289  *
1290  * @returns The path.
1291  */
1292 String Application::GetIncludeConfDir()
1293 {
1294         return ScriptGlobal::Get("IncludeConfDir", &Empty);
1295 }
1296
1297 /**
1298  * Sets the path for the package data dir.
1299  *
1300  * @param path The new path.
1301  */
1302 void Application::DeclareIncludeConfDir(const String& path)
1303 {
1304         if (!ScriptGlobal::Exists("IncludeConfDir"))
1305                 ScriptGlobal::Set("IncludeConfDir", path);
1306 }
1307
1308 /**
1309  * Retrieves the path for the state file.
1310  *
1311  * @returns The path.
1312  */
1313 String Application::GetStatePath()
1314 {
1315         return ScriptGlobal::Get("StatePath", &Empty);
1316 }
1317
1318 /**
1319  * Sets the path for the state file.
1320  *
1321  * @param path The new path.
1322  */
1323 void Application::DeclareStatePath(const String& path)
1324 {
1325         if (!ScriptGlobal::Exists("StatePath"))
1326                 ScriptGlobal::Set("StatePath", path);
1327 }
1328
1329 /**
1330  * Retrieves the path for the modified attributes file.
1331  *
1332  * @returns The path.
1333  */
1334 String Application::GetModAttrPath()
1335 {
1336         return ScriptGlobal::Get("ModAttrPath", &Empty);
1337 }
1338
1339 /**
1340  * Sets the path for the modified attributes file.
1341  *
1342  * @param path The new path.
1343  */
1344 void Application::DeclareModAttrPath(const String& path)
1345 {
1346         if (!ScriptGlobal::Exists("ModAttrPath"))
1347                 ScriptGlobal::Set("ModAttrPath", path);
1348 }
1349
1350 /**
1351  * Retrieves the path for the objects file.
1352  *
1353  * @returns The path.
1354  */
1355 String Application::GetObjectsPath()
1356 {
1357         return ScriptGlobal::Get("ObjectsPath", &Empty);
1358 }
1359
1360 /**
1361  * Sets the path for the objects file.
1362  *
1363  * @param path The new path.
1364  */
1365 void Application::DeclareObjectsPath(const String& path)
1366 {
1367         if (!ScriptGlobal::Exists("ObjectsPath"))
1368                 ScriptGlobal::Set("ObjectsPath", path);
1369 }
1370
1371 /**
1372 * Retrieves the path for the vars file.
1373 *
1374 * @returns The path.
1375 */
1376 String Application::GetVarsPath()
1377 {
1378         return ScriptGlobal::Get("VarsPath", &Empty);
1379 }
1380
1381 /**
1382 * Sets the path for the vars file.
1383 *
1384 * @param path The new path.
1385 */
1386 void Application::DeclareVarsPath(const String& path)
1387 {
1388         if (!ScriptGlobal::Exists("VarsPath"))
1389                 ScriptGlobal::Set("VarsPath", path);
1390 }
1391
1392 /**
1393  * Retrieves the path for the PID file.
1394  *
1395  * @returns The path.
1396  */
1397 String Application::GetPidPath()
1398 {
1399         return ScriptGlobal::Get("PidPath", &Empty);
1400 }
1401
1402 /**
1403  * Sets the path for the PID file.
1404  *
1405  * @param path The new path.
1406  */
1407 void Application::DeclarePidPath(const String& path)
1408 {
1409         if (!ScriptGlobal::Exists("PidPath"))
1410                 ScriptGlobal::Set("PidPath", path);
1411 }
1412
1413 /**
1414  * Retrieves the name of the user.
1415  *
1416  * @returns The name.
1417  */
1418 String Application::GetRunAsUser()
1419 {
1420         return ScriptGlobal::Get("RunAsUser");
1421 }
1422
1423 /**
1424  * Sets the name of the user.
1425  *
1426  * @param path The new user name.
1427  */
1428 void Application::DeclareRunAsUser(const String& user)
1429 {
1430         if (!ScriptGlobal::Exists("RunAsUser"))
1431                 ScriptGlobal::Set("RunAsUser", user);
1432 }
1433
1434 /**
1435  * Retrieves the name of the group.
1436  *
1437  * @returns The name.
1438  */
1439 String Application::GetRunAsGroup()
1440 {
1441         return ScriptGlobal::Get("RunAsGroup");
1442 }
1443
1444 /**
1445  * Sets the name of the group.
1446  *
1447  * @param path The new group name.
1448  */
1449 void Application::DeclareRunAsGroup(const String& group)
1450 {
1451         if (!ScriptGlobal::Exists("RunAsGroup"))
1452                 ScriptGlobal::Set("RunAsGroup", group);
1453 }
1454
1455 /**
1456  * Retrieves the file rlimit.
1457  *
1458  * @returns The limit.
1459  */
1460 int Application::GetRLimitFiles()
1461 {
1462         return ScriptGlobal::Get("RLimitFiles");
1463 }
1464
1465 int Application::GetDefaultRLimitFiles()
1466 {
1467         return 16 * 1024;
1468 }
1469
1470 /**
1471  * Sets the file rlimit.
1472  *
1473  * @param path The new file rlimit.
1474  */
1475 void Application::DeclareRLimitFiles(int limit)
1476 {
1477         if (!ScriptGlobal::Exists("RLimitFiles"))
1478                 ScriptGlobal::Set("RLimitFiles", limit);
1479 }
1480
1481 /**
1482  * Retrieves the process rlimit.
1483  *
1484  * @returns The limit.
1485  */
1486 int Application::GetRLimitProcesses()
1487 {
1488         return ScriptGlobal::Get("RLimitProcesses");
1489 }
1490
1491 int Application::GetDefaultRLimitProcesses()
1492 {
1493         return 16 * 1024;
1494 }
1495
1496 /**
1497  * Sets the process rlimit.
1498  *
1499  * @param path The new process rlimit.
1500  */
1501 void Application::DeclareRLimitProcesses(int limit)
1502 {
1503         if (!ScriptGlobal::Exists("RLimitProcesses"))
1504                 ScriptGlobal::Set("RLimitProcesses", limit);
1505 }
1506
1507 /**
1508  * Retrieves the stack rlimit.
1509  *
1510  * @returns The limit.
1511  */
1512 int Application::GetRLimitStack()
1513 {
1514         return ScriptGlobal::Get("RLimitStack");
1515 }
1516
1517 int Application::GetDefaultRLimitStack()
1518 {
1519         return 256 * 1024;
1520 }
1521
1522 /**
1523  * Sets the stack rlimit.
1524  *
1525  * @param path The new stack rlimit.
1526  */
1527 void Application::DeclareRLimitStack(int limit)
1528 {
1529         if (!ScriptGlobal::Exists("RLimitStack"))
1530                 ScriptGlobal::Set("RLimitStack", limit);
1531 }
1532
1533 /**
1534  * Sets the concurrency level.
1535  *
1536  * @param path The new concurrency level.
1537  */
1538 void Application::DeclareConcurrency(int ncpus)
1539 {
1540         if (!ScriptGlobal::Exists("Concurrency"))
1541                 ScriptGlobal::Set("Concurrency", ncpus);
1542 }
1543
1544 /**
1545  * Retrieves the concurrency level.
1546  *
1547  * @returns The concurrency level.
1548  */
1549 int Application::GetConcurrency()
1550 {
1551         Value defaultConcurrency = std::thread::hardware_concurrency();
1552         return ScriptGlobal::Get("Concurrency", &defaultConcurrency);
1553 }
1554
1555 /**
1556  * Returns the global thread pool.
1557  *
1558  * @returns The global thread pool.
1559  */
1560 ThreadPool& Application::GetTP()
1561 {
1562         static ThreadPool tp;
1563         return tp;
1564 }
1565
1566 double Application::GetStartTime()
1567 {
1568         return m_StartTime;
1569 }
1570
1571 void Application::SetStartTime(double ts)
1572 {
1573         m_StartTime = ts;
1574 }
1575
1576 double Application::GetMainTime()
1577 {
1578         return m_MainTime;
1579 }
1580
1581 void Application::SetMainTime(double ts)
1582 {
1583         m_MainTime = ts;
1584 }
1585
1586 bool Application::GetScriptDebuggerEnabled()
1587 {
1588         return m_ScriptDebuggerEnabled;
1589 }
1590
1591 void Application::SetScriptDebuggerEnabled(bool enabled)
1592 {
1593         m_ScriptDebuggerEnabled = enabled;
1594 }
1595
1596 double Application::GetLastReloadFailed()
1597 {
1598         return m_LastReloadFailed;
1599 }
1600
1601 void Application::SetLastReloadFailed(double ts)
1602 {
1603         m_LastReloadFailed = ts;
1604 }
1605
1606 void Application::ValidateName(const Lazy<String>& lvalue, const ValidationUtils& utils)
1607 {
1608         ObjectImpl<Application>::ValidateName(lvalue, utils);
1609
1610         if (lvalue() != "app")
1611                 BOOST_THROW_EXCEPTION(ValidationError(this, { "name" }, "Application object must be named 'app'."));
1612 }