Merge branch 'feature/systemd-reload-6118' into next

Fixes #6118
This commit is contained in:
Gunnar Beutner 2014-05-18 18:46:46 +02:00
commit 1e57861486
5 changed files with 88 additions and 19 deletions

View File

@ -161,7 +161,7 @@ static void SigHupHandler(int)
} }
#endif /* _WIN32 */ #endif /* _WIN32 */
static bool Daemonize(const String& stderrFile) static bool Daemonize(void)
{ {
#ifndef _WIN32 #ifndef _WIN32
pid_t pid = fork(); pid_t pid = fork();
@ -169,9 +169,39 @@ static bool Daemonize(const String& stderrFile)
return false; return false;
} }
if (pid) if (pid) {
exit(0); // systemd requires that the pidfile of the daemon is written before the forking
// process terminates. So wait till either the forked daemon has written a pidfile or died.
int status;
int ret;
pid_t readpid;
do {
Utility::Sleep(0.1);
readpid = Application::ReadPidFile(Application::GetPidPath());
ret = waitpid(pid, &status, WNOHANG);
} while (readpid != pid && ret == 0);
if (ret == pid) {
Log(LogCritical, "icinga-app", "The daemon could not be started. See logfile for details.");
exit(EXIT_FAILURE);
} else if (ret == -1) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("waitpid")
<< boost::errinfo_errno(errno));
}
exit(0);
}
#endif /* _WIN32 */
return true;
}
static bool SetDaemonIO(const String& stderrFile)
{
#ifndef _WIN32
int fdnull = open("/dev/null", O_RDWR); int fdnull = open("/dev/null", O_RDWR);
if (fdnull >= 0) { if (fdnull >= 0) {
if (fdnull != 0) if (fdnull != 0)
@ -484,12 +514,16 @@ int Main(void)
} }
if (g_AppParams.count("daemonize")) { if (g_AppParams.count("daemonize")) {
String errorLog; if (!g_AppParams.count("reload-internal")) {
// no additional fork neccessary on reload
Daemonize();
}
String errorLog;
if (g_AppParams.count("errorlog")) if (g_AppParams.count("errorlog"))
errorLog = g_AppParams["errorlog"].as<std::string>(); errorLog = g_AppParams["errorlog"].as<std::string>();
Daemonize(errorLog); SetDaemonIO(errorLog);
Logger::DisableConsoleLog(); Logger::DisableConsoleLog();
} }

View File

@ -46,6 +46,7 @@ REGISTER_TYPE(Application);
Application *Application::m_Instance = NULL; Application *Application::m_Instance = NULL;
bool Application::m_ShuttingDown = false; bool Application::m_ShuttingDown = false;
bool Application::m_RequestRestart = false; bool Application::m_RequestRestart = false;
pid_t Application::m_ReloadProcess = 0;
static bool l_Restarting = false; static bool l_Restarting = false;
bool Application::m_Debugging = false; bool Application::m_Debugging = false;
int Application::m_ArgC; int Application::m_ArgC;
@ -91,7 +92,15 @@ void Application::Stop(void)
WSACleanup(); WSACleanup();
#endif /* _WIN32 */ #endif /* _WIN32 */
ClosePidFile(); // Getting a shutdown-signal when a restart is in progress usually
// means that the restart succeeded and the new process wants to take
// over. Write the PID of the new process to the pidfile before this
// process exits to keep systemd happy.
if (l_Restarting) {
UpdatePidFile(GetPidPath(), m_ReloadProcess);
ClosePidFile(false);
} else
ClosePidFile(true);
DynamicObject::Stop(); DynamicObject::Stop();
} }
@ -218,7 +227,7 @@ void Application::SetArgV(char **argv)
* Processes events for registered sockets and timers and calls whatever * Processes events for registered sockets and timers and calls whatever
* handlers have been set up for these events. * handlers have been set up for these events.
*/ */
void Application::RunEventLoop(void) const void Application::RunEventLoop(void)
{ {
Timer::Initialize(); Timer::Initialize();
@ -254,7 +263,7 @@ mainloop:
goto mainloop; goto mainloop;
l_Restarting = true; l_Restarting = true;
StartReloadProcess(); m_ReloadProcess = StartReloadProcess();
goto mainloop; goto mainloop;
} }
@ -285,7 +294,7 @@ static void ReloadProcessCallback(const ProcessResult& pr)
l_Restarting = false; l_Restarting = false;
} }
void Application::StartReloadProcess(void) const pid_t Application::StartReloadProcess(void)
{ {
Log(LogInformation, "base", "Got reload command: Starting new instance."); Log(LogInformation, "base", "Got reload command: Starting new instance.");
@ -305,6 +314,8 @@ void Application::StartReloadProcess(void) const
Process::Ptr process = make_shared<Process>(Process::PrepareCommand(args)); Process::Ptr process = make_shared<Process>(Process::PrepareCommand(args));
process->SetTimeout(300); process->SetTimeout(300);
process->Run(&ReloadProcessCallback); process->Run(&ReloadProcessCallback);
return process->GetPID();
} }
/** /**
@ -619,8 +630,9 @@ int Application::Run(void)
* if the PID file is already locked by another instance of the application. * if the PID file is already locked by another instance of the application.
* *
* @param filename The name of the PID file. * @param filename The name of the PID file.
* @param pid The PID to write; default is the current PID
*/ */
void Application::UpdatePidFile(const String& filename) void Application::UpdatePidFile(const String& filename, pid_t pid)
{ {
ASSERT(!OwnsLock()); ASSERT(!OwnsLock());
ObjectLock olock(this); ObjectLock olock(this);
@ -663,20 +675,27 @@ void Application::UpdatePidFile(const String& filename)
} }
#endif /* _WIN32 */ #endif /* _WIN32 */
fprintf(m_PidFile, "%d\n", Utility::GetPid()); fprintf(m_PidFile, "%d\n", pid);
fflush(m_PidFile); fflush(m_PidFile);
} }
/** /**
* Closes the PID file. Does nothing if the PID file is not currently open. * Closes the PID file. Does nothing if the PID file is not currently open.
*/ */
void Application::ClosePidFile(void) void Application::ClosePidFile(bool unlink)
{ {
ASSERT(!OwnsLock()); ASSERT(!OwnsLock());
ObjectLock olock(this); ObjectLock olock(this);
if (m_PidFile != NULL) if (m_PidFile != NULL)
{
if (unlink) {
String pidpath = GetPidPath();
::unlink(pidpath.CStr());
}
fclose(m_PidFile); fclose(m_PidFile);
}
m_PidFile = NULL; m_PidFile = NULL;
} }

View File

@ -23,6 +23,7 @@
#include "base/i2-base.h" #include "base/i2-base.h"
#include "base/application.th" #include "base/application.th"
#include "base/threadpool.h" #include "base/threadpool.h"
#include "base/utility.h"
namespace icinga namespace icinga
{ {
@ -67,8 +68,8 @@ public:
static void SetDebugging(bool debug); static void SetDebugging(bool debug);
static bool IsDebugging(void); static bool IsDebugging(void);
void UpdatePidFile(const String& filename); void UpdatePidFile(const String& filename, pid_t pid = Utility::GetPid());
void ClosePidFile(void); void ClosePidFile(bool unlink);
static pid_t ReadPidFile(const String& filename); static pid_t ReadPidFile(const String& filename);
static String GetExePath(const String& argv0); static String GetExePath(const String& argv0);
@ -113,9 +114,9 @@ protected:
virtual void OnConfigLoaded(void); virtual void OnConfigLoaded(void);
virtual void Stop(void); virtual void Stop(void);
void RunEventLoop(void) const; void RunEventLoop(void);
void StartReloadProcess(void) const; pid_t StartReloadProcess(void);
virtual void OnShutdown(void); virtual void OnShutdown(void);
@ -124,7 +125,10 @@ private:
static bool m_ShuttingDown; /**< Whether the application is in the process of static bool m_ShuttingDown; /**< Whether the application is in the process of
shutting down. */ shutting down. */
static bool m_RequestRestart; static bool m_RequestRestart; /**< A restart was requested through SIGHUP */
static pid_t m_ReloadProcess; /**< The PID of a subprocess doing a reload,
only valid when l_Restarting==true */
static int m_ArgC; /**< The number of command-line arguments. */ static int m_ArgC; /**< The number of command-line arguments. */
static char **m_ArgV; /**< Command-line arguments. */ static char **m_ArgV; /**< Command-line arguments. */
FILE *m_PidFile; /**< The PID file */ FILE *m_PidFile; /**< The PID file */

View File

@ -410,9 +410,10 @@ void Process::Run(const boost::function<void(const ProcessResult&)>& callback)
m_Process = pi.hProcess; m_Process = pi.hProcess;
m_FD = outReadPipe; m_FD = outReadPipe;
m_PID = pi.dwProcessId;
Log(LogInformation, "base", "Running command '" + m_Arguments + Log(LogInformation, "base", "Running command '" + m_Arguments +
"': PID " + Convert::ToString(pi.dwProcessId)); "': PID " + Convert::ToString(m_PID));
#else /* _WIN32 */ #else /* _WIN32 */
int fds[2]; int fds[2];
@ -514,8 +515,10 @@ void Process::Run(const boost::function<void(const ProcessResult&)>& callback)
// parent process // parent process
m_PID = m_Process;
Log(LogInformation, "base", "Running command '" + boost::algorithm::join(m_Arguments, " ") + Log(LogInformation, "base", "Running command '" + boost::algorithm::join(m_Arguments, " ") +
"': PID " + Convert::ToString(m_Process)); "': PID " + Convert::ToString(m_PID));
m_Arguments.clear(); m_Arguments.clear();
@ -636,6 +639,12 @@ bool Process::DoEvents(void)
return false; return false;
} }
pid_t Process::GetPID(void) const
{
return m_PID;
}
int Process::GetTID(void) const int Process::GetTID(void) const
{ {
return (reinterpret_cast<uintptr_t>(this) / sizeof(void *)) % IOTHREADS; return (reinterpret_cast<uintptr_t>(this) / sizeof(void *)) % IOTHREADS;

View File

@ -73,6 +73,8 @@ public:
void Run(const boost::function<void (const ProcessResult&)>& callback = boost::function<void (const ProcessResult&)>()); void Run(const boost::function<void (const ProcessResult&)>& callback = boost::function<void (const ProcessResult&)>());
pid_t GetPID(void) const;
static Arguments PrepareCommand(const Value& command); static Arguments PrepareCommand(const Value& command);
static void StaticInitialize(void); static void StaticInitialize(void);
@ -85,6 +87,7 @@ private:
double m_Timeout; double m_Timeout;
ProcessHandle m_Process; ProcessHandle m_Process;
pid_t m_PID;
ConsoleHandle m_FD; ConsoleHandle m_FD;
std::ostringstream m_OutputStream; std::ostringstream m_OutputStream;