Implement support for signals

fixes #7744
This commit is contained in:
Gunnar Beutner 2014-11-20 06:53:57 +01:00
parent 184e38b122
commit f84cc8de39
10 changed files with 186 additions and 1 deletions

View File

@ -27,7 +27,7 @@ set(base_SOURCES
convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp logger.cpp logger.thpp
netstring.cpp networkstream.cpp object.cpp primitivetype.cpp process.cpp
ringbuffer.cpp scriptfunction.cpp scriptfunctionwrapper.cpp
ringbuffer.cpp scriptfunction.cpp scriptfunctionwrapper.cpp scriptsignal.cpp
scriptutils.cpp scriptvariable.cpp serializer.cpp socket.cpp stacktrace.cpp
statsfunction.cpp stdiostream.cpp stream.cpp streamlogger.cpp streamlogger.thpp string.cpp
sysloglogger.cpp sysloglogger.thpp tcpsocket.cpp thinmutex.cpp threadpool.cpp timer.cpp

56
lib/base/scriptsignal.cpp Normal file
View File

@ -0,0 +1,56 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 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 "base/scriptsignal.hpp"
#include "base/scriptvariable.hpp"
using namespace icinga;
void ScriptSignal::AddSlot(const Callback& slot)
{
m_Slots.push_back(slot);
}
Value ScriptSignal::Invoke(const std::vector<Value>& arguments)
{
BOOST_FOREACH(const Callback& slot, m_Slots)
slot(arguments);
}
ScriptSignal::Ptr ScriptSignal::GetByName(const String& name)
{
ScriptVariable::Ptr sv = ScriptVariable::GetByName(name);
if (!sv)
return ScriptSignal::Ptr();
return sv->GetData();
}
void ScriptSignal::Register(const String& name, const ScriptSignal::Ptr& function)
{
ScriptVariable::Ptr sv = ScriptVariable::Set(name, function);
sv->SetConstant(true);
}
void ScriptSignal::Unregister(const String& name)
{
ScriptVariable::Unregister(name);
}

65
lib/base/scriptsignal.hpp Normal file
View File

@ -0,0 +1,65 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 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. *
******************************************************************************/
#ifndef SCRIPTSIGNAL_H
#define SCRIPTSIGNAL_H
#include "base/i2-base.hpp"
#include "base/value.hpp"
#include <vector>
#include <boost/function.hpp>
namespace icinga
{
/**
* A signal that can be subscribed to by scripts.
*
* @ingroup base
*/
class I2_BASE_API ScriptSignal : public Object
{
public:
DECLARE_PTR_TYPEDEFS(ScriptSignal);
typedef boost::function<void (const std::vector<Value>& arguments)> Callback;
void AddSlot(const Callback& slot);
Value Invoke(const std::vector<Value>& arguments = std::vector<Value>());
static ScriptSignal::Ptr GetByName(const String& name);
static void Register(const String& name, const ScriptSignal::Ptr& signal);
static void Unregister(const String& name);
private:
std::vector<Callback> m_Slots;
};
#define REGISTER_SCRIPTSIGNAL(name) \
namespace { namespace UNIQUE_NAME(sig) { namespace sig ## name { \
void RegisterSignal(void) { \
ScriptSignal::Ptr sig = new icinga::ScriptSignal(); \
ScriptSignal::Register(#name, sig); \
} \
INITIALIZE_ONCE(RegisterSignal); \
} } }
}
#endif /* SCRIPTSIGNAL_H */

View File

@ -26,6 +26,7 @@
#include "base/objectlock.hpp"
#include "base/dynamictype.hpp"
#include "base/application.hpp"
#include "base/configerror.hpp"
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <algorithm>
@ -45,6 +46,7 @@ REGISTER_SCRIPTFUNCTION(typeof, &ScriptUtils::TypeOf);
REGISTER_SCRIPTFUNCTION(keys, &ScriptUtils::Keys);
REGISTER_SCRIPTFUNCTION(random, &Utility::Random);
REGISTER_SCRIPTFUNCTION(__get_object, &ScriptUtils::GetObject);
REGISTER_SCRIPTFUNCTION(assert, &ScriptUtils::Assert);
bool ScriptUtils::Regex(const String& pattern, const String& text)
{
@ -218,3 +220,9 @@ DynamicObject::Ptr ScriptUtils::GetObject(const String& type, const String& name
return dtype->GetObject(name);
}
void ScriptUtils::Assert(const Value& arg)
{
if (!arg.ToBool())
BOOST_THROW_EXCEPTION(ConfigError("Assertion failed"));
}

View File

@ -45,6 +45,7 @@ public:
static Type::Ptr TypeOf(const Value& value);
static Array::Ptr Keys(const Dictionary::Ptr& dict);
static DynamicObject::Ptr GetObject(const String& type, const String& name);
static void Assert(const Value& arg);
private:
ScriptUtils(void);

View File

@ -30,6 +30,7 @@
#include "base/convert.hpp"
#include "base/scriptvariable.hpp"
#include "base/context.hpp"
#include "base/scriptsignal.hpp"
#include "config.h"
#include <boost/program_options.hpp>
#include <boost/tuple/tuple.hpp>
@ -42,6 +43,7 @@ namespace po = boost::program_options;
static po::variables_map g_AppParams;
REGISTER_CLICOMMAND("daemon", DaemonCommand);
REGISTER_SCRIPTSIGNAL(onload);
static String LoadAppType(const String& typeSpec)
{
@ -156,6 +158,9 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con
ScriptVariable::WriteVariablesFile(varsfile);
ScriptSignal::Ptr loadSignal = ScriptSignal::GetByName("onload");
loadSignal->Invoke();
return true;
}

View File

@ -248,6 +248,7 @@ for return T_APPLY_FOR;
__function return T_FUNCTION;
__return return T_RETURN;
__for return T_FOR;
__signal return T_SIGNAL;
=\> return T_FOLLOWS;
\<\< return T_SHIFT_LEFT;
\>\> return T_SHIFT_RIGHT;

View File

@ -160,6 +160,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
%token T_FUNCTION "function (T_FUNCTION)"
%token T_RETURN "return (T_RETURN)"
%token T_FOR "for (T_FOR)"
%token T_SIGNAL "signal (T_SIGNAL)"
%token T_FOLLOWS "=> (T_FOLLOWS)"
%type <text> identifier
@ -802,6 +803,11 @@ rterm: T_STRING
$$ = new FunctionExpression("", *$3, aexpr, DebugInfoRange(@1, @5));
delete $3;
}
| T_SIGNAL identifier T_SET_ADD rterm
{
$$ = new SlotExpression($2, $4, DebugInfoRange(@1, @4));
free($2);
}
| T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope
{
$$ = new ForExpression($3, $5, $7, $9, DebugInfoRange(@1, @9));

View File

@ -26,6 +26,7 @@
#include "base/json.hpp"
#include "base/scriptfunction.hpp"
#include "base/scriptvariable.hpp"
#include "base/scriptsignal.hpp"
#include "base/utility.hpp"
#include "base/objectlock.hpp"
#include "base/object.hpp"
@ -427,6 +428,33 @@ Value FunctionExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhin
return func;
}
static void InvokeSlot(const Value& funcName, const std::vector<Value>& arguments)
{
ScriptFunction::Ptr func;
if (funcName.IsObjectType<ScriptFunction>())
func = funcName;
else
func = ScriptFunction::GetByName(funcName);
if (!func)
BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
func->Invoke(arguments);
}
Value SlotExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
ScriptSignal::Ptr sig = ScriptSignal::GetByName(m_Signal);
if (!sig)
BOOST_THROW_EXCEPTION(ConfigError("Signal '" + m_Signal + "' does not exist."));
sig->AddSlot(boost::bind(InvokeSlot, m_Slot->Evaluate(context), _1));
return Empty;
}
Value ApplyExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
String name = m_Name->Evaluate(context, dhint);

View File

@ -581,6 +581,21 @@ private:
boost::shared_ptr<Expression> m_Expression;
};
class I2_CONFIG_API SlotExpression : public DebuggableExpression
{
public:
SlotExpression(const String& signal, Expression *slot, const DebugInfo& debugInfo = DebugInfo())
: DebuggableExpression(debugInfo), m_Signal(signal), m_Slot(slot)
{ }
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
private:
String m_Signal;
Expression *m_Slot;
};
class I2_CONFIG_API ApplyExpression : public DebuggableExpression
{
public: