}
#ifndef _WIN32
+// The PID of the Icinga umbrella process
pid_t l_UmbrellaPid = 0;
+
+// Whether the umbrella process allowed us to continue working beyond config validation
static std::atomic<bool> l_AllowedToWork (false);
#endif /* _WIN32 */
+/**
+ * Do the actual work (config loading, ...)
+ *
+ * @param configs Files to read config from
+ *
+ * @return Exit code
+ */
static inline
int RunWorker(const std::vector<std::string>& configs)
{
}
#ifndef _WIN32
+/**
+ * The possible states of a seemless worker being started by StartUnixWorker().
+ */
enum class UnixWorkerState : uint_fast8_t
{
Pending,
Failed
};
+// The signals to block temporarily in StartUnixWorker().
static const sigset_t l_UnixWorkerSignals = ([]() -> sigset_t {
sigset_t s;
return s;
})();
+// The PID of the seemless worker currently being started by StartUnixWorker()
static std::atomic<pid_t> l_CurrentlyStartingUnixWorkerPid (-1);
+
+// The state of the seemless worker currently being started by StartUnixWorker()
static std::atomic<UnixWorkerState> l_CurrentlyStartingUnixWorkerState (UnixWorkerState::Pending);
+
+// The last temination signal we received
static std::atomic<int> l_TermSignal (-1);
+
+// Whether someone requested to re-load config (and we didn't handle that request, yet)
static std::atomic<bool> l_RequestedReload (false);
+
+// Whether someone requested to re-open logs (and we didn't handle that request, yet)
static std::atomic<bool> l_RequestedReopenLogs (false);
+/**
+ * Umbrella process' signal handlers
+ */
static void UmbrellaSignalHandler(int num, siginfo_t *info, void*)
{
switch (num) {
case SIGUSR1:
+ // Someone requested to re-open logs
l_RequestedReopenLogs.store(true);
break;
case SIGUSR2:
if (l_CurrentlyStartingUnixWorkerState.load() == UnixWorkerState::Pending
&& info->si_pid == l_CurrentlyStartingUnixWorkerPid.load()) {
+ // The seemless worker currently being started by StartUnixWorker() successfully loaded its config
l_CurrentlyStartingUnixWorkerState.store(UnixWorkerState::LoadedConfig);
}
break;
case SIGCHLD:
if (l_CurrentlyStartingUnixWorkerState.load() == UnixWorkerState::Pending
&& info->si_pid == l_CurrentlyStartingUnixWorkerPid.load()) {
+ // The seemless worker currently being started by StartUnixWorker() failed
l_CurrentlyStartingUnixWorkerState.store(UnixWorkerState::Failed);
}
break;
case SIGINT:
case SIGTERM:
+ // Someone requested our termination
+
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
l_TermSignal.store(num);
break;
case SIGHUP:
+ // Someone requested to re-load config
l_RequestedReload.store(true);
break;
default:
+ // Programming error (or someone has broken the userspace)
VERIFY(!"Caught unexpected signal");
}
}
+/**
+ * Seemless worker's signal handlers
+ */
static void WorkerSignalHandler(int num, siginfo_t *info, void*)
{
switch (num) {
case SIGUSR2:
if (info->si_pid == l_UmbrellaPid) {
+ // The umbrella process allowed us to continue working beyond config validation
l_AllowedToWork.store(true);
}
break;
case SIGINT:
case SIGTERM:
if (info->si_pid == l_UmbrellaPid) {
+ // The umbrella process requested our termination
Application::RequestShutdown();
}
break;
default:
+ // Programming error (or someone has broken the userspace)
VERIFY(!"Caught unexpected signal");
}
}
#ifdef HAVE_SYSTEMD
+// When we last notified the watchdog.
static std::atomic<double> l_LastNotifiedWatchdog (0);
+/**
+ * Notify the watchdog if not notified during the last 2.5s.
+ */
static void NotifyWatchdog()
{
double now = Utility::GetTime();
}
#endif /* HAVE_SYSTEMD */
+/**
+ * Starts seemless worker process doing the actual work (config loading, ...)
+ *
+ * @param configs Files to read config from
+ *
+ * @return The worker's PID on success, -1 on failure (if the worker couldn't load its config)
+ */
static pid_t StartUnixWorker(const std::vector<std::string>& configs)
{
Log(LogNotice, "cli") << "Spawning seemless worker process doing the actual work";
exit(EXIT_FAILURE);
}
+ /* Block the signal handlers we'd like to change in the child process until we changed them.
+ * Block SIGUSR2 and SIGCHLD handlers until we've set l_CurrentlyStartingUnixWorkerPid.
+ */
(void)sigprocmask(SIG_BLOCK, &l_UnixWorkerSignals, nullptr);
pid_t pid = fork();
Log(LogNotice, "cli") << "Spawned worker process (PID " << pid << "), waiting for it to load its config";
+ // Wait for the newly spawned process to either load its config or fail.
for (;;) {
#ifdef HAVE_SYSTEMD
NotifyWatchdog();
break;
}
+ // Reset flags for the next time
l_CurrentlyStartingUnixWorkerPid.store(-1);
l_CurrentlyStartingUnixWorkerState.store(UnixWorkerState::Pending);
return pid;
}
+/**
+ * Workaround to instantiate Application (which is abstract) in DaemonCommand#Run()
+ */
class PidFileManagementApp : public Application
{
public:
}
#ifndef _WIN32
+ /* The Application manages the PID file,
+ * but on *nix this process doesn't load any config
+ * so there's no central Application instance.
+ */
PidFileManagementApp app;
try {
(void)sigaction(SIGHUP, &sa, nullptr);
}
+ // The PID of the current seemless worker
pid_t currentWorker = StartUnixWorker(configs);
if (currentWorker == -1) {
return EXIT_FAILURE;
}
+ // Immediately allow the first (non-reload) worker to continue working beyond config validation
(void)kill(currentWorker, SIGUSR2);
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
#endif /* HAVE_SYSTEMD */
+ // Whether we already forwarded a termination signal to the seemless worker
bool requestedTermination = false;
+
+ // Whether we already notified systemd about our termination
bool notifiedTermination = false;
for (;;) {
Log(LogNotice, "cli") << "Waited for " << Utility::FormatDuration(Utility::GetTime() - start) << " on old process to exit.";
}
+ // Old instance shut down, allow the new one to continue working beyond config validation
(void)kill(nextWorker, SIGUSR2);
currentWorker = nextWorker;
}
#endif /* HAVE_SYSTEMD */
+ // If killed by signal, forward it via the exit code (to be as seemless as possible)
return WIFSIGNALED(status) ? 128 + WTERMSIG(status) : WEXITSTATUS(status);
}
}