From 33bd909b712152ee1ed5d6d5342a820e2f2f2424 Mon Sep 17 00:00:00 2001 From: Gerd von Egidy Date: Mon, 14 Apr 2014 00:16:48 +0200 Subject: [PATCH] Add --reload command-line parameter. Refs #5788 --- etc/init.d/icinga2.cmake | 23 +++++++---------- icinga-app/icinga.cpp | 46 +++++++++++++++++++++++++++++++++ lib/base/application.cpp | 56 +++++++++++++++++++++++++++++++++++++++- lib/base/application.h | 1 + 4 files changed, 111 insertions(+), 15 deletions(-) diff --git a/etc/init.d/icinga2.cmake b/etc/init.d/icinga2.cmake index 33419bb13..1920c2325 100644 --- a/etc/init.d/icinga2.cmake +++ b/etc/init.d/icinga2.cmake @@ -61,11 +61,13 @@ start() { chown $ICINGA2_USER:$ICINGA2_COMMAND_GROUP $ICINGA2_STATE_DIR/run/icinga2/cmd chmod 2755 $ICINGA2_STATE_DIR/run/icinga2/cmd - echo "Starting Icinga 2: " - $DAEMON -c $ICINGA2_CONFIG_FILE -d -e $ICINGA2_ERROR_LOG -u $ICINGA2_USER -g $ICINGA2_GROUP - - echo "Done" - echo + echo "Starting Icinga 2: " + if ! $DAEMON -c $ICINGA2_CONFIG_FILE -d -e $ICINGA2_ERROR_LOG -u $ICINGA2_USER -g $ICINGA2_GROUP; then + echo "Error starting Icinga." + exit 1 + else + echo "Done" + fi } # Restart Icinga 2 @@ -104,15 +106,9 @@ stop() { # Reload Icinga 2 reload() { printf "Reloading Icinga 2: " - if [ ! -e $ICINGA2_PID_FILE ]; then - echo "The PID file '$ICINGA2_PID_FILE' does not exist." + 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 - fi - - pid=`cat $ICINGA2_PID_FILE` - - if ! kill -HUP $pid >/dev/null 2>&1; then - echo "Failed - Icinga 2 is not running." else echo "Done" fi @@ -162,7 +158,6 @@ case "$1" in start ;; reload) - checkconfig reload fail reload ;; checkconfig) diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 0c538f822..d2f9834f0 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -187,6 +187,34 @@ static bool Daemonize(const String& stderrFile) return true; } +/** + * Terminate another process and wait till it has ended + * + * @params target PID of the process to end + */ +static void TerminateAndWaitForEnd(pid_t target) +{ +#ifndef _WIN32 + // allow 15 seconds timeout + double timeout = Utility::GetTime() + 15; + + int ret = kill(target, SIGTERM); + + while(Utility::GetTime() < timeout && (ret==0 || errno!=ESRCH)) + { + Utility::Sleep(0.1); + ret = kill(target, 0); + } + + // timeout and the process still seems to live: kill it + if (ret == 0 || errno != ESRCH) + kill(target, SIGKILL); + +#else + // TODO: implement this for Win32 +#endif /* _WIN32 */ +} + int Main(void) { int argc = Application::GetArgC(); @@ -252,6 +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") #ifndef _WIN32 ("daemonize,d", "detach from the controlling terminal") ("user,u", po::value(), "user to run Icinga as") @@ -405,6 +434,17 @@ 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."); + 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)) return EXIT_FAILURE; @@ -413,6 +453,12 @@ 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); + Log(LogInformation, "icinga-app", "Previous instance has ended, taking over now."); + } + if (g_AppParams.count("daemonize")) { String errorLog; diff --git a/lib/base/application.cpp b/lib/base/application.cpp index 6e94b35bf..fc19b93b3 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -638,7 +638,7 @@ void Application::UpdatePidFile(const String& filename) } #endif /* _WIN32 */ - fprintf(m_PidFile, "%d", Utility::GetPid()); + fprintf(m_PidFile, "%d\n", Utility::GetPid()); fflush(m_PidFile); } @@ -656,6 +656,60 @@ void Application::ClosePidFile(void) m_PidFile = NULL; } +/** + * Checks if another process currently owns the pidfile and read it + * + * @param filename The name of the PID file. + * @returns -1: no process owning the pidfile, pid of the process otherwise + */ +pid_t Application::ReadPidFile(const String& filename) +{ + FILE *pidfile = fopen(filename.CStr(), "r"); + + if (pidfile == NULL) + return -1; + +#ifndef _WIN32 + int fd = fileno(pidfile); + + struct flock lock; + + lock.l_start = 0; + lock.l_len = 0; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + + if (fcntl(fd, F_GETLK, &lock) < 0) { + int error = errno; + fclose(pidfile); + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("fcntl") + << boost::errinfo_errno(error)); + } + + if (lock.l_type == F_UNLCK) { + // nobody has locked the file: no icinga running + fclose(pidfile); + return -1; + } +#endif /* _WIN32 */ + + pid_t runningpid; + int res = fscanf(pidfile, "%d", &runningpid); + fclose(pidfile); + + // bogus result? + if (res != 1) + return -1; + +#ifdef _WIN32 + // TODO: add check if the read pid is still running or not +#endif /* _WIN32 */ + + return runningpid; +} + + /** * Retrieves the path of the installation prefix. * diff --git a/lib/base/application.h b/lib/base/application.h index 6df97f737..9597409e5 100644 --- a/lib/base/application.h +++ b/lib/base/application.h @@ -69,6 +69,7 @@ public: void UpdatePidFile(const String& filename); void ClosePidFile(void); + static pid_t ReadPidFile(const String& filename); static String GetExePath(const String& argv0);