diff --git a/etc/init.d/icinga2.cmake b/etc/init.d/icinga2.cmake index 1920c2325..f181fa90a 100644 --- a/etc/init.d/icinga2.cmake +++ b/etc/init.d/icinga2.cmake @@ -106,11 +106,13 @@ stop() { # Reload Icinga 2 reload() { printf "Reloading Icinga 2: " - if ! $DAEMON -c $ICINGA2_CONFIG_FILE -d --reload -e $ICINGA2_ERROR_LOG -u $ICINGA2_USER -g $ICINGA2_GROUP; then - echo "Error reloading Icinga." - exit 1 - else + + pid=`cat $ICINGA2_PID_FILE` + if kill -HUP $pid >/dev/null 2>&1; then echo "Done" + else + echo "Error: Icinga not running" + exit 3 fi } diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index d2f9834f0..4eb7779fb 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -203,7 +203,7 @@ static void TerminateAndWaitForEnd(pid_t target) while(Utility::GetTime() < timeout && (ret==0 || errno!=ESRCH)) { Utility::Sleep(0.1); - ret = kill(target, 0); + ret = kill(target, SIGTERM); } // timeout and the process still seems to live: kill it @@ -280,7 +280,7 @@ int Main(void) ("validate,C", "exit after validating the configuration") ("debug,x", "enable debugging") ("errorlog,e", po::value(), "log fatal errors to the specified log file (only works in combination with --daemonize)") - ("reload,r", "reload a running icinga instance") + ("reload-internal", "used internally to implement config reload: do not call manually, send SIGHUP instead") #ifndef _WIN32 ("daemonize,d", "detach from the controlling terminal") ("user,u", po::value(), "user to run Icinga as") @@ -434,15 +434,12 @@ int Main(void) return EXIT_FAILURE; } - pid_t runningpid = Application::ReadPidFile(Application::GetPidPath()); - if (g_AppParams.count("reload")) { - if (runningpid < 0) { - Log(LogCritical, "icinga-app", "No instance of Icinga currently running: can't reload."); + if (!g_AppParams.count("validate") && !g_AppParams.count("reload-internal")) { + pid_t runningpid = Application::ReadPidFile(Application::GetPidPath()); + if (runningpid >= 0) { + Log(LogCritical, "icinga-app", "Another instance of Icinga already running with PID " + Convert::ToString(runningpid)); return EXIT_FAILURE; } - } else if (!g_AppParams.count("validate") && runningpid >= 0) { - Log(LogCritical, "icinga-app", "Another instance of Icinga already running at PID " + Convert::ToString(runningpid)); - return EXIT_FAILURE; } if (!LoadConfigFiles(appType)) @@ -453,9 +450,9 @@ int Main(void) return EXIT_SUCCESS; } - if (g_AppParams.count("reload")) { - Log(LogInformation, "icinga-app", "Terminating previous instance of icinga (PID " + Convert::ToString(runningpid) + ")"); - TerminateAndWaitForEnd(runningpid); + if(g_AppParams.count("reload-internal")) { + Log(LogInformation, "icinga-app", "Terminating previous instance of Icinga (PID " + Convert::ToString(Utility::GetParentPid()) + ")"); + TerminateAndWaitForEnd(Utility::GetParentPid()); Log(LogInformation, "icinga-app", "Previous instance has ended, taking over now."); } diff --git a/lib/base/application.cpp b/lib/base/application.cpp index fc19b93b3..a3a602905 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -46,6 +46,7 @@ REGISTER_TYPE(Application); Application *Application::m_Instance = NULL; bool Application::m_ShuttingDown = false; +bool Application::m_RequestRestart = false; bool Application::m_Restarting = false; bool Application::m_Debugging = false; int Application::m_ArgC; @@ -219,7 +220,8 @@ void Application::RunEventLoop(void) const double lastLoop = Utility::GetTime(); - while (!m_ShuttingDown && !m_Restarting) { +mainloop: + while (!m_ShuttingDown && !m_RequestRestart) { /* Watches for changes to the system time. Adjusts timers if necessary. */ Utility::Sleep(2.5); @@ -238,8 +240,21 @@ void Application::RunEventLoop(void) const } lastLoop = now; - } + if (m_RequestRestart) { + m_RequestRestart = false; // we are now handling the request, once is enough + + // are we already restarting? ignore request if we already are + if (m_Restarting) + goto mainloop; + + m_Restarting = true; + StartReloadProcess(); + + goto mainloop; + } + } + Log(LogInformation, "base", "Shutting down Icinga..."); DynamicObject::StopObjects(); Application::GetInstance()->OnShutdown(); @@ -259,6 +274,33 @@ void Application::OnShutdown(void) /* Nothing to do here. */ } +void Application::StartReloadProcess(void) const +{ + Log(LogInformation, "base", "Got reload command: Starting new instance."); + + // prepare arguments + std::vector args; + args.push_back(GetExePath(m_ArgV[0])); + + for (int i=1; i < Application::GetArgC(); i++) + if (std::string(Application::GetArgV()[i]) != "--reload-internal") + args.push_back(Application::GetArgV()[i]); + args.push_back("--reload-internal"); + + Process::Ptr process = make_shared(args); + + process->SetTimeout(15); + + process->Run(boost::bind(&Application::ReloadProcessCallback, _1)); +} + +void Application::ReloadProcessCallback(const ProcessResult& pr) +{ + if (pr.ExitStatus != 0) + Log(LogCritical, "base", "Found error in config: reloading aborted"); + m_Restarting=false; +} + /** * Signals the application to shut down during the next * execution of the event loop. @@ -274,7 +316,7 @@ void Application::RequestShutdown(void) */ void Application::RequestRestart(void) { - m_Restarting = true; + m_RequestRestart = true; } /** @@ -563,29 +605,6 @@ int Application::Run(void) result = Main(); - if (m_Restarting) { - Log(LogInformation, "base", "Restarting application."); - -#ifndef _WIN32 - String exePath = GetExePath(m_ArgV[0]); - - int fdcount = getdtablesize(); - - for (int i = 3; i < fdcount; i++) - (void) close(i); - - (void) execv(exePath.CStr(), m_ArgV); -#else /* _WIN32 */ - STARTUPINFO si; - PROCESS_INFORMATION pi; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - CreateProcess(NULL, GetCommandLine(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); -#endif /* _WIN32 */ - - _exit(0); - } - return result; } diff --git a/lib/base/application.h b/lib/base/application.h index 9597409e5..9f5e5ebe1 100644 --- a/lib/base/application.h +++ b/lib/base/application.h @@ -24,6 +24,7 @@ #include "base/application.th" #include "base/threadpool.h" #include "base/dynamicobject.h" +#include "base/process.h" namespace icinga { @@ -109,6 +110,8 @@ protected: void RunEventLoop(void) const; + void StartReloadProcess(void) const; + virtual void OnShutdown(void); private: @@ -116,6 +119,7 @@ private: static bool m_ShuttingDown; /**< Whether the application is in the process of shutting down. */ + static bool m_RequestRestart; static bool m_Restarting; static int m_ArgC; /**< The number of command-line arguments. */ static char **m_ArgV; /**< Command-line arguments. */ @@ -135,6 +139,8 @@ private: static void SigAbrtHandler(int signum); static void ExceptionHandler(void); + + static void ReloadProcessCallback(const ProcessResult& pr); }; } diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index 56559fea2..dc3c8232b 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -331,6 +331,21 @@ pid_t Utility::GetPid(void) #endif /* _WIN32 */ } +/** + * Returns the ID of the parent process. + * + * @returns The PID. + */ +pid_t Utility::GetParentPid(void) +{ +#ifndef _WIN32 + return getppid(); +#else /* _WIN32 */ + // TODO + return 0; +#endif /* _WIN32 */ +} + /** * Sleeps for the specified amount of time. * diff --git a/lib/base/utility.h b/lib/base/utility.h index f8c05a5fe..ada33d43a 100644 --- a/lib/base/utility.h +++ b/lib/base/utility.h @@ -74,6 +74,7 @@ public: static double GetTime(void); static pid_t GetPid(void); + static pid_t GetParentPid(void); static void Sleep(double timeout);