diff --git a/base/asynctask.cpp b/base/asynctask.cpp index 229aed342..c7c0ffe42 100644 --- a/base/asynctask.cpp +++ b/base/asynctask.cpp @@ -1,53 +1,81 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - #include "i2-base.h" using namespace icinga; -AsyncTask::AsyncTask(const AsyncTask::CompletionCallback& completionCallback) - : m_Finished(false), m_CompletionCallback(completionCallback) +/** + * Constructor for the AsyncTaskBase class. + */ +AsyncTaskBase::AsyncTaskBase(void) + : m_Finished(false), m_ResultRetrieved(false) { } -AsyncTask::~AsyncTask(void) +/** + * Destructor for the AsyncTaskBase class. + */ +AsyncTaskBase::~AsyncTaskBase(void) { if (!m_Finished) { Logger::Write(LogCritical, "base", "Contract violation: " - "AsyncTask was destroyed before its completion callback was invoked."); + "AsyncTask was destroyed before its completion callback was invoked."); + } else if (!m_ResultRetrieved) { + Logger::Write(LogCritical, "base", "Contract violation: " + "AsyncTask was destroyed before its result was retrieved."); } } -void AsyncTask::Start(void) +/** + * Starts the async task. The caller must hold a reference to the AsyncTask + * object until the completion callback is invoked. + */ +void AsyncTaskBase::Start(void) { assert(Application::IsMainThread()); - Run(); + CallWithExceptionGuard(boost::bind(&AsyncTaskBase::Run, this)); } -void AsyncTask::Finish(void) +/** + * Finishes the task using an exception. + * + * @param ex The exception. + */ +void AsyncTaskBase::Finish(const boost::exception_ptr& ex) { - Event::Post(boost::bind(&AsyncTask::ForwardCallback, static_cast(GetSelf()))); + m_Exception = ex; + + FinishInternal(); } -void AsyncTask::ForwardCallback(void) +/** + * Finishes the task and causes the completion callback to be invoked. This + * function must be called before the object is destroyed. + */ +void AsyncTaskBase::FinishInternal(void) { - m_CompletionCallback(GetSelf()); - m_CompletionCallback = CompletionCallback(); - m_Finished = true; + assert(!m_Finished); + + Event::Post(boost::bind(&AsyncTaskBase::InvokeCompletionCallback, + static_cast(GetSelf()))); +} + +/** + * Invokes the provided callback function and catches any exceptions it throws. + * Exceptions are stored into the task so that they can be re-thrown when the + * task owner calls GetResult(). + * + * @param task The task where exceptions should be saved. + * @param function The function that should be invoked. + * @returns true if no exception occured, false otherwise. + */ +bool AsyncTaskBase::CallWithExceptionGuard(function function) +{ + try { + function(); + + return true; + } catch (const exception&) { + Finish(boost::current_exception()); + + return false; + } } diff --git a/base/asynctask.h b/base/asynctask.h index 762c037c3..7569cfab8 100644 --- a/base/asynctask.h +++ b/base/asynctask.h @@ -23,29 +23,98 @@ namespace icinga { -class I2_BASE_API AsyncTask : public Object +class I2_BASE_API AsyncTaskBase : public Object { public: - typedef shared_ptr Ptr; - typedef weak_ptr WeakPtr; + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; - typedef function CompletionCallback; - - AsyncTask(const CompletionCallback& completionCallback); - ~AsyncTask(void); + AsyncTaskBase(void); + ~AsyncTaskBase(void); void Start(void); + void Finish(const boost::exception_ptr& ex); - void Finish(void); + bool CallWithExceptionGuard(function function); protected: virtual void Run(void) = 0; -private: - void ForwardCallback(void); + virtual void InvokeCompletionCallback(void) = 0; - bool m_Finished; - CompletionCallback m_CompletionCallback; + void FinishInternal(void); + + bool m_Finished; /**< Whether the task is finished. */ + bool m_ResultRetrieved; /**< Whether the result was retrieved. */ + boost::exception_ptr m_Exception; +}; + +/** + * An asynchronous task. + * + * @ingroup base + */ + template +class AsyncTask : public AsyncTaskBase +{ +public: + typedef shared_ptr > Ptr; + typedef weak_ptr > WeakPtr; + + typedef function&)> CompletionCallback; + + /** + * Constructor for the AsyncTask class. + * + * @param completionCallback Function that is called when the task is completed. + */ + AsyncTask(const CompletionCallback& completionCallback) + : m_CompletionCallback(completionCallback) + { } + + /** + * Retrieves the result of the task. Throws an exception if one is stored in + * the AsyncTask object. + * + * @returns The task's result. + */ + TResult GetResult(void) + { + if (!m_Finished) + throw runtime_error("GetResult called on an unfinished AsyncTask"); + + if (m_ResultRetrieved) + throw runtime_error("GetResult called on an AsyncTask whose result was already retrieved."); + + if (m_Exception) + boost::rethrow_exception(m_Exception); + + return m_Result; + } + + void Finish(const TResult& result) + { + m_Result = result; + FinishInternal(); + } + +private: + /** + * Used by the Finish method to proxy the completion callback into the main + * thread. Invokes the completion callback and marks the task as finished. + */ + virtual void InvokeCompletionCallback(void) + { + m_Finished = true; + m_CompletionCallback(GetSelf()); + + /* Clear callback because the bound function might hold a + * reference to this task. */ + m_CompletionCallback = CompletionCallback(); + } + + CompletionCallback m_CompletionCallback; /**< The completion callback. */ + TResult m_Result; /**< The task's result. */ }; } diff --git a/base/base.vcxproj.filters b/base/base.vcxproj.filters index b033e23c3..d871a78cf 100644 --- a/base/base.vcxproj.filters +++ b/base/base.vcxproj.filters @@ -79,15 +79,15 @@ Quelldateien - - Quelldateien - Quelldateien Quelldateien + + Quelldateien + diff --git a/base/configobject.cpp b/base/configobject.cpp index 14c526ba6..bd4f56577 100644 --- a/base/configobject.cpp +++ b/base/configobject.cpp @@ -198,7 +198,7 @@ void ConfigObject::RemoveTag(const string& key) } ScriptTask::Ptr ConfigObject::InvokeHook(const string& hook, - const vector& arguments, AsyncTask::CompletionCallback callback) + const vector& arguments, ScriptTask::CompletionCallback callback) { Dictionary::Ptr hooks; string funcName; diff --git a/base/configobject.h b/base/configobject.h index 293feda81..0e9f2213c 100644 --- a/base/configobject.h +++ b/base/configobject.h @@ -66,7 +66,7 @@ public: void RemoveTag(const string& key); ScriptTask::Ptr InvokeHook(const string& hook, - const vector& arguments, AsyncTask::CompletionCallback callback); + const vector& arguments, ScriptTask::CompletionCallback callback); string GetType(void) const; string GetName(void) const; diff --git a/base/process.cpp b/base/process.cpp index 23bde9c51..7e92b1cc2 100644 --- a/base/process.cpp +++ b/base/process.cpp @@ -97,7 +97,7 @@ void Process::WorkerThreadProc(void) it++; tasks.erase(prev); - task->Finish(); + task->Finish(task->m_Result); } else { it++; } @@ -112,9 +112,7 @@ void Process::WorkerThreadProc(void) lock.unlock(); - if (!task->InitTask()) { - task->Finish(); - } else { + if (task->CallWithExceptionGuard(boost::bind(&Process::InitTask, task))) { int fd = task->GetFD(); if (fd >= 0) tasks[fd] = task; @@ -125,9 +123,9 @@ void Process::WorkerThreadProc(void) } } -bool Process::InitTask(void) +void Process::InitTask(void) { - time(&m_ExecutionStart); + time(&m_Result.ExecutionStart); #ifdef _MSC_VER m_FP = _popen(m_Command.c_str(), "r"); @@ -138,7 +136,7 @@ bool Process::InitTask(void) m_FP = popen_noshell_compat(m_Command.c_str(), "r", (popen_noshell_pass_to_pclose *)m_PCloseArg); - if (m_FP == NULL) // TODO: add check for valgrind + if (m_FP == NULL) m_UsePopen = true; } @@ -146,13 +144,8 @@ bool Process::InitTask(void) m_FP = popen(m_Command.c_str(), "r"); #endif /* _MSC_VER */ - if (m_FP == NULL) { - m_ExitStatus = 128; - m_ExecutionEnd = m_ExecutionStart; - return false; - } - - return true; + if (m_FP == NULL) + throw runtime_error("Could not create process."); } bool Process::RunTask(void) @@ -180,7 +173,7 @@ bool Process::RunTask(void) } #endif /* _MSC_VER */ - time(&m_ExecutionEnd); + time(&m_Result.ExecutionEnd); #ifndef _MSC_VER if (WIFEXITED(status)) { @@ -204,8 +197,8 @@ bool Process::RunTask(void) } #endif /* _MSC_VER */ - m_ExitStatus = exitcode; - m_Output = output; + m_Result.ExitStatus = exitcode; + m_Result.Output = output; return false; } @@ -215,23 +208,3 @@ int Process::GetFD(void) const return fileno(m_FP); } -long Process::GetExitStatus(void) const -{ - return m_ExitStatus; -} - -string Process::GetOutput(void) const -{ - return m_Output; -} - -time_t Process::GetExecutionStart(void) const -{ - return m_ExecutionStart; -} - -time_t Process::GetExecutionEnd(void) const -{ - return m_ExecutionEnd; -} - diff --git a/base/process.h b/base/process.h index ec9bde5b9..03623f8df 100644 --- a/base/process.h +++ b/base/process.h @@ -23,7 +23,15 @@ namespace icinga { -class I2_BASE_API Process : public AsyncTask +struct ProcessResult +{ + time_t ExecutionStart; + time_t ExecutionEnd; + long ExitStatus; + string Output; +}; + +class I2_BASE_API Process : public AsyncTask { public: typedef shared_ptr Ptr; @@ -33,12 +41,6 @@ public: Process(const string& command, const CompletionCallback& completionCallback); - time_t GetExecutionStart(void) const; - time_t GetExecutionEnd(void) const; - - long GetExitStatus(void) const; - string GetOutput(void) const; - private: static bool m_ThreadCreated; @@ -51,10 +53,7 @@ private: void *m_PCloseArg; #endif /* _MSC_VER */ - time_t m_ExecutionStart; - time_t m_ExecutionEnd; - long m_ExitStatus; - string m_Output; + ProcessResult m_Result; virtual void Run(void); @@ -64,7 +63,7 @@ private: static void WorkerThreadProc(void); - bool InitTask(void); + void InitTask(void); bool RunTask(void); int GetFD(void) const; diff --git a/base/scripttask.cpp b/base/scripttask.cpp index be1bd0f58..9956d4e4e 100644 --- a/base/scripttask.cpp +++ b/base/scripttask.cpp @@ -10,13 +10,3 @@ void ScriptTask::Run(void) { m_Function->Invoke(GetSelf(), m_Arguments); } - -void ScriptTask::SetResult(const Variant& result) -{ - m_Result = result; -} - -Variant ScriptTask::GetResult(void) -{ - return m_Result; -} \ No newline at end of file diff --git a/base/scripttask.h b/base/scripttask.h index 20dbbc822..a3c933518 100644 --- a/base/scripttask.h +++ b/base/scripttask.h @@ -23,7 +23,7 @@ namespace icinga { -class I2_BASE_API ScriptTask : public AsyncTask +class I2_BASE_API ScriptTask : public AsyncTask { public: typedef shared_ptr Ptr; @@ -31,16 +31,12 @@ public: ScriptTask(const ScriptFunction::Ptr& function, const vector& arguments, CompletionCallback callback); - void SetResult(const Variant& result); - Variant GetResult(void); - protected: virtual void Run(void); private: ScriptFunction::Ptr m_Function; vector m_Arguments; - Variant m_Result; }; } diff --git a/cib/configobjectadapter.cpp b/cib/configobjectadapter.cpp index eec2c19e7..4e9153d9a 100644 --- a/cib/configobjectadapter.cpp +++ b/cib/configobjectadapter.cpp @@ -47,7 +47,7 @@ void ConfigObjectAdapter::RemoveTag(const string& key) } ScriptTask::Ptr ConfigObjectAdapter::InvokeHook(const string& hook, - const vector& arguments, AsyncTask::CompletionCallback callback) + const vector& arguments, ScriptTask::CompletionCallback callback) { return m_ConfigObject->InvokeHook(hook, arguments, callback); } \ No newline at end of file diff --git a/cib/configobjectadapter.h b/cib/configobjectadapter.h index c4ff5dd18..07dc57c47 100644 --- a/cib/configobjectadapter.h +++ b/cib/configobjectadapter.h @@ -58,7 +58,7 @@ public: void RemoveTag(const string& key); ScriptTask::Ptr InvokeHook(const string& hook, - const vector& arguments, AsyncTask::CompletionCallback callback); + const vector& arguments, ScriptTask::CompletionCallback callback); private: ConfigObject::Ptr m_ConfigObject; diff --git a/cib/nagioschecktask.cpp b/cib/nagioschecktask.cpp index deda3a816..5174d4da8 100644 --- a/cib/nagioschecktask.cpp +++ b/cib/nagioschecktask.cpp @@ -53,22 +53,21 @@ void NagiosCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vectorStart(); } -void NagiosCheckTask::ProcessFinishedHandler(const ScriptTask::Ptr& task, const AsyncTask::Ptr& aprocess, CheckResult result) +void NagiosCheckTask::ProcessFinishedHandler(const ScriptTask::Ptr& task, const Process::Ptr& process, CheckResult result) { - Process::Ptr process = static_pointer_cast(aprocess); + ProcessResult pr; + pr = process->GetResult(); - result.SetExecutionStart(process->GetExecutionStart()); - result.SetExecutionEnd(process->GetExecutionEnd()); - - string output = process->GetOutput(); - long exitcode = process->GetExitStatus(); + result.SetExecutionStart(pr.ExecutionStart); + result.SetExecutionEnd(pr.ExecutionEnd); + string output = pr.Output; boost::algorithm::trim(output); ProcessCheckOutput(result, output); ServiceState state; - switch (exitcode) { + switch (pr.ExitStatus) { case 0: state = StateOK; break; @@ -89,8 +88,7 @@ void NagiosCheckTask::ProcessFinishedHandler(const ScriptTask::Ptr& task, const time(&now); result.SetScheduleEnd(now); - task->SetResult(result.GetDictionary()); - task->Finish(); + task->Finish(result.GetDictionary()); } void NagiosCheckTask::ProcessCheckOutput(CheckResult& result, const string& output) @@ -129,5 +127,5 @@ void NagiosCheckTask::ProcessCheckOutput(CheckResult& result, const string& outp void NagiosCheckTask::Register(void) { ScriptFunction::Ptr func = boost::make_shared(&NagiosCheckTask::ScriptFunc); - ScriptFunction::Register("builtin::NagiosCheck", func); + ScriptFunction::Register("native::NagiosCheck", func); } diff --git a/cib/nagioschecktask.h b/cib/nagioschecktask.h index f24242514..483d1d6df 100644 --- a/cib/nagioschecktask.h +++ b/cib/nagioschecktask.h @@ -31,7 +31,7 @@ public: static void Register(void); private: - static void ProcessFinishedHandler(const ScriptTask::Ptr& task, const AsyncTask::Ptr& aprocess, CheckResult result); + static void ProcessFinishedHandler(const ScriptTask::Ptr& task, const Process::Ptr& process, CheckResult result); static void ProcessCheckOutput(CheckResult& result, const string& output); }; diff --git a/components/checker/checkercomponent.cpp b/components/checker/checkercomponent.cpp index f402125c6..7eabca487 100644 --- a/components/checker/checkercomponent.cpp +++ b/components/checker/checkercomponent.cpp @@ -99,10 +99,8 @@ void CheckerComponent::CheckTimerHandler(void) Logger::Write(LogInformation, "checker", msgbuf.str()); } -void CheckerComponent::CheckCompletedHandler(Service service, const AsyncTask::Ptr& atask) +void CheckerComponent::CheckCompletedHandler(Service service, const ScriptTask::Ptr& task) { - ScriptTask::Ptr task = static_pointer_cast(atask); - service.RemoveTag("current_task"); /* if the service isn't in the set of pending services diff --git a/components/checker/checkercomponent.h b/components/checker/checkercomponent.h index ccddc69b9..dd98c64d3 100644 --- a/components/checker/checkercomponent.h +++ b/components/checker/checkercomponent.h @@ -60,7 +60,7 @@ private: void CheckTimerHandler(void); void ResultTimerHandler(void); - void CheckCompletedHandler(Service service, const AsyncTask::Ptr& atask); + void CheckCompletedHandler(Service service, const ScriptTask::Ptr& task); void AdjustCheckTimer(void); diff --git a/icinga-app/icinga-standalone.conf b/icinga-app/icinga-standalone.conf index 87ff4dbf9..863cfb383 100644 --- a/icinga-app/icinga-standalone.conf +++ b/icinga-app/icinga-standalone.conf @@ -41,7 +41,7 @@ object host "localhost" { abstract object service "nagios-service" { hooks = { - check = "builtin::NagiosCheck" + check = "native::NagiosCheck" }, macros = { @@ -50,7 +50,6 @@ abstract object service "nagios-service" { } abstract object service "ping" inherits "nagios-service" { - check_type = "nagios", check_command = "$plugindir$/check_ping -H $address$", check_interval = 30 } diff --git a/icinga/icingaapplication.cpp b/icinga/icingaapplication.cpp index 53e202faf..3ca027a84 100644 --- a/icinga/icingaapplication.cpp +++ b/icinga/icingaapplication.cpp @@ -34,12 +34,6 @@ IcingaApplication::IcingaApplication(void) : m_PidPath(DefaultPidPath) { } -void TestScriptFunc(const ScriptTask::Ptr& task, const vector& arguments) -{ - std::cout << "Got " << arguments.size() << " arguments." << std::endl; - task->Finish(); -} - /** * The entry point for the Icinga application. *