1 /******************************************************************************
3 * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
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. *
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. *
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 ******************************************************************************/
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>
43 #include <sys/prctl.h>
44 #endif /* __linux__ */
49 #include <systemd/sd-daemon.h>
50 #endif /* HAVE_SYSTEMD */
52 using namespace icinga;
54 REGISTER_TYPE(Application);
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;
72 * Constructor for the Application class.
74 void Application::OnConfigLoaded()
78 ASSERT(m_Instance == nullptr);
83 * Destructor for the application class.
85 void Application::Stop(bool runtimeRemoved)
87 m_ShuttingDown = true;
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.
99 UpdatePidFile(GetPidPath(), m_ReloadProcess);
100 } catch (const std::exception&) {
102 Log(LogCritical, "Application", "Cannot update PID file. Aborting restart operation.");
106 Log(LogDebug, "Application")
107 << "Keeping pid '" << m_ReloadProcess << "' open.";
113 ObjectImpl<Application>::Stop(runtimeRemoved);
116 Application::~Application()
118 m_Instance = nullptr;
121 void Application::Exit(int rc)
126 for (const Logger::Ptr& logger : Logger::GetLoggers()) {
134 _exit(rc); // Yay, our static destructors are pretty much beyond repair at this point.
135 #endif /* I2_DEBUG */
138 void Application::InitializeBase()
141 /* disable GUI-based error messages for LoadLibrary() */
142 SetErrorMode(SEM_FAILCRITICALERRORS);
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()));
152 memset(&sa, 0, sizeof(sa));
153 sa.sa_handler = SIG_IGN;
154 sigaction(SIGPIPE, &sa, nullptr);
157 Loader::ExecuteDeferredInitializers();
159 /* make sure the thread pool gets initialized */
163 void Application::UninitializeBase()
165 Timer::Uninitialize();
171 * Retrieves a pointer to the application singleton object.
173 * @returns The application object.
175 Application::Ptr Application::GetInstance()
180 void Application::SetResourceLimits()
185 # ifdef RLIMIT_NOFILE
186 rlim_t fileLimit = GetRLimitFiles();
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();
195 rl.rlim_cur = fileLimit;
196 rl.rlim_max = rl.rlim_cur;
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 */
206 rlim_t processLimit = GetRLimitProcesses();
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();
215 rl.rlim_cur = processLimit;
216 rl.rlim_max = rl.rlim_cur;
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 */
226 int argc = Application::GetArgC();
227 char **argv = Application::GetArgV();
228 bool set_stack_rlimit = true;
230 for (int i = 0; i < argc; i++) {
231 if (strcmp(argv[i], "--no-stack-rlimit") == 0) {
232 set_stack_rlimit = false;
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;
244 stackLimit = GetRLimitStack();
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();
253 if (set_stack_rlimit)
254 rl.rlim_cur = stackLimit;
256 rl.rlim_cur = rl.rlim_max;
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)));
268 new_argv[0] = argv[0];
269 new_argv[1] = strdup("--no-stack-rlimit");
276 for (int i = 1; i < argc; i++)
277 new_argv[i + 1] = argv[i];
279 new_argv[argc + 1] = nullptr;
281 (void) execvp(new_argv[0], new_argv);
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 */
289 #endif /* __linux__ */
292 int Application::GetArgC()
297 void Application::SetArgC(int argc)
302 char **Application::GetArgV()
307 void Application::SetArgV(char **argv)
313 * Processes events for registered sockets and timers and calls whatever
314 * handlers have been set up for these events.
316 void Application::RunEventLoop()
320 sd_notify(0, "READY=1");
321 #endif /* HAVE_SYSTEMD */
323 double lastLoop = Utility::GetTime();
326 while (!m_ShuttingDown && !m_RequestRestart) {
327 /* Watches for changes to the system time. Adjusts timers if necessary. */
330 if (m_RequestReopenLogs) {
331 Log(LogNotice, "Application", "Reopening log files");
332 m_RequestReopenLogs = false;
336 double now = Utility::GetTime();
337 double timeDiff = lastLoop - now;
340 sd_notify(0, "WATCHDOG=1");
341 #endif /* HAVE_SYSTEMD */
343 if (std::fabs(timeDiff) > 15) {
344 /* We made a significant jump in time. */
345 Log(LogInformation, "Application")
347 << (timeDiff < 0 ? "forward" : "backward")
348 << " in time: " << std::fabs(timeDiff) << " seconds";
350 Timer::AdjustTimers(-timeDiff);
356 if (m_RequestRestart) {
357 m_RequestRestart = false; // we are now handling the request, once is enough
360 sd_notify(0, "RELOADING=1");
361 #endif /* HAVE_SYSTEMD */
363 // are we already restarting? ignore request if we already are
368 m_ReloadProcess = StartReloadProcess();
374 sd_notify(0, "STOPPING=1");
375 #endif /* HAVE_SYSTEMD */
377 Log(LogInformation, "Application", "Shutting down...");
379 ConfigObject::StopObjects();
380 Application::GetInstance()->OnShutdown();
385 bool Application::IsShuttingDown()
387 return m_ShuttingDown;
390 void Application::OnShutdown()
392 /* Nothing to do here. */
395 static void ReloadProcessCallbackInternal(const ProcessResult& pr)
397 if (pr.ExitStatus != 0) {
398 Application::SetLastReloadFailed(Utility::GetTime());
399 Log(LogCritical, "Application", "Found error in config: reloading aborted");
403 Application::Exit(7); /* keep this exit code in sync with icinga-app */
407 static void ReloadProcessCallback(const ProcessResult& pr)
409 l_Restarting = false;
411 std::thread t(std::bind(&ReloadProcessCallbackInternal, pr));
415 pid_t Application::StartReloadProcess()
417 Log(LogInformation, "Application", "Got reload command: Starting new instance.");
421 args.push_back(GetExePath(m_ArgV[0]));
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]);
427 i++; // the next parameter after --reload-internal is the pid, remove that too
431 args.push_back("--reload-internal");
432 args.push_back(Convert::ToString(Utility::GetPid()));
434 args.push_back("--validate");
437 Process::Ptr process = new Process(Process::PrepareCommand(new Array(std::move(args))));
438 process->SetTimeout(300);
439 process->Run(&ReloadProcessCallback);
441 return process->GetPID();
445 * Signals the application to shut down during the next
446 * execution of the event loop.
448 void Application::RequestShutdown()
450 Log(LogInformation, "Application", "Received request to shut down.");
452 m_ShuttingDown = true;
456 * Signals the application to restart during the next
457 * execution of the event loop.
459 void Application::RequestRestart()
461 m_RequestRestart = true;
465 * Signals the application to reopen log files during the
466 * next execution of the event loop.
468 void Application::RequestReopenLogs()
470 m_RequestReopenLogs = true;
474 * Retrieves the full path of the executable.
476 * @param argv0 The first command-line argument.
479 String Application::GetExePath(const String& argv0)
481 String executablePath;
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));
491 String workingDirectory = buffer;
494 executablePath = workingDirectory + "/" + argv0;
496 executablePath = argv0;
498 bool foundSlash = false;
499 for (size_t i = 0; i < argv0.GetLength(); i++) {
500 if (argv0[i] == '/') {
507 const char *pathEnv = getenv("PATH");
509 std::vector<String> paths = String(pathEnv).Split(":");
511 bool foundPath = false;
512 for (const String& path : paths) {
513 String pathTest = path + "/" + argv0;
515 if (access(pathTest.CStr(), X_OK) == 0) {
516 executablePath = pathTest;
523 executablePath.Clear();
524 BOOST_THROW_EXCEPTION(std::runtime_error("Could not determine executable path."));
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));
538 char FullExePath[MAXPATHLEN];
540 if (!GetModuleFileName(nullptr, FullExePath, sizeof(FullExePath)))
541 BOOST_THROW_EXCEPTION(win32_error()
542 << boost::errinfo_api_function("GetModuleFileName")
543 << errinfo_win32_error(GetLastError()));
550 * Display version and path information.
552 void Application::DisplayInfoMessage(std::ostream& os, bool skipVersion)
554 os << "Application information:" << "\n";
557 os << " Application version: " << GetAppVersion() << "\n";
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";
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";
579 << "Build information:" << "\n"
580 << " Compiler: " << ScriptGlobal::Get("BuildCompilerName") << " " << ScriptGlobal::Get("BuildCompilerVersion") << "\n"
581 << " Build host: " << ScriptGlobal::Get("BuildHostName") << "\n";
585 * Displays a message that tells users what to do when they encounter a bug.
587 void Application::DisplayBugMessage(std::ostream& os)
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"
596 String Application::GetCrashReportFilename()
598 return GetLocalStateDir() + "/log/icinga2/crash/report." + Convert::ToString(Utility::GetTime());
602 void Application::AttachDebugger(const String& filename, bool interactive)
606 prctl(PR_SET_DUMPABLE, 1);
607 #endif /* __linux __ */
609 String my_pid = Convert::ToString(Utility::GetPid());
614 BOOST_THROW_EXCEPTION(posix_error()
615 << boost::errinfo_api_function("fork")
616 << boost::errinfo_errno(errno));
621 int fd = open(filename.CStr(), O_CREAT | O_RDWR | O_APPEND, 0600);
624 BOOST_THROW_EXCEPTION(posix_error()
625 << boost::errinfo_api_function("open")
626 << boost::errinfo_errno(errno)
627 << boost::errinfo_file_name(filename));
631 /* redirect stdout to the file */
636 /* redirect stderr to stdout */
644 char *my_pid_str = strdup(my_pid.CStr());
647 const char *uargv[] = {
654 argv = const_cast<char **>(uargv);
656 (void) execvp(argv[0], argv);
658 const char *uargv[] = {
664 "thread apply all bt full",
672 argv = const_cast<char **>(uargv);
674 (void) execvp(argv[0], argv);
677 perror("Failed to launch GDB");
683 if (waitpid(pid, &status, 0) < 0) {
684 BOOST_THROW_EXCEPTION(posix_error()
685 << boost::errinfo_api_function("waitpid")
686 << boost::errinfo_errno(errno));
690 prctl(PR_SET_DUMPABLE, 0);
691 #endif /* __linux __ */
699 * Signal handler for SIGINT and SIGTERM. Prepares the application for cleanly
700 * shutting down during the next execution of the event loop.
702 * @param - The signal number.
704 void Application::SigIntTermHandler(int signum)
707 memset(&sa, 0, sizeof(sa));
708 sa.sa_handler = SIG_DFL;
709 sigaction(signum, &sa, nullptr);
711 Application::Ptr instance = Application::GetInstance();
716 instance->RequestShutdown();
721 * Signal handler for SIGUSR1. This signal causes Icinga to re-open
722 * its log files and is mainly for use by logrotate.
724 * @param - The signal number.
726 void Application::SigUsr1Handler(int)
732 * Signal handler for SIGUSR2. Hands over PID to child and commits suicide
734 * @param - The signal number.
736 void Application::SigUsr2Handler(int)
738 Log(LogInformation, "Application", "Reload requested, letting new process take over.");
740 sd_notifyf(0, "MAINPID=%lu", (unsigned long) m_ReloadProcess);
741 #endif /* HAVE_SYSTEMD */
747 * Signal handler for SIGABRT. Helps with debugging ASSERT()s.
749 * @param - The signal number.
751 void Application::SigAbrtHandler(int)
755 memset(&sa, 0, sizeof(sa));
756 sa.sa_handler = SIG_DFL;
757 sigaction(SIGABRT, &sa, nullptr);
760 std::cerr << "Caught SIGABRT." << std::endl
761 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << std::endl
764 String fname = GetCrashReportFilename();
765 String dirName = Utility::DirName(fname);
767 if (!Utility::PathExists(dirName)) {
769 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
771 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
773 std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
777 bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger"));
779 if (!interactive_debugger) {
781 ofs.open(fname.CStr());
783 Log(LogCritical, "Application")
784 << "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'" << "\n";
786 DisplayInfoMessage(ofs);
789 ofs << "Stacktrace:" << "\n";
792 DisplayBugMessage(ofs);
797 Log(LogCritical, "Application", "Icinga 2 has terminated unexpectedly. Attaching debugger...");
800 AttachDebugger(fname, interactive_debugger);
805 * Console control handler. Prepares the application for cleanly
806 * shutting down during the next execution of the event loop.
808 BOOL WINAPI Application::CtrlHandler(DWORD type)
810 Application::Ptr instance = Application::GetInstance();
815 instance->RequestShutdown();
817 SetConsoleCtrlHandler(nullptr, FALSE);
821 bool Application::IsProcessElevated() {
822 BOOL fIsElevated = FALSE;
823 DWORD dwError = ERROR_SUCCESS;
824 HANDLE hToken = nullptr;
826 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
827 dwError = GetLastError();
829 TOKEN_ELEVATION elevation;
832 if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
833 dwError = GetLastError();
835 fIsElevated = elevation.TokenIsElevated;
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));
849 BOOST_THROW_EXCEPTION(std::runtime_error(mBuf));
857 * Handler for unhandled exceptions.
859 void Application::ExceptionHandler()
861 if (l_InExceptionHandler)
865 l_InExceptionHandler = true;
869 memset(&sa, 0, sizeof(sa));
870 sa.sa_handler = SIG_DFL;
871 sigaction(SIGABRT, &sa, nullptr);
874 String fname = GetCrashReportFilename();
875 String dirName = Utility::DirName(fname);
877 if (!Utility::PathExists(dirName)) {
879 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
881 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
883 std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
887 bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger"));
889 if (!interactive_debugger) {
891 ofs.open(fname.CStr());
893 ofs << "Caught unhandled exception." << "\n"
894 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
897 DisplayInfoMessage(ofs);
900 RethrowUncaughtException();
901 } catch (const std::exception& ex) {
902 Log(LogCritical, "Application")
903 << DiagnosticInformation(ex, false) << "\n"
905 << "Additional information is available in '" << fname << "'" << "\n";
908 << DiagnosticInformation(ex)
912 DisplayBugMessage(ofs);
917 AttachDebugger(fname, interactive_debugger);
923 LONG CALLBACK Application::SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi)
925 if (l_InExceptionHandler)
926 return EXCEPTION_CONTINUE_SEARCH;
928 l_InExceptionHandler = true;
930 String fname = GetCrashReportFilename();
931 String dirName = Utility::DirName(fname);
933 if (!Utility::PathExists(dirName)) {
935 if (mkdir(dirName.CStr(), 0700) < 0 && errno != EEXIST) {
937 if (mkdir(dirName.CStr()) < 0 && errno != EEXIST) {
939 std::cerr << "Could not create directory '" << dirName << "': Error " << errno << ", " << strerror(errno) << "\n";
944 ofs.open(fname.CStr());
946 Log(LogCritical, "Application")
947 << "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'";
949 DisplayInfoMessage(ofs);
951 ofs << "Caught unhandled SEH exception." << "\n"
952 << "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
955 StackTrace trace(exi);
956 ofs << "Stacktrace:" << "\n";
959 DisplayBugMessage(ofs);
961 return EXCEPTION_CONTINUE_SEARCH;
966 * Installs the exception handlers.
968 void Application::InstallExceptionHandlers()
970 std::set_terminate(&Application::ExceptionHandler);
974 memset(&sa, 0, sizeof(sa));
975 sa.sa_handler = &Application::SigAbrtHandler;
976 sigaction(SIGABRT, &sa, nullptr);
978 SetUnhandledExceptionFilter(&Application::SEHUnhandledExceptionFilter);
983 * Runs the application.
985 * @returns The application's exit code.
987 int Application::Run()
991 memset(&sa, 0, sizeof(sa));
992 sa.sa_handler = &Application::SigIntTermHandler;
993 sigaction(SIGINT, &sa, nullptr);
994 sigaction(SIGTERM, &sa, nullptr);
996 sa.sa_handler = &Application::SigUsr1Handler;
997 sigaction(SIGUSR1, &sa, nullptr);
999 sa.sa_handler = &Application::SigUsr2Handler;
1000 sigaction(SIGUSR2, &sa, nullptr);
1002 SetConsoleCtrlHandler(&Application::CtrlHandler, TRUE);
1006 UpdatePidFile(GetPidPath());
1007 } catch (const std::exception&) {
1008 Log(LogCritical, "Application")
1009 << "Cannot update PID file '" << GetPidPath() << "'. Aborting.";
1010 return EXIT_FAILURE;
1013 SetMainTime(Utility::GetTime());
1018 void Application::UpdatePidFile(const String& filename)
1020 UpdatePidFile(filename, Utility::GetPid());
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.
1027 * @param filename The name of the PID file.
1028 * @param pid The PID to write; default is the current PID
1030 void Application::UpdatePidFile(const String& filename, pid_t pid)
1032 ObjectLock olock(this);
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+");
1042 m_PidFile = fopen(filename.CStr(), "w");
1045 Log(LogCritical, "Application")
1046 << "Could not open PID file '" << filename << "'.";
1047 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open PID file '" + filename + "'"));
1051 int fd = fileno(m_PidFile);
1053 Utility::SetCloExec(fd);
1059 lock.l_type = F_WRLCK;
1060 lock.l_whence = SEEK_SET;
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.");
1065 Application::Exit(EXIT_FAILURE);
1068 if (ftruncate(fd, 0) < 0) {
1069 Log(LogCritical, "Application")
1070 << "ftruncate() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
1072 BOOST_THROW_EXCEPTION(posix_error()
1073 << boost::errinfo_api_function("ftruncate")
1074 << boost::errinfo_errno(errno));
1078 fprintf(m_PidFile, "%lu\n", (unsigned long)pid);
1083 * Closes the PID file. Does nothing if the PID file is not currently open.
1085 void Application::ClosePidFile(bool unlink)
1087 ObjectLock olock(this);
1091 String pidpath = GetPidPath();
1092 ::unlink(pidpath.CStr());
1098 m_PidFile = nullptr;
1102 * Checks if another process currently owns the pidfile and read it
1104 * @param filename The name of the PID file.
1105 * @returns 0: no process owning the pidfile, pid of the process otherwise
1107 pid_t Application::ReadPidFile(const String& filename)
1109 FILE *pidfile = fopen(filename.CStr(), "r");
1115 int fd = fileno(pidfile);
1121 lock.l_type = F_WRLCK;
1122 lock.l_whence = SEEK_SET;
1124 if (fcntl(fd, F_GETLK, &lock) < 0) {
1127 BOOST_THROW_EXCEPTION(posix_error()
1128 << boost::errinfo_api_function("fcntl")
1129 << boost::errinfo_errno(error));
1132 if (lock.l_type == F_UNLCK) {
1133 // nobody has locked the file: no icinga running
1140 int res = fscanf(pidfile, "%d", &runningpid);
1148 HANDLE hProcess = OpenProcess(0, FALSE, runningpid);
1153 CloseHandle(hProcess);
1161 * Retrieves the path of the installation prefix.
1163 * @returns The path.
1165 String Application::GetPrefixDir()
1167 return ScriptGlobal::Get("PrefixDir");
1171 * Sets the path for the installation prefix.
1173 * @param path The new path.
1175 void Application::DeclarePrefixDir(const String& path)
1177 if (!ScriptGlobal::Exists("PrefixDir"))
1178 ScriptGlobal::Set("PrefixDir", path);
1182 * Retrives the path of the sysconf dir.
1184 * @returns The path.
1186 String Application::GetSysconfDir()
1188 return ScriptGlobal::Get("SysconfDir");
1192 * Sets the path of the sysconf dir.
1194 * @param path The new path.
1196 void Application::DeclareSysconfDir(const String& path)
1198 if (!ScriptGlobal::Exists("SysconfDir"))
1199 ScriptGlobal::Set("SysconfDir", path);
1203 * Retrieves the path for the run dir.
1205 * @returns The path.
1207 String Application::GetRunDir()
1209 return ScriptGlobal::Get("RunDir");
1213 * Sets the path of the run dir.
1215 * @param path The new path.
1217 void Application::DeclareRunDir(const String& path)
1219 if (!ScriptGlobal::Exists("RunDir"))
1220 ScriptGlobal::Set("RunDir", path);
1224 * Retrieves the path for the local state dir.
1226 * @returns The path.
1228 String Application::GetLocalStateDir()
1230 return ScriptGlobal::Get("LocalStateDir");
1234 * Sets the path for the local state dir.
1236 * @param path The new path.
1238 void Application::DeclareLocalStateDir(const String& path)
1240 if (!ScriptGlobal::Exists("LocalStateDir"))
1241 ScriptGlobal::Set("LocalStateDir", path);
1245 * Retrieves the path for the local state dir.
1247 * @returns The path.
1249 String Application::GetZonesDir()
1251 return ScriptGlobal::Get("ZonesDir", &Empty);
1255 * Sets the path of the zones dir.
1257 * @param path The new path.
1259 void Application::DeclareZonesDir(const String& path)
1261 if (!ScriptGlobal::Exists("ZonesDir"))
1262 ScriptGlobal::Set("ZonesDir", path);
1266 * Retrieves the path for the package data dir.
1268 * @returns The path.
1270 String Application::GetPkgDataDir()
1272 String defaultValue = "";
1273 return ScriptGlobal::Get("PkgDataDir", &Empty);
1277 * Sets the path for the package data dir.
1279 * @param path The new path.
1281 void Application::DeclarePkgDataDir(const String& path)
1283 if (!ScriptGlobal::Exists("PkgDataDir"))
1284 ScriptGlobal::Set("PkgDataDir", path);
1288 * Retrieves the path for the include conf dir.
1290 * @returns The path.
1292 String Application::GetIncludeConfDir()
1294 return ScriptGlobal::Get("IncludeConfDir", &Empty);
1298 * Sets the path for the package data dir.
1300 * @param path The new path.
1302 void Application::DeclareIncludeConfDir(const String& path)
1304 if (!ScriptGlobal::Exists("IncludeConfDir"))
1305 ScriptGlobal::Set("IncludeConfDir", path);
1309 * Retrieves the path for the state file.
1311 * @returns The path.
1313 String Application::GetStatePath()
1315 return ScriptGlobal::Get("StatePath", &Empty);
1319 * Sets the path for the state file.
1321 * @param path The new path.
1323 void Application::DeclareStatePath(const String& path)
1325 if (!ScriptGlobal::Exists("StatePath"))
1326 ScriptGlobal::Set("StatePath", path);
1330 * Retrieves the path for the modified attributes file.
1332 * @returns The path.
1334 String Application::GetModAttrPath()
1336 return ScriptGlobal::Get("ModAttrPath", &Empty);
1340 * Sets the path for the modified attributes file.
1342 * @param path The new path.
1344 void Application::DeclareModAttrPath(const String& path)
1346 if (!ScriptGlobal::Exists("ModAttrPath"))
1347 ScriptGlobal::Set("ModAttrPath", path);
1351 * Retrieves the path for the objects file.
1353 * @returns The path.
1355 String Application::GetObjectsPath()
1357 return ScriptGlobal::Get("ObjectsPath", &Empty);
1361 * Sets the path for the objects file.
1363 * @param path The new path.
1365 void Application::DeclareObjectsPath(const String& path)
1367 if (!ScriptGlobal::Exists("ObjectsPath"))
1368 ScriptGlobal::Set("ObjectsPath", path);
1372 * Retrieves the path for the vars file.
1374 * @returns The path.
1376 String Application::GetVarsPath()
1378 return ScriptGlobal::Get("VarsPath", &Empty);
1382 * Sets the path for the vars file.
1384 * @param path The new path.
1386 void Application::DeclareVarsPath(const String& path)
1388 if (!ScriptGlobal::Exists("VarsPath"))
1389 ScriptGlobal::Set("VarsPath", path);
1393 * Retrieves the path for the PID file.
1395 * @returns The path.
1397 String Application::GetPidPath()
1399 return ScriptGlobal::Get("PidPath", &Empty);
1403 * Sets the path for the PID file.
1405 * @param path The new path.
1407 void Application::DeclarePidPath(const String& path)
1409 if (!ScriptGlobal::Exists("PidPath"))
1410 ScriptGlobal::Set("PidPath", path);
1414 * Retrieves the name of the user.
1416 * @returns The name.
1418 String Application::GetRunAsUser()
1420 return ScriptGlobal::Get("RunAsUser");
1424 * Sets the name of the user.
1426 * @param path The new user name.
1428 void Application::DeclareRunAsUser(const String& user)
1430 if (!ScriptGlobal::Exists("RunAsUser"))
1431 ScriptGlobal::Set("RunAsUser", user);
1435 * Retrieves the name of the group.
1437 * @returns The name.
1439 String Application::GetRunAsGroup()
1441 return ScriptGlobal::Get("RunAsGroup");
1445 * Sets the name of the group.
1447 * @param path The new group name.
1449 void Application::DeclareRunAsGroup(const String& group)
1451 if (!ScriptGlobal::Exists("RunAsGroup"))
1452 ScriptGlobal::Set("RunAsGroup", group);
1456 * Retrieves the file rlimit.
1458 * @returns The limit.
1460 int Application::GetRLimitFiles()
1462 return ScriptGlobal::Get("RLimitFiles");
1465 int Application::GetDefaultRLimitFiles()
1471 * Sets the file rlimit.
1473 * @param path The new file rlimit.
1475 void Application::DeclareRLimitFiles(int limit)
1477 if (!ScriptGlobal::Exists("RLimitFiles"))
1478 ScriptGlobal::Set("RLimitFiles", limit);
1482 * Retrieves the process rlimit.
1484 * @returns The limit.
1486 int Application::GetRLimitProcesses()
1488 return ScriptGlobal::Get("RLimitProcesses");
1491 int Application::GetDefaultRLimitProcesses()
1497 * Sets the process rlimit.
1499 * @param path The new process rlimit.
1501 void Application::DeclareRLimitProcesses(int limit)
1503 if (!ScriptGlobal::Exists("RLimitProcesses"))
1504 ScriptGlobal::Set("RLimitProcesses", limit);
1508 * Retrieves the stack rlimit.
1510 * @returns The limit.
1512 int Application::GetRLimitStack()
1514 return ScriptGlobal::Get("RLimitStack");
1517 int Application::GetDefaultRLimitStack()
1523 * Sets the stack rlimit.
1525 * @param path The new stack rlimit.
1527 void Application::DeclareRLimitStack(int limit)
1529 if (!ScriptGlobal::Exists("RLimitStack"))
1530 ScriptGlobal::Set("RLimitStack", limit);
1534 * Sets the concurrency level.
1536 * @param path The new concurrency level.
1538 void Application::DeclareConcurrency(int ncpus)
1540 if (!ScriptGlobal::Exists("Concurrency"))
1541 ScriptGlobal::Set("Concurrency", ncpus);
1545 * Retrieves the concurrency level.
1547 * @returns The concurrency level.
1549 int Application::GetConcurrency()
1551 Value defaultConcurrency = std::thread::hardware_concurrency();
1552 return ScriptGlobal::Get("Concurrency", &defaultConcurrency);
1556 * Returns the global thread pool.
1558 * @returns The global thread pool.
1560 ThreadPool& Application::GetTP()
1562 static ThreadPool tp;
1566 double Application::GetStartTime()
1571 void Application::SetStartTime(double ts)
1576 double Application::GetMainTime()
1581 void Application::SetMainTime(double ts)
1586 bool Application::GetScriptDebuggerEnabled()
1588 return m_ScriptDebuggerEnabled;
1591 void Application::SetScriptDebuggerEnabled(bool enabled)
1593 m_ScriptDebuggerEnabled = enabled;
1596 double Application::GetLastReloadFailed()
1598 return m_LastReloadFailed;
1601 void Application::SetLastReloadFailed(double ts)
1603 m_LastReloadFailed = ts;
1606 void Application::ValidateName(const Lazy<String>& lvalue, const ValidationUtils& utils)
1608 ObjectImpl<Application>::ValidateName(lvalue, utils);
1610 if (lvalue() != "app")
1611 BOOST_THROW_EXCEPTION(ValidationError(this, { "name" }, "Application object must be named 'app'."));