From 70fa21dfbcd21cf07812cd270188d4c4748e2ad7 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 6 Mar 2013 11:03:50 +0100 Subject: [PATCH] Various bugfixes. --- .../notification/notificationcomponent.cpp | 29 +- configure.ac | 4 +- lib/base/application.cpp | 30 +- lib/base/dynamicobject.cpp | 17 +- lib/base/dynamicobject.h | 2 +- lib/base/dynamictype.cpp | 32 +- lib/base/eventqueue.cpp | 65 ++-- lib/base/eventqueue.h | 5 +- lib/base/object.h | 2 +- lib/base/objectlock.cpp | 11 +- lib/base/objectlock.h | 4 - lib/base/process-unix.cpp | 86 ++++-- lib/base/process-windows.cpp | 2 +- lib/base/process.cpp | 7 +- lib/base/process.h | 5 +- lib/base/ringbuffer.cpp | 4 +- lib/base/ringbuffer.h | 2 +- lib/base/scriptfunction.cpp | 2 - lib/base/timer.cpp | 4 +- lib/icinga/externalcommandprocessor.cpp | 279 ++++++++++++++---- lib/icinga/host.cpp | 27 +- lib/icinga/host.h | 3 +- lib/icinga/hostgroup.cpp | 23 +- lib/icinga/hostgroup.h | 3 +- lib/icinga/pluginnotificationtask.cpp | 2 +- lib/icinga/service-check.cpp | 36 ++- lib/icinga/service-comment.cpp | 23 +- lib/icinga/service-downtime.cpp | 23 +- lib/icinga/service-notification.cpp | 28 +- lib/icinga/service.cpp | 7 +- lib/icinga/service.h | 10 +- lib/icinga/servicegroup.cpp | 23 +- lib/icinga/servicegroup.h | 3 +- 33 files changed, 546 insertions(+), 257 deletions(-) diff --git a/components/notification/notificationcomponent.cpp b/components/notification/notificationcomponent.cpp index 02ba944e2..a79942155 100644 --- a/components/notification/notificationcomponent.cpp +++ b/components/notification/notificationcomponent.cpp @@ -60,21 +60,27 @@ void NotificationComponent::NotificationTimerHandler(void) Service::Ptr service = dynamic_pointer_cast(object); bool reachable = service->IsReachable(); - ObjectLock olock(service); + bool send_notification; - if (service->GetStateType() == StateTypeSoft) - continue; + { + ObjectLock olock(service); - if (service->GetState() == StateOK) - continue; + if (service->GetStateType() == StateTypeSoft) + continue; - if (service->GetNotificationInterval() <= 0) - continue; + if (service->GetState() == StateOK) + continue; - if (service->GetLastNotification() > now - service->GetNotificationInterval()) - continue; + if (service->GetNotificationInterval() <= 0) + continue; - if (reachable && !service->IsInDowntime() && !service->IsAcknowledged()) + if (service->GetLastNotification() > now - service->GetNotificationInterval()) + continue; + + send_notification = reachable && !service->IsInDowntime() && !service->IsAcknowledged(); + } + + if (send_notification) service->RequestNotifications(NotificationProblem); } } @@ -99,5 +105,8 @@ void NotificationComponent::SendNotificationsRequestHandler(const Endpoint::Ptr& Service::Ptr service = Service::GetByName(svc); + if (!service) + return; + service->SendNotifications(static_cast(type)); } diff --git a/configure.ac b/configure.ac index fee72cac0..8bcaa7774 100644 --- a/configure.ac +++ b/configure.ac @@ -75,8 +75,8 @@ AC_CHECK_FUNCS([backtrace_symbols execvpe pipe2]) AC_MSG_CHECKING(whether to enable debugging) AC_ARG_ENABLE(debug, [ --enable-debug=[no/yes] turn on debugging (default=no)],, enable_debug=no) if test "x$enable_debug" = "xyes"; then - CFLAGS="$CFLAGS -g -O0 -D_DEBUG" - CXXFLAGS="$CXXFLAGS -g -O0 -D_DEBUG" + CFLAGS="$CFLAGS -g -O0 -D_DEBUG -Wall -Wextra" + CXXFLAGS="$CXXFLAGS -g -O0 -D_DEBUG -Wall -Wextra" else CFLAGS="$CFLAGS -DNDEBUG" CXXFLAGS="$CXXFLAGS -DNDEBUG" diff --git a/lib/base/application.cpp b/lib/base/application.cpp index d9c76496a..92f839a48 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -109,6 +109,7 @@ void Application::ShutdownTimerHandler(void) if (m_ShuttingDown) { Logger::Write(LogInformation, "base", "Shutting down Icinga..."); Application::GetInstance()->OnShutdown(); + DynamicObject::DeactivateObjects(); GetEQ().Stop(); m_ShuttingDown = false; @@ -285,17 +286,17 @@ void Application::SigIntHandler(int signum) { assert(signum == SIGINT); + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &sa, NULL); + Application::Ptr instance = Application::GetInstance(); if (!instance) return; instance->RequestShutdown(); - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGINT, &sa, NULL); } /** @@ -307,6 +308,11 @@ void Application::SigAbrtHandler(int signum) { assert(signum == SIGABRT); + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGABRT, &sa, NULL); + std::cerr << "Caught SIGABRT." << std::endl; Utility::PrintStacktrace(std::cerr, 1); @@ -337,6 +343,13 @@ BOOL WINAPI Application::CtrlHandler(DWORD type) */ void Application::ExceptionHandler(void) { +#ifndef _WIN32 + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGABRT, &sa, NULL); +#endif /* _WIN32 */ + try { throw; } catch (const std::exception& ex) { @@ -349,13 +362,6 @@ void Application::ExceptionHandler(void) DisplayBugMessage(); -#ifndef _WIN32 - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGABRT, &sa, NULL); -#endif /* _WIN32 */ - abort(); } diff --git a/lib/base/dynamicobject.cpp b/lib/base/dynamicobject.cpp index f1ff3e911..df9c382ce 100644 --- a/lib/base/dynamicobject.cpp +++ b/lib/base/dynamicobject.cpp @@ -128,7 +128,7 @@ void DynamicObject::ApplyUpdate(const Dictionary::Ptr& serializedUpdate, boost::mutex::scoped_lock lock(m_AttributeMutex); if ((allowedTypes & Attribute_Config) != 0 && !configTxValue.IsEmpty()) { - double oldConfigTx, configTx = configTxValue; + double configTx = configTxValue; if (configTx > m_ConfigTx) { DynamicObject::AttributeIterator at; @@ -226,7 +226,7 @@ void DynamicObject::Set(const String& name, const Value& data) */ void DynamicObject::Touch(const String& name) { - assert(!OwnsLock()); + assert(OwnsLock()); boost::mutex::scoped_lock lock(m_AttributeMutex); @@ -284,7 +284,7 @@ void DynamicObject::InternalSetAttribute(const String& name, const Value& data, m_ConfigTx = tx; } - if (m_Registered) { + if (IsRegistered()) { /* We can't call GetSelf() in the constructor or destructor. * The Register() function will take care of adding this * object to the list of modified objects later on if we can't @@ -352,6 +352,7 @@ bool DynamicObject::IsAbstract(void) const */ bool DynamicObject::IsRegistered(void) const { + ObjectLock olock(GetType()); return m_Registered; } @@ -392,11 +393,6 @@ void DynamicObject::OnRegistrationCompleted(void) { assert(!OwnsLock()); - { - ObjectLock olock(this); - m_Registered = true; - } - Start(); OnRegistered(GetSelf()); @@ -406,11 +402,6 @@ void DynamicObject::OnUnregistrationCompleted(void) { assert(!OwnsLock()); - { - ObjectLock olock(this); - m_Registered = false; - } - OnUnregistered(GetSelf()); } diff --git a/lib/base/dynamicobject.h b/lib/base/dynamicobject.h index 0c5014f30..b002eaac8 100644 --- a/lib/base/dynamicobject.h +++ b/lib/base/dynamicobject.h @@ -117,7 +117,7 @@ private: Attribute m_Source; Attribute m_Methods; - bool m_Registered; + bool m_Registered; /**< protected by the type mutex */ static double m_CurrentTx; diff --git a/lib/base/dynamictype.cpp b/lib/base/dynamictype.cpp index f5e06baaf..29f08caec 100644 --- a/lib/base/dynamictype.cpp +++ b/lib/base/dynamictype.cpp @@ -83,29 +83,37 @@ void DynamicType::RegisterObject(const DynamicObject::Ptr& object) { String name = object->GetName(); - ObjectLock olock(this); + { + ObjectLock olock(this); - ObjectMap::iterator it = m_ObjectMap.find(name); + ObjectMap::iterator it = m_ObjectMap.find(name); - if (it != m_ObjectMap.end()) { - if (it->second == object) - return; + if (it != m_ObjectMap.end()) { + if (it->second == object) + return; - BOOST_THROW_EXCEPTION(runtime_error("RegisterObject() found existing object with the same name: " + name)); + BOOST_THROW_EXCEPTION(runtime_error("RegisterObject() found existing object with the same name: " + name)); + } + + m_ObjectMap[name] = object; + m_ObjectSet.insert(object); + + object->m_Registered = true; } - m_ObjectMap[name] = object; - m_ObjectSet.insert(object); - object->OnRegistrationCompleted(); } void DynamicType::UnregisterObject(const DynamicObject::Ptr& object) { - ObjectLock olock(this); + { + ObjectLock olock(this); - m_ObjectMap.erase(object->GetName()); - m_ObjectSet.erase(object); + m_ObjectMap.erase(object->GetName()); + m_ObjectSet.erase(object); + + object->m_Registered = false; + } object->OnUnregistrationCompleted(); } diff --git a/lib/base/eventqueue.cpp b/lib/base/eventqueue.cpp index 4564f4671..f959dfd19 100644 --- a/lib/base/eventqueue.cpp +++ b/lib/base/eventqueue.cpp @@ -25,20 +25,20 @@ using namespace icinga; * @threadsafety Always. */ EventQueue::EventQueue(void) - : m_Stopped(false), m_LastReport(0) + : m_Stopped(false) { - int thread_count = thread::hardware_concurrency(); + unsigned int threads = thread::hardware_concurrency(); - if (thread_count < 4) - thread_count = 4; + if (threads == 0) + threads = 1; - for (int i = 0; i < thread_count; i++) + threads *= 8; + + for (unsigned int i = 0; i < threads; i++) m_Threads.create_thread(boost::bind(&EventQueue::QueueThreadProc, this)); - m_ReportTimer = boost::make_shared(); - m_ReportTimer->OnTimerExpired.connect(boost::bind(&EventQueue::ReportTimerHandler, this)); - m_ReportTimer->SetInterval(5); - m_ReportTimer->Start(); + thread reportThread(boost::bind(&EventQueue::ReportThreadProc, this)); + reportThread.detach(); } /** @@ -46,8 +46,6 @@ EventQueue::EventQueue(void) */ EventQueue::~EventQueue(void) { - m_ReportTimer->Stop(); - Stop(); Join(); } @@ -95,17 +93,39 @@ void EventQueue::QueueThreadProc(void) } BOOST_FOREACH(const Callback& ev, events) { +#ifdef _DEBUG + struct rusage usage_start, usage_end; + double st = Utility::GetTime(); + (void) getrusage(RUSAGE_THREAD, &usage_start); +#endif /* _DEBUG */ ev(); +#ifdef _DEBUG double et = Utility::GetTime(); + (void) getrusage(RUSAGE_THREAD, &usage_end); - if (et - st > 0.25) { + double duser = (usage_end.ru_utime.tv_sec - usage_start.ru_utime.tv_sec) + + (usage_end.ru_utime.tv_usec - usage_start.ru_utime.tv_usec) / 1000000.0; + + double dsys = (usage_end.ru_stime.tv_sec - usage_start.ru_stime.tv_sec) + + (usage_end.ru_stime.tv_usec - usage_start.ru_stime.tv_usec) / 1000000.0; + + double dwait = (et - st) - (duser + dsys); + + int dminfaults = usage_end.ru_minflt - usage_start.ru_minflt; + int dmajfaults = usage_end.ru_majflt - usage_start.ru_majflt; + + int dvctx = usage_end.ru_nvcsw - usage_start.ru_nvcsw; + int divctx = usage_end.ru_nivcsw - usage_start.ru_nivcsw; + + if (et - st > 0.5) { stringstream msgbuf; - msgbuf << "Event call took " << et - st << " seconds."; + msgbuf << "Event call took user:" << duser << "s, system:" << dsys << "s, wait:" << dwait << "s, minor_faults:" << dminfaults << ", major_faults:" << dmajfaults << ", voluntary_csw:" << dvctx << ", involuntary_csw:" << divctx; Logger::Write(LogWarning, "base", msgbuf.str()); } +#endif /* _DEBUG */ } } } @@ -123,15 +143,18 @@ void EventQueue::Post(const EventQueue::Callback& callback) m_CV.notify_one(); } -void EventQueue::ReportTimerHandler(void) +void EventQueue::ReportThreadProc(void) { - int pending; + for (;;) { + Utility::Sleep(5); - { - boost::mutex::scoped_lock lock(m_Mutex); - pending = m_Events.size(); + int pending; + + { + boost::mutex::scoped_lock lock(m_Mutex); + pending = m_Events.size(); + } + + Logger::Write(LogInformation, "base", "Pending tasks: " + Convert::ToString(pending)); } - - if (pending > 1000) - Logger::Write(LogCritical, "base", "More than 1000 pending events: " + Convert::ToString(pending)); } diff --git a/lib/base/eventqueue.h b/lib/base/eventqueue.h index f6edf989f..0a383f5ac 100644 --- a/lib/base/eventqueue.h +++ b/lib/base/eventqueue.h @@ -49,14 +49,11 @@ private: boost::mutex m_Mutex; condition_variable m_CV; - double m_LastReport; - shared_ptr m_ReportTimer; - bool m_Stopped; vector m_Events; void QueueThreadProc(void); - void ReportTimerHandler(void); + void ReportThreadProc(void); }; } diff --git a/lib/base/object.h b/lib/base/object.h index b03fb2d78..4acba266f 100644 --- a/lib/base/object.h +++ b/lib/base/object.h @@ -117,7 +117,7 @@ private: mutable MutexType m_Mutex; - friend class ObjectLock; + friend struct ObjectLock; }; /** diff --git a/lib/base/objectlock.cpp b/lib/base/objectlock.cpp index 3445e20f1..f5b4f7b3f 100644 --- a/lib/base/objectlock.cpp +++ b/lib/base/objectlock.cpp @@ -56,7 +56,6 @@ void ObjectLock::Lock(void) boost::mutex::scoped_lock lock(Object::m_DebugMutex); m_Object->m_Locked = true; m_Object->m_LockOwner = boost::this_thread::get_id(); - m_TS = Utility::GetTime(); } #endif /* _DEBUG */ } @@ -67,16 +66,8 @@ void ObjectLock::Unlock(void) { boost::mutex::scoped_lock lock(Object::m_DebugMutex); - if (m_Lock.owns_lock()) { - double dt = Utility::GetTime() - m_TS; - - if (dt > 0.05) { - std::cerr << "Held object lock for " << dt << " seconds at:"; - Utility::PrintStacktrace(std::cerr); - } - + if (m_Lock.owns_lock()) m_Object->m_Locked = false; - } } #endif /* _DEBUG */ diff --git a/lib/base/objectlock.h b/lib/base/objectlock.h index 26532a506..6b43580ee 100644 --- a/lib/base/objectlock.h +++ b/lib/base/objectlock.h @@ -39,10 +39,6 @@ public: private: const Object *m_Object; Object::MutexType::scoped_lock m_Lock; - -#ifdef _DEBUG - double m_TS; -#endif /* _DEBUG */ }; } diff --git a/lib/base/process-unix.cpp b/lib/base/process-unix.cpp index a00d53f22..2522550e7 100644 --- a/lib/base/process-unix.cpp +++ b/lib/base/process-unix.cpp @@ -24,6 +24,7 @@ using namespace icinga; +condition_variable Process::m_CV; int Process::m_TaskFd; Timer::Ptr Process::m_StatusTimer; extern char **environ; @@ -47,7 +48,12 @@ void Process::Initialize(void) m_TaskFd = fds[1]; - for (int i = 0; i < thread::hardware_concurrency(); i++) { + unsigned int threads = thread::hardware_concurrency(); + + if (threads == 0) + threads = 2; + + for (unsigned int i = 0; i < threads; i++) { int childTaskFd = dup(fds[0]); if (childTaskFd < 0) @@ -104,44 +110,63 @@ void Process::WorkerThreadProc(int taskFd) if (rc == 0) continue; - for (int i = 0; i < idx; i++) { if ((pfds[i].revents & (POLLIN|POLLHUP)) == 0) continue; - while (pfds[i].fd == taskFd && tasks.size() < MaxTasksPerThread) { - Process::Ptr task; + if (pfds[i].fd == taskFd) { + vector new_tasks; - { + int want = MaxTasksPerThread - tasks.size(); + + if (want > m_Tasks.size()) + want = m_Tasks.size(); + + if (want > 0) { boost::mutex::scoped_lock lock(m_Mutex); /* Read one byte for every task we take from the pending tasks list. */ - char buffer; - int rc = read(taskFd, &buffer, sizeof(buffer)); + char buffer[MaxTasksPerThread]; - if (rc < 0) { + assert(want =< sizeof(buffer)); + + int have = read(taskFd, &buffer, want); + + if (have < 0) { if (errno == EAGAIN) break; /* Someone else was faster and took our task. */ BOOST_THROW_EXCEPTION(PosixException("read() failed.", errno)); } - assert(!m_Tasks.empty()); + while (have > 0) { + assert(!m_Tasks.empty()); - task = m_Tasks.front(); - m_Tasks.pop_front(); + Process::Ptr task = m_Tasks.front(); + m_Tasks.pop_front(); + + new_tasks.push_back(task); + + have--; + } + + m_CV.notify_all(); } - try { - task->InitTask(); + BOOST_FOREACH(const Process::Ptr& task, new_tasks) { + try { + task->InitTask(); - int fd = task->m_FD; + int fd = task->m_FD; - if (fd >= 0) - tasks[fd] = task; - } catch (...) { - task->FinishException(boost::current_exception()); + if (fd >= 0) + tasks[fd] = task; + } catch (...) { + task->FinishException(boost::current_exception()); + } } + + continue; } it = tasks.find(pfds[i].fd); @@ -161,14 +186,23 @@ void Process::WorkerThreadProc(int taskFd) } } -void Process::NotifyWorker(void) +void Process::QueueTask(void) { - /** - * This little gem which is commonly known as the "self-pipe trick" - * takes care of waking up the select() call in the worker thread. - */ - if (write(m_TaskFd, "T", 1) < 0) - BOOST_THROW_EXCEPTION(PosixException("write() failed.", errno)); + { + boost::mutex::scoped_lock lock(m_Mutex); + + while (m_Tasks.size() >= PIPE_BUF) + m_CV.wait(lock); + + m_Tasks.push_back(GetSelf()); + + /** + * This little gem which is commonly known as the "self-pipe trick" + * takes care of waking up the select() call in the worker thread. + */ + if (write(m_TaskFd, "T", 1) < 0) + BOOST_THROW_EXCEPTION(PosixException("write() failed.", errno)); + } } void Process::InitTask(void) @@ -196,7 +230,7 @@ void Process::InitTask(void) // build argv char **argv = new char *[m_Arguments.size() + 1]; - for (int i = 0; i < m_Arguments.size(); i++) + for (unsigned int i = 0; i < m_Arguments.size(); i++) argv[i] = strdup(m_Arguments[i].CStr()); argv[m_Arguments.size()] = NULL; diff --git a/lib/base/process-windows.cpp b/lib/base/process-windows.cpp index 98cf0fc62..3140a7998 100644 --- a/lib/base/process-windows.cpp +++ b/lib/base/process-windows.cpp @@ -32,7 +32,7 @@ void Process::WorkerThreadProc(void) // TODO: implement } -void Process::NotifyWorker(void) +void Process::QueueTask(void) { // TODO: implement } diff --git a/lib/base/process.cpp b/lib/base/process.cpp index 843602f59..4b2df758a 100644 --- a/lib/base/process.cpp +++ b/lib/base/process.cpp @@ -67,10 +67,5 @@ vector Process::SplitCommand(const Value& command) void Process::Run(void) { - { - boost::mutex::scoped_lock lock(m_Mutex); - m_Tasks.push_back(GetSelf()); - } - - NotifyWorker(); + QueueTask(); } diff --git a/lib/base/process.h b/lib/base/process.h index e141accff..1b0dbd168 100644 --- a/lib/base/process.h +++ b/lib/base/process.h @@ -48,7 +48,7 @@ public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; - static const deque::size_type MaxTasksPerThread = 128; + static const deque::size_type MaxTasksPerThread = 512; Process(const vector& arguments, const Dictionary::Ptr& extraEnvironment = Dictionary::Ptr()); @@ -71,12 +71,13 @@ private: static boost::mutex m_Mutex; static deque m_Tasks; #ifndef _WIN32 + static condition_variable m_CV; static int m_TaskFd; static Timer::Ptr m_StatusTimer; #endif /* _WIN32 */ - static void NotifyWorker(void); + void QueueTask(void); void SpawnTask(void); diff --git a/lib/base/ringbuffer.cpp b/lib/base/ringbuffer.cpp index c5993d9e1..00b850cf4 100644 --- a/lib/base/ringbuffer.cpp +++ b/lib/base/ringbuffer.cpp @@ -42,10 +42,10 @@ void RingBuffer::InsertValue(RingBuffer::SizeType tv, int num) { ObjectLock olock(this); - vector::size_type offsetTarget = tv % m_Slots.size(); + RingBuffer::SizeType offsetTarget = tv % m_Slots.size(); if (tv > m_TimeValue) { - vector::size_type offset = m_TimeValue % m_Slots.size(); + RingBuffer::SizeType offset = m_TimeValue % m_Slots.size(); /* walk towards the target offset, resetting slots to 0 */ while (offset != offsetTarget) { diff --git a/lib/base/ringbuffer.h b/lib/base/ringbuffer.h index 8be88e74d..44ba73115 100644 --- a/lib/base/ringbuffer.h +++ b/lib/base/ringbuffer.h @@ -44,7 +44,7 @@ public: private: vector m_Slots; - int m_TimeValue; + SizeType m_TimeValue; }; } diff --git a/lib/base/scriptfunction.cpp b/lib/base/scriptfunction.cpp index e2f8fc32a..3bfc9c87a 100644 --- a/lib/base/scriptfunction.cpp +++ b/lib/base/scriptfunction.cpp @@ -72,8 +72,6 @@ ScriptFunction::Ptr ScriptFunction::GetByName(const String& name) */ void ScriptFunction::Invoke(const ScriptTask::Ptr& task, const vector& arguments) { - ObjectLock olock(this); - m_Callback(task, arguments); } diff --git a/lib/base/timer.cpp b/lib/base/timer.cpp index 00bebd72e..67813b736 100644 --- a/lib/base/timer.cpp +++ b/lib/base/timer.cpp @@ -94,8 +94,7 @@ void Timer::Call(void) OnTimerExpired(self); - /* Re-enable the timer so it can be called again. */ - Start(); + Reschedule(); } /** @@ -285,7 +284,6 @@ void Timer::TimerThreadProc(void) /* Remove the timer from the list so it doesn't get called again * until the current call is completed. */ - timer->m_Started = false; m_Timers.erase(timer); lock.unlock(); diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index f8e502dce..c07494e36 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -87,7 +87,7 @@ void ExternalCommandProcessor::Execute(double time, const String& command, const */ void ExternalCommandProcessor::Initialize(void) { - RegisterCommand("PROCESS_HOST_CHECK_RESULT", &ExternalCommandProcessor::ProcessServiceCheckResult); + RegisterCommand("PROCESS_HOST_CHECK_RESULT", &ExternalCommandProcessor::ProcessHostCheckResult); RegisterCommand("PROCESS_SERVICE_CHECK_RESULT", &ExternalCommandProcessor::ProcessServiceCheckResult); RegisterCommand("SCHEDULE_HOST_CHECK", &ExternalCommandProcessor::ScheduleHostCheck); RegisterCommand("SCHEDULE_FORCED_HOST_CHECK", &ExternalCommandProcessor::ScheduleForcedHostCheck); @@ -167,8 +167,8 @@ void ExternalCommandProcessor::ProcessHostCheckResult(double time, const vector< if (!hc->GetEnablePassiveChecks()) BOOST_THROW_EXCEPTION(invalid_argument("Got passive check result for host '" + arguments[0] + "' which has passive checks disabled.")); - int exitStatus = Convert::ToDouble(arguments[2]); - Dictionary::Ptr result = PluginCheckTask::ParseCheckOutput(arguments[3]); + int exitStatus = Convert::ToDouble(arguments[1]); + Dictionary::Ptr result = PluginCheckTask::ParseCheckOutput(arguments[2]); result->Set("state", PluginCheckTask::ExitStatusToState(exitStatus)); result->Set("schedule_start", time); @@ -180,10 +180,14 @@ void ExternalCommandProcessor::ProcessHostCheckResult(double time, const vector< Logger::Write(LogInformation, "icinga", "Processing passive check result for host '" + arguments[0] + "'"); hc->ProcessCheckResult(result); - /* Reschedule the next check. The side effect of this is that for as long - * as we receive passive results for a service we won't execute any - * active checks. */ - hc->SetNextCheck(Utility::GetTime() + hc->GetCheckInterval()); + { + ObjectLock olock(hc); + + /* Reschedule the next check. The side effect of this is that for as long + * as we receive passive results for a service we won't execute any + * active checks. */ + hc->SetNextCheck(Utility::GetTime() + hc->GetCheckInterval()); + } } void ExternalCommandProcessor::ProcessServiceCheckResult(double time, const vector& arguments) @@ -209,10 +213,14 @@ void ExternalCommandProcessor::ProcessServiceCheckResult(double time, const vect Logger::Write(LogInformation, "icinga", "Processing passive check result for service '" + arguments[1] + "'"); service->ProcessCheckResult(result); - /* Reschedule the next check. The side effect of this is that for as long - * as we receive passive results for a service we won't execute any - * active checks. */ - service->SetNextCheck(Utility::GetTime() + service->GetCheckInterval()); + { + ObjectLock olock(service); + + /* Reschedule the next check. The side effect of this is that for as long + * as we receive passive results for a service we won't execute any + * active checks. */ + service->SetNextCheck(Utility::GetTime() + service->GetCheckInterval()); + } } void ExternalCommandProcessor::ScheduleHostCheck(double, const vector& arguments) @@ -233,7 +241,12 @@ void ExternalCommandProcessor::ScheduleHostCheck(double, const vector& a } Logger::Write(LogInformation, "icinga", "Rescheduling next check for host '" + arguments[0] + "'"); - hc->SetNextCheck(planned_check); + + { + ObjectLock olock(hc); + + hc->SetNextCheck(planned_check); + } } void ExternalCommandProcessor::ScheduleForcedHostCheck(double, const vector& arguments) @@ -246,8 +259,13 @@ void ExternalCommandProcessor::ScheduleForcedHostCheck(double, const vectorGetHostCheckService(); Logger::Write(LogInformation, "icinga", "Rescheduling next check for host '" + arguments[0] + "'"); - hc->SetForceNextCheck(true); - hc->SetNextCheck(Convert::ToDouble(arguments[1])); + + { + ObjectLock olock(hc); + + hc->SetForceNextCheck(true); + hc->SetNextCheck(Convert::ToDouble(arguments[1])); + } } void ExternalCommandProcessor::ScheduleSvcCheck(double, const vector& arguments) @@ -266,7 +284,12 @@ void ExternalCommandProcessor::ScheduleSvcCheck(double, const vector& ar } Logger::Write(LogInformation, "icinga", "Rescheduling next check for service '" + arguments[1] + "'"); - service->SetNextCheck(planned_check); + + { + ObjectLock olock(service); + + service->SetNextCheck(planned_check); + } } void ExternalCommandProcessor::ScheduleForcedSvcCheck(double, const vector& arguments) @@ -277,8 +300,13 @@ void ExternalCommandProcessor::ScheduleForcedSvcCheck(double, const vectorSetForceNextCheck(true); - service->SetNextCheck(Convert::ToDouble(arguments[2])); + + { + ObjectLock olock(service); + + service->SetForceNextCheck(true); + service->SetNextCheck(Convert::ToDouble(arguments[2])); + } } void ExternalCommandProcessor::EnableHostCheck(double, const vector& arguments) @@ -291,8 +319,14 @@ void ExternalCommandProcessor::EnableHostCheck(double, const vector& arg Logger::Write(LogInformation, "icinga", "Enabling active checks for host '" + arguments[0] + "'"); Service::Ptr hc = host->GetHostCheckService(); - if (hc) + if (!hc) + return; + + { + ObjectLock olock(hc); + hc->SetEnableActiveChecks(true); + } } void ExternalCommandProcessor::DisableHostCheck(double, const vector& arguments) @@ -305,8 +339,14 @@ void ExternalCommandProcessor::DisableHostCheck(double, const vector& ar Logger::Write(LogInformation, "icinga", "Disabling active checks for host '" + arguments[0] + "'"); Service::Ptr hc = host->GetHostCheckService(); - if (hc) + if (!hc) + return; + + { + ObjectLock olock(hc); + hc->SetEnableActiveChecks(false); + } } void ExternalCommandProcessor::EnableSvcCheck(double, const vector& arguments) @@ -317,7 +357,12 @@ void ExternalCommandProcessor::EnableSvcCheck(double, const vector& argu Service::Ptr service = Service::GetByNamePair(arguments[0], arguments[1]); Logger::Write(LogInformation, "icinga", "Enabling active checks for service '" + arguments[1] + "'"); - service->SetEnableActiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(true); + } } void ExternalCommandProcessor::DisableSvcCheck(double, const vector& arguments) @@ -328,7 +373,12 @@ void ExternalCommandProcessor::DisableSvcCheck(double, const vector& arg Service::Ptr service = Service::GetByNamePair(arguments[0], arguments[1]); Logger::Write(LogInformation, "icinga", "Disabling active checks for service '" + arguments[1] + "'"); - service->SetEnableActiveChecks(false); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(false); + } } void ExternalCommandProcessor::ShutdownProcess(double, const vector&) @@ -348,8 +398,13 @@ void ExternalCommandProcessor::ScheduleForcedHostSvcChecks(double, const vector< BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) { Logger::Write(LogInformation, "icinga", "Rescheduling next check for service '" + service->GetName() + "'"); - service->SetNextCheck(planned_check); - service->SetForceNextCheck(true); + + { + ObjectLock olock(service); + + service->SetNextCheck(planned_check); + service->SetForceNextCheck(true); + } } } @@ -370,7 +425,12 @@ void ExternalCommandProcessor::ScheduleHostSvcChecks(double, const vectorGetName() + "'"); - service->SetNextCheck(planned_check); + + { + ObjectLock olock(service); + + service->SetNextCheck(planned_check); + } } } @@ -396,7 +456,12 @@ void ExternalCommandProcessor::DisableHostSvcChecks(double, const vector BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) { Logger::Write(LogInformation, "icinga", "Disabling active checks for service '" + service->GetName() + "'"); - service->SetEnableActiveChecks(false); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(false); + } } } @@ -413,7 +478,12 @@ void ExternalCommandProcessor::AcknowledgeSvcProblem(double, const vectorGetName() + "'"); - service->AcknowledgeProblem(sticky ? AcknowledgementSticky : AcknowledgementNormal); + + { + ObjectLock olock(service); + + service->AcknowledgeProblem(sticky ? AcknowledgementSticky : AcknowledgementNormal); + } } void ExternalCommandProcessor::AcknowledgeSvcProblemExpire(double, const vector& arguments) @@ -430,7 +500,12 @@ void ExternalCommandProcessor::AcknowledgeSvcProblemExpire(double, const vector< BOOST_THROW_EXCEPTION(invalid_argument("The service '" + arguments[1] + "' is OK.")); Logger::Write(LogInformation, "icinga", "Setting timed acknowledgement for service '" + service->GetName() + "'"); - service->AcknowledgeProblem(sticky ? AcknowledgementSticky : AcknowledgementNormal, timestamp); + + { + ObjectLock olock(service); + + service->AcknowledgeProblem(sticky ? AcknowledgementSticky : AcknowledgementNormal, timestamp); + } } void ExternalCommandProcessor::RemoveSvcAcknowledgement(double, const vector& arguments) @@ -441,7 +516,12 @@ void ExternalCommandProcessor::RemoveSvcAcknowledgement(double, const vectorGetName() + "'"); - service->ClearAcknowledgement(); + + { + ObjectLock olock(service); + + service->ClearAcknowledgement(); + } } void ExternalCommandProcessor::AcknowledgeHostProblem(double, const vector& arguments) @@ -456,6 +536,8 @@ void ExternalCommandProcessor::AcknowledgeHostProblem(double, const vectorGetName() + "'"); Service::Ptr service = host->GetHostCheckService(); if (service) { + ObjectLock olock(service); + if (service->GetState() == StateOK) BOOST_THROW_EXCEPTION(invalid_argument("The host '" + arguments[0] + "' is OK.")); @@ -476,6 +558,8 @@ void ExternalCommandProcessor::AcknowledgeHostProblemExpire(double, const vector Logger::Write(LogInformation, "icinga", "Setting timed acknowledgement for host '" + host->GetName() + "'"); Service::Ptr service = host->GetHostCheckService(); if (service) { + ObjectLock olock(service); + if (service->GetState() == StateOK) BOOST_THROW_EXCEPTION(invalid_argument("The host '" + arguments[0] + "' is OK.")); @@ -493,6 +577,8 @@ void ExternalCommandProcessor::RemoveHostAcknowledgement(double, const vectorGetName() + "'"); Service::Ptr service = host->GetHostCheckService(); if (service) { + ObjectLock olock(service); + service->ClearAcknowledgement(); } } @@ -507,7 +593,12 @@ void ExternalCommandProcessor::EnableHostgroupSvcChecks(double, const vectorGetMembers()) { BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) { Logger::Write(LogInformation, "icinga", "Enabling active checks for service '" + service->GetName() + "'"); - service->SetEnableActiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(true); + } } } } @@ -522,7 +613,12 @@ void ExternalCommandProcessor::DisableHostgroupSvcChecks(double, const vectorGetMembers()) { BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) { Logger::Write(LogInformation, "icinga", "Disabling active checks for service '" + service->GetName() + "'"); - service->SetEnableActiveChecks(false); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(false); + } } } } @@ -536,7 +632,12 @@ void ExternalCommandProcessor::EnableServicegroupSvcChecks(double, const vector< BOOST_FOREACH(const Service::Ptr& service, sg->GetMembers()) { Logger::Write(LogInformation, "icinga", "Enabling active checks for service '" + service->GetName() + "'"); - service->SetEnableActiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(true); + } } } @@ -549,7 +650,12 @@ void ExternalCommandProcessor::DisableServicegroupSvcChecks(double, const vector BOOST_FOREACH(const Service::Ptr& service, sg->GetMembers()) { Logger::Write(LogInformation, "icinga", "Disabling active checks for service '" + service->GetName() + "'"); - service->SetEnableActiveChecks(false); + + { + ObjectLock olock(service); + + service->SetEnableActiveChecks(false); + } } } @@ -563,8 +669,14 @@ void ExternalCommandProcessor::EnablePassiveHostChecks(double, const vectorGetHostCheckService(); - if (hc) + if (!hc) + return; + + { + ObjectLock olock(hc); + hc->SetEnablePassiveChecks(true); + } } void ExternalCommandProcessor::DisablePassiveHostChecks(double, const vector& arguments) @@ -577,8 +689,14 @@ void ExternalCommandProcessor::DisablePassiveHostChecks(double, const vectorGetHostCheckService(); - if (hc) + if (!hc) + return; + + { + ObjectLock olock(hc); + hc->SetEnablePassiveChecks(false); + } } void ExternalCommandProcessor::EnablePassiveSvcChecks(double, const vector& arguments) @@ -589,7 +707,12 @@ void ExternalCommandProcessor::EnablePassiveSvcChecks(double, const vectorSetEnablePassiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnablePassiveChecks(true); + } } void ExternalCommandProcessor::DisablePassiveSvcChecks(double, const vector& arguments) @@ -600,7 +723,12 @@ void ExternalCommandProcessor::DisablePassiveSvcChecks(double, const vectorSetEnablePassiveChecks(false); + + { + ObjectLock olock(service); + + service->SetEnablePassiveChecks(false); + } } void ExternalCommandProcessor::EnableServicegroupPassiveSvcChecks(double, const vector& arguments) @@ -612,7 +740,12 @@ void ExternalCommandProcessor::EnableServicegroupPassiveSvcChecks(double, const BOOST_FOREACH(const Service::Ptr& service, sg->GetMembers()) { Logger::Write(LogInformation, "icinga", "Enabling passive checks for service '" + service->GetName() + "'"); - service->SetEnablePassiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnablePassiveChecks(true); + } } } @@ -625,7 +758,12 @@ void ExternalCommandProcessor::DisableServicegroupPassiveSvcChecks(double, const BOOST_FOREACH(const Service::Ptr& service, sg->GetMembers()) { Logger::Write(LogInformation, "icinga", "Disabling passive checks for service '" + service->GetName() + "'"); - service->SetEnablePassiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnablePassiveChecks(true); + } } } @@ -639,7 +777,12 @@ void ExternalCommandProcessor::EnableHostgroupPassiveSvcChecks(double, const vec BOOST_FOREACH(const Host::Ptr& host, hg->GetMembers()) { BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) { Logger::Write(LogInformation, "icinga", "Enabling passive checks for service '" + service->GetName() + "'"); - service->SetEnablePassiveChecks(true); + + { + ObjectLock olock(service); + + service->SetEnablePassiveChecks(true); + } } } } @@ -654,7 +797,12 @@ void ExternalCommandProcessor::DisableHostgroupPassiveSvcChecks(double, const ve BOOST_FOREACH(const Host::Ptr& host, hg->GetMembers()) { BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) { Logger::Write(LogInformation, "icinga", "Disabling passive checks for service '" + service->GetName() + "'"); - service->SetEnablePassiveChecks(false); + + { + ObjectLock olock(service); + + service->SetEnablePassiveChecks(false); + } } } } @@ -962,9 +1110,8 @@ void ExternalCommandProcessor::SendCustomHostNotification(double time, const vec Logger::Write(LogInformation, "icinga", "Sending custom notification for host " + host->GetName()); Service::Ptr service = host->GetHostCheckService(); - if (service) { + if (service) service->RequestNotifications(NotificationCustom); - } } void ExternalCommandProcessor::SendCustomSvcNotification(double time, const vector& arguments) @@ -986,9 +1133,14 @@ void ExternalCommandProcessor::DelayHostNotification(double time, const vectorGetName()); - Service::Ptr service = host->GetHostCheckService(); - if (service) { - service->SetLastNotification(Convert::ToDouble(arguments[1])); + Service::Ptr hc = host->GetHostCheckService(); + if (!hc) + return; + + { + ObjectLock olock(hc); + + hc->SetLastNotification(Convert::ToDouble(arguments[1])); } } @@ -1000,7 +1152,12 @@ void ExternalCommandProcessor::DelaySvcNotification(double time, const vectorGetName()); - service->SetLastNotification(Convert::ToDouble(arguments[2])); + + { + ObjectLock olock(service); + + service->SetLastNotification(Convert::ToDouble(arguments[2])); + } } void ExternalCommandProcessor::EnableHostNotifications(double, const vector& arguments) @@ -1013,8 +1170,14 @@ void ExternalCommandProcessor::EnableHostNotifications(double, const vectorGetHostCheckService(); - if (hc) + if (!hc) + return; + + { + ObjectLock olock(hc); + hc->SetEnableNotifications(true); + } } void ExternalCommandProcessor::DisableHostNotifications(double, const vector& arguments) @@ -1027,8 +1190,14 @@ void ExternalCommandProcessor::DisableHostNotifications(double, const vectorGetHostCheckService(); - if (hc) + if (!hc) + return; + + { + ObjectLock olock(hc); + hc->SetEnableNotifications(false); + } } void ExternalCommandProcessor::EnableSvcNotifications(double, const vector& arguments) @@ -1039,7 +1208,12 @@ void ExternalCommandProcessor::EnableSvcNotifications(double, const vectorSetEnableNotifications(true); + + { + ObjectLock olock(service); + + service->SetEnableNotifications(true); + } } void ExternalCommandProcessor::DisableSvcNotifications(double, const vector& arguments) @@ -1050,5 +1224,10 @@ void ExternalCommandProcessor::DisableSvcNotifications(double, const vectorSetEnableNotifications(false); + + { + ObjectLock olock(service); + + service->SetEnableNotifications(false); + } } diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 60be41e92..69b762af4 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -23,7 +23,8 @@ using namespace icinga; boost::mutex Host::m_ServiceMutex; map > Host::m_ServicesCache; -bool Host::m_ServicesCacheValid = true; +bool Host::m_ServicesCacheNeedsUpdate = false; +Timer::Ptr Host::m_ServicesCacheTimer; REGISTER_SCRIPTFUNCTION("ValidateServiceDictionary", &Host::ValidateServiceDictionary); @@ -134,7 +135,7 @@ bool Host::IsReachable(void) const set parentHosts = GetParentHosts(); BOOST_FOREACH(const Host::Ptr& host, parentHosts) { - Service::Ptr hc = GetHostCheckService(); + Service::Ptr hc = host->GetHostCheckService(); /* ignore hosts that don't have a hostcheck */ if (!hc) @@ -325,10 +326,17 @@ void Host::InvalidateServicesCache(void) { boost::mutex::scoped_lock lock(m_ServiceMutex); - if (m_ServicesCacheValid) - Utility::QueueAsyncCallback(boost::bind(&Host::RefreshServicesCache)); + if (m_ServicesCacheNeedsUpdate) + return; /* Someone else has already requested a refresh. */ - m_ServicesCacheValid = false; + if (!m_ServicesCacheTimer) { + m_ServicesCacheTimer = boost::make_shared(); + m_ServicesCacheTimer->SetInterval(0.5); + m_ServicesCacheTimer->OnTimerExpired.connect(boost::bind(&Host::RefreshServicesCache)); + } + + m_ServicesCacheTimer->Start(); + m_ServicesCacheNeedsUpdate = true; } } @@ -337,12 +345,13 @@ void Host::RefreshServicesCache(void) { boost::mutex::scoped_lock lock(m_ServiceMutex); - if (m_ServicesCacheValid) - return; - - m_ServicesCacheValid = true; + assert(m_ServicesCacheNeedsUpdate); + m_ServicesCacheTimer->Stop(); + m_ServicesCacheNeedsUpdate = false; } + Logger::Write(LogInformation, "icinga", "Updating services cache."); + map > newServicesCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { diff --git a/lib/icinga/host.h b/lib/icinga/host.h index ac57ceca0..b76a8e7c1 100644 --- a/lib/icinga/host.h +++ b/lib/icinga/host.h @@ -80,7 +80,8 @@ private: static boost::mutex m_ServiceMutex; static map > > m_ServicesCache; - static bool m_ServicesCacheValid; + static bool m_ServicesCacheNeedsUpdate; + static Timer::Ptr m_ServicesCacheTimer; void UpdateSlaveServices(void); diff --git a/lib/icinga/hostgroup.cpp b/lib/icinga/hostgroup.cpp index e259d445b..a769736a4 100644 --- a/lib/icinga/hostgroup.cpp +++ b/lib/icinga/hostgroup.cpp @@ -23,7 +23,8 @@ using namespace icinga; boost::mutex HostGroup::m_Mutex; map > HostGroup::m_MembersCache; -bool HostGroup::m_MembersCacheValid = true; +bool HostGroup::m_MembersCacheNeedsUpdate = false; +Timer::Ptr HostGroup::m_MembersCacheTimer; REGISTER_TYPE(HostGroup); @@ -120,10 +121,17 @@ void HostGroup::InvalidateMembersCache(void) { boost::mutex::scoped_lock lock(m_Mutex); - if (m_MembersCacheValid) - Utility::QueueAsyncCallback(boost::bind(&HostGroup::RefreshMembersCache)); + if (m_MembersCacheNeedsUpdate) + return; /* Someone else has already requested a refresh. */ - m_MembersCacheValid = false; + if (!m_MembersCacheTimer) { + m_MembersCacheTimer = boost::make_shared(); + m_MembersCacheTimer->SetInterval(0.5); + m_MembersCacheTimer->OnTimerExpired.connect(boost::bind(&HostGroup::RefreshMembersCache)); + } + + m_MembersCacheTimer->Start(); + m_MembersCacheNeedsUpdate = true; } /** @@ -134,10 +142,9 @@ void HostGroup::RefreshMembersCache(void) { boost::mutex::scoped_lock lock(m_Mutex); - if (m_MembersCacheValid) - return; - - m_MembersCacheValid = true; + assert(m_MembersCacheNeedsUpdate); + m_MembersCacheTimer->Stop(); + m_MembersCacheNeedsUpdate = false; } map > newMembersCache; diff --git a/lib/icinga/hostgroup.h b/lib/icinga/hostgroup.h index 3528d1b8d..f51bff919 100644 --- a/lib/icinga/hostgroup.h +++ b/lib/icinga/hostgroup.h @@ -57,7 +57,8 @@ private: static boost::mutex m_Mutex; static map > m_MembersCache; - static bool m_MembersCacheValid; + static bool m_MembersCacheNeedsUpdate; + static Timer::Ptr m_MembersCacheTimer; static void RefreshMembersCache(void); }; diff --git a/lib/icinga/pluginnotificationtask.cpp b/lib/icinga/pluginnotificationtask.cpp index d83b27e0e..305b97426 100644 --- a/lib/icinga/pluginnotificationtask.cpp +++ b/lib/icinga/pluginnotificationtask.cpp @@ -44,7 +44,7 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vecto Notification::Ptr notification = arguments[0]; Dictionary::Ptr macros = arguments[1]; - NotificationType type = static_cast(static_cast(arguments[2])); +// NotificationType type = static_cast(static_cast(arguments[2])); Value raw_command = notification->GetNotificationCommand(); diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index 75faa7892..3edc315df 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -334,6 +334,8 @@ void Service::SetForceNextCheck(bool forced) */ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) { + bool reachable = IsReachable(); + assert(!OwnsLock()); ObjectLock olock(this); @@ -413,6 +415,8 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) Service::UpdateStatistics(cr); + bool send_notification = hardChange && reachable && !IsInDowntime() && !IsAcknowledged(); + olock.Unlock(); /* Flush the object so other instances see the service's @@ -431,7 +435,7 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) EndpointManager::GetInstance()->SendMulticastMessage(rm); - if (hardChange && IsReachable() && !IsInDowntime() && !IsAcknowledged()) + if (send_notification) RequestNotifications(recovery ? NotificationRecovery : NotificationProblem); } @@ -521,16 +525,21 @@ bool Service::IsAllowedChecker(const String& checker) const void Service::BeginExecuteCheck(const function& callback) { assert(!OwnsLock()); - ObjectLock olock(this); - /* don't run another check if there is one pending */ - if (m_CurrentTask) { - olock.Unlock(); + { + ObjectLock olock(this); - /* we need to call the callback anyway */ - callback(); + /* don't run another check if there is one pending */ + if (m_CheckRunning) { + olock.Unlock(); - return; + /* we need to call the callback anyway */ + callback(); + + return; + } + + m_CheckRunning = true; } /* keep track of scheduling info in case the check type doesn't provide its own information */ @@ -545,12 +554,12 @@ void Service::BeginExecuteCheck(const function& callback) Host::Ptr host = GetHost(); - olock.Unlock(); - macroDicts.push_back(CalculateDynamicMacros()); - macroDicts.push_back(host->GetMacros()); - macroDicts.push_back(host->CalculateDynamicMacros()); + if (host) { + macroDicts.push_back(host->GetMacros()); + macroDicts.push_back(host->CalculateDynamicMacros()); + } IcingaApplication::Ptr app = IcingaApplication::GetInstance(); macroDicts.push_back(app->GetMacros()); @@ -570,7 +579,7 @@ void Service::BeginExecuteCheck(const function& callback) ScriptTask::Ptr task = MakeMethodTask("check", arguments); { - ObjectLock slock(this); + ObjectLock olock(this); self->m_CurrentTask = task; } @@ -642,6 +651,7 @@ void Service::CheckCompletedHandler(const Dictionary::Ptr& checkInfo, { ObjectLock olock(this); m_CurrentTask.reset(); + m_CheckRunning = false; } /* figure out when the next check is for this service; the call to diff --git a/lib/icinga/service-comment.cpp b/lib/icinga/service-comment.cpp index 70542fce7..4bc38547f 100644 --- a/lib/icinga/service-comment.cpp +++ b/lib/icinga/service-comment.cpp @@ -25,7 +25,8 @@ int Service::m_NextCommentID = 1; boost::mutex Service::m_CommentMutex; map Service::m_LegacyCommentsCache; map Service::m_CommentsCache; -bool Service::m_CommentsCacheValid = true; +bool Service::m_CommentsCacheNeedsUpdate = false; +Timer::Ptr Service::m_CommentsCacheTimer; Timer::Ptr Service::m_CommentsExpireTimer; /** @@ -176,10 +177,17 @@ void Service::InvalidateCommentsCache(void) { boost::mutex::scoped_lock lock(m_CommentMutex); - if (m_CommentsCacheValid) - Utility::QueueAsyncCallback(boost::bind(&Service::RefreshCommentsCache)); + if (m_CommentsCacheNeedsUpdate) + return; /* Someone else has already requested a refresh. */ - m_CommentsCacheValid = false; + if (!m_CommentsCacheTimer) { + m_CommentsCacheTimer = boost::make_shared(); + m_CommentsCacheTimer->SetInterval(0.5); + m_CommentsCacheTimer->OnTimerExpired.connect(boost::bind(&Service::RefreshCommentsCache)); + } + + m_CommentsCacheTimer->Start(); + m_CommentsCacheNeedsUpdate = true; } /** @@ -190,10 +198,9 @@ void Service::RefreshCommentsCache(void) { boost::mutex::scoped_lock lock(m_CommentMutex); - if (m_CommentsCacheValid) - return; - - m_CommentsCacheValid = true; + assert(m_CommentsCacheNeedsUpdate); + m_CommentsCacheTimer->Stop(); + m_CommentsCacheNeedsUpdate = false; } map newLegacyCommentsCache; diff --git a/lib/icinga/service-downtime.cpp b/lib/icinga/service-downtime.cpp index a2598c38a..de4387cfc 100644 --- a/lib/icinga/service-downtime.cpp +++ b/lib/icinga/service-downtime.cpp @@ -25,7 +25,8 @@ int Service::m_NextDowntimeID = 1; boost::mutex Service::m_DowntimeMutex; map Service::m_LegacyDowntimesCache; map Service::m_DowntimesCache; -bool Service::m_DowntimesCacheValid = true; +bool Service::m_DowntimesCacheNeedsUpdate = false; +Timer::Ptr Service::m_DowntimesCacheTimer; Timer::Ptr Service::m_DowntimesExpireTimer; /** @@ -250,10 +251,17 @@ void Service::InvalidateDowntimesCache(void) { boost::mutex::scoped_lock lock(m_DowntimeMutex); - if (m_DowntimesCacheValid) - Utility::QueueAsyncCallback(boost::bind(&Service::RefreshDowntimesCache)); + if (m_DowntimesCacheNeedsUpdate) + return; /* Someone else has already requested a refresh. */ - m_DowntimesCacheValid = false; + if (!m_DowntimesCacheTimer) { + m_DowntimesCacheTimer = boost::make_shared(); + m_DowntimesCacheTimer->SetInterval(0.5); + m_DowntimesCacheTimer->OnTimerExpired.connect(boost::bind(&Service::RefreshNotificationsCache)); + } + + m_DowntimesCacheTimer->Start(); + m_DowntimesCacheNeedsUpdate = true; } /** @@ -264,10 +272,9 @@ void Service::RefreshDowntimesCache(void) { boost::mutex::scoped_lock lock(m_DowntimeMutex); - if (m_DowntimesCacheValid) - return; - - m_DowntimesCacheValid = true; + assert(m_DowntimesCacheNeedsUpdate); + m_DowntimesCacheTimer->Stop(); + m_DowntimesCacheNeedsUpdate = false; } map newLegacyDowntimesCache; diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp index f2247be31..1c4ff329b 100644 --- a/lib/icinga/service-notification.cpp +++ b/lib/icinga/service-notification.cpp @@ -23,14 +23,18 @@ using namespace icinga; boost::mutex Service::m_NotificationMutex; map > Service::m_NotificationsCache; -bool Service::m_NotificationsCacheValid = true; +bool Service::m_NotificationsCacheNeedsUpdate = false; +Timer::Ptr Service::m_NotificationsCacheTimer; /** * @threadsafety Always. */ void Service::RequestNotifications(NotificationType type) { - SetLastNotification(Utility::GetTime()); + { + ObjectLock olock(this); + SetLastNotification(Utility::GetTime()); + } RequestMessage msg; msg.SetMethod("icinga::SendNotifications"); @@ -83,10 +87,17 @@ void Service::InvalidateNotificationsCache(void) { boost::mutex::scoped_lock lock(m_NotificationMutex); - if (m_NotificationsCacheValid) - Utility::QueueAsyncCallback(boost::bind(&Service::RefreshNotificationsCache)); + if (m_NotificationsCacheNeedsUpdate) + return; /* Someone else has already requested a refresh. */ - m_NotificationsCacheValid = false; + if (!m_NotificationsCacheTimer) { + m_NotificationsCacheTimer = boost::make_shared(); + m_NotificationsCacheTimer->SetInterval(0.5); + m_NotificationsCacheTimer->OnTimerExpired.connect(boost::bind(&Service::RefreshNotificationsCache)); + } + + m_NotificationsCacheTimer->Start(); + m_NotificationsCacheNeedsUpdate = true; } /** @@ -97,10 +108,9 @@ void Service::RefreshNotificationsCache(void) { boost::mutex::scoped_lock lock(m_NotificationMutex); - if (m_NotificationsCacheValid) - return; - - m_NotificationsCacheValid = true; + assert(m_NotificationsCacheNeedsUpdate); + m_NotificationsCacheTimer->Stop(); + m_NotificationsCacheNeedsUpdate = false; } map > newNotificationsCache; diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 31f8766f0..cdce9cff9 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -24,7 +24,7 @@ using namespace icinga; REGISTER_TYPE(Service); Service::Service(const Dictionary::Ptr& serializedObject) - : DynamicObject(serializedObject) + : DynamicObject(serializedObject), m_CheckRunning(false) { RegisterAttribute("display_name", Attribute_Config, &m_DisplayName); @@ -106,9 +106,6 @@ Service::Ptr Service::GetByName(const String& name) { DynamicObject::Ptr configObject = DynamicObject::GetObject("Service", name); - if (!configObject) - BOOST_THROW_EXCEPTION(invalid_argument("Service '" + name + "' does not exist.")); - return dynamic_pointer_cast(configObject); } @@ -250,6 +247,8 @@ bool Service::IsReachable(void) const */ AcknowledgementType Service::GetAcknowledgement(void) { + assert(OwnsLock()); + if (m_Acknowledgement.IsEmpty()) return AcknowledgementNone; diff --git a/lib/icinga/service.h b/lib/icinga/service.h index 24b41f11e..b26f03809 100644 --- a/lib/icinga/service.h +++ b/lib/icinga/service.h @@ -284,6 +284,7 @@ private: Attribute m_ForceNextCheck; ScriptTask::Ptr m_CurrentTask; + bool m_CheckRunning; long m_SchedulingOffset; void CheckCompletedHandler(const Dictionary::Ptr& checkInfo, @@ -297,7 +298,8 @@ private: static boost::mutex m_DowntimeMutex; static map m_LegacyDowntimesCache; static map m_DowntimesCache; - static bool m_DowntimesCacheValid; + static bool m_DowntimesCacheNeedsUpdate; + static Timer::Ptr m_DowntimesCacheTimer; static Timer::Ptr m_DowntimesExpireTimer; static void DowntimesExpireTimerHandler(void); @@ -314,7 +316,8 @@ private: static boost::mutex m_CommentMutex; static map m_LegacyCommentsCache; static map m_CommentsCache; - static bool m_CommentsCacheValid; + static bool m_CommentsCacheNeedsUpdate; + static Timer::Ptr m_CommentsCacheTimer; static Timer::Ptr m_CommentsExpireTimer; static void CommentsExpireTimerHandler(void); @@ -331,7 +334,8 @@ private: static boost::mutex m_NotificationMutex; static map > m_NotificationsCache; - static bool m_NotificationsCacheValid; + static bool m_NotificationsCacheNeedsUpdate; + static Timer::Ptr m_NotificationsCacheTimer; static void RefreshNotificationsCache(void); }; diff --git a/lib/icinga/servicegroup.cpp b/lib/icinga/servicegroup.cpp index ed668d8fd..68eae8725 100644 --- a/lib/icinga/servicegroup.cpp +++ b/lib/icinga/servicegroup.cpp @@ -23,7 +23,8 @@ using namespace icinga; boost::mutex ServiceGroup::m_Mutex; map > ServiceGroup::m_MembersCache; -bool ServiceGroup::m_MembersCacheValid = true; +bool ServiceGroup::m_MembersCacheNeedsUpdate = false; +Timer::Ptr ServiceGroup::m_MembersCacheTimer; REGISTER_TYPE(ServiceGroup); @@ -120,10 +121,17 @@ void ServiceGroup::InvalidateMembersCache(void) { boost::mutex::scoped_lock lock(m_Mutex); - if (m_MembersCacheValid) - Utility::QueueAsyncCallback(boost::bind(&ServiceGroup::RefreshMembersCache)); + if (m_MembersCacheNeedsUpdate) + return; /* Someone else has already requested a refresh. */ - m_MembersCacheValid = false; + if (!m_MembersCacheTimer) { + m_MembersCacheTimer = boost::make_shared(); + m_MembersCacheTimer->SetInterval(0.5); + m_MembersCacheTimer->OnTimerExpired.connect(boost::bind(&ServiceGroup::RefreshMembersCache)); + } + + m_MembersCacheTimer->Start(); + m_MembersCacheNeedsUpdate = true; } /** @@ -134,10 +142,9 @@ void ServiceGroup::RefreshMembersCache(void) { boost::mutex::scoped_lock lock(m_Mutex); - if (m_MembersCacheValid) - return; - - m_MembersCacheValid = true; + assert(m_MembersCacheNeedsUpdate); + m_MembersCacheTimer->Stop(); + m_MembersCacheNeedsUpdate = false; } map > newMembersCache; diff --git a/lib/icinga/servicegroup.h b/lib/icinga/servicegroup.h index 6451eacad..39d1d210c 100644 --- a/lib/icinga/servicegroup.h +++ b/lib/icinga/servicegroup.h @@ -57,7 +57,8 @@ private: static boost::mutex m_Mutex; static map > m_MembersCache; - static bool m_MembersCacheValid; + static bool m_MembersCacheNeedsUpdate; + static Timer::Ptr m_MembersCacheTimer; static void RefreshMembersCache(void); };