From 1a8692d972c4d9c5e6aa79a042cedd1b84941b8a Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 7 Aug 2018 13:55:41 +0200 Subject: [PATCH] Implement support for namespaces --- doc/17-language-reference.md | 32 ++++ icinga-app/icinga.cpp | 20 ++- itl/hangman | 4 +- lib/base/CMakeLists.txt | 1 + lib/base/function.hpp | 20 ++- lib/base/json-script.cpp | 16 +- lib/base/json.cpp | 21 +++ lib/base/math-script.cpp | 73 ++++---- lib/base/namespace-script.cpp | 101 +++++++++++ lib/base/namespace.cpp | 223 +++++++++++++++++++++++++ lib/base/namespace.hpp | 123 ++++++++++++++ lib/base/scriptglobal.cpp | 22 ++- lib/base/scriptglobal.hpp | 9 +- lib/base/scriptutils.cpp | 8 +- lib/base/serializer.cpp | 22 +++ lib/base/type.cpp | 21 ++- lib/config/config_parser.yy | 2 +- lib/config/configitembuilder.cpp | 1 + lib/config/expression.cpp | 34 +++- lib/config/expression.hpp | 13 ++ lib/config/vmops.hpp | 21 +++ lib/db_ido/db_ido-itl.conf | 6 +- lib/db_ido/idochecktask.cpp | 2 +- lib/icinga/cib.cpp | 6 +- lib/icinga/icinga-itl.conf | 6 +- lib/icinga/legacytimeperiod.cpp | 2 +- lib/methods/clusterchecktask.cpp | 2 +- lib/methods/clusterzonechecktask.cpp | 2 +- lib/methods/dummychecktask.cpp | 2 +- lib/methods/exceptionchecktask.cpp | 2 +- lib/methods/icingachecktask.cpp | 2 +- lib/methods/methods-itl.conf | 62 +++---- lib/methods/nullchecktask.cpp | 2 +- lib/methods/nulleventtask.cpp | 2 +- lib/methods/pluginchecktask.cpp | 2 +- lib/methods/plugineventtask.cpp | 2 +- lib/methods/pluginnotificationtask.cpp | 2 +- lib/methods/randomchecktask.cpp | 2 +- lib/methods/timeperiodtask.cpp | 4 +- lib/remote/consolehandler.cpp | 12 +- lib/remote/jsonrpc.cpp | 2 +- lib/remote/statushandler.cpp | 14 +- lib/remote/variablequeryhandler.cpp | 7 +- 43 files changed, 789 insertions(+), 143 deletions(-) create mode 100644 lib/base/namespace-script.cpp create mode 100644 lib/base/namespace.cpp create mode 100644 lib/base/namespace.hpp diff --git a/doc/17-language-reference.md b/doc/17-language-reference.md index f7fae4695..0e48a6c25 100644 --- a/doc/17-language-reference.md +++ b/doc/17-language-reference.md @@ -203,6 +203,38 @@ to dereference a reference: *p = "Hi!" log(value) // Prints "Hi!" because the variable was changed +### Namespaces + +Namespaces can be used to organize variables and functions. They are used to avoid name conflicts. The `namespace` +keyword is used to create a new namespace: + + namespace Utils { + function calculate() { + return 2 + 2 + } + } + +The namespace is made available as a global variable which has the namespace's name (e.g. `Utils`): + + Utils.calculate() + +The `using` keyword can be used to make all attributes in a namespace available to a script without having to +explicitly specify the namespace's name for each access: + + using Utils + calculate() + +The `using` keyword only has an effect for the current file and only for code that follows the keyword: + + calculate() // This will not work. + using Utils + +The following namespaces are automatically imported as if by using the `using` keyword: + +* System +* Types +* Icinga + ### Function Calls Functions can be called using the `()` operator: diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 26ed4cf69..24da88644 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -21,6 +21,7 @@ #include "config/configcompilercontext.hpp" #include "config/configcompiler.hpp" #include "config/configitembuilder.hpp" +#include "config/expression.hpp" #include "base/application.hpp" #include "base/logger.hpp" #include "base/timer.hpp" @@ -440,7 +441,24 @@ static int Main() key = define; value = "1"; } - ScriptGlobal::Set(key, value); + + std::vector keyTokens = key.Split("."); + + std::unique_ptr expr; + std::unique_ptr varExpr{new VariableExpression(keyTokens[0], {}, DebugInfo())}; + expr = std::move(varExpr); + + for (size_t i = 1; i < keyTokens.size(); i++) { + std::unique_ptr indexerExpr{new IndexerExpression(std::move(expr), MakeLiteral(keyTokens[i]))}; + indexerExpr->SetOverrideFrozen(); + expr = std::move(indexerExpr); + } + + std::unique_ptr setExpr{new SetExpression(std::move(expr), OpSetLiteral, MakeLiteral(value))}; + setExpr->SetOverrideFrozen(); + + ScriptFrame frame(true); + setExpr->Evaluate(frame); } } diff --git a/itl/hangman b/itl/hangman index f4dc118e0..48cd177d7 100644 --- a/itl/hangman +++ b/itl/hangman @@ -17,8 +17,8 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -if (!globals.irc) { - globals.irc = globals.log +if (!globals.contains("irc")) { + globals.irc = log } hm = { diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index e784a3957..3cdc08556 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -54,6 +54,7 @@ set(base_SOURCES math-script.cpp netstring.cpp netstring.hpp networkstream.cpp networkstream.hpp + namespace.cpp namespace.hpp namespace-script.cpp number.cpp number.hpp number-script.cpp object.cpp object.hpp object-script.cpp objectlock.cpp objectlock.hpp diff --git a/lib/base/function.hpp b/lib/base/function.hpp index eae0a0792..493be13ee 100644 --- a/lib/base/function.hpp +++ b/lib/base/function.hpp @@ -75,13 +75,29 @@ private: #define REGISTER_FUNCTION(ns, name, callback, args) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \ - ScriptGlobal::Set(#ns "." #name, sf); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ }, 10) #define REGISTER_SAFE_FUNCTION(ns, name, callback, args) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \ - ScriptGlobal::Set(#ns "." #name, sf); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ + }, 10) + +#define REGISTER_FUNCTION_NONCONST(ns, name, callback, args) \ + INITIALIZE_ONCE_WITH_PRIORITY([]() { \ + Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ + }, 10) + +#define REGISTER_SAFE_FUNCTION_NONCONST(ns, name, callback, args) \ + INITIALIZE_ONCE_WITH_PRIORITY([]() { \ + Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ }, 10) } diff --git a/lib/base/json-script.cpp b/lib/base/json-script.cpp index 9df6ee783..d99c71bde 100644 --- a/lib/base/json-script.cpp +++ b/lib/base/json-script.cpp @@ -32,11 +32,15 @@ static String JsonEncodeShim(const Value& value) } INITIALIZE_ONCE([]() { - Dictionary::Ptr jsonObj = new Dictionary({ - /* Methods */ - { "encode", new Function("Json#encode", JsonEncodeShim, { "value" }, true) }, - { "decode", new Function("Json#decode", JsonDecode, { "value" }, true) } - }); + auto jsonNSBehavior = new ConstNamespaceBehavior(); + Namespace::Ptr jsonNS = new Namespace(jsonNSBehavior); - ScriptGlobal::Set("Json", jsonObj); + /* Methods */ + jsonNS->Set("encode", new Function("Json#encode", JsonEncodeShim, { "value" }, true)); + jsonNS->Set("decode", new Function("Json#decode", JsonDecode, { "value" }, true)); + + jsonNSBehavior->Freeze(); + + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + systemNS->SetAttribute("Json", std::make_shared(jsonNS)); }); diff --git a/lib/base/json.cpp b/lib/base/json.cpp index c074aed56..27119f434 100644 --- a/lib/base/json.cpp +++ b/lib/base/json.cpp @@ -19,6 +19,7 @@ #include "base/json.hpp" #include "base/debug.hpp" +#include "base/namespace.hpp" #include "base/dictionary.hpp" #include "base/array.hpp" #include "base/objectlock.hpp" @@ -39,6 +40,19 @@ typedef unsigned int yajl_size; typedef size_t yajl_size; #endif /* YAJL_MAJOR */ +static void EncodeNamespace(yajl_gen handle, const Namespace::Ptr& ns) +{ + yajl_gen_map_open(handle); + + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + yajl_gen_string(handle, reinterpret_cast(kv.first.CStr()), kv.first.GetLength()); + Encode(handle, kv.second->Get()); + } + + yajl_gen_map_close(handle); +} + static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict) { yajl_gen_map_open(handle); @@ -83,6 +97,13 @@ static void Encode(yajl_gen handle, const Value& value) case ValueObject: { const Object::Ptr& obj = value.Get(); + Namespace::Ptr ns = dynamic_pointer_cast(obj); + + if (ns) { + EncodeNamespace(handle, ns); + break; + } + Dictionary::Ptr dict = dynamic_pointer_cast(obj); if (dict) { diff --git a/lib/base/math-script.cpp b/lib/base/math-script.cpp index dab48a5df..288cbca12 100644 --- a/lib/base/math-script.cpp +++ b/lib/base/math-script.cpp @@ -22,6 +22,7 @@ #include "base/functionwrapper.hpp" #include "base/scriptframe.hpp" #include "base/initialize.hpp" +#include "base/namespace.hpp" #include #include @@ -158,40 +159,44 @@ static double MathSign(double x) } INITIALIZE_ONCE([]() { - Dictionary::Ptr mathObj = new Dictionary({ - /* Constants */ - { "E", 2.71828182845904523536 }, - { "LN2", 0.693147180559945309417 }, - { "LN10", 2.30258509299404568402 }, - { "LOG2E", 1.44269504088896340736 }, - { "LOG10E", 0.434294481903251827651 }, - { "PI", 3.14159265358979323846 }, - { "SQRT1_2", 0.707106781186547524401 }, - { "SQRT2", 1.41421356237309504880 }, + auto mathNSBehavior = new ConstNamespaceBehavior(); + Namespace::Ptr mathNS = new Namespace(mathNSBehavior); - /* Methods */ - { "abs", new Function("Math#abs", MathAbs, { "x" }, true) }, - { "acos", new Function("Math#acos", MathAcos, { "x" }, true) }, - { "asin", new Function("Math#asin", MathAsin, { "x" }, true) }, - { "atan", new Function("Math#atan", MathAtan, { "x" }, true) }, - { "atan2", new Function("Math#atan2", MathAtan2, { "x", "y" }, true) }, - { "ceil", new Function("Math#ceil", MathCeil, { "x" }, true) }, - { "cos", new Function("Math#cos", MathCos, { "x" }, true) }, - { "exp", new Function("Math#exp", MathExp, { "x" }, true) }, - { "floor", new Function("Math#floor", MathFloor, { "x" }, true) }, - { "log", new Function("Math#log", MathLog, { "x" }, true) }, - { "max", new Function("Math#max", MathMax, {}, true) }, - { "min", new Function("Math#min", MathMin, {}, true) }, - { "pow", new Function("Math#pow", MathPow, { "x", "y" }, true) }, - { "random", new Function("Math#random", MathRandom, {}, true) }, - { "round", new Function("Math#round", MathRound, { "x" }, true) }, - { "sin", new Function("Math#sin", MathSin, { "x" }, true) }, - { "sqrt", new Function("Math#sqrt", MathSqrt, { "x" }, true) }, - { "tan", new Function("Math#tan", MathTan, { "x" }, true) }, - { "isnan", new Function("Math#isnan", MathIsnan, { "x" }, true) }, - { "isinf", new Function("Math#isinf", MathIsinf, { "x" }, true) }, - { "sign", new Function("Math#sign", MathSign, { "x" }, true) } - }); + /* Constants */ + mathNS->Set("E", 2.71828182845904523536); + mathNS->Set("LN2", 0.693147180559945309417); + mathNS->Set("LN10", 2.30258509299404568402); + mathNS->Set("LOG2E", 1.44269504088896340736); + mathNS->Set("LOG10E", 0.434294481903251827651); + mathNS->Set("PI", 3.14159265358979323846); + mathNS->Set("SQRT1_2", 0.707106781186547524401); + mathNS->Set("SQRT2", 1.41421356237309504880); - ScriptGlobal::Set("Math", mathObj); + /* Methods */ + mathNS->Set("abs", new Function("Math#abs", MathAbs, { "x" }, true)); + mathNS->Set("acos", new Function("Math#acos", MathAcos, { "x" }, true)); + mathNS->Set("asin", new Function("Math#asin", MathAsin, { "x" }, true)); + mathNS->Set("atan", new Function("Math#atan", MathAtan, { "x" }, true)); + mathNS->Set("atan2", new Function("Math#atan2", MathAtan2, { "x", "y" }, true)); + mathNS->Set("ceil", new Function("Math#ceil", MathCeil, { "x" }, true)); + mathNS->Set("cos", new Function("Math#cos", MathCos, { "x" }, true)); + mathNS->Set("exp", new Function("Math#exp", MathExp, { "x" }, true)); + mathNS->Set("floor", new Function("Math#floor", MathFloor, { "x" }, true)); + mathNS->Set("log", new Function("Math#log", MathLog, { "x" }, true)); + mathNS->Set("max", new Function("Math#max", MathMax, {}, true)); + mathNS->Set("min", new Function("Math#min", MathMin, {}, true)); + mathNS->Set("pow", new Function("Math#pow", MathPow, { "x", "y" }, true)); + mathNS->Set("random", new Function("Math#random", MathRandom, {}, true)); + mathNS->Set("round", new Function("Math#round", MathRound, { "x" }, true)); + mathNS->Set("sin", new Function("Math#sin", MathSin, { "x" }, true)); + mathNS->Set("sqrt", new Function("Math#sqrt", MathSqrt, { "x" }, true)); + mathNS->Set("tan", new Function("Math#tan", MathTan, { "x" }, true)); + mathNS->Set("isnan", new Function("Math#isnan", MathIsnan, { "x" }, true)); + mathNS->Set("isinf", new Function("Math#isinf", MathIsinf, { "x" }, true)); + mathNS->Set("sign", new Function("Math#sign", MathSign, { "x" }, true)); + + mathNSBehavior->Freeze(); + + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + systemNS->SetAttribute("Math", std::make_shared(mathNS)); }); diff --git a/lib/base/namespace-script.cpp b/lib/base/namespace-script.cpp new file mode 100644 index 000000000..9f5471d60 --- /dev/null +++ b/lib/base/namespace-script.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * + * * + * 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/namespace.hpp" +#include "base/function.hpp" +#include "base/functionwrapper.hpp" +#include "base/scriptframe.hpp" +#include "base/array.hpp" + +using namespace icinga; + +static void NamespaceSet(const String& key, const Value& value) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Set(key, value); +} + +static Value NamespaceGet(const String& key) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + return self->Get(key); +} + +static void NamespaceRemove(const String& key) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Remove(key); +} + +static bool NamespaceContains(const String& key) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + return self->Contains(key); +} + +static Array::Ptr NamespaceKeys() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + + ArrayData keys; + ObjectLock olock(self); + for (const Namespace::Pair& kv : self) { + keys.push_back(kv.first); + } + return new Array(std::move(keys)); +} + +static Array::Ptr NamespaceValues() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + + ArrayData values; + ObjectLock olock(self); + for (const Namespace::Pair& kv : self) { + values.push_back(kv.second->Get()); + } + return new Array(std::move(values)); +} + +Object::Ptr Namespace::GetPrototype() +{ + static Dictionary::Ptr prototype = new Dictionary({ + { "set", new Function("Namespace#set", NamespaceSet, { "key", "value" }) }, + { "get", new Function("Namespace#get", NamespaceGet, { "key" }) }, + { "remove", new Function("Namespace#remove", NamespaceRemove, { "key" }) }, + { "contains", new Function("Namespace#contains", NamespaceContains, { "key" }, true) }, + { "keys", new Function("Namespace#keys", NamespaceKeys, {}, true) }, + { "values", new Function("Namespace#values", NamespaceValues, {}, true) }, + }); + + return prototype; +} + diff --git a/lib/base/namespace.cpp b/lib/base/namespace.cpp new file mode 100644 index 000000000..d106a0ee3 --- /dev/null +++ b/lib/base/namespace.cpp @@ -0,0 +1,223 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * + * * + * 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/namespace.hpp" +#include "base/objectlock.hpp" +#include "base/debug.hpp" +#include "base/primitivetype.hpp" +#include "base/debuginfo.hpp" +#include "base/exception.hpp" +#include + +using namespace icinga; + +template class std::map >; + +REGISTER_PRIMITIVE_TYPE(Namespace, Object, Namespace::GetPrototype()); + +Namespace::Namespace(NamespaceBehavior *behavior) + : m_Behavior(std::unique_ptr(behavior)) +{ } + +Value Namespace::Get(const String& field) const +{ + Value value; + if (!GetOwnField(field, &value)) + BOOST_THROW_EXCEPTION(ScriptError("Namespace does not contain field '" + field + "'")); + return value; +} + +bool Namespace::Get(const String& field, Value *value) const +{ + auto nsVal = GetAttribute(field); + + if (!nsVal) + return false; + + *value = nsVal->Get(DebugInfo()); + return true; +} + +void Namespace::Set(const String& field, const Value& value, bool overrideFrozen) +{ + return SetFieldByName(field, value, overrideFrozen, DebugInfo()); +} + +bool Namespace::Contains(const String& field) const +{ + return HasOwnField(field); +} + +void Namespace::Remove(const String& field, bool overrideFrozen) +{ + m_Behavior->Remove(this, field, overrideFrozen); +} + +void Namespace::RemoveAttribute(const String& field) +{ + ObjectLock olock(this); + + Namespace::Iterator it; + it = m_Data.find(field); + + if (it == m_Data.end()) + return; + + m_Data.erase(it); +} + +std::shared_ptr Namespace::GetAttribute(const String& key) const +{ + ObjectLock olock(this); + + auto it = m_Data.find(key); + + if (it == m_Data.end()) + return nullptr; + + return it->second; +} + +void Namespace::SetAttribute(const String& key, const std::shared_ptr& nsVal) +{ + ObjectLock olock(this); + + m_Data[key] = nsVal; +} + +Value Namespace::GetFieldByName(const String& field, bool, const DebugInfo& debugInfo) const +{ + auto nsVal = GetAttribute(field); + + if (nsVal) + return nsVal->Get(debugInfo); + else + return GetPrototypeField(const_cast(this), field, true, debugInfo); +} + +void Namespace::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) +{ + auto nsVal = GetAttribute(field); + + if (!nsVal) + m_Behavior->Register(this, field, value, overrideFrozen, debugInfo); + else + nsVal->Set(value, overrideFrozen, debugInfo); +} + +bool Namespace::HasOwnField(const String& field) const +{ + return GetAttribute(field) != nullptr; +} + +bool Namespace::GetOwnField(const String& field, Value *result) const +{ + auto nsVal = GetAttribute(field); + + if (!nsVal) + return false; + + *result = nsVal->Get(DebugInfo()); + return true; +} + +EmbeddedNamespaceValue::EmbeddedNamespaceValue(const Value& value) + : m_Value(value) +{ } + +Value EmbeddedNamespaceValue::Get(const DebugInfo& debugInfo) const +{ + return m_Value; +} + +void EmbeddedNamespaceValue::Set(const Value& value, bool, const DebugInfo&) +{ + m_Value = value; +} + +void ConstEmbeddedNamespaceValue::Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) +{ + if (!overrideFrozen) + BOOST_THROW_EXCEPTION(ScriptError("Constant must not be modified.", debugInfo)); + + EmbeddedNamespaceValue::Set(value, overrideFrozen, debugInfo); +} + +void NamespaceBehavior::Register(const Namespace::Ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const +{ + ns->SetAttribute(field, std::make_shared(value)); +} + +void NamespaceBehavior::Remove(const Namespace::Ptr& ns, const String& field, bool overrideFrozen) +{ + if (!overrideFrozen) { + auto attr = ns->GetAttribute(field); + + if (dynamic_pointer_cast(attr)) + BOOST_THROW_EXCEPTION(ScriptError("Constants must not be removed.")); + } + + ns->RemoveAttribute(field); +} + +void ConstNamespaceBehavior::Register(const Namespace::Ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const +{ + if (m_Frozen && !overrideFrozen) + BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.", debugInfo)); + + ns->SetAttribute(field, std::make_shared(value)); +} + +void ConstNamespaceBehavior::Remove(const Namespace::Ptr& ns, const String& field, bool overrideFrozen) +{ + if (m_Frozen && !overrideFrozen) + BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.")); + + NamespaceBehavior::Remove(ns, field, overrideFrozen); +} + +void ConstNamespaceBehavior::Freeze() +{ + m_Frozen = true; +} + +Namespace::Iterator Namespace::Begin() +{ + ASSERT(OwnsLock()); + + return m_Data.begin(); +} + +Namespace::Iterator Namespace::End() +{ + ASSERT(OwnsLock()); + + return m_Data.end(); +} + +Namespace::Iterator icinga::begin(const Namespace::Ptr& x) +{ + return x->Begin(); +} + +Namespace::Iterator icinga::end(const Namespace::Ptr& x) +{ + return x->End(); +} + diff --git a/lib/base/namespace.hpp b/lib/base/namespace.hpp new file mode 100644 index 000000000..21ebe929b --- /dev/null +++ b/lib/base/namespace.hpp @@ -0,0 +1,123 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * + * * + * 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 NAMESPACE_H +#define NAMESPACE_H + +#include "base/i2-base.hpp" +#include "base/object.hpp" +#include "base/value.hpp" +#include "base/debuginfo.hpp" +#include +#include + +namespace icinga +{ + +struct NamespaceValue +{ + virtual Value Get(const DebugInfo& debugInfo = DebugInfo()) const = 0; + virtual void Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo = DebugInfo()) = 0; +}; + +struct EmbeddedNamespaceValue : public NamespaceValue +{ + EmbeddedNamespaceValue(const Value& value); + + Value Get(const DebugInfo& debugInfo) const override; + void Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; + +private: + Value m_Value; +}; + +struct ConstEmbeddedNamespaceValue : public EmbeddedNamespaceValue +{ + using EmbeddedNamespaceValue::EmbeddedNamespaceValue; + + void Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; +}; + +class Namespace; + +struct NamespaceBehavior +{ + virtual void Register(const boost::intrusive_ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const; + virtual void Remove(const boost::intrusive_ptr& ns, const String& field, bool overrideFrozen); +}; + +struct ConstNamespaceBehavior : public NamespaceBehavior +{ + void Register(const boost::intrusive_ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const override; + void Remove(const boost::intrusive_ptr& ns, const String& field, bool overrideFrozen) override; + void Freeze(); + +private: + bool m_Frozen; +}; + +/** + * A namespace. + * + * @ingroup base + */ +class Namespace final : public Object +{ +public: + DECLARE_OBJECT(Namespace); + + typedef std::map >::iterator Iterator; + + typedef std::map >::value_type Pair; + + Namespace(NamespaceBehavior *behavior = new NamespaceBehavior); + + Value Get(const String& field) const; + bool Get(const String& field, Value *value) const; + void Set(const String& field, const Value& value, bool overrideFrozen = false); + bool Contains(const String& field) const; + void Remove(const String& field, bool overrideFrozen = false); + + std::shared_ptr GetAttribute(const String& field) const; + void SetAttribute(const String& field, const std::shared_ptr& nsVal); + void RemoveAttribute(const String& field); + + Iterator Begin(); + Iterator End(); + + Value GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const override; + void SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; + bool HasOwnField(const String& field) const override; + bool GetOwnField(const String& field, Value *result) const override; + + static Object::Ptr GetPrototype(); + +private: + std::map > m_Data; + std::unique_ptr m_Behavior; +}; + +Namespace::Iterator begin(const Namespace::Ptr& x); +Namespace::Iterator end(const Namespace::Ptr& x); + +} + +extern template class std::map >; + +#endif /* NAMESPACE_H */ diff --git a/lib/base/scriptglobal.cpp b/lib/base/scriptglobal.cpp index 225bb695b..69ccac06e 100644 --- a/lib/base/scriptglobal.cpp +++ b/lib/base/scriptglobal.cpp @@ -26,11 +26,12 @@ #include "base/convert.hpp" #include "base/objectlock.hpp" #include "base/exception.hpp" +#include "base/namespace.hpp" #include using namespace icinga; -Dictionary::Ptr ScriptGlobal::m_Globals = new Dictionary(); +Namespace::Ptr ScriptGlobal::m_Globals = new Namespace(); Value ScriptGlobal::Get(const String& name, const Value *defaultValue) { @@ -46,7 +47,7 @@ Value ScriptGlobal::Get(const String& name, const Value *defaultValue) return result; } -void ScriptGlobal::Set(const String& name, const Value& value) +void ScriptGlobal::Set(const String& name, const Value& value, bool overrideFrozen) { std::vector tokens = name.Split("."); @@ -56,7 +57,7 @@ void ScriptGlobal::Set(const String& name, const Value& value) { ObjectLock olock(m_Globals); - Dictionary::Ptr parent = m_Globals; + Namespace::Ptr parent = m_Globals; for (std::vector::size_type i = 0; i < tokens.size(); i++) { const String& token = tokens[i]; @@ -65,7 +66,7 @@ void ScriptGlobal::Set(const String& name, const Value& value) Value vparent; if (!parent->Get(token, &vparent)) { - Dictionary::Ptr dict = new Dictionary(); + Namespace::Ptr dict = new Namespace(); parent->Set(token, dict); parent = dict; } else { @@ -74,16 +75,21 @@ void ScriptGlobal::Set(const String& name, const Value& value) } } - parent->Set(tokens[tokens.size() - 1], value); + parent->SetFieldByName(tokens[tokens.size() - 1], value, overrideFrozen, DebugInfo()); } } +void ScriptGlobal::SetConst(const String& name, const Value& value) +{ + GetGlobals()->SetAttribute(name, std::make_shared(value)); +} + bool ScriptGlobal::Exists(const String& name) { return m_Globals->Contains(name); } -Dictionary::Ptr ScriptGlobal::GetGlobals() +Namespace::Ptr ScriptGlobal::GetGlobals() { return m_Globals; } @@ -102,8 +108,8 @@ void ScriptGlobal::WriteToFile(const String& filename) StdioStream::Ptr sfp = new StdioStream(&fp, false); ObjectLock olock(m_Globals); - for (const Dictionary::Pair& kv : m_Globals) { - Value value = kv.second; + for (const Namespace::Pair& kv : m_Globals) { + Value value = kv.second->Get(); if (value.IsObject()) value = Convert::ToString(value); diff --git a/lib/base/scriptglobal.hpp b/lib/base/scriptglobal.hpp index e2b5a8f41..562152bb4 100644 --- a/lib/base/scriptglobal.hpp +++ b/lib/base/scriptglobal.hpp @@ -21,7 +21,7 @@ #define SCRIPTGLOBAL_H #include "base/i2-base.hpp" -#include "base/dictionary.hpp" +#include "base/namespace.hpp" namespace icinga { @@ -35,15 +35,16 @@ class ScriptGlobal { public: static Value Get(const String& name, const Value *defaultValue = nullptr); - static void Set(const String& name, const Value& value); + static void Set(const String& name, const Value& value, bool overrideFrozen = false); + static void SetConst(const String& name, const Value& value); static bool Exists(const String& name); static void WriteToFile(const String& filename); - static Dictionary::Ptr GetGlobals(); + static Namespace::Ptr GetGlobals(); private: - static Dictionary::Ptr m_Globals; + static Namespace::Ptr m_Globals; }; } diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index d0c947e6e..befccde5f 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -83,11 +83,11 @@ enum MatchType void ScriptUtils::StaticInitialize() { - ScriptGlobal::Set("MatchAll", MatchAll); - ScriptGlobal::Set("MatchAny", MatchAny); + ScriptGlobal::Set("MatchAll", MatchAll, true); + ScriptGlobal::Set("MatchAny", MatchAny, true); - ScriptGlobal::Set("GlobFile", GlobFile); - ScriptGlobal::Set("GlobDirectory", GlobDirectory); + ScriptGlobal::Set("GlobFile", GlobFile, true); + ScriptGlobal::Set("GlobDirectory", GlobDirectory, true); } String ScriptUtils::CastString(const Value& value) diff --git a/lib/base/serializer.cpp b/lib/base/serializer.cpp index 1bd8b0c8c..82e55983b 100644 --- a/lib/base/serializer.cpp +++ b/lib/base/serializer.cpp @@ -23,6 +23,7 @@ #include "base/objectlock.hpp" #include "base/convert.hpp" #include "base/exception.hpp" +#include "base/namespace.hpp" #include #include @@ -119,6 +120,22 @@ static Dictionary::Ptr SerializeDictionary(const Dictionary::Ptr& input, int att return new Dictionary(std::move(result)); } +static Dictionary::Ptr SerializeNamespace(const Namespace::Ptr& input, int attributeTypes, SerializeStack& stack) +{ + DictionaryData result; + + ObjectLock olock(input); + + for (const Namespace::Pair& kv : input) { + Value val = kv.second->Get(); + stack.Push(kv.first, val); + result.emplace_back(kv.first, Serialize(val, attributeTypes)); + stack.Pop(); + } + + return new Dictionary(std::move(result)); +} + static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes, SerializeStack& stack) { Type::Ptr type = input->GetReflectionType(); @@ -243,6 +260,11 @@ static Value SerializeInternal(const Value& value, int attributeTypes, Serialize if (dict) return SerializeDictionary(dict, attributeTypes, stack); + Namespace::Ptr ns = dynamic_pointer_cast(input); + + if (ns) + return SerializeNamespace(ns, attributeTypes, stack); + return SerializeObject(input, attributeTypes, stack); } diff --git a/lib/base/type.cpp b/lib/base/type.cpp index 138269a87..b8bddd7fb 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -19,6 +19,7 @@ #include "base/type.hpp" #include "base/scriptglobal.hpp" +#include "base/namespace.hpp" #include "base/objectlock.hpp" using namespace icinga; @@ -39,19 +40,19 @@ String Type::ToString() const void Type::Register(const Type::Ptr& type) { - VERIFY(!GetByName(type->GetName())); - - ScriptGlobal::Set("Types." + type->GetName(), type); + ScriptGlobal::Set("Types." + type->GetName(), type, true); } Type::Ptr Type::GetByName(const String& name) { - Dictionary::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); + Namespace::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); if (!typesNS) return nullptr; - Value ptype = typesNS->Get(name); + Value ptype; + if (!typesNS->Get(name, &ptype)) + return nullptr; if (!ptype.IsObjectType()) return nullptr; @@ -63,14 +64,16 @@ std::vector Type::GetAllTypes() { std::vector types; - Dictionary::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); + Namespace::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); if (typesNS) { ObjectLock olock(typesNS); - for (const Dictionary::Pair& kv : typesNS) { - if (kv.second.IsObjectType()) - types.push_back(kv.second); + for (const Namespace::Pair& kv : typesNS) { + Value value = kv.second->Get(); + + if (value.IsObjectType()) + types.push_back(value); } } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 497df8a38..889d144d3 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -648,7 +648,7 @@ lterm: T_LIBRARY rterm } | T_CONST T_IDENTIFIER T_SET rterm { - $$ = new SetExpression(MakeIndexer(ScopeGlobal, *$2), OpSetLiteral, std::unique_ptr($4)); + $$ = new SetConstExpression(*$2, std::unique_ptr($4), @$); delete $2; } | T_VAR rterm diff --git a/lib/config/configitembuilder.cpp b/lib/config/configitembuilder.cpp index d81b595d2..392156cae 100644 --- a/lib/config/configitembuilder.cpp +++ b/lib/config/configitembuilder.cpp @@ -31,6 +31,7 @@ ConfigItemBuilder::ConfigItemBuilder(const DebugInfo& debugInfo) void ConfigItemBuilder::SetType(const Type::Ptr& type) { + ASSERT(type); m_Type = type; } diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index af3a7399e..9cfc44a22 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -29,6 +29,7 @@ #include "base/scriptglobal.hpp" #include "base/loader.hpp" #include "base/reference.hpp" +#include "base/namespace.hpp" #include #include @@ -625,6 +626,28 @@ void SetExpression::SetOverrideFrozen() m_OverrideFrozen = true; } +ExpressionResult SetConstExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + auto globals = ScriptGlobal::GetGlobals(); + + auto attr = globals->GetAttribute(m_Name); + + if (dynamic_pointer_cast(attr)) { + std::ostringstream msgbuf; + msgbuf << "Value for constant '" << m_Name << "' was modified. This behaviour is deprecated.\n"; + ShowCodeLocation(msgbuf, GetDebugInfo(), false); + Log(LogWarning, msgbuf.str()); + } + + ExpressionResult operandres = m_Operand->Evaluate(frame); + CHECK_RESULT(operandres); + Value operand = operandres.GetValue(); + + globals->SetAttribute(m_Name, std::make_shared(operand)); + + return Empty; +} + ExpressionResult ConditionalExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { ExpressionResult condition = m_Condition->Evaluate(frame, dhint); @@ -701,7 +724,16 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value * if (m_Operand1->GetReference(frame, init_dict, &vparent, &vindex, &psdhint)) { if (init_dict) { - Value old_value = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_Operand1->GetDebugInfo()); + Value old_value; + bool has_field = true; + + if (vparent.IsObject()) { + Object::Ptr oparent = vparent; + has_field = oparent->HasOwnField(vindex); + } + + if (has_field) + old_value = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_Operand1->GetDebugInfo()); if (old_value.IsEmpty() && !old_value.IsString()) VMOps::SetField(vparent, vindex, new Dictionary(), m_OverrideFrozen, m_Operand1->GetDebugInfo()); diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 4a7e9b0c8..865ded4a7 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -637,6 +637,19 @@ private: friend void BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec); }; +class SetConstExpression final : public UnaryExpression +{ +public: + SetConstExpression(const String& name, std::unique_ptr operand, const DebugInfo& debugInfo = DebugInfo()) + : UnaryExpression(std::move(operand), debugInfo), m_Name(name) + { } + +protected: + String m_Name; + + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; +}; + class SetExpression final : public BinaryExpression { public: diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index 24224af0a..7bfa5628d 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -28,6 +28,7 @@ #include "base/debuginfo.hpp" #include "base/array.hpp" #include "base/dictionary.hpp" +#include "base/namespace.hpp" #include "base/function.hpp" #include "base/scriptglobal.hpp" #include "base/exception.hpp" @@ -225,6 +226,26 @@ public: ExpressionResult res = expression->Evaluate(frame); CHECK_RESULT_LOOP(res); } + } else if (value.IsObjectType()) { + if (fvvar.IsEmpty()) + BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for namespace.", debugInfo)); + + Namespace::Ptr ns = value; + std::vector keys; + + { + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + keys.push_back(kv.first); + } + } + + for (const String& key : keys) { + frame.Locals->Set(fkvar, key); + frame.Locals->Set(fvvar, ns->Get(key)); + ExpressionResult res = expression->Evaluate(frame); + CHECK_RESULT_LOOP(res); + } } else BOOST_THROW_EXCEPTION(ScriptError("Invalid type in for expression: " + value.GetTypeName(), debugInfo)); diff --git a/lib/db_ido/db_ido-itl.conf b/lib/db_ido/db_ido-itl.conf index e6958234a..ca1272a01 100644 --- a/lib/db_ido/db_ido-itl.conf +++ b/lib/db_ido/db_ido-itl.conf @@ -18,10 +18,8 @@ ******************************************************************************/ System.assert(Internal.run_with_activation_context(function() { - var _Internal = Internal.clone() - - template CheckCommand "ido-check-command" use (_Internal) { - execute = _Internal.IdoCheck + template CheckCommand "ido-check-command" use (checkFunc = Internal.IdoCheck) { + execute = checkFunc } object CheckCommand "ido" { diff --git a/lib/db_ido/idochecktask.cpp b/lib/db_ido/idochecktask.cpp index 20fc57d0b..ed87b9e0b 100644 --- a/lib/db_ido/idochecktask.cpp +++ b/lib/db_ido/idochecktask.cpp @@ -32,7 +32,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/icinga/cib.cpp b/lib/icinga/cib.cpp index e68df57ad..d84dad4c8 100644 --- a/lib/icinga/cib.cpp +++ b/lib/icinga/cib.cpp @@ -268,13 +268,13 @@ std::pair CIB::GetFeatureStats() Dictionary::Ptr status = new Dictionary(); Array::Ptr perfdata = new Array(); - Dictionary::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); + Namespace::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); if (statsFunctions) { ObjectLock olock(statsFunctions); - for (const Dictionary::Pair& kv : statsFunctions) - static_cast(kv.second)->Invoke({ status, perfdata }); + for (const Namespace::Pair& kv : statsFunctions) + static_cast(kv.second->Get())->Invoke({ status, perfdata }); } return std::make_pair(status, perfdata); diff --git a/lib/icinga/icinga-itl.conf b/lib/icinga/icinga-itl.conf index 6fcfaf518..b734a7032 100644 --- a/lib/icinga/icinga-itl.conf +++ b/lib/icinga/icinga-itl.conf @@ -18,10 +18,8 @@ ******************************************************************************/ System.assert(Internal.run_with_activation_context(function() { - var _Internal = Internal.clone() - - template TimePeriod "legacy-timeperiod" use (_Internal) default { - update = _Internal.LegacyTimePeriod + template TimePeriod "legacy-timeperiod" use (LegacyTimePeriod = Internal.LegacyTimePeriod) default { + update = LegacyTimePeriod } })) diff --git a/lib/icinga/legacytimeperiod.cpp b/lib/icinga/legacytimeperiod.cpp index 8075d2810..777a1cc10 100644 --- a/lib/icinga/legacytimeperiod.cpp +++ b/lib/icinga/legacytimeperiod.cpp @@ -28,7 +28,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, LegacyTimePeriod, &LegacyTimePeriod::ScriptFunc, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, LegacyTimePeriod, &LegacyTimePeriod::ScriptFunc, "tp:begin:end"); bool LegacyTimePeriod::IsInTimeRange(tm *begin, tm *end, int stride, tm *reference) { diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp index 3e9bdea21..da51f5343 100644 --- a/lib/methods/clusterchecktask.cpp +++ b/lib/methods/clusterchecktask.cpp @@ -33,7 +33,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, ClusterCheck, &ClusterCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, ClusterCheck, &ClusterCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index 2b1badf0c..67ce5f85b 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -29,7 +29,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index 51cc63480..85985f367 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -31,7 +31,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, DummyCheck, &DummyCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, DummyCheck, &DummyCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/exceptionchecktask.cpp b/lib/methods/exceptionchecktask.cpp index 3f07c396a..e68a66aa8 100644 --- a/lib/methods/exceptionchecktask.cpp +++ b/lib/methods/exceptionchecktask.cpp @@ -29,7 +29,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, ExceptionCheck, &ExceptionCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, ExceptionCheck, &ExceptionCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void ExceptionCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index ae263a4dc..0f10739a7 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -34,7 +34,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, IcingaCheck, &IcingaCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, IcingaCheck, &IcingaCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/methods-itl.conf b/lib/methods/methods-itl.conf index a45439ccd..22d8fff26 100644 --- a/lib/methods/methods-itl.conf +++ b/lib/methods/methods-itl.conf @@ -18,68 +18,58 @@ ******************************************************************************/ System.assert(Internal.run_with_activation_context(function() { - var _Internal = Internal.clone() - - template CheckCommand "icinga-check-command" use (_Internal) { - execute = _Internal.IcingaCheck + template CheckCommand "icinga-check-command" use (IcingaCheck = Internal.IcingaCheck) { + execute = IcingaCheck vars.icinga_min_version = "" } - template CheckCommand "cluster-check-command" use (_Internal) { - execute = _Internal.ClusterCheck + template CheckCommand "cluster-check-command" use (ClusterCheck = Internal.ClusterCheck) { + execute = ClusterCheck } - template CheckCommand "cluster-zone-check-command" use (_Internal) { - execute = _Internal.ClusterZoneCheck + template CheckCommand "cluster-zone-check-command" use (ClusterZoneCheck = Internal.ClusterZoneCheck) { + execute = ClusterZoneCheck } - template CheckCommand "plugin-check-command" use (_Internal) default { - execute = _Internal.PluginCheck + template CheckCommand "plugin-check-command" use (PluginCheck = Internal.PluginCheck) default { + execute = PluginCheck } - template CheckCommand "clr-check-command" use (_Internal) { - if (_Internal.ClrCheck) { - execute = _Internal.ClrCheck - } else { - execute = _Internal.NullCheck - } + template NotificationCommand "plugin-notification-command" use (PluginNotification = Internal.PluginNotification) default { + execute = PluginNotification } - template NotificationCommand "plugin-notification-command" use (_Internal) default { - execute = _Internal.PluginNotification + template EventCommand "plugin-event-command" use (PluginEvent = Internal.PluginEvent) default { + execute = PluginEvent } - template EventCommand "plugin-event-command" use (_Internal) default { - execute = _Internal.PluginEvent + template CheckCommand "dummy-check-command" use (DummyCheck = Internal.DummyCheck) { + execute = DummyCheck } - template CheckCommand "dummy-check-command" use (_Internal) { - execute = _Internal.DummyCheck + template CheckCommand "random-check-command" use (RandomCheck = Internal.RandomCheck) { + execute = RandomCheck } - template CheckCommand "random-check-command" use (_Internal) { - execute = _Internal.RandomCheck + template CheckCommand "exception-check-command" use (ExceptionCheck = Internal.ExceptionCheck) { + execute = ExceptionCheck } - template CheckCommand "exception-check-command" use (_Internal) { - execute = _Internal.ExceptionCheck + template CheckCommand "null-check-command" use (NullCheck = Internal.NullCheck) { + execute = NullCheck } - template CheckCommand "null-check-command" use (_Internal) { - execute = _Internal.NullCheck + template EventCommand "null-event-command" use (NullEvent = Internal.NullEvent) { + execute = NullEvent } - template EventCommand "null-event-command" use (_Internal) { - execute = _Internal.NullEvent + template TimePeriod "empty-timeperiod" use (EmptyTimePeriod = Internal.EmptyTimePeriod) { + update = EmptyTimePeriod } - template TimePeriod "empty-timeperiod" use (_Internal) { - update = _Internal.EmptyTimePeriod - } - - template TimePeriod "even-minutes-timeperiod" use (_Internal) { - update = _Internal.EvenMinutesTimePeriod + template TimePeriod "even-minutes-timeperiod" use (EvenMinutesTimePeriod = Internal.EvenMinutesTimePeriod) { + update = EvenMinutesTimePeriod } })) diff --git a/lib/methods/nullchecktask.cpp b/lib/methods/nullchecktask.cpp index 85ee94444..c141decfe 100644 --- a/lib/methods/nullchecktask.cpp +++ b/lib/methods/nullchecktask.cpp @@ -30,7 +30,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, NullCheck, &NullCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, NullCheck, &NullCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void NullCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/nulleventtask.cpp b/lib/methods/nulleventtask.cpp index 1805e3e9f..e5a80ed76 100644 --- a/lib/methods/nulleventtask.cpp +++ b/lib/methods/nulleventtask.cpp @@ -23,7 +23,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, NullEvent, &NullEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, NullEvent, &NullEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); void NullEventTask::ScriptFunc(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 402c36e0a..1709583a4 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -31,7 +31,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, PluginCheck, &PluginCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, PluginCheck, &PluginCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 00b55fb8a..d42db23f3 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -31,7 +31,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, PluginEvent, &PluginEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, PluginEvent, &PluginEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index e345e2a01..948f2dbed 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -32,7 +32,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros"); void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, const User::Ptr& user, const CheckResult::Ptr& cr, int itype, diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp index c228057a5..25b3b593a 100644 --- a/lib/methods/randomchecktask.cpp +++ b/lib/methods/randomchecktask.cpp @@ -29,7 +29,7 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, RandomCheck, &RandomCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, RandomCheck, &RandomCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/timeperiodtask.cpp b/lib/methods/timeperiodtask.cpp index 9cef126ed..ccd7bc979 100644 --- a/lib/methods/timeperiodtask.cpp +++ b/lib/methods/timeperiodtask.cpp @@ -22,8 +22,8 @@ using namespace icinga; -REGISTER_FUNCTION(Internal, EmptyTimePeriod, &TimePeriodTask::EmptyTimePeriodUpdate, "tp:begin:end"); -REGISTER_FUNCTION(Internal, EvenMinutesTimePeriod, &TimePeriodTask::EvenMinutesTimePeriodUpdate, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, EmptyTimePeriod, &TimePeriodTask::EmptyTimePeriodUpdate, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, EvenMinutesTimePeriod, &TimePeriodTask::EvenMinutesTimePeriodUpdate, "tp:begin:end"); Array::Ptr TimePeriodTask::EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, double, double) { diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index 5c5af049d..045cb59d5 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -27,6 +27,7 @@ #include "base/logger.hpp" #include "base/serializer.hpp" #include "base/timer.hpp" +#include "base/namespace.hpp" #include "base/initialize.hpp" #include #include @@ -233,6 +234,15 @@ static void AddSuggestions(std::vector& matches, const String& word, con } } + if (value.IsObjectType()) { + Namespace::Ptr ns = value; + + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + AddSuggestion(matches, word, prefix + kv.first); + } + } + if (withFields) { Type::Ptr type = value.GetReflectionType(); @@ -275,7 +285,7 @@ std::vector ConsoleHandler::GetAutocompletionSuggestions(const String& w { ObjectLock olock(ScriptGlobal::GetGlobals()); - for (const Dictionary::Pair& kv : ScriptGlobal::GetGlobals()) { + for (const Namespace::Pair& kv : ScriptGlobal::GetGlobals()) { AddSuggestion(matches, word, kv.first); } } diff --git a/lib/remote/jsonrpc.cpp b/lib/remote/jsonrpc.cpp index fa6c3df00..710d184aa 100644 --- a/lib/remote/jsonrpc.cpp +++ b/lib/remote/jsonrpc.cpp @@ -37,7 +37,7 @@ static bool GetDebugJsonRpcCached() debugJsonRpc = false; - Dictionary::Ptr internal = ScriptGlobal::Get("Internal", &Empty); + Namespace::Ptr internal = ScriptGlobal::Get("Internal", &Empty); if (!internal) return false; diff --git a/lib/remote/statushandler.cpp b/lib/remote/statushandler.cpp index b2a9fca10..7ef59ed68 100644 --- a/lib/remote/statushandler.cpp +++ b/lib/remote/statushandler.cpp @@ -22,6 +22,7 @@ #include "remote/filterutility.hpp" #include "base/serializer.hpp" #include "base/statsfunction.hpp" +#include "base/namespace.hpp" using namespace icinga; @@ -35,24 +36,29 @@ public: void FindTargets(const String& type, const std::function& addTarget) const override { - Dictionary::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); + Namespace::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); if (statsFunctions) { ObjectLock olock(statsFunctions); - for (const Dictionary::Pair& kv : statsFunctions) + for (const Namespace::Pair& kv : statsFunctions) addTarget(GetTargetByName("Status", kv.first)); } } Value GetTargetByName(const String& type, const String& name) const override { - Dictionary::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); + Namespace::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); if (!statsFunctions) BOOST_THROW_EXCEPTION(std::invalid_argument("No status functions are available.")); - Function::Ptr func = statsFunctions->Get(name); + Value vfunc; + + if (!statsFunctions->Get(name, &vfunc)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid status function name.")); + + Function::Ptr func = vfunc; if (!func) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid status function name.")); diff --git a/lib/remote/variablequeryhandler.cpp b/lib/remote/variablequeryhandler.cpp index 4a36fc8ce..04cc254b9 100644 --- a/lib/remote/variablequeryhandler.cpp +++ b/lib/remote/variablequeryhandler.cpp @@ -24,6 +24,7 @@ #include "base/scriptglobal.hpp" #include "base/logger.hpp" #include "base/serializer.hpp" +#include "base/namespace.hpp" #include using namespace icinga; @@ -48,10 +49,10 @@ public: const std::function& addTarget) const override { { - Dictionary::Ptr globals = ScriptGlobal::GetGlobals(); + Namespace::Ptr globals = ScriptGlobal::GetGlobals(); ObjectLock olock(globals); - for (const Dictionary::Pair& kv : globals) { - addTarget(GetTargetForVar(kv.first, kv.second)); + for (const Namespace::Pair& kv : globals) { + addTarget(GetTargetForVar(kv.first, kv.second->Get())); } } }