summaryrefslogtreecommitdiffstats
path: root/lib/config
diff options
context:
space:
mode:
Diffstat (limited to 'lib/config')
-rw-r--r--lib/config/CMakeLists.txt47
-rw-r--r--lib/config/activationcontext.cpp61
-rw-r--r--lib/config/activationcontext.hpp46
-rw-r--r--lib/config/applyrule-targeted.cpp266
-rw-r--r--lib/config/applyrule.cpp189
-rw-r--r--lib/config/applyrule.hpp126
-rw-r--r--lib/config/config_lexer.ll253
-rw-r--r--lib/config/config_parser.yy1243
-rw-r--r--lib/config/configcompiler.cpp364
-rw-r--r--lib/config/configcompiler.hpp161
-rw-r--r--lib/config/configcompilercontext.cpp57
-rw-r--r--lib/config/configcompilercontext.hpp42
-rw-r--r--lib/config/configfragment.hpp26
-rw-r--r--lib/config/configitem.cpp849
-rw-r--r--lib/config/configitem.hpp106
-rw-r--r--lib/config/configitembuilder.cpp120
-rw-r--r--lib/config/configitembuilder.hpp58
-rw-r--r--lib/config/expression.cpp1068
-rw-r--r--lib/config/expression.hpp986
-rw-r--r--lib/config/i2-config.hpp16
-rw-r--r--lib/config/objectrule.cpp18
-rw-r--r--lib/config/objectrule.hpp33
-rw-r--r--lib/config/vmops.hpp274
23 files changed, 6409 insertions, 0 deletions
diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt
new file mode 100644
index 0000000..80b8c2c
--- /dev/null
+++ b/lib/config/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+
+find_package(BISON 2.3.0 REQUIRED)
+find_package(FLEX 2.5.31 REQUIRED)
+
+bison_target(config_parser config_parser.yy ${CMAKE_CURRENT_BINARY_DIR}/config_parser.cc)
+set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/config_parser.cc PROPERTY EXCLUDE_UNITY_BUILD TRUE)
+
+flex_target(config_lexer config_lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/config_lexer.cc)
+set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/config_lexer.cc PROPERTY EXCLUDE_UNITY_BUILD TRUE)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/config_parser.cc PROPERTY COMPILE_FLAGS "-Wno-deprecated-register -Wno-parentheses-equality -Wno-unused-function")
+ set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/config_lexer.cc PROPERTY COMPILE_FLAGS "-Wno-deprecated-register -Wno-null-conversion")
+endif()
+
+add_flex_bison_dependency(config_lexer config_parser)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+
+set(config_SOURCES
+ i2-config.hpp
+ activationcontext.cpp activationcontext.hpp
+ applyrule.cpp applyrule-targeted.cpp applyrule.hpp
+ configcompiler.cpp configcompiler.hpp
+ configcompilercontext.cpp configcompilercontext.hpp
+ configfragment.hpp
+ configitem.cpp configitem.hpp
+ configitembuilder.cpp configitembuilder.hpp
+ expression.cpp expression.hpp
+ objectrule.cpp objectrule.hpp
+ vmops.hpp
+ ${FLEX_config_lexer_OUTPUTS} ${BISON_config_parser_OUTPUTS}
+)
+
+if(ICINGA2_UNITY_BUILD)
+ mkunity_target(config config config_SOURCES)
+endif()
+
+add_library(config OBJECT ${config_SOURCES})
+
+add_dependencies(config base)
+
+set_target_properties (
+ config PROPERTIES
+ FOLDER Lib
+)
diff --git a/lib/config/activationcontext.cpp b/lib/config/activationcontext.cpp
new file mode 100644
index 0000000..d050875
--- /dev/null
+++ b/lib/config/activationcontext.cpp
@@ -0,0 +1,61 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/activationcontext.hpp"
+#include "base/exception.hpp"
+
+using namespace icinga;
+
+boost::thread_specific_ptr<std::stack<ActivationContext::Ptr> > ActivationContext::m_ActivationStack;
+
+std::stack<ActivationContext::Ptr>& ActivationContext::GetActivationStack()
+{
+ std::stack<ActivationContext::Ptr> *actx = m_ActivationStack.get();
+
+ if (!actx) {
+ actx = new std::stack<ActivationContext::Ptr>();
+ m_ActivationStack.reset(actx);
+ }
+
+ return *actx;
+}
+
+void ActivationContext::PushContext(const ActivationContext::Ptr& context)
+{
+ GetActivationStack().push(context);
+}
+
+void ActivationContext::PopContext()
+{
+ ASSERT(!GetActivationStack().empty());
+ GetActivationStack().pop();
+}
+
+ActivationContext::Ptr ActivationContext::GetCurrentContext()
+{
+ std::stack<ActivationContext::Ptr>& astack = GetActivationStack();
+
+ if (astack.empty())
+ BOOST_THROW_EXCEPTION(std::runtime_error("Objects may not be created outside of an activation context."));
+
+ return astack.top();
+}
+
+ActivationScope::ActivationScope(ActivationContext::Ptr context)
+ : m_Context(std::move(context))
+{
+ if (!m_Context)
+ m_Context = new ActivationContext();
+
+ ActivationContext::PushContext(m_Context);
+}
+
+ActivationScope::~ActivationScope()
+{
+ ActivationContext::PopContext();
+}
+
+ActivationContext::Ptr ActivationScope::GetContext() const
+{
+ return m_Context;
+}
+
diff --git a/lib/config/activationcontext.hpp b/lib/config/activationcontext.hpp
new file mode 100644
index 0000000..3fe5d09
--- /dev/null
+++ b/lib/config/activationcontext.hpp
@@ -0,0 +1,46 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef ACTIVATIONCONTEXT_H
+#define ACTIVATIONCONTEXT_H
+
+#include "config/i2-config.hpp"
+#include "base/object.hpp"
+#include <boost/thread/tss.hpp>
+#include <stack>
+
+namespace icinga
+{
+
+class ActivationContext final : public Object
+{
+public:
+ DECLARE_PTR_TYPEDEFS(ActivationContext);
+
+ static ActivationContext::Ptr GetCurrentContext();
+
+private:
+ static void PushContext(const ActivationContext::Ptr& context);
+ static void PopContext();
+
+ static std::stack<ActivationContext::Ptr>& GetActivationStack();
+
+ static boost::thread_specific_ptr<std::stack<ActivationContext::Ptr> > m_ActivationStack;
+
+ friend class ActivationScope;
+};
+
+class ActivationScope
+{
+public:
+ ActivationScope(ActivationContext::Ptr context = nullptr);
+ ~ActivationScope();
+
+ ActivationContext::Ptr GetContext() const;
+
+private:
+ ActivationContext::Ptr m_Context;
+};
+
+}
+
+#endif /* ACTIVATIONCONTEXT_H */
diff --git a/lib/config/applyrule-targeted.cpp b/lib/config/applyrule-targeted.cpp
new file mode 100644
index 0000000..c5bfe20
--- /dev/null
+++ b/lib/config/applyrule-targeted.cpp
@@ -0,0 +1,266 @@
+/* Icinga 2 | (c) 2022 Icinga GmbH | GPLv2+ */
+
+#include "base/string.hpp"
+#include "config/applyrule.hpp"
+#include "config/expression.hpp"
+#include <utility>
+#include <vector>
+
+using namespace icinga;
+
+/**
+ * @returns All ApplyRules targeting only specific parent objects including the given host. (See AddTargetedRule().)
+ */
+const std::set<ApplyRule::Ptr>& ApplyRule::GetTargetedHostRules(const Type::Ptr& sourceType, const String& host)
+{
+ auto perSourceType (m_Rules.find(sourceType.get()));
+
+ if (perSourceType != m_Rules.end()) {
+ auto perHost (perSourceType->second.Targeted.find(host));
+
+ if (perHost != perSourceType->second.Targeted.end()) {
+ return perHost->second.ForHost;
+ }
+ }
+
+ static const std::set<ApplyRule::Ptr> noRules;
+ return noRules;
+}
+
+/**
+ * @returns All ApplyRules targeting only specific parent objects including the given service. (See AddTargetedRule().)
+ */
+const std::set<ApplyRule::Ptr>& ApplyRule::GetTargetedServiceRules(const Type::Ptr& sourceType, const String& host, const String& service)
+{
+ auto perSourceType (m_Rules.find(sourceType.get()));
+
+ if (perSourceType != m_Rules.end()) {
+ auto perHost (perSourceType->second.Targeted.find(host));
+
+ if (perHost != perSourceType->second.Targeted.end()) {
+ auto perService (perHost->second.ForServices.find(service));
+
+ if (perService != perHost->second.ForServices.end()) {
+ return perService->second;
+ }
+ }
+ }
+
+ static const std::set<ApplyRule::Ptr> noRules;
+ return noRules;
+}
+
+/**
+ * If the given ApplyRule targets only specific parent objects, add it to the respective "index".
+ *
+ * - The above means for apply T "N" to Host: assign where host.name == "H" [ || host.name == "h" ... ]
+ * - For apply T "N" to Service it means: assign where host.name == "H" && service.name == "S" [ || host.name == "h" && service.name == "s" ... ]
+ *
+ * The order of operands of || && == doesn't matter.
+ *
+ * @returns Whether the rule has been added to the "index".
+ */
+bool ApplyRule::AddTargetedRule(const ApplyRule::Ptr& rule, const String& targetType, ApplyRule::PerSourceType& rules)
+{
+ if (targetType == "Host") {
+ std::vector<const String *> hosts;
+
+ if (GetTargetHosts(rule->m_Filter.get(), hosts)) {
+ for (auto host : hosts) {
+ rules.Targeted[*host].ForHost.emplace(rule);
+ }
+
+ return true;
+ }
+ } else if (targetType == "Service") {
+ std::vector<std::pair<const String *, const String *>> services;
+
+ if (GetTargetServices(rule->m_Filter.get(), services)) {
+ for (auto service : services) {
+ rules.Targeted[*service.first].ForServices[*service.second].emplace(rule);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * If the given assign filter is like the following, extract the host names ("H", "h", ...) into the vector:
+ *
+ * host.name == "H" [ || host.name == "h" ... ]
+ *
+ * The order of operands of || == doesn't matter.
+ *
+ * @returns Whether the given assign filter is like above.
+ */
+bool ApplyRule::GetTargetHosts(Expression* assignFilter, std::vector<const String *>& hosts, const Dictionary::Ptr& constants)
+{
+ auto lor (dynamic_cast<LogicalOrExpression*>(assignFilter));
+
+ if (lor) {
+ return GetTargetHosts(lor->GetOperand1().get(), hosts, constants)
+ && GetTargetHosts(lor->GetOperand2().get(), hosts, constants);
+ }
+
+ auto name (GetComparedName(assignFilter, "host", constants));
+
+ if (name) {
+ hosts.emplace_back(name);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * If the given assign filter is like the following, extract the host+service names ("H"+"S", "h"+"s", ...) into the vector:
+ *
+ * host.name == "H" && service.name == "S" [ || host.name == "h" && service.name == "s" ... ]
+ *
+ * The order of operands of || && == doesn't matter.
+ *
+ * @returns Whether the given assign filter is like above.
+ */
+bool ApplyRule::GetTargetServices(Expression* assignFilter, std::vector<std::pair<const String *, const String *>>& services, const Dictionary::Ptr& constants)
+{
+ auto lor (dynamic_cast<LogicalOrExpression*>(assignFilter));
+
+ if (lor) {
+ return GetTargetServices(lor->GetOperand1().get(), services, constants)
+ && GetTargetServices(lor->GetOperand2().get(), services, constants);
+ }
+
+ auto service (GetTargetService(assignFilter, constants));
+
+ if (service.first) {
+ services.emplace_back(service);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * If the given filter is like the following, extract the host+service names ("H"+"S"):
+ *
+ * host.name == "H" && service.name == "S"
+ *
+ * The order of operands of && == doesn't matter.
+ *
+ * @returns {host, service} on success and {nullptr, nullptr} on failure.
+ */
+std::pair<const String *, const String *> ApplyRule::GetTargetService(Expression* assignFilter, const Dictionary::Ptr& constants)
+{
+ auto land (dynamic_cast<LogicalAndExpression*>(assignFilter));
+
+ if (!land) {
+ return {nullptr, nullptr};
+ }
+
+ auto op1 (land->GetOperand1().get());
+ auto op2 (land->GetOperand2().get());
+ auto host (GetComparedName(op1, "host", constants));
+
+ if (!host) {
+ std::swap(op1, op2);
+ host = GetComparedName(op1, "host", constants);
+ }
+
+ if (host) {
+ auto service (GetComparedName(op2, "service", constants));
+
+ if (service) {
+ return {host, service};
+ }
+ }
+
+ return {nullptr, nullptr};
+}
+
+/**
+ * If the given filter is like the following, extract the object name ("N"):
+ *
+ * $lcType$.name == "N"
+ *
+ * The order of operands of == doesn't matter.
+ *
+ * @returns The object name on success and nullptr on failure.
+ */
+const String * ApplyRule::GetComparedName(Expression* assignFilter, const char * lcType, const Dictionary::Ptr& constants)
+{
+ auto eq (dynamic_cast<EqualExpression*>(assignFilter));
+
+ if (!eq) {
+ return nullptr;
+ }
+
+ auto op1 (eq->GetOperand1().get());
+ auto op2 (eq->GetOperand2().get());
+
+ if (IsNameIndexer(op1, lcType, constants)) {
+ return GetConstString(op2, constants);
+ }
+
+ if (IsNameIndexer(op2, lcType, constants)) {
+ return GetConstString(op1, constants);
+ }
+
+ return nullptr;
+}
+
+/**
+ * @returns Whether the given expression is like $lcType$.name.
+ */
+bool ApplyRule::IsNameIndexer(Expression* exp, const char * lcType, const Dictionary::Ptr& constants)
+{
+ auto ixr (dynamic_cast<IndexerExpression*>(exp));
+
+ if (!ixr) {
+ return false;
+ }
+
+ auto var (dynamic_cast<VariableExpression*>(ixr->GetOperand1().get()));
+
+ if (!var || var->GetVariable() != lcType) {
+ return false;
+ }
+
+ auto val (GetConstString(ixr->GetOperand2().get(), constants));
+
+ return val && *val == "name";
+}
+
+/**
+ * @returns If the given expression is a constant string, its address. nullptr on failure.
+ */
+const String * ApplyRule::GetConstString(Expression* exp, const Dictionary::Ptr& constants)
+{
+ auto cnst (GetConst(exp, constants));
+
+ return cnst && cnst->IsString() ? &cnst->Get<String>() : nullptr;
+}
+
+/**
+ * @returns If the given expression is a constant, its address. nullptr on failure.
+ */
+const Value * ApplyRule::GetConst(Expression* exp, const Dictionary::Ptr& constants)
+{
+ auto lit (dynamic_cast<LiteralExpression*>(exp));
+
+ if (lit) {
+ return &lit->GetValue();
+ }
+
+ if (constants) {
+ auto var (dynamic_cast<VariableExpression*>(exp));
+
+ if (var) {
+ return constants->GetRef(var->GetVariable());
+ }
+ }
+
+ return nullptr;
+}
diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp
new file mode 100644
index 0000000..8739971
--- /dev/null
+++ b/lib/config/applyrule.cpp
@@ -0,0 +1,189 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/applyrule.hpp"
+#include "base/logger.hpp"
+#include <set>
+#include <unordered_set>
+
+using namespace icinga;
+
+ApplyRule::RuleMap ApplyRule::m_Rules;
+ApplyRule::TypeMap ApplyRule::m_Types;
+
+ApplyRule::ApplyRule(String name, Expression::Ptr expression,
+ Expression::Ptr filter, String package, String fkvar, String fvvar, Expression::Ptr fterm,
+ bool ignoreOnError, DebugInfo di, Dictionary::Ptr scope)
+ : m_Name(std::move(name)), m_Expression(std::move(expression)), m_Filter(std::move(filter)), m_Package(std::move(package)), m_FKVar(std::move(fkvar)),
+ m_FVVar(std::move(fvvar)), m_FTerm(std::move(fterm)), m_IgnoreOnError(ignoreOnError), m_DebugInfo(std::move(di)), m_Scope(std::move(scope)), m_HasMatches(false)
+{ }
+
+String ApplyRule::GetName() const
+{
+ return m_Name;
+}
+
+Expression::Ptr ApplyRule::GetExpression() const
+{
+ return m_Expression;
+}
+
+Expression::Ptr ApplyRule::GetFilter() const
+{
+ return m_Filter;
+}
+
+String ApplyRule::GetPackage() const
+{
+ return m_Package;
+}
+
+Expression::Ptr ApplyRule::GetFTerm() const
+{
+ return m_FTerm;
+}
+
+bool ApplyRule::GetIgnoreOnError() const
+{
+ return m_IgnoreOnError;
+}
+
+const DebugInfo& ApplyRule::GetDebugInfo() const
+{
+ return m_DebugInfo;
+}
+
+Dictionary::Ptr ApplyRule::GetScope() const
+{
+ return m_Scope;
+}
+
+void ApplyRule::AddRule(const String& sourceType, const String& targetType, const String& name,
+ const Expression::Ptr& expression, const Expression::Ptr& filter, const String& package, const String& fkvar,
+ const String& fvvar, const Expression::Ptr& fterm, bool ignoreOnError, const DebugInfo& di, const Dictionary::Ptr& scope)
+{
+ auto actualTargetType (&targetType);
+
+ if (*actualTargetType == "") {
+ auto& targetTypes (GetTargetTypes(sourceType));
+
+ if (targetTypes.size() == 1u) {
+ actualTargetType = &targetTypes[0];
+ }
+ }
+
+ ApplyRule::Ptr rule = new ApplyRule(name, expression, filter, package, fkvar, fvvar, fterm, ignoreOnError, di, scope);
+ auto& rules (m_Rules[Type::GetByName(sourceType).get()]);
+
+ if (!AddTargetedRule(rule, *actualTargetType, rules)) {
+ rules.Regular[Type::GetByName(*actualTargetType).get()].emplace_back(std::move(rule));
+ }
+}
+
+bool ApplyRule::EvaluateFilter(ScriptFrame& frame) const
+{
+ return Convert::ToBool(m_Filter->Evaluate(frame));
+}
+
+void ApplyRule::RegisterType(const String& sourceType, const std::vector<String>& targetTypes)
+{
+ m_Types[sourceType] = targetTypes;
+}
+
+bool ApplyRule::IsValidSourceType(const String& sourceType)
+{
+ return m_Types.find(sourceType) != m_Types.end();
+}
+
+bool ApplyRule::IsValidTargetType(const String& sourceType, const String& targetType)
+{
+ auto it = m_Types.find(sourceType);
+
+ if (it == m_Types.end())
+ return false;
+
+ if (it->second.size() == 1 && targetType == "")
+ return true;
+
+ for (const String& type : it->second) {
+ if (type == targetType)
+ return true;
+ }
+
+ return false;
+}
+
+const std::vector<String>& ApplyRule::GetTargetTypes(const String& sourceType)
+{
+ auto it = m_Types.find(sourceType);
+
+ if (it == m_Types.end()) {
+ static const std::vector<String> noTypes;
+ return noTypes;
+ }
+
+ return it->second;
+}
+
+void ApplyRule::AddMatch()
+{
+ m_HasMatches.store(true, std::memory_order_relaxed);
+}
+
+bool ApplyRule::HasMatches() const
+{
+ return m_HasMatches.load(std::memory_order_relaxed);
+}
+
+const std::vector<ApplyRule::Ptr>& ApplyRule::GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType)
+{
+ auto perSourceType (m_Rules.find(sourceType.get()));
+
+ if (perSourceType != m_Rules.end()) {
+ auto perTargetType (perSourceType->second.Regular.find(targetType.get()));
+
+ if (perTargetType != perSourceType->second.Regular.end()) {
+ return perTargetType->second;
+ }
+ }
+
+ static const std::vector<ApplyRule::Ptr> noRules;
+ return noRules;
+}
+
+void ApplyRule::CheckMatches(bool silent)
+{
+ for (auto& perSourceType : m_Rules) {
+ for (auto& perTargetType : perSourceType.second.Regular) {
+ for (auto& rule : perTargetType.second) {
+ CheckMatches(rule, perSourceType.first, silent);
+ }
+ }
+
+ std::unordered_set<ApplyRule*> targeted;
+
+ for (auto& perHost : perSourceType.second.Targeted) {
+ for (auto& rule : perHost.second.ForHost) {
+ targeted.emplace(rule.get());
+ }
+
+ for (auto& perService : perHost.second.ForServices) {
+ for (auto& rule : perService.second) {
+ targeted.emplace(rule.get());
+ }
+ }
+ }
+
+ for (auto rule : targeted) {
+ CheckMatches(rule, perSourceType.first, silent);
+ }
+ }
+}
+
+void ApplyRule::CheckMatches(const ApplyRule::Ptr& rule, Type* sourceType, bool silent)
+{
+ if (!rule->HasMatches() && !silent) {
+ Log(LogWarning, "ApplyRule")
+ << "Apply rule '" << rule->GetName() << "' (" << rule->GetDebugInfo() << ") for type '"
+ << sourceType->GetName() << "' does not match anywhere!";
+ }
+}
diff --git a/lib/config/applyrule.hpp b/lib/config/applyrule.hpp
new file mode 100644
index 0000000..cf9b6e5
--- /dev/null
+++ b/lib/config/applyrule.hpp
@@ -0,0 +1,126 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef APPLYRULE_H
+#define APPLYRULE_H
+
+#include "config/i2-config.hpp"
+#include "config/expression.hpp"
+#include "base/debuginfo.hpp"
+#include "base/shared-object.hpp"
+#include "base/type.hpp"
+#include <unordered_map>
+#include <atomic>
+
+namespace icinga
+{
+
+/**
+ * @ingroup config
+ */
+class ApplyRule : public SharedObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(ApplyRule);
+
+ struct PerHost
+ {
+ std::set<ApplyRule::Ptr> ForHost;
+ std::unordered_map<String /* service */, std::set<ApplyRule::Ptr>> ForServices;
+ };
+
+ struct PerSourceType
+ {
+ std::unordered_map<Type* /* target type */, std::vector<ApplyRule::Ptr>> Regular;
+ std::unordered_map<String /* host */, PerHost> Targeted;
+ };
+
+ /*
+ * m_Rules[T::TypeInstance.get()].Targeted["H"].ForHost
+ * contains all apply rules like apply T "x" to Host { ... }
+ * which target only specific hosts incl. "H", e.g. via
+ * assign where host.name == "H" || host.name == "h".
+ *
+ * m_Rules[T::TypeInstance.get()].Targeted["H"].ForServices["S"]
+ * contains all apply rules like apply T "x" to Service { ... }
+ * which target only specific services on specific hosts,
+ * e.g. via assign where host.name == "H" && service.name == "S".
+ *
+ * m_Rules[T::TypeInstance.get()].Regular[C::TypeInstance.get()]
+ * contains all other apply rules like apply T "x" to C { ... }.
+ */
+ typedef std::unordered_map<Type* /* source type */, PerSourceType> RuleMap;
+
+ typedef std::map<String, std::vector<String> > TypeMap;
+
+ String GetName() const;
+ Expression::Ptr GetExpression() const;
+ Expression::Ptr GetFilter() const;
+ String GetPackage() const;
+
+ inline const String& GetFKVar() const noexcept
+ {
+ return m_FKVar;
+ }
+
+ inline const String& GetFVVar() const noexcept
+ {
+ return m_FVVar;
+ }
+
+ Expression::Ptr GetFTerm() const;
+ bool GetIgnoreOnError() const;
+ const DebugInfo& GetDebugInfo() const;
+ Dictionary::Ptr GetScope() const;
+ void AddMatch();
+ bool HasMatches() const;
+
+ bool EvaluateFilter(ScriptFrame& frame) const;
+
+ static void AddRule(const String& sourceType, const String& targetType, const String& name, const Expression::Ptr& expression,
+ const Expression::Ptr& filter, const String& package, const String& fkvar, const String& fvvar, const Expression::Ptr& fterm,
+ bool ignoreOnError, const DebugInfo& di, const Dictionary::Ptr& scope);
+ static const std::vector<ApplyRule::Ptr>& GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType);
+ static const std::set<ApplyRule::Ptr>& GetTargetedHostRules(const Type::Ptr& sourceType, const String& host);
+ static const std::set<ApplyRule::Ptr>& GetTargetedServiceRules(const Type::Ptr& sourceType, const String& host, const String& service);
+ static bool GetTargetHosts(Expression* assignFilter, std::vector<const String *>& hosts, const Dictionary::Ptr& constants = nullptr);
+ static bool GetTargetServices(Expression* assignFilter, std::vector<std::pair<const String *, const String *>>& services, const Dictionary::Ptr& constants = nullptr);
+
+ static void RegisterType(const String& sourceType, const std::vector<String>& targetTypes);
+ static bool IsValidSourceType(const String& sourceType);
+ static bool IsValidTargetType(const String& sourceType, const String& targetType);
+ static const std::vector<String>& GetTargetTypes(const String& sourceType);
+
+ static void CheckMatches(bool silent);
+ static void CheckMatches(const ApplyRule::Ptr& rule, Type* sourceType, bool silent);
+
+private:
+ String m_Name;
+ Expression::Ptr m_Expression;
+ Expression::Ptr m_Filter;
+ String m_Package;
+ String m_FKVar;
+ String m_FVVar;
+ Expression::Ptr m_FTerm;
+ bool m_IgnoreOnError;
+ DebugInfo m_DebugInfo;
+ Dictionary::Ptr m_Scope;
+ std::atomic<bool> m_HasMatches;
+
+ static TypeMap m_Types;
+ static RuleMap m_Rules;
+
+ static bool AddTargetedRule(const ApplyRule::Ptr& rule, const String& targetType, PerSourceType& rules);
+ static std::pair<const String *, const String *> GetTargetService(Expression* assignFilter, const Dictionary::Ptr& constants);
+ static const String * GetComparedName(Expression* assignFilter, const char * lcType, const Dictionary::Ptr& constants);
+ static bool IsNameIndexer(Expression* exp, const char * lcType, const Dictionary::Ptr& constants);
+ static const String * GetConstString(Expression* exp, const Dictionary::Ptr& constants);
+ static const Value * GetConst(Expression* exp, const Dictionary::Ptr& constants);
+
+ ApplyRule(String name, Expression::Ptr expression,
+ Expression::Ptr filter, String package, String fkvar, String fvvar, Expression::Ptr fterm,
+ bool ignoreOnError, DebugInfo di, Dictionary::Ptr scope);
+};
+
+}
+
+#endif /* APPLYRULE_H */
diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll
new file mode 100644
index 0000000..abfdaff
--- /dev/null
+++ b/lib/config/config_lexer.ll
@@ -0,0 +1,253 @@
+%{
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/configcompiler.hpp"
+#include "config/expression.hpp"
+#include "base/exception.hpp"
+#include <utility>
+
+using namespace icinga;
+
+#include "config/config_parser.hh"
+#include <sstream>
+
+#define YYLTYPE icinga::CompilerDebugInfo
+
+#define YY_EXTRA_TYPE ConfigCompiler *
+#define YY_USER_ACTION \
+do { \
+ yylloc->Path = yyextra->GetPath(); \
+ yylloc->FirstLine = yylineno; \
+ yylloc->FirstColumn = yycolumn; \
+ yylloc->LastLine = yylineno; \
+ yylloc->LastColumn = yycolumn + yyleng - 1; \
+ yycolumn += yyleng; \
+} while (0);
+
+#define YY_INPUT(buf, result, max_size) \
+do { \
+ result = yyextra->ReadInput(buf, max_size); \
+} while (0)
+%}
+
+%option reentrant noyywrap yylineno
+%option bison-bridge bison-locations
+%option never-interactive nounistd
+%option noinput nounput
+
+%x C_COMMENT
+%x STRING
+%x HEREDOC
+
+%%
+\" {
+ yyextra->m_LexBuffer.Clear();
+
+ yyextra->m_LocationBegin = *yylloc;
+
+ BEGIN(STRING);
+ }
+
+<STRING>\" {
+ BEGIN(INITIAL);
+
+ yylloc->FirstLine = yyextra->m_LocationBegin.FirstLine;
+ yylloc->FirstColumn = yyextra->m_LocationBegin.FirstColumn;
+
+ yylval->text = new String(std::move(yyextra->m_LexBuffer));
+
+ return T_STRING;
+ }
+
+<STRING>\n {
+ BOOST_THROW_EXCEPTION(ScriptError("Unterminated string literal", DebugInfoRange(yyextra->m_LocationBegin, *yylloc)));
+ }
+
+<STRING>\\[0-7]{1,3} {
+ /* octal escape sequence */
+ int result;
+
+ (void) sscanf(yytext + 1, "%o", &result);
+
+ if (result > 0xff) {
+ /* error, constant is out-of-bounds */
+ BOOST_THROW_EXCEPTION(ScriptError("Constant is out of bounds: " + String(yytext), *yylloc));
+ }
+
+ yyextra->m_LexBuffer += static_cast<char>(result);
+ }
+
+<STRING>\\[0-9]+ {
+ /* generate error - bad escape sequence; something
+ * like '\48' or '\0777777'
+ */
+ BOOST_THROW_EXCEPTION(ScriptError("Bad escape sequence found: " + String(yytext), *yylloc));
+ }
+<STRING>\\n { yyextra->m_LexBuffer += '\n'; }
+<STRING>\\\\ { yyextra->m_LexBuffer += '\\'; }
+<STRING>\\\" { yyextra->m_LexBuffer += '"'; }
+<STRING>\\t { yyextra->m_LexBuffer += '\t'; }
+<STRING>\\r { yyextra->m_LexBuffer += '\r'; }
+<STRING>\\b { yyextra->m_LexBuffer += '\b'; }
+<STRING>\\f { yyextra->m_LexBuffer += '\f'; }
+<STRING>\\\n { yyextra->m_LexBuffer += yytext[1]; }
+<STRING>\\. {
+ BOOST_THROW_EXCEPTION(ScriptError("Bad escape sequence found: " + String(yytext), *yylloc));
+ }
+
+<STRING>[^\\\n\"]+ {
+ char *yptr = yytext;
+
+ while (*yptr)
+ yyextra->m_LexBuffer += *yptr++;
+ }
+
+<STRING><<EOF>> {
+ BOOST_THROW_EXCEPTION(ScriptError("End-of-file while in string literal", DebugInfoRange(yyextra->m_LocationBegin, *yylloc)));
+ }
+
+\{\{\{ {
+ yyextra->m_LexBuffer.Clear();
+
+ yyextra->m_LocationBegin = *yylloc;
+
+ BEGIN(HEREDOC);
+ }
+
+<HEREDOC><<EOF>> {
+ BOOST_THROW_EXCEPTION(ScriptError("End-of-file while in string literal", DebugInfoRange(yyextra->m_LocationBegin, *yylloc)));
+ }
+
+<HEREDOC>\}\}\} {
+ BEGIN(INITIAL);
+
+ yylloc->FirstLine = yyextra->m_LocationBegin.FirstLine;
+ yylloc->FirstColumn = yyextra->m_LocationBegin.FirstColumn;
+
+ yylval->text = new String(std::move(yyextra->m_LexBuffer));
+
+ return T_STRING;
+ }
+
+<HEREDOC>(.|\n) { yyextra->m_LexBuffer += yytext[0]; }
+
+<INITIAL>{
+"/*" BEGIN(C_COMMENT);
+}
+
+<C_COMMENT>{
+"*/" BEGIN(INITIAL);
+[^*] /* ignore comment */
+"*" /* ignore star */
+}
+
+<C_COMMENT><<EOF>> {
+ BOOST_THROW_EXCEPTION(ScriptError("End-of-file while in comment", *yylloc));
+ }
+
+
+\/\/[^\n]* /* ignore C++-style comments */
+#[^\n]* /* ignore shell-style comments */
+[ \t] /* ignore whitespace */
+
+<INITIAL>{
+object return T_OBJECT;
+template return T_TEMPLATE;
+include return T_INCLUDE;
+include_recursive return T_INCLUDE_RECURSIVE;
+include_zones return T_INCLUDE_ZONES;
+library return T_LIBRARY;
+null return T_NULL;
+true { yylval->boolean = 1; return T_BOOLEAN; }
+false { yylval->boolean = 0; return T_BOOLEAN; }
+const return T_CONST;
+var return T_VAR;
+this return T_THIS;
+globals return T_GLOBALS;
+locals return T_LOCALS;
+use return T_USE;
+using return T_USING;
+apply return T_APPLY;
+default return T_DEFAULT;
+to return T_TO;
+where return T_WHERE;
+import return T_IMPORT;
+assign return T_ASSIGN;
+ignore return T_IGNORE;
+function return T_FUNCTION;
+return return T_RETURN;
+break return T_BREAK;
+continue return T_CONTINUE;
+for return T_FOR;
+if return T_IF;
+else return T_ELSE;
+while return T_WHILE;
+throw return T_THROW;
+try return T_TRY;
+except return T_EXCEPT;
+ignore_on_error return T_IGNORE_ON_ERROR;
+current_filename return T_CURRENT_FILENAME;
+current_line return T_CURRENT_LINE;
+debugger return T_DEBUGGER;
+namespace return T_NAMESPACE;
+=\> return T_FOLLOWS;
+\<\< return T_SHIFT_LEFT;
+\>\> return T_SHIFT_RIGHT;
+\<= return T_LESS_THAN_OR_EQUAL;
+\>= return T_GREATER_THAN_OR_EQUAL;
+== return T_EQUAL;
+!= return T_NOT_EQUAL;
+!in return T_NOT_IN;
+in return T_IN;
+&& return T_LOGICAL_AND;
+\|\| return T_LOGICAL_OR;
+\{\{ return T_NULLARY_LAMBDA_BEGIN;
+\}\} return T_NULLARY_LAMBDA_END;
+[a-zA-Z_][a-zA-Z0-9\_]* { yylval->text = new String(yytext); return T_IDENTIFIER; }
+@[a-zA-Z_][a-zA-Z0-9\_]* { yylval->text = new String(yytext + 1); return T_IDENTIFIER; }
+\<[^ \>]*\> { yytext[yyleng-1] = '\0'; yylval->text = new String(yytext + 1); return T_STRING_ANGLE; }
+[0-9]+(\.[0-9]+)?ms { yylval->num = strtod(yytext, NULL) / 1000; return T_NUMBER; }
+[0-9]+(\.[0-9]+)?d { yylval->num = strtod(yytext, NULL) * 60 * 60 * 24; return T_NUMBER; }
+[0-9]+(\.[0-9]+)?h { yylval->num = strtod(yytext, NULL) * 60 * 60; return T_NUMBER; }
+[0-9]+(\.[0-9]+)?m { yylval->num = strtod(yytext, NULL) * 60; return T_NUMBER; }
+[0-9]+(\.[0-9]+)?s { yylval->num = strtod(yytext, NULL); return T_NUMBER; }
+[0-9]+(\.[0-9]+)? { yylval->num = strtod(yytext, NULL); return T_NUMBER; }
+= { yylval->csop = OpSetLiteral; return T_SET; }
+\+= { yylval->csop = OpSetAdd; return T_SET_ADD; }
+-= { yylval->csop = OpSetSubtract; return T_SET_SUBTRACT; }
+\*= { yylval->csop = OpSetMultiply; return T_SET_MULTIPLY; }
+\/= { yylval->csop = OpSetDivide; return T_SET_DIVIDE; }
+\%= { yylval->csop = OpSetModulo; return T_SET_MODULO; }
+\^= { yylval->csop = OpSetXor; return T_SET_XOR; }
+\&= { yylval->csop = OpSetBinaryAnd; return T_SET_BINARY_AND; }
+\|= { yylval->csop = OpSetBinaryOr; return T_SET_BINARY_OR; }
+\+ return T_PLUS;
+\- return T_MINUS;
+\* return T_MULTIPLY;
+\/ return T_DIVIDE_OP;
+\% return T_MODULO;
+\^ return T_XOR;
+\& return T_BINARY_AND;
+\| return T_BINARY_OR;
+\< return T_LESS_THAN;
+\> return T_GREATER_THAN;
+}
+
+\( { yyextra->m_IgnoreNewlines.push(true); return '('; }
+\) { yyextra->m_IgnoreNewlines.pop(); return ')'; }
+[\r\n]+ { yycolumn -= strlen(yytext) - 1; if (!yyextra->m_IgnoreNewlines.top()) { return T_NEWLINE; } }
+<<EOF>> { if (!yyextra->m_Eof) { yyextra->m_Eof = true; return T_NEWLINE; } else { yyterminate(); } }
+. return yytext[0];
+
+%%
+
+void ConfigCompiler::InitializeScanner()
+{
+ yylex_init(&m_Scanner);
+ yyset_extra(this, m_Scanner);
+}
+
+void ConfigCompiler::DestroyScanner()
+{
+ yylex_destroy(m_Scanner);
+}
diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy
new file mode 100644
index 0000000..939681e
--- /dev/null
+++ b/lib/config/config_parser.yy
@@ -0,0 +1,1243 @@
+%{
+#define YYDEBUG 1
+
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/i2-config.hpp"
+#include "config/configcompiler.hpp"
+#include "config/expression.hpp"
+#include "config/applyrule.hpp"
+#include "config/objectrule.hpp"
+#include "base/value.hpp"
+#include "base/utility.hpp"
+#include "base/exception.hpp"
+#include "base/configtype.hpp"
+#include "base/exception.hpp"
+#include <sstream>
+#include <stack>
+
+#define YYLTYPE icinga::CompilerDebugInfo
+#define YYERROR_VERBOSE
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+do { \
+ if (N) { \
+ (Current).Path = YYRHSLOC(Rhs, 1).Path; \
+ (Current).FirstLine = YYRHSLOC(Rhs, 1).FirstLine; \
+ (Current).FirstColumn = YYRHSLOC(Rhs, 1).FirstColumn; \
+ (Current).LastLine = YYRHSLOC(Rhs, N).LastLine; \
+ (Current).LastColumn = YYRHSLOC(Rhs, N).LastColumn; \
+ } else { \
+ (Current).Path = YYRHSLOC(Rhs, 0).Path; \
+ (Current).FirstLine = (Current).LastLine = \
+ YYRHSLOC(Rhs, 0).LastLine; \
+ (Current).FirstColumn = (Current).LastColumn = \
+ YYRHSLOC(Rhs, 0).LastColumn; \
+ } \
+} while (0)
+
+#define YY_LOCATION_PRINT(file, loc) \
+do { \
+ std::ostringstream msgbuf; \
+ msgbuf << loc; \
+ std::string str = msgbuf.str(); \
+ fputs(str.c_str(), file); \
+} while (0)
+
+#define YYINITDEPTH 10000
+
+using namespace icinga;
+
+template<typename T>
+static void MakeRBinaryOp(Expression** result, Expression *left, Expression *right, const DebugInfo& diLeft, const DebugInfo& diRight)
+{
+ *result = new T(std::unique_ptr<Expression>(left), std::unique_ptr<Expression>(right), DebugInfoRange(diLeft, diRight));
+}
+
+%}
+
+%pure-parser
+
+%locations
+%defines
+%error-verbose
+%glr-parser
+
+%parse-param { std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> > *llist }
+%parse-param { ConfigCompiler *context }
+%lex-param { void *scanner }
+
+%union {
+ String *text;
+ double num;
+ bool boolean;
+ icinga::Expression *expr;
+ icinga::DictExpression *dexpr;
+ CombinedSetOp csop;
+ std::vector<String> *slist;
+ std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> > *llist;
+ std::vector<std::unique_ptr<Expression> > *elist;
+ std::vector<std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression> > > *ebranchlist;
+ std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression> > *ebranch;
+ std::pair<String, std::unique_ptr<Expression> > *cvitem;
+ std::map<String, std::unique_ptr<Expression> > *cvlist;
+ icinga::ScopeSpecifier scope;
+}
+
+%token T_NEWLINE "new-line"
+%token <text> T_STRING
+%token <text> T_STRING_ANGLE
+%token <num> T_NUMBER
+%token <boolean> T_BOOLEAN
+%token T_NULL
+%token <text> T_IDENTIFIER
+
+%token <csop> T_SET "= (T_SET)"
+%token <csop> T_SET_ADD "+= (T_SET_ADD)"
+%token <csop> T_SET_SUBTRACT "-= (T_SET_SUBTRACT)"
+%token <csop> T_SET_MULTIPLY "*= (T_SET_MULTIPLY)"
+%token <csop> T_SET_DIVIDE "/= (T_SET_DIVIDE)"
+%token <csop> T_SET_MODULO "%= (T_SET_MODULO)"
+%token <csop> T_SET_XOR "^= (T_SET_XOR)"
+%token <csop> T_SET_BINARY_AND "&= (T_SET_BINARY_AND)"
+%token <csop> T_SET_BINARY_OR "|= (T_SET_BINARY_OR)"
+
+%token T_SHIFT_LEFT "<< (T_SHIFT_LEFT)"
+%token T_SHIFT_RIGHT ">> (T_SHIFT_RIGHT)"
+%token T_EQUAL "== (T_EQUAL)"
+%token T_NOT_EQUAL "!= (T_NOT_EQUAL)"
+%token T_IN "in (T_IN)"
+%token T_NOT_IN "!in (T_NOT_IN)"
+%token T_LOGICAL_AND "&& (T_LOGICAL_AND)"
+%token T_LOGICAL_OR "|| (T_LOGICAL_OR)"
+%token T_LESS_THAN_OR_EQUAL "<= (T_LESS_THAN_OR_EQUAL)"
+%token T_GREATER_THAN_OR_EQUAL ">= (T_GREATER_THAN_OR_EQUAL)"
+%token T_PLUS "+ (T_PLUS)"
+%token T_MINUS "- (T_MINUS)"
+%token T_MULTIPLY "* (T_MULTIPLY)"
+%token T_DIVIDE_OP "/ (T_DIVIDE_OP)"
+%token T_MODULO "% (T_MODULO)"
+%token T_XOR "^ (T_XOR)"
+%token T_BINARY_AND "& (T_BINARY_AND)"
+%token T_BINARY_OR "| (T_BINARY_OR)"
+%token T_LESS_THAN "< (T_LESS_THAN)"
+%token T_GREATER_THAN "> (T_GREATER_THAN)"
+
+%token T_VAR "var (T_VAR)"
+%token T_GLOBALS "globals (T_GLOBALS)"
+%token T_LOCALS "locals (T_LOCALS)"
+%token T_CONST "const (T_CONST)"
+%token T_DEFAULT "default (T_DEFAULT)"
+%token T_IGNORE_ON_ERROR "ignore_on_error (T_IGNORE_ON_ERROR)"
+%token T_CURRENT_FILENAME "current_filename (T_CURRENT_FILENAME)"
+%token T_CURRENT_LINE "current_line (T_CURRENT_LINE)"
+%token T_DEBUGGER "debugger (T_DEBUGGER)"
+%token T_NAMESPACE "namespace (T_NAMESPACE)"
+%token T_USE "use (T_USE)"
+%token T_USING "using (T_USING)"
+%token T_OBJECT "object (T_OBJECT)"
+%token T_TEMPLATE "template (T_TEMPLATE)"
+%token T_INCLUDE "include (T_INCLUDE)"
+%token T_INCLUDE_RECURSIVE "include_recursive (T_INCLUDE_RECURSIVE)"
+%token T_INCLUDE_ZONES "include_zones (T_INCLUDE_ZONES)"
+%token T_LIBRARY "library (T_LIBRARY)"
+%token T_APPLY "apply (T_APPLY)"
+%token T_TO "to (T_TO)"
+%token T_WHERE "where (T_WHERE)"
+%token T_IMPORT "import (T_IMPORT)"
+%token T_ASSIGN "assign (T_ASSIGN)"
+%token T_IGNORE "ignore (T_IGNORE)"
+%token T_FUNCTION "function (T_FUNCTION)"
+%token T_RETURN "return (T_RETURN)"
+%token T_BREAK "break (T_BREAK)"
+%token T_CONTINUE "continue (T_CONTINUE)"
+%token T_FOR "for (T_FOR)"
+%token T_IF "if (T_IF)"
+%token T_ELSE "else (T_ELSE)"
+%token T_WHILE "while (T_WHILE)"
+%token T_THROW "throw (T_THROW)"
+%token T_TRY "try (T_TRY)"
+%token T_EXCEPT "except (T_EXCEPT)"
+%token T_FOLLOWS "=> (T_FOLLOWS)"
+%token T_NULLARY_LAMBDA_BEGIN "{{ (T_NULLARY_LAMBDA_BEGIN)"
+%token T_NULLARY_LAMBDA_END "}} (T_NULLARY_LAMBDA_END)"
+
+%type <text> identifier
+%type <elist> rterm_items
+%type <elist> rterm_items_inner
+%type <slist> identifier_items
+%type <slist> identifier_items_inner
+%type <csop> combined_set_op
+%type <llist> statements
+%type <llist> lterm_items
+%type <llist> lterm_items_inner
+%type <expr> rterm
+%type <expr> rterm_array
+%type <dexpr> rterm_dict
+%type <dexpr> rterm_scope_require_side_effect
+%type <dexpr> rterm_scope
+%type <ebranchlist> else_if_branches
+%type <ebranch> else_if_branch
+%type <expr> rterm_side_effect
+%type <expr> rterm_no_side_effect
+%type <expr> rterm_no_side_effect_no_dict
+%type <expr> lterm
+%type <expr> object
+%type <expr> apply
+%type <expr> optional_rterm
+%type <text> target_type_specifier
+%type <boolean> default_specifier
+%type <boolean> ignore_specifier
+%type <cvlist> use_specifier
+%type <cvlist> use_specifier_items
+%type <cvitem> use_specifier_item
+%type <num> object_declaration
+
+%right T_FOLLOWS
+%right T_INCLUDE T_INCLUDE_RECURSIVE T_INCLUDE_ZONES T_OBJECT T_TEMPLATE T_APPLY T_IMPORT T_ASSIGN T_IGNORE T_WHERE
+%right T_FUNCTION T_FOR
+%left T_SET T_SET_ADD T_SET_SUBTRACT T_SET_MULTIPLY T_SET_DIVIDE T_SET_MODULO T_SET_XOR T_SET_BINARY_AND T_SET_BINARY_OR
+%right '?' ':'
+%left T_LOGICAL_OR
+%left T_LOGICAL_AND
+%left T_RETURN T_BREAK T_CONTINUE
+%left T_IDENTIFIER
+%left T_BINARY_OR
+%left T_XOR
+%left T_BINARY_AND
+%nonassoc T_EQUAL T_NOT_EQUAL
+%left T_IN T_NOT_IN
+%nonassoc T_LESS_THAN T_LESS_THAN_OR_EQUAL T_GREATER_THAN T_GREATER_THAN_OR_EQUAL
+%left T_SHIFT_LEFT T_SHIFT_RIGHT
+%left T_PLUS T_MINUS
+%left T_MULTIPLY T_DIVIDE_OP T_MODULO
+%left UNARY_MINUS UNARY_PLUS
+%right REF_OP DEREF_OP
+%right '!' '~'
+%left '.' '(' '['
+%left T_VAR T_THIS T_GLOBALS T_LOCALS
+%right ';' ','
+%right T_NEWLINE
+%{
+
+int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner);
+
+extern int yydebug;
+
+void yyerror(const YYLTYPE *locp, std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> > *, ConfigCompiler *context, const char *err)
+{
+ bool incomplete = context && context->m_Eof && (context->m_OpenBraces > 0);
+ BOOST_THROW_EXCEPTION(ScriptError(err, *locp, incomplete));
+}
+
+int yyparse(std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> > *llist, ConfigCompiler *context);
+
+static void BeginFlowControlBlock(ConfigCompiler *compiler, int allowedTypes, bool inherit)
+{
+ if (inherit)
+ allowedTypes |= compiler->m_FlowControlInfo.top();
+
+ compiler->m_FlowControlInfo.push(allowedTypes);
+}
+
+static void EndFlowControlBlock(ConfigCompiler *compiler)
+{
+ compiler->m_FlowControlInfo.pop();
+}
+
+static void UseFlowControl(ConfigCompiler *compiler, FlowControlType type, const CompilerDebugInfo& location)
+{
+ int fci = compiler->m_FlowControlInfo.top();
+
+ if ((type & fci) != type)
+ BOOST_THROW_EXCEPTION(ScriptError("Invalid flow control statement.", location));
+}
+
+std::unique_ptr<Expression> ConfigCompiler::Compile()
+{
+ std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> > llist;
+
+ //yydebug = 1;
+
+ m_IgnoreNewlines.push(false);
+ BeginFlowControlBlock(this, 0, false);
+
+ if (yyparse(&llist, this) != 0)
+ return NULL;
+
+ EndFlowControlBlock(this);
+ m_IgnoreNewlines.pop();
+
+ std::vector<std::unique_ptr<Expression> > dlist;
+ decltype(llist.size()) num = 0;
+ for (auto& litem : llist) {
+ if (!litem.second.SideEffect && num != llist.size() - 1) {
+ yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
+ }
+ dlist.emplace_back(std::move(litem.first));
+ num++;
+ }
+
+ std::unique_ptr<DictExpression> expr{new DictExpression(std::move(dlist))};
+ expr->MakeInline();
+ return std::move(expr);
+}
+
+#define scanner (context->GetScanner())
+
+%}
+
+%%
+script: statements
+ {
+ llist->swap(*$1);
+ delete $1;
+ }
+ ;
+
+statements: optional_newlines lterm_items
+ {
+ $$ = $2;
+ }
+ ;
+
+lterm_items: /* empty */
+ {
+ $$ = new std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> >();
+ }
+ | lterm_items_inner
+ | lterm_items_inner sep
+ ;
+
+lterm_items_inner: lterm %dprec 2
+ {
+ $$ = new std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> >();
+ $$->emplace_back(std::unique_ptr<Expression>($1), EItemInfo{true, @1});
+ }
+ | rterm_no_side_effect
+ {
+ $$ = new std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> >();
+ $$->emplace_back(std::unique_ptr<Expression>($1), EItemInfo{false, @1});
+ }
+ | lterm_items_inner sep lterm %dprec 1
+ {
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> >();
+
+ if ($3) {
+ $$->emplace_back(std::unique_ptr<Expression>($3), EItemInfo{true, @3});
+ }
+ }
+ | lterm_items_inner sep rterm_no_side_effect %dprec 1
+ {
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new std::vector<std::pair<std::unique_ptr<Expression>, EItemInfo> >();
+
+ if ($3) {
+ $$->emplace_back(std::unique_ptr<Expression>($3), EItemInfo{false, @3});
+ }
+ }
+ ;
+
+identifier: T_IDENTIFIER
+ | T_STRING
+ ;
+
+object:
+ {
+ context->m_ObjectAssign.push(true);
+ context->m_SeenAssign.push(false);
+ context->m_SeenIgnore.push(false);
+ context->m_Assign.push(0);
+ context->m_Ignore.push(0);
+ }
+ object_declaration rterm optional_rterm use_specifier default_specifier ignore_specifier
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope_require_side_effect
+ {
+ EndFlowControlBlock(context);
+
+ context->m_ObjectAssign.pop();
+
+ bool abstract = $2;
+ bool defaultTmpl = $6;
+
+ if (!abstract && defaultTmpl)
+ BOOST_THROW_EXCEPTION(ScriptError("'default' keyword is invalid for object definitions", @6));
+
+ bool seen_assign = context->m_SeenAssign.top();
+ context->m_SeenAssign.pop();
+
+ bool seen_ignore = context->m_SeenIgnore.top();
+ context->m_SeenIgnore.pop();
+
+ std::unique_ptr<Expression> ignore{std::move(context->m_Ignore.top())};
+ context->m_Ignore.pop();
+
+ std::unique_ptr<Expression> assign{std::move(context->m_Assign.top())};
+ context->m_Assign.pop();
+
+ std::unique_ptr<Expression> filter;
+
+ if (seen_assign) {
+ if (ignore) {
+ std::unique_ptr<Expression> rex{new LogicalNegateExpression(std::move(ignore), DebugInfoRange(@2, @5))};
+
+ filter.reset(new LogicalAndExpression(std::move(assign), std::move(rex), DebugInfoRange(@2, @5)));
+ } else
+ filter.swap(assign);
+ } else if (seen_ignore) {
+ BOOST_THROW_EXCEPTION(ScriptError("object rule 'ignore where' cannot be used without 'assign where'", DebugInfoRange(@2, @4)));
+ }
+
+ $$ = new ObjectExpression(abstract, std::unique_ptr<Expression>($3), std::unique_ptr<Expression>($4),
+ std::move(filter), context->GetZone(), context->GetPackage(), std::move(*$5), $6, $7,
+ std::unique_ptr<Expression>($9), DebugInfoRange(@2, @7));
+ delete $5;
+ }
+ ;
+
+object_declaration: T_OBJECT
+ {
+ $$ = false;
+ }
+ | T_TEMPLATE
+ {
+ $$ = true;
+ }
+ ;
+
+identifier_items: /* empty */
+ {
+ $$ = new std::vector<String>();
+ }
+ | identifier_items_inner
+ | identifier_items_inner ','
+ ;
+
+identifier_items_inner: identifier
+ {
+ $$ = new std::vector<String>();
+ $$->emplace_back(std::move(*$1));
+ delete $1;
+ }
+ | identifier_items_inner ',' identifier
+ {
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new std::vector<String>();
+
+ $$->emplace_back(std::move(*$3));
+ delete $3;
+ }
+ ;
+
+combined_set_op: T_SET
+ | T_SET_ADD
+ | T_SET_SUBTRACT
+ | T_SET_MULTIPLY
+ | T_SET_DIVIDE
+ | T_SET_MODULO
+ | T_SET_XOR
+ | T_SET_BINARY_AND
+ | T_SET_BINARY_OR
+ ;
+
+optional_var: /* empty */
+ | T_VAR
+ ;
+
+lterm: T_LIBRARY rterm
+ {
+ $$ = new LibraryExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | rterm combined_set_op rterm
+ {
+ $$ = new SetExpression(std::unique_ptr<Expression>($1), $2, std::unique_ptr<Expression>($3), @$);
+ }
+ | T_INCLUDE rterm
+ {
+ $$ = new IncludeExpression(Utility::DirName(context->GetPath()), std::unique_ptr<Expression>($2), NULL, NULL, IncludeRegular, false, context->GetZone(), context->GetPackage(), @$);
+ }
+ | T_INCLUDE T_STRING_ANGLE
+ {
+ $$ = new IncludeExpression(Utility::DirName(context->GetPath()), MakeLiteral(std::move(*$2)), NULL, NULL, IncludeRegular, true, context->GetZone(), context->GetPackage(), @$);
+ delete $2;
+ }
+ | T_INCLUDE_RECURSIVE rterm
+ {
+ $$ = new IncludeExpression(Utility::DirName(context->GetPath()), std::unique_ptr<Expression>($2), MakeLiteral("*.conf"), NULL, IncludeRecursive, false, context->GetZone(), context->GetPackage(), @$);
+ }
+ | T_INCLUDE_RECURSIVE rterm ',' rterm
+ {
+ $$ = new IncludeExpression(Utility::DirName(context->GetPath()), std::unique_ptr<Expression>($2), std::unique_ptr<Expression>($4), NULL, IncludeRecursive, false, context->GetZone(), context->GetPackage(), @$);
+ }
+ | T_INCLUDE_ZONES rterm ',' rterm
+ {
+ $$ = new IncludeExpression(Utility::DirName(context->GetPath()), std::unique_ptr<Expression>($4), MakeLiteral("*.conf"), std::unique_ptr<Expression>($2), IncludeZones, false, context->GetZone(), context->GetPackage(), @$);
+ }
+ | T_INCLUDE_ZONES rterm ',' rterm ',' rterm
+ {
+ $$ = new IncludeExpression(Utility::DirName(context->GetPath()), std::unique_ptr<Expression>($4), std::unique_ptr<Expression>($6), std::unique_ptr<Expression>($2), IncludeZones, false, context->GetZone(), context->GetPackage(), @$);
+ }
+ | T_IMPORT rterm
+ {
+ $$ = new ImportExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | T_ASSIGN T_WHERE
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope %dprec 2
+ {
+ EndFlowControlBlock(context);
+
+ if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top()))
+ BOOST_THROW_EXCEPTION(ScriptError("'assign' keyword not valid in this context.", @$));
+
+ context->m_SeenAssign.top() = true;
+
+ if (context->m_Assign.top())
+ context->m_Assign.top() = new LogicalOrExpression(std::unique_ptr<Expression>(context->m_Assign.top()), std::unique_ptr<Expression>($4), @$);
+ else
+ context->m_Assign.top() = $4;
+
+ $$ = MakeLiteralRaw();
+ }
+ | T_ASSIGN T_WHERE rterm %dprec 1
+ {
+ ASSERT(!dynamic_cast<DictExpression *>($3));
+
+ if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top()))
+ BOOST_THROW_EXCEPTION(ScriptError("'assign' keyword not valid in this context.", @$));
+
+ context->m_SeenAssign.top() = true;
+
+ if (context->m_Assign.top())
+ context->m_Assign.top() = new LogicalOrExpression(std::unique_ptr<Expression>(context->m_Assign.top()), std::unique_ptr<Expression>($3), @$);
+ else
+ context->m_Assign.top() = $3;
+
+ $$ = MakeLiteralRaw();
+ }
+ | T_IGNORE T_WHERE
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope %dprec 2
+ {
+ EndFlowControlBlock(context);
+
+ if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top()))
+ BOOST_THROW_EXCEPTION(ScriptError("'ignore' keyword not valid in this context.", @$));
+
+ context->m_SeenIgnore.top() = true;
+
+ if (context->m_Ignore.top())
+ context->m_Ignore.top() = new LogicalOrExpression(std::unique_ptr<Expression>(context->m_Ignore.top()), std::unique_ptr<Expression>($4), @$);
+ else
+ context->m_Ignore.top() = $4;
+
+ $$ = MakeLiteralRaw();
+ }
+ | T_IGNORE T_WHERE rterm %dprec 1
+ {
+ ASSERT(!dynamic_cast<DictExpression *>($3));
+
+ if ((context->m_Apply.empty() || !context->m_Apply.top()) && (context->m_ObjectAssign.empty() || !context->m_ObjectAssign.top()))
+ BOOST_THROW_EXCEPTION(ScriptError("'ignore' keyword not valid in this context.", @$));
+
+ context->m_SeenIgnore.top() = true;
+
+ if (context->m_Ignore.top())
+ context->m_Ignore.top() = new LogicalOrExpression(std::unique_ptr<Expression>(context->m_Ignore.top()), std::unique_ptr<Expression>($3), @$);
+ else
+ context->m_Ignore.top() = $3;
+
+ $$ = MakeLiteralRaw();
+ }
+ | T_RETURN optional_rterm
+ {
+ UseFlowControl(context, FlowControlReturn, @$);
+ $$ = new ReturnExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | T_BREAK
+ {
+ UseFlowControl(context, FlowControlBreak, @$);
+ $$ = new BreakExpression(@$);
+ }
+ | T_CONTINUE
+ {
+ UseFlowControl(context, FlowControlContinue, @$);
+ $$ = new ContinueExpression(@$);
+ }
+ | T_DEBUGGER
+ {
+ $$ = new BreakpointExpression(@$);
+ }
+ | T_NAMESPACE rterm
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope_require_side_effect
+ {
+ EndFlowControlBlock(context);
+
+ std::unique_ptr<Expression> expr{$2};
+ BindToScope(expr, ScopeGlobal);
+ $$ = new SetExpression(std::move(expr), OpSetLiteral, std::unique_ptr<Expression>(new NamespaceExpression(std::unique_ptr<Expression>($4), @$)), @$);
+ }
+ | T_USING rterm
+ {
+ Expression::Ptr expr{$2};
+ context->AddImport(std::move(expr));
+ $$ = MakeLiteralRaw();
+ }
+ | apply
+ | object
+ | T_FOR '(' optional_var identifier T_FOLLOWS optional_var identifier T_IN rterm ')'
+ {
+ BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true);
+ }
+ rterm_scope_require_side_effect
+ {
+ EndFlowControlBlock(context);
+
+ $$ = new ForExpression(std::move(*$4), std::move(*$7), std::unique_ptr<Expression>($9), std::unique_ptr<Expression>($12), @$);
+ delete $4;
+ delete $7;
+ }
+ | T_FOR '(' optional_var identifier T_IN rterm ')'
+ {
+ BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true);
+ }
+ rterm_scope_require_side_effect
+ {
+ EndFlowControlBlock(context);
+
+ $$ = new ForExpression(std::move(*$4), "", std::unique_ptr<Expression>($6), std::unique_ptr<Expression>($9), @$);
+ delete $4;
+ }
+ | T_FUNCTION identifier '(' identifier_items ')' use_specifier
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope
+ {
+ EndFlowControlBlock(context);
+
+ std::unique_ptr<FunctionExpression> fexpr{new FunctionExpression(*$2, std::move(*$4), std::move(*$6), std::unique_ptr<Expression>($8), @$)};
+ delete $4;
+ delete $6;
+
+ $$ = new SetExpression(MakeIndexer(ScopeThis, std::move(*$2)), OpSetLiteral, std::move(fexpr), @$);
+ delete $2;
+ }
+ | T_CONST T_IDENTIFIER T_SET rterm
+ {
+ $$ = new SetConstExpression(std::move(*$2), std::unique_ptr<Expression>($4), @$);
+ delete $2;
+ }
+ | T_VAR rterm
+ {
+ std::unique_ptr<Expression> expr{$2};
+ BindToScope(expr, ScopeLocal);
+ $$ = new SetExpression(std::move(expr), OpSetLiteral, MakeLiteral(), @$);
+ }
+ | T_VAR rterm combined_set_op rterm
+ {
+ std::unique_ptr<Expression> expr{$2};
+ BindToScope(expr, ScopeLocal);
+ $$ = new SetExpression(std::move(expr), $3, std::unique_ptr<Expression>($4), @$);
+ }
+ | T_WHILE '(' rterm ')'
+ {
+ BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true);
+ }
+ rterm_scope
+ {
+ EndFlowControlBlock(context);
+
+ $$ = new WhileExpression(std::unique_ptr<Expression>($3), std::unique_ptr<Expression>($6), @$);
+ }
+ | T_THROW rterm
+ {
+ $$ = new ThrowExpression(std::unique_ptr<Expression>($2), false, @$);
+ }
+ | T_TRY rterm_scope T_EXCEPT rterm_scope
+ {
+ $$ = new TryExceptExpression(std::unique_ptr<Expression>($2), std::unique_ptr<Expression>($4), @$);
+ }
+ | rterm_side_effect
+ ;
+
+rterm_items: /* empty */
+ {
+ $$ = new std::vector<std::unique_ptr<Expression> >();
+ }
+ | rterm_items_inner
+ | rterm_items_inner ',' optional_newlines
+ | rterm_items_inner newlines
+ ;
+
+rterm_items_inner: rterm
+ {
+ $$ = new std::vector<std::unique_ptr<Expression> >();
+ $$->emplace_back($1);
+ }
+ | rterm_items_inner ',' optional_newlines rterm
+ {
+ $$ = $1;
+ $$->emplace_back($4);
+ }
+ ;
+
+rterm_array: '['
+ {
+ context->m_OpenBraces++;
+ }
+ optional_newlines rterm_items ']'
+ {
+ context->m_OpenBraces--;
+ $$ = new ArrayExpression(std::move(*$4), @$);
+ delete $4;
+ }
+ ;
+
+rterm_dict: '{'
+ {
+ BeginFlowControlBlock(context, 0, false);
+ context->m_IgnoreNewlines.push(false);
+ context->m_OpenBraces++;
+ }
+ statements '}'
+ {
+ EndFlowControlBlock(context);
+ context->m_OpenBraces--;
+ context->m_IgnoreNewlines.pop();
+ std::vector<std::unique_ptr<Expression> > dlist;
+ for (auto& litem : *$3) {
+ if (!litem.second.SideEffect)
+ yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
+ dlist.emplace_back(std::move(litem.first));
+ }
+ delete $3;
+ $$ = new DictExpression(std::move(dlist), @$);
+ }
+ ;
+
+rterm_scope_require_side_effect: '{'
+ {
+ context->m_IgnoreNewlines.push(false);
+ context->m_OpenBraces++;
+ }
+ statements '}'
+ {
+ context->m_OpenBraces--;
+ context->m_IgnoreNewlines.pop();
+ std::vector<std::unique_ptr<Expression> > dlist;
+ for (auto& litem : *$3) {
+ if (!litem.second.SideEffect)
+ yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
+ dlist.emplace_back(std::move(litem.first));
+ }
+ delete $3;
+ $$ = new DictExpression(std::move(dlist), @$);
+ $$->MakeInline();
+ }
+ ;
+
+rterm_scope: '{'
+ {
+ context->m_IgnoreNewlines.push(false);
+ context->m_OpenBraces++;
+ }
+ statements '}'
+ {
+ context->m_OpenBraces--;
+ context->m_IgnoreNewlines.pop();
+ std::vector<std::unique_ptr<Expression> > dlist;
+ decltype($3->size()) num = 0;
+ for (auto& litem : *$3) {
+ if (!litem.second.SideEffect && num != $3->size() - 1)
+ yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
+ dlist.emplace_back(std::move(litem.first));
+ num++;
+ }
+ delete $3;
+ $$ = new DictExpression(std::move(dlist), @$);
+ $$->MakeInline();
+ }
+ ;
+
+else_if_branch: T_ELSE T_IF '(' rterm ')' rterm_scope
+ {
+ $$ = new std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression> >(std::unique_ptr<Expression>($4), std::unique_ptr<Expression>($6));
+ }
+ ;
+
+else_if_branches: /* empty */
+ {
+ $$ = new std::vector<std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression> > >();
+ }
+ | else_if_branches else_if_branch
+ {
+ $$ = $1;
+ $$->emplace_back(std::move(*$2));
+ delete $2;
+ }
+ ;
+
+rterm_side_effect: rterm '(' rterm_items ')'
+ {
+ $$ = new FunctionCallExpression(std::unique_ptr<Expression>($1), std::move(*$3), @$);
+ delete $3;
+ }
+ | T_IF '(' rterm ')' rterm_scope else_if_branches
+ {
+ std::vector<std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression> > > ebranches;
+ $6->swap(ebranches);
+ delete $6;
+
+ std::unique_ptr<Expression> afalse;
+
+ for (int i = ebranches.size() - 1; i >= 0; i--) {
+ auto& ebranch = ebranches[i];
+ afalse.reset(new ConditionalExpression(std::move(ebranch.first), std::move(ebranch.second), std::move(afalse), @6));
+ }
+
+ $$ = new ConditionalExpression(std::unique_ptr<Expression>($3), std::unique_ptr<Expression>($5), std::move(afalse), @$);
+ }
+ | T_IF '(' rterm ')' rterm_scope else_if_branches T_ELSE rterm_scope
+ {
+ std::vector<std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression> > > ebranches;
+ $6->swap(ebranches);
+ delete $6;
+
+ $8->MakeInline();
+
+ std::unique_ptr<Expression> afalse{$8};
+
+ for (int i = ebranches.size() - 1; i >= 0; i--) {
+ auto& ebranch = ebranches[i];
+ afalse.reset(new ConditionalExpression(std::move(ebranch.first), std::move(ebranch.second), std::move(afalse), @6));
+ }
+
+ $$ = new ConditionalExpression(std::unique_ptr<Expression>($3), std::unique_ptr<Expression>($5), std::move(afalse), @$);
+ }
+ | rterm '?' rterm ':' rterm
+ {
+ $$ = new ConditionalExpression(std::unique_ptr<Expression>($1), std::unique_ptr<Expression>($3), std::unique_ptr<Expression>($5), @$);
+ }
+ ;
+
+rterm_no_side_effect_no_dict: T_STRING
+ {
+ $$ = MakeLiteralRaw(std::move(*$1));
+ delete $1;
+ }
+ | T_NUMBER
+ {
+ $$ = MakeLiteralRaw($1);
+ }
+ | T_BOOLEAN
+ {
+ $$ = MakeLiteralRaw($1);
+ }
+ | T_NULL
+ {
+ $$ = MakeLiteralRaw();
+ }
+ | rterm '.' T_IDENTIFIER %dprec 2
+ {
+ $$ = new IndexerExpression(std::unique_ptr<Expression>($1), MakeLiteral(std::move(*$3)), @$);
+ delete $3;
+ }
+ | rterm '[' rterm ']'
+ {
+ $$ = new IndexerExpression(std::unique_ptr<Expression>($1), std::unique_ptr<Expression>($3), @$);
+ }
+ | T_IDENTIFIER
+ {
+ $$ = new VariableExpression(std::move(*$1), context->GetImports(), @1);
+ delete $1;
+ }
+ | T_MULTIPLY rterm %prec DEREF_OP
+ {
+ $$ = new DerefExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | T_BINARY_AND rterm %prec REF_OP
+ {
+ $$ = new RefExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | '!' rterm
+ {
+ $$ = new LogicalNegateExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | '~' rterm
+ {
+ $$ = new NegateExpression(std::unique_ptr<Expression>($2), @$);
+ }
+ | T_PLUS rterm %prec UNARY_PLUS
+ {
+ $$ = $2;
+ }
+ | T_MINUS rterm %prec UNARY_MINUS
+ {
+ $$ = new SubtractExpression(MakeLiteral(0), std::unique_ptr<Expression>($2), @$);
+ }
+ | T_THIS
+ {
+ $$ = new GetScopeExpression(ScopeThis);
+ }
+ | T_GLOBALS
+ {
+ $$ = new GetScopeExpression(ScopeGlobal);
+ }
+ | T_LOCALS
+ {
+ $$ = new GetScopeExpression(ScopeLocal);
+ }
+ | T_CURRENT_FILENAME
+ {
+ $$ = MakeLiteralRaw(@$.Path);
+ }
+ | T_CURRENT_LINE
+ {
+ $$ = MakeLiteralRaw(@$.FirstLine);
+ }
+ | identifier T_FOLLOWS
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope %dprec 2
+ {
+ EndFlowControlBlock(context);
+
+ std::vector<String> args;
+ args.emplace_back(std::move(*$1));
+ delete $1;
+
+ $$ = new FunctionExpression("<anonymous>", std::move(args), {}, std::unique_ptr<Expression>($4), @$);
+ }
+ | identifier T_FOLLOWS rterm %dprec 1
+ {
+ ASSERT(!dynamic_cast<DictExpression *>($3));
+
+ std::vector<String> args;
+ args.emplace_back(std::move(*$1));
+ delete $1;
+
+ $$ = new FunctionExpression("<anonymous>", std::move(args), {}, std::unique_ptr<Expression>($3), @$);
+ }
+ | '(' identifier_items ')' use_specifier T_FOLLOWS
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope %dprec 2
+ {
+ EndFlowControlBlock(context);
+
+ $$ = new FunctionExpression("<anonymous>", std::move(*$2), std::move(*$4), std::unique_ptr<Expression>($7), @$);
+ delete $2;
+ delete $4;
+ }
+ | '(' identifier_items ')' use_specifier T_FOLLOWS rterm %dprec 1
+ {
+ ASSERT(!dynamic_cast<DictExpression *>($6));
+
+ $$ = new FunctionExpression("<anonymous>", std::move(*$2), std::move(*$4), std::unique_ptr<Expression>($6), @$);
+ delete $2;
+ delete $4;
+ }
+ | rterm_array
+ | '('
+ {
+ context->m_OpenBraces++;
+ }
+ rterm ')'
+ {
+ context->m_OpenBraces--;
+ $$ = $3;
+ }
+ | rterm T_LOGICAL_OR rterm { MakeRBinaryOp<LogicalOrExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_LOGICAL_AND rterm { MakeRBinaryOp<LogicalAndExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_BINARY_OR rterm { MakeRBinaryOp<BinaryOrExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_BINARY_AND rterm { MakeRBinaryOp<BinaryAndExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_IN rterm { MakeRBinaryOp<InExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_NOT_IN rterm { MakeRBinaryOp<NotInExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_EQUAL rterm { MakeRBinaryOp<EqualExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_NOT_EQUAL rterm { MakeRBinaryOp<NotEqualExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_LESS_THAN rterm { MakeRBinaryOp<LessThanExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_LESS_THAN_OR_EQUAL rterm { MakeRBinaryOp<LessThanOrEqualExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_GREATER_THAN rterm { MakeRBinaryOp<GreaterThanExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_GREATER_THAN_OR_EQUAL rterm { MakeRBinaryOp<GreaterThanOrEqualExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_SHIFT_LEFT rterm { MakeRBinaryOp<ShiftLeftExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_SHIFT_RIGHT rterm { MakeRBinaryOp<ShiftRightExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_PLUS rterm { MakeRBinaryOp<AddExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_MINUS rterm { MakeRBinaryOp<SubtractExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_MULTIPLY rterm { MakeRBinaryOp<MultiplyExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_DIVIDE_OP rterm { MakeRBinaryOp<DivideExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_MODULO rterm { MakeRBinaryOp<ModuloExpression>(&$$, $1, $3, @1, @3); }
+ | rterm T_XOR rterm { MakeRBinaryOp<XorExpression>(&$$, $1, $3, @1, @3); }
+ | T_FUNCTION '(' identifier_items ')' use_specifier
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope
+ {
+ EndFlowControlBlock(context);
+
+ $$ = new FunctionExpression("<anonymous>", std::move(*$3), std::move(*$5), std::unique_ptr<Expression>($7), @$);
+ delete $3;
+ delete $5;
+ }
+ | T_NULLARY_LAMBDA_BEGIN
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ statements T_NULLARY_LAMBDA_END
+ {
+ EndFlowControlBlock(context);
+
+ std::vector<std::unique_ptr<Expression> > dlist;
+ decltype(dlist.size()) num = 0;
+ for (auto& litem : *$3) {
+ if (!litem.second.SideEffect && num != $3->size() - 1)
+ yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
+ dlist.emplace_back(std::move(litem.first));
+ num++;
+ }
+ delete $3;
+ std::unique_ptr<DictExpression> aexpr{new DictExpression(std::move(dlist), @$)};
+ aexpr->MakeInline();
+
+ $$ = new FunctionExpression("<anonymous>", {}, {}, std::move(aexpr), @$);
+ }
+ ;
+
+rterm_no_side_effect:
+ rterm_no_side_effect_no_dict %dprec 1
+ | rterm_dict %dprec 2
+ {
+ std::unique_ptr<Expression> expr{$1};
+ BindToScope(expr, ScopeThis);
+ $$ = expr.release();
+ }
+ ;
+
+rterm:
+ rterm_side_effect %dprec 2
+ | rterm_no_side_effect %dprec 1
+ ;
+
+target_type_specifier: /* empty */
+ {
+ $$ = new String();
+ }
+ | T_TO identifier
+ {
+ $$ = $2;
+ }
+ ;
+
+default_specifier: /* empty */
+ {
+ $$ = false;
+ }
+ | T_DEFAULT
+ {
+ $$ = true;
+ }
+ ;
+
+ignore_specifier: /* empty */
+ {
+ $$ = false;
+ }
+ | T_IGNORE_ON_ERROR
+ {
+ $$ = true;
+ }
+ ;
+
+use_specifier: /* empty */
+ {
+ $$ = new std::map<String, std::unique_ptr<Expression> >();
+ }
+ | T_USE '(' use_specifier_items ')'
+ {
+ $$ = $3;
+ }
+ ;
+
+use_specifier_items: use_specifier_item
+ {
+ $$ = new std::map<String, std::unique_ptr<Expression> >();
+ $$->emplace(std::move(*$1));
+ delete $1;
+ }
+ | use_specifier_items ',' use_specifier_item
+ {
+ $$ = $1;
+ $$->emplace(std::move(*$3));
+ delete $3;
+ }
+ ;
+
+use_specifier_item: identifier
+ {
+ std::unique_ptr<Expression> var (new VariableExpression(*$1, context->GetImports(), @1));
+ $$ = new std::pair<String, std::unique_ptr<Expression> >(std::move(*$1), std::move(var));
+ delete $1;
+ }
+ | identifier T_SET rterm
+ {
+ $$ = new std::pair<String, std::unique_ptr<Expression> >(std::move(*$1), std::unique_ptr<Expression>($3));
+ delete $1;
+ }
+ ;
+
+apply_for_specifier: /* empty */
+ | T_FOR '(' optional_var identifier T_FOLLOWS optional_var identifier T_IN rterm ')'
+ {
+ context->m_FKVar.top() = std::move(*$4);
+ delete $4;
+
+ context->m_FVVar.top() = std::move(*$7);
+ delete $7;
+
+ context->m_FTerm.top() = $9;
+ }
+ | T_FOR '(' optional_var identifier T_IN rterm ')'
+ {
+ context->m_FKVar.top() = std::move(*$4);
+ delete $4;
+
+ context->m_FVVar.top() = "";
+
+ context->m_FTerm.top() = $6;
+ }
+ ;
+
+optional_rterm: /* empty */
+ {
+ $$ = MakeLiteralRaw();
+ }
+ | rterm
+ ;
+
+apply:
+ {
+ context->m_Apply.push(true);
+ context->m_SeenAssign.push(false);
+ context->m_SeenIgnore.push(false);
+ context->m_Assign.push(NULL);
+ context->m_Ignore.push(NULL);
+ context->m_FKVar.push("");
+ context->m_FVVar.push("");
+ context->m_FTerm.push(NULL);
+ }
+ T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier use_specifier ignore_specifier
+ {
+ BeginFlowControlBlock(context, FlowControlReturn, false);
+ }
+ rterm_scope_require_side_effect
+ {
+ EndFlowControlBlock(context);
+
+ context->m_Apply.pop();
+
+ String type = std::move(*$3);
+ delete $3;
+ String target = std::move(*$6);
+ delete $6;
+
+ if (!ApplyRule::IsValidSourceType(type))
+ BOOST_THROW_EXCEPTION(ScriptError("'apply' cannot be used with type '" + type + "'", @3));
+
+ if (!ApplyRule::IsValidTargetType(type, target)) {
+ if (target == "") {
+ auto& types (ApplyRule::GetTargetTypes(type));
+ String typeNames;
+
+ for (std::vector<String>::size_type i = 0; i < types.size(); i++) {
+ if (typeNames != "") {
+ if (i == types.size() - 1)
+ typeNames += " or ";
+ else
+ typeNames += ", ";
+ }
+
+ typeNames += "'" + types[i] + "'";
+ }
+
+ BOOST_THROW_EXCEPTION(ScriptError("'apply' target type is ambiguous (can be one of " + typeNames + "): use 'to' to specify a type", DebugInfoRange(@2, @3)));
+ } else
+ BOOST_THROW_EXCEPTION(ScriptError("'apply' target type '" + target + "' is invalid", @6));
+ }
+
+ bool seen_assign = context->m_SeenAssign.top();
+ context->m_SeenAssign.pop();
+
+ // assign && !ignore
+ if (!seen_assign && !context->m_FTerm.top())
+ BOOST_THROW_EXCEPTION(ScriptError("'apply' is missing 'assign'/'for'", DebugInfoRange(@2, @3)));
+
+ std::unique_ptr<Expression> ignore{context->m_Ignore.top()};
+ context->m_Ignore.pop();
+
+ std::unique_ptr<Expression> assign;
+
+ if (!seen_assign)
+ assign = MakeLiteral(true);
+ else
+ assign.reset(context->m_Assign.top());
+
+ context->m_Assign.pop();
+
+ std::unique_ptr<Expression> filter;
+
+ if (ignore) {
+ std::unique_ptr<Expression>rex{new LogicalNegateExpression(std::move(ignore), DebugInfoRange(@2, @5))};
+
+ filter.reset(new LogicalAndExpression(std::move(assign), std::move(rex), DebugInfoRange(@2, @5)));
+ } else
+ filter.swap(assign);
+
+ String fkvar = std::move(context->m_FKVar.top());
+ context->m_FKVar.pop();
+
+ String fvvar = std::move(context->m_FVVar.top());
+ context->m_FVVar.pop();
+
+ std::unique_ptr<Expression> fterm{context->m_FTerm.top()};
+ context->m_FTerm.pop();
+
+ $$ = new ApplyExpression(std::move(type), std::move(target), std::unique_ptr<Expression>($4), std::move(filter), context->GetPackage(), std::move(fkvar), std::move(fvvar), std::move(fterm), std::move(*$7), $8, std::unique_ptr<Expression>($10), DebugInfoRange(@2, @8));
+ delete $7;
+ }
+ ;
+
+newlines: T_NEWLINE
+ | T_NEWLINE newlines
+ ;
+
+optional_newlines: /* empty */
+ | newlines
+ ;
+
+/* required separator */
+sep: ',' optional_newlines
+ | ';' optional_newlines
+ | newlines
+ ;
+
+%%
diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp
new file mode 100644
index 0000000..62f02ba
--- /dev/null
+++ b/lib/config/configcompiler.cpp
@@ -0,0 +1,364 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/configcompiler.hpp"
+#include "config/configitem.hpp"
+#include "base/logger.hpp"
+#include "base/utility.hpp"
+#include "base/loader.hpp"
+#include "base/context.hpp"
+#include "base/exception.hpp"
+#include <fstream>
+
+using namespace icinga;
+
+std::vector<String> ConfigCompiler::m_IncludeSearchDirs;
+std::mutex ConfigCompiler::m_ZoneDirsMutex;
+std::map<String, std::vector<ZoneFragment> > ConfigCompiler::m_ZoneDirs;
+
+/**
+ * Constructor for the ConfigCompiler class.
+ *
+ * @param path The path of the configuration file (or another name that
+ * identifies the source of the configuration text).
+ * @param input Input stream for the configuration file.
+ * @param zone The zone.
+ */
+ConfigCompiler::ConfigCompiler(String path, std::istream *input,
+ String zone, String package)
+ : m_Path(std::move(path)), m_Input(input), m_Zone(std::move(zone)),
+ m_Package(std::move(package)), m_Eof(false), m_OpenBraces(0)
+{
+ InitializeScanner();
+}
+
+/**
+ * Destructor for the ConfigCompiler class.
+ */
+ConfigCompiler::~ConfigCompiler()
+{
+ DestroyScanner();
+}
+
+/**
+ * Reads data from the input stream. Used internally by the lexer.
+ *
+ * @param buffer Where to store data.
+ * @param max_size The maximum number of bytes to read from the stream.
+ * @returns The actual number of bytes read.
+ */
+size_t ConfigCompiler::ReadInput(char *buffer, size_t max_size)
+{
+ m_Input->read(buffer, max_size);
+ return static_cast<size_t>(m_Input->gcount());
+}
+
+/**
+ * Retrieves the scanner object.
+ *
+ * @returns The scanner object.
+ */
+void *ConfigCompiler::GetScanner() const
+{
+ return m_Scanner;
+}
+
+/**
+ * Retrieves the path for the input file.
+ *
+ * @returns The path.
+ */
+const char *ConfigCompiler::GetPath() const
+{
+ return m_Path.CStr();
+}
+
+void ConfigCompiler::SetZone(const String& zone)
+{
+ m_Zone = zone;
+}
+
+String ConfigCompiler::GetZone() const
+{
+ return m_Zone;
+}
+
+void ConfigCompiler::SetPackage(const String& package)
+{
+ m_Package = package;
+}
+
+String ConfigCompiler::GetPackage() const
+{
+ return m_Package;
+}
+
+void ConfigCompiler::CollectIncludes(std::vector<std::unique_ptr<Expression> >& expressions,
+ const String& file, const String& zone, const String& package)
+{
+ try {
+ expressions.emplace_back(CompileFile(file, zone, package));
+ } catch (const std::exception& ex) {
+ Log(LogWarning, "ConfigCompiler")
+ << "Cannot compile file '"
+ << file << "': " << DiagnosticInformation(ex);
+ }
+}
+
+/**
+ * Handles an include directive.
+ *
+ * @param relativeBath The path this include is relative to.
+ * @param path The path from the include directive.
+ * @param search Whether to search global include dirs.
+ * @param debuginfo Debug information.
+ */
+std::unique_ptr<Expression> ConfigCompiler::HandleInclude(const String& relativeBase, const String& path,
+ bool search, const String& zone, const String& package, const DebugInfo& debuginfo)
+{
+ String upath;
+
+ if (search || (IsAbsolutePath(path)))
+ upath = path;
+ else
+ upath = relativeBase + "/" + path;
+
+ String includePath = upath;
+
+ if (search) {
+ for (const String& dir : m_IncludeSearchDirs) {
+ String spath = dir + "/" + path;
+
+ if (Utility::PathExists(spath)) {
+ includePath = spath;
+ break;
+ }
+ }
+ }
+
+ std::vector<std::unique_ptr<Expression> > expressions;
+ auto funcCallback = [&expressions, zone, package](const String& file) { CollectIncludes(expressions, file, zone, package); };
+
+ if (!Utility::Glob(includePath, funcCallback, GlobFile) && includePath.FindFirstOf("*?") == String::NPos) {
+ std::ostringstream msgbuf;
+ msgbuf << "Include file '" + path + "' does not exist";
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debuginfo));
+ }
+
+ std::unique_ptr<DictExpression> expr{new DictExpression(std::move(expressions))};
+ expr->MakeInline();
+ return std::move(expr);
+}
+
+/**
+ * Handles recursive includes.
+ *
+ * @param relativeBase The path this include is relative to.
+ * @param path The directory path.
+ * @param pattern The file pattern.
+ * @param debuginfo Debug information.
+ */
+std::unique_ptr<Expression> ConfigCompiler::HandleIncludeRecursive(const String& relativeBase, const String& path,
+ const String& pattern, const String& zone, const String& package, const DebugInfo&)
+{
+ String ppath;
+
+ if (IsAbsolutePath(path))
+ ppath = path;
+ else
+ ppath = relativeBase + "/" + path;
+
+ std::vector<std::unique_ptr<Expression> > expressions;
+ Utility::GlobRecursive(ppath, pattern, [&expressions, zone, package](const String& file) {
+ CollectIncludes(expressions, file, zone, package);
+ }, GlobFile);
+
+ std::unique_ptr<DictExpression> dict{new DictExpression(std::move(expressions))};
+ dict->MakeInline();
+ return std::move(dict);
+}
+
+void ConfigCompiler::HandleIncludeZone(const String& relativeBase, const String& tag, const String& path, const String& pattern, const String& package, std::vector<std::unique_ptr<Expression> >& expressions)
+{
+ String zoneName = Utility::BaseName(path);
+
+ String ppath;
+
+ if (IsAbsolutePath(path))
+ ppath = path;
+ else
+ ppath = relativeBase + "/" + path;
+
+ RegisterZoneDir(tag, ppath, zoneName);
+
+ Utility::GlobRecursive(ppath, pattern, [&expressions, zoneName, package](const String& file) {
+ CollectIncludes(expressions, file, zoneName, package);
+ }, GlobFile);
+}
+
+/**
+ * Handles zone includes.
+ *
+ * @param relativeBase The path this include is relative to.
+ * @param tag The tag name.
+ * @param path The directory path.
+ * @param pattern The file pattern.
+ * @param debuginfo Debug information.
+ */
+std::unique_ptr<Expression> ConfigCompiler::HandleIncludeZones(const String& relativeBase, const String& tag,
+ const String& path, const String& pattern, const String& package, const DebugInfo&)
+{
+ String ppath;
+ String newRelativeBase = relativeBase;
+
+ if (IsAbsolutePath(path))
+ ppath = path;
+ else {
+ ppath = relativeBase + "/" + path;
+ newRelativeBase = ".";
+ }
+
+ std::vector<std::unique_ptr<Expression> > expressions;
+ Utility::Glob(ppath + "/*", [newRelativeBase, tag, pattern, package, &expressions](const String& path) {
+ HandleIncludeZone(newRelativeBase, tag, path, pattern, package, expressions);
+ }, GlobDirectory);
+
+ return std::unique_ptr<Expression>(new DictExpression(std::move(expressions)));
+}
+
+/**
+ * Compiles a stream.
+ *
+ * @param path A name identifying the stream.
+ * @param stream The input stream.
+ * @returns Configuration items.
+ */
+std::unique_ptr<Expression> ConfigCompiler::CompileStream(const String& path,
+ std::istream *stream, const String& zone, const String& package)
+{
+ CONTEXT("Compiling configuration stream with name '" << path << "'");
+
+ stream->exceptions(std::istream::badbit);
+
+ ConfigCompiler ctx(path, stream, zone, package);
+
+ try {
+ return ctx.Compile();
+ } catch (const ScriptError& ex) {
+ return std::unique_ptr<Expression>(new ThrowExpression(MakeLiteral(ex.what()), ex.IsIncompleteExpression(), ex.GetDebugInfo()));
+ } catch (const std::exception& ex) {
+ return std::unique_ptr<Expression>(new ThrowExpression(MakeLiteral(DiagnosticInformation(ex)), false));
+ }
+}
+
+/**
+ * Compiles a file.
+ *
+ * @param path The path.
+ * @returns Configuration items.
+ */
+std::unique_ptr<Expression> ConfigCompiler::CompileFile(const String& path, const String& zone,
+ const String& package)
+{
+ CONTEXT("Compiling configuration file '" << path << "'");
+
+ std::ifstream stream(path.CStr(), std::ifstream::in);
+
+ if (!stream)
+ BOOST_THROW_EXCEPTION(posix_error()
+ << boost::errinfo_api_function("std::ifstream::open")
+ << boost::errinfo_errno(errno)
+ << boost::errinfo_file_name(path));
+
+ Log(LogNotice, "ConfigCompiler")
+ << "Compiling config file: " << path;
+
+ return CompileStream(path, &stream, zone, package);
+}
+
+/**
+ * Compiles a snippet of text.
+ *
+ * @param path A name identifying the text.
+ * @param text The text.
+ * @returns Configuration items.
+ */
+std::unique_ptr<Expression> ConfigCompiler::CompileText(const String& path, const String& text,
+ const String& zone, const String& package)
+{
+ std::stringstream stream(text);
+ return CompileStream(path, &stream, zone, package);
+}
+
+/**
+ * Adds a directory to the list of include search dirs.
+ *
+ * @param dir The new dir.
+ */
+void ConfigCompiler::AddIncludeSearchDir(const String& dir)
+{
+ Log(LogInformation, "ConfigCompiler")
+ << "Adding include search dir: " << dir;
+
+ m_IncludeSearchDirs.push_back(dir);
+}
+
+std::vector<ZoneFragment> ConfigCompiler::GetZoneDirs(const String& zone)
+{
+ std::unique_lock<std::mutex> lock(m_ZoneDirsMutex);
+ auto it = m_ZoneDirs.find(zone);
+ if (it == m_ZoneDirs.end())
+ return std::vector<ZoneFragment>();
+ else
+ return it->second;
+}
+
+void ConfigCompiler::RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName)
+{
+ ZoneFragment zf;
+ zf.Tag = tag;
+ zf.Path = ppath;
+
+ std::unique_lock<std::mutex> lock(m_ZoneDirsMutex);
+ m_ZoneDirs[zoneName].push_back(zf);
+}
+
+bool ConfigCompiler::HasZoneConfigAuthority(const String& zoneName)
+{
+ std::vector<ZoneFragment> zoneDirs = m_ZoneDirs[zoneName];
+
+ bool empty = zoneDirs.empty();
+
+ if (!empty) {
+ std::vector<String> paths;
+ paths.reserve(zoneDirs.size());
+
+ for (const ZoneFragment& zf : zoneDirs) {
+ paths.push_back(zf.Path);
+ }
+
+ Log(LogNotice, "ConfigCompiler")
+ << "Registered authoritative config directories for zone '" << zoneName << "': " << Utility::NaturalJoin(paths);
+ }
+
+ return !empty;
+}
+
+
+bool ConfigCompiler::IsAbsolutePath(const String& path)
+{
+#ifndef _WIN32
+ return (path.GetLength() > 0 && path[0] == '/');
+#else /* _WIN32 */
+ return !PathIsRelative(path.CStr());
+#endif /* _WIN32 */
+}
+
+void ConfigCompiler::AddImport(const Expression::Ptr& import)
+{
+ m_Imports.push_back(import);
+}
+
+std::vector<Expression::Ptr> ConfigCompiler::GetImports() const
+{
+ return m_Imports;
+}
diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp
new file mode 100644
index 0000000..fe00bed
--- /dev/null
+++ b/lib/config/configcompiler.hpp
@@ -0,0 +1,161 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CONFIGCOMPILER_H
+#define CONFIGCOMPILER_H
+
+#include "config/i2-config.hpp"
+#include "config/expression.hpp"
+#include "base/debuginfo.hpp"
+#include "base/registry.hpp"
+#include "base/initialize.hpp"
+#include "base/singleton.hpp"
+#include "base/string.hpp"
+#include <future>
+#include <iostream>
+#include <stack>
+
+typedef union YYSTYPE YYSTYPE;
+typedef void *yyscan_t;
+
+namespace icinga
+{
+
+struct CompilerDebugInfo
+{
+ const char *Path;
+
+ int FirstLine;
+ int FirstColumn;
+
+ int LastLine;
+ int LastColumn;
+
+ operator DebugInfo() const
+ {
+ DebugInfo di;
+ di.Path = Path;
+ di.FirstLine = FirstLine;
+ di.FirstColumn = FirstColumn;
+ di.LastLine = LastLine;
+ di.LastColumn = LastColumn;
+ return di;
+ }
+};
+
+struct EItemInfo
+{
+ bool SideEffect;
+ CompilerDebugInfo DebugInfo;
+};
+
+enum FlowControlType
+{
+ FlowControlReturn = 1,
+ FlowControlContinue = 2,
+ FlowControlBreak = 4
+};
+
+struct ZoneFragment
+{
+ String Tag;
+ String Path;
+};
+
+/**
+ * The configuration compiler can be used to compile a configuration file
+ * into a number of configuration items.
+ *
+ * @ingroup config
+ */
+class ConfigCompiler
+{
+public:
+ explicit ConfigCompiler(String path, std::istream *input,
+ String zone = String(), String package = String());
+ virtual ~ConfigCompiler();
+
+ std::unique_ptr<Expression> Compile();
+
+ static std::unique_ptr<Expression>CompileStream(const String& path, std::istream *stream,
+ const String& zone = String(), const String& package = String());
+ static std::unique_ptr<Expression>CompileFile(const String& path, const String& zone = String(),
+ const String& package = String());
+ static std::unique_ptr<Expression>CompileText(const String& path, const String& text,
+ const String& zone = String(), const String& package = String());
+
+ static void AddIncludeSearchDir(const String& dir);
+
+ const char *GetPath() const;
+
+ void SetZone(const String& zone);
+ String GetZone() const;
+
+ void SetPackage(const String& package);
+ String GetPackage() const;
+
+ void AddImport(const Expression::Ptr& import);
+ std::vector<Expression::Ptr> GetImports() const;
+
+ static void CollectIncludes(std::vector<std::unique_ptr<Expression> >& expressions,
+ const String& file, const String& zone, const String& package);
+
+ static std::unique_ptr<Expression> HandleInclude(const String& relativeBase, const String& path, bool search,
+ const String& zone, const String& package, const DebugInfo& debuginfo = DebugInfo());
+ static std::unique_ptr<Expression> HandleIncludeRecursive(const String& relativeBase, const String& path,
+ const String& pattern, const String& zone, const String& package, const DebugInfo& debuginfo = DebugInfo());
+ static std::unique_ptr<Expression> HandleIncludeZones(const String& relativeBase, const String& tag,
+ const String& path, const String& pattern, const String& package, const DebugInfo& debuginfo = DebugInfo());
+
+ size_t ReadInput(char *buffer, size_t max_bytes);
+ void *GetScanner() const;
+
+ static std::vector<ZoneFragment> GetZoneDirs(const String& zone);
+ static void RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName);
+
+ static bool HasZoneConfigAuthority(const String& zoneName);
+
+private:
+ std::promise<Expression::Ptr> m_Promise;
+
+ String m_Path;
+ std::istream *m_Input;
+ String m_Zone;
+ String m_Package;
+ std::vector<Expression::Ptr> m_Imports;
+
+ void *m_Scanner;
+
+ static std::vector<String> m_IncludeSearchDirs;
+ static std::mutex m_ZoneDirsMutex;
+ static std::map<String, std::vector<ZoneFragment> > m_ZoneDirs;
+
+ void InitializeScanner();
+ void DestroyScanner();
+
+ static void HandleIncludeZone(const String& relativeBase, const String& tag, const String& path, const String& pattern, const String& package, std::vector<std::unique_ptr<Expression> >& expressions);
+
+ static bool IsAbsolutePath(const String& path);
+
+public:
+ bool m_Eof;
+ int m_OpenBraces;
+
+ String m_LexBuffer;
+ CompilerDebugInfo m_LocationBegin;
+
+ std::stack<bool> m_IgnoreNewlines;
+ std::stack<bool> m_Apply;
+ std::stack<bool> m_ObjectAssign;
+ std::stack<bool> m_SeenAssign;
+ std::stack<bool> m_SeenIgnore;
+ std::stack<Expression *> m_Assign;
+ std::stack<Expression *> m_Ignore;
+ std::stack<String> m_FKVar;
+ std::stack<String> m_FVVar;
+ std::stack<Expression *> m_FTerm;
+ std::stack<int> m_FlowControlInfo;
+};
+
+}
+
+#endif /* CONFIGCOMPILER_H */
diff --git a/lib/config/configcompilercontext.cpp b/lib/config/configcompilercontext.cpp
new file mode 100644
index 0000000..0161181
--- /dev/null
+++ b/lib/config/configcompilercontext.cpp
@@ -0,0 +1,57 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/configcompilercontext.hpp"
+#include "base/singleton.hpp"
+#include "base/json.hpp"
+#include "base/netstring.hpp"
+#include "base/exception.hpp"
+#include "base/application.hpp"
+#include "base/utility.hpp"
+
+using namespace icinga;
+
+ConfigCompilerContext *ConfigCompilerContext::GetInstance()
+{
+ return Singleton<ConfigCompilerContext>::GetInstance();
+}
+
+void ConfigCompilerContext::OpenObjectsFile(const String& filename)
+{
+ try {
+ m_ObjectsFP = std::make_unique<AtomicFile>(filename, 0600);
+ } catch (const std::exception& ex) {
+ Log(LogCritical, "cli", "Could not create temporary objects file: " + DiagnosticInformation(ex, false));
+ Application::Exit(1);
+ }
+}
+
+void ConfigCompilerContext::WriteObject(const Dictionary::Ptr& object)
+{
+ if (!m_ObjectsFP)
+ return;
+
+ String json = JsonEncode(object);
+
+ {
+ std::unique_lock<std::mutex> lock(m_Mutex);
+ NetString::WriteStringToStream(*m_ObjectsFP, json);
+ }
+}
+
+void ConfigCompilerContext::CancelObjectsFile()
+{
+ if (!m_ObjectsFP)
+ return;
+
+ m_ObjectsFP.reset(nullptr);
+}
+
+void ConfigCompilerContext::FinishObjectsFile()
+{
+ if (!m_ObjectsFP)
+ return;
+
+ m_ObjectsFP->Commit();
+ m_ObjectsFP.reset(nullptr);
+}
+
diff --git a/lib/config/configcompilercontext.hpp b/lib/config/configcompilercontext.hpp
new file mode 100644
index 0000000..c3d5317
--- /dev/null
+++ b/lib/config/configcompilercontext.hpp
@@ -0,0 +1,42 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CONFIGCOMPILERCONTEXT_H
+#define CONFIGCOMPILERCONTEXT_H
+
+#include "config/i2-config.hpp"
+#include "base/atomic-file.hpp"
+#include "base/dictionary.hpp"
+#include <fstream>
+#include <memory>
+#include <mutex>
+
+namespace icinga
+{
+
+/*
+ * @ingroup config
+ */
+class ConfigCompilerContext
+{
+public:
+ void OpenObjectsFile(const String& filename);
+ void WriteObject(const Dictionary::Ptr& object);
+ void CancelObjectsFile();
+ void FinishObjectsFile();
+
+ inline bool IsOpen() const noexcept
+ {
+ return (bool)m_ObjectsFP;
+ }
+
+ static ConfigCompilerContext *GetInstance();
+
+private:
+ std::unique_ptr<AtomicFile> m_ObjectsFP;
+
+ mutable std::mutex m_Mutex;
+};
+
+}
+
+#endif /* CONFIGCOMPILERCONTEXT_H */
diff --git a/lib/config/configfragment.hpp b/lib/config/configfragment.hpp
new file mode 100644
index 0000000..883aef8
--- /dev/null
+++ b/lib/config/configfragment.hpp
@@ -0,0 +1,26 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CONFIGFRAGMENT_H
+#define CONFIGFRAGMENT_H
+
+#include "config/configcompiler.hpp"
+#include "base/initialize.hpp"
+#include "base/debug.hpp"
+#include "base/exception.hpp"
+#include "base/application.hpp"
+
+/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */
+#define REGISTER_CONFIG_FRAGMENT(name, fragment) \
+ INITIALIZE_ONCE_WITH_PRIORITY([]() { \
+ std::unique_ptr<icinga::Expression> expression = icinga::ConfigCompiler::CompileText(name, fragment); \
+ VERIFY(expression); \
+ try { \
+ icinga::ScriptFrame frame(true); \
+ expression->Evaluate(frame); \
+ } catch (const std::exception& ex) { \
+ std::cerr << icinga::DiagnosticInformation(ex) << std::endl; \
+ icinga::Application::Exit(1); \
+ } \
+ }, icinga::InitializePriority::EvaluateConfigFragments)
+
+#endif /* CONFIGFRAGMENT_H */
diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp
new file mode 100644
index 0000000..9dc0f1a
--- /dev/null
+++ b/lib/config/configitem.cpp
@@ -0,0 +1,849 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/configitem.hpp"
+#include "config/configcompilercontext.hpp"
+#include "config/applyrule.hpp"
+#include "config/objectrule.hpp"
+#include "config/configcompiler.hpp"
+#include "base/application.hpp"
+#include "base/configtype.hpp"
+#include "base/objectlock.hpp"
+#include "base/convert.hpp"
+#include "base/logger.hpp"
+#include "base/debug.hpp"
+#include "base/workqueue.hpp"
+#include "base/exception.hpp"
+#include "base/stdiostream.hpp"
+#include "base/netstring.hpp"
+#include "base/serializer.hpp"
+#include "base/json.hpp"
+#include "base/exception.hpp"
+#include "base/function.hpp"
+#include "base/utility.hpp"
+#include <boost/algorithm/string/join.hpp>
+#include <atomic>
+#include <sstream>
+#include <fstream>
+#include <algorithm>
+#include <random>
+#include <unordered_map>
+
+using namespace icinga;
+
+std::mutex ConfigItem::m_Mutex;
+ConfigItem::TypeMap ConfigItem::m_Items;
+ConfigItem::TypeMap ConfigItem::m_DefaultTemplates;
+ConfigItem::ItemList ConfigItem::m_UnnamedItems;
+ConfigItem::IgnoredItemList ConfigItem::m_IgnoredItems;
+
+REGISTER_FUNCTION(Internal, run_with_activation_context, &ConfigItem::RunWithActivationContext, "func");
+
+/**
+ * Constructor for the ConfigItem class.
+ *
+ * @param type The object type.
+ * @param name The name of the item.
+ * @param unit The unit of the item.
+ * @param abstract Whether the item is a template.
+ * @param exprl Expression list for the item.
+ * @param debuginfo Debug information.
+ */
+ConfigItem::ConfigItem(Type::Ptr type, String name,
+ bool abstract, Expression::Ptr exprl,
+ Expression::Ptr filter, bool defaultTmpl, bool ignoreOnError,
+ DebugInfo debuginfo, Dictionary::Ptr scope,
+ String zone, String package)
+ : m_Type(std::move(type)), m_Name(std::move(name)), m_Abstract(abstract),
+ m_Expression(std::move(exprl)), m_Filter(std::move(filter)),
+ m_DefaultTmpl(defaultTmpl), m_IgnoreOnError(ignoreOnError),
+ m_DebugInfo(std::move(debuginfo)), m_Scope(std::move(scope)), m_Zone(std::move(zone)),
+ m_Package(std::move(package))
+{
+}
+
+/**
+ * Retrieves the type of the configuration item.
+ *
+ * @returns The type.
+ */
+Type::Ptr ConfigItem::GetType() const
+{
+ return m_Type;
+}
+
+/**
+ * Retrieves the name of the configuration item.
+ *
+ * @returns The name.
+ */
+String ConfigItem::GetName() const
+{
+ return m_Name;
+}
+
+/**
+ * Checks whether the item is abstract.
+ *
+ * @returns true if the item is abstract, false otherwise.
+ */
+bool ConfigItem::IsAbstract() const
+{
+ return m_Abstract;
+}
+
+bool ConfigItem::IsDefaultTemplate() const
+{
+ return m_DefaultTmpl;
+}
+
+bool ConfigItem::IsIgnoreOnError() const
+{
+ return m_IgnoreOnError;
+}
+
+/**
+ * Retrieves the debug information for the configuration item.
+ *
+ * @returns The debug information.
+ */
+DebugInfo ConfigItem::GetDebugInfo() const
+{
+ return m_DebugInfo;
+}
+
+Dictionary::Ptr ConfigItem::GetScope() const
+{
+ return m_Scope;
+}
+
+ConfigObject::Ptr ConfigItem::GetObject() const
+{
+ return m_Object;
+}
+
+/**
+ * Retrieves the expression list for the configuration item.
+ *
+ * @returns The expression list.
+ */
+Expression::Ptr ConfigItem::GetExpression() const
+{
+ return m_Expression;
+}
+
+/**
+* Retrieves the object filter for the configuration item.
+*
+* @returns The filter expression.
+*/
+Expression::Ptr ConfigItem::GetFilter() const
+{
+ return m_Filter;
+}
+
+class DefaultValidationUtils final : public ValidationUtils
+{
+public:
+ bool ValidateName(const String& type, const String& name) const override
+ {
+ ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name);
+
+ if (!item || item->IsAbstract())
+ return false;
+
+ return true;
+ }
+};
+
+/**
+ * Commits the configuration item by creating a ConfigObject
+ * object.
+ *
+ * @returns The ConfigObject that was created/updated.
+ */
+ConfigObject::Ptr ConfigItem::Commit(bool discard)
+{
+ Type::Ptr type = GetType();
+
+#ifdef I2_DEBUG
+ Log(LogDebug, "ConfigItem")
+ << "Commit called for ConfigItem Type=" << type->GetName() << ", Name=" << GetName();
+#endif /* I2_DEBUG */
+
+ /* Make sure the type is valid. */
+ if (!type || !ConfigObject::TypeInstance->IsAssignableFrom(type))
+ BOOST_THROW_EXCEPTION(ScriptError("Type '" + type->GetName() + "' does not exist.", m_DebugInfo));
+
+ if (IsAbstract())
+ return nullptr;
+
+ ConfigObject::Ptr dobj = static_pointer_cast<ConfigObject>(type->Instantiate(std::vector<Value>()));
+
+ dobj->SetDebugInfo(m_DebugInfo);
+ dobj->SetZoneName(m_Zone);
+ dobj->SetPackage(m_Package);
+ dobj->SetName(m_Name);
+
+ DebugHint debugHints;
+
+ ScriptFrame frame(true, dobj);
+ if (m_Scope)
+ m_Scope->CopyTo(frame.Locals);
+ try {
+ m_Expression->Evaluate(frame, &debugHints);
+ } catch (const std::exception& ex) {
+ if (m_IgnoreOnError) {
+ Log(LogNotice, "ConfigObject")
+ << "Ignoring config object '" << m_Name << "' of type '" << type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
+
+ {
+ std::unique_lock<std::mutex> lock(m_Mutex);
+ m_IgnoredItems.push_back(m_DebugInfo.Path);
+ }
+
+ return nullptr;
+ }
+
+ throw;
+ }
+
+ if (discard)
+ m_Expression.reset();
+
+ String item_name;
+ String short_name = dobj->GetShortName();
+
+ if (!short_name.IsEmpty()) {
+ item_name = short_name;
+ dobj->SetName(short_name);
+ } else
+ item_name = m_Name;
+
+ String name = item_name;
+
+ auto *nc = dynamic_cast<NameComposer *>(type.get());
+
+ if (nc) {
+ if (name.IsEmpty())
+ BOOST_THROW_EXCEPTION(ScriptError("Object name must not be empty.", m_DebugInfo));
+
+ name = nc->MakeName(name, dobj);
+
+ if (name.IsEmpty())
+ BOOST_THROW_EXCEPTION(std::runtime_error("Could not determine name for object"));
+ }
+
+ if (name != item_name)
+ dobj->SetShortName(item_name);
+
+ dobj->SetName(name);
+
+ Dictionary::Ptr dhint = debugHints.ToDictionary();
+
+ try {
+ DefaultValidationUtils utils;
+ dobj->Validate(FAConfig, utils);
+ } catch (ValidationError& ex) {
+ if (m_IgnoreOnError) {
+ Log(LogNotice, "ConfigObject")
+ << "Ignoring config object '" << m_Name << "' of type '" << type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
+
+ {
+ std::unique_lock<std::mutex> lock(m_Mutex);
+ m_IgnoredItems.push_back(m_DebugInfo.Path);
+ }
+
+ return nullptr;
+ }
+
+ ex.SetDebugHint(dhint);
+ throw;
+ }
+
+ try {
+ dobj->OnConfigLoaded();
+ } catch (const std::exception& ex) {
+ if (m_IgnoreOnError) {
+ Log(LogNotice, "ConfigObject")
+ << "Ignoring config object '" << m_Name << "' of type '" << m_Type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
+
+ {
+ std::unique_lock<std::mutex> lock(m_Mutex);
+ m_IgnoredItems.push_back(m_DebugInfo.Path);
+ }
+
+ return nullptr;
+ }
+
+ throw;
+ }
+
+ Value serializedObject;
+
+ try {
+ if (ConfigCompilerContext::GetInstance()->IsOpen()) {
+ serializedObject = Serialize(dobj, FAConfig);
+ } else {
+ AssertNoCircularReferences(dobj);
+ }
+ } catch (const CircularReferenceError& ex) {
+ BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed"));
+ }
+
+ if (ConfigCompilerContext::GetInstance()->IsOpen()) {
+ Dictionary::Ptr persistentItem = new Dictionary({
+ { "type", type->GetName() },
+ { "name", GetName() },
+ { "properties", serializedObject },
+ { "debug_hints", dhint },
+ { "debug_info", new Array({
+ m_DebugInfo.Path,
+ m_DebugInfo.FirstLine,
+ m_DebugInfo.FirstColumn,
+ m_DebugInfo.LastLine,
+ m_DebugInfo.LastColumn,
+ }) }
+ });
+
+ ConfigCompilerContext::GetInstance()->WriteObject(persistentItem);
+ }
+
+ dhint.reset();
+
+ dobj->Register();
+
+ m_Object = dobj;
+
+ return dobj;
+}
+
+/**
+ * Registers the configuration item.
+ */
+void ConfigItem::Register()
+{
+ m_ActivationContext = ActivationContext::GetCurrentContext();
+
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ /* If this is a non-abstract object with a composite name
+ * we register it in m_UnnamedItems instead of m_Items. */
+ if (!m_Abstract && dynamic_cast<NameComposer *>(m_Type.get()))
+ m_UnnamedItems.emplace_back(this);
+ else {
+ auto& items = m_Items[m_Type];
+
+ auto it = items.find(m_Name);
+
+ if (it != items.end()) {
+ std::ostringstream msgbuf;
+ msgbuf << "A configuration item of type '" << m_Type->GetName()
+ << "' and name '" << GetName() << "' already exists ("
+ << it->second->GetDebugInfo() << "), new declaration: " << GetDebugInfo();
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str()));
+ }
+
+ m_Items[m_Type][m_Name] = this;
+
+ if (m_DefaultTmpl)
+ m_DefaultTemplates[m_Type][m_Name] = this;
+ }
+}
+
+/**
+ * Unregisters the configuration item.
+ */
+void ConfigItem::Unregister()
+{
+ if (m_Object) {
+ m_Object->Unregister();
+ m_Object.reset();
+ }
+
+ std::unique_lock<std::mutex> lock(m_Mutex);
+ m_UnnamedItems.erase(std::remove(m_UnnamedItems.begin(), m_UnnamedItems.end(), this), m_UnnamedItems.end());
+ m_Items[m_Type].erase(m_Name);
+ m_DefaultTemplates[m_Type].erase(m_Name);
+}
+
+/**
+ * Retrieves a configuration item by type and name.
+ *
+ * @param type The type of the ConfigItem that is to be looked up.
+ * @param name The name of the ConfigItem that is to be looked up.
+ * @returns The configuration item.
+ */
+ConfigItem::Ptr ConfigItem::GetByTypeAndName(const Type::Ptr& type, const String& name)
+{
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ auto it = m_Items.find(type);
+
+ if (it == m_Items.end())
+ return nullptr;
+
+ auto it2 = it->second.find(name);
+
+ if (it2 == it->second.end())
+ return nullptr;
+
+ return it2->second;
+}
+
+bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems)
+{
+ typedef std::pair<ConfigItem::Ptr, bool> ItemPair;
+ std::unordered_map<Type*, std::vector<ItemPair>> itemsByType;
+ std::vector<ItemPair>::size_type total = 0;
+
+ {
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ for (const TypeMap::value_type& kv : m_Items) {
+ std::vector<ItemPair> items;
+
+ for (const ItemMap::value_type& kv2 : kv.second) {
+ if (kv2.second->m_Abstract || kv2.second->m_Object)
+ continue;
+
+ if (kv2.second->m_ActivationContext != context)
+ continue;
+
+ items.emplace_back(kv2.second, false);
+ }
+
+ if (!items.empty()) {
+ total += items.size();
+ itemsByType.emplace(kv.first.get(), std::move(items));
+ }
+ }
+
+ ItemList newUnnamedItems;
+
+ for (const ConfigItem::Ptr& item : m_UnnamedItems) {
+ if (item->m_ActivationContext != context) {
+ newUnnamedItems.push_back(item);
+ continue;
+ }
+
+ if (item->m_Abstract || item->m_Object)
+ continue;
+
+ itemsByType[item->m_Type.get()].emplace_back(item, true);
+ ++total;
+ }
+
+ m_UnnamedItems.swap(newUnnamedItems);
+ }
+
+ if (!total)
+ return true;
+
+ // Shuffle all items to evenly distribute them over the threads of the workqueue. This increases perfomance
+ // noticably in environments with lots of objects and available threads.
+ for (auto& kv : itemsByType) {
+ std::shuffle(std::begin(kv.second), std::end(kv.second), std::default_random_engine{});
+ }
+
+#ifdef I2_DEBUG
+ Log(LogDebug, "configitem")
+ << "Committing " << total << " new items.";
+#endif /* I2_DEBUG */
+
+ std::set<Type::Ptr> types;
+ std::set<Type::Ptr> completed_types;
+ int itemsCount {0};
+
+ for (const Type::Ptr& type : Type::GetAllTypes()) {
+ if (ConfigObject::TypeInstance->IsAssignableFrom(type))
+ types.insert(type);
+ }
+
+ while (types.size() != completed_types.size()) {
+ for (const Type::Ptr& type : types) {
+ if (completed_types.find(type) != completed_types.end())
+ continue;
+
+ bool unresolved_dep = false;
+
+ /* skip this type (for now) if there are unresolved load dependencies */
+ for (auto pLoadDep : type->GetLoadDependencies()) {
+ if (types.find(pLoadDep) != types.end() && completed_types.find(pLoadDep) == completed_types.end()) {
+ unresolved_dep = true;
+ break;
+ }
+ }
+
+ if (unresolved_dep)
+ continue;
+
+ std::atomic<int> committed_items(0);
+ std::mutex newItemsMutex;
+
+ {
+ auto items (itemsByType.find(type.get()));
+
+ if (items != itemsByType.end()) {
+ upq.ParallelFor(items->second, [&committed_items, &newItems, &newItemsMutex](const ItemPair& ip) {
+ const ConfigItem::Ptr& item = ip.first;
+
+ if (!item->Commit(ip.second)) {
+ if (item->IsIgnoreOnError()) {
+ item->Unregister();
+ }
+
+ return;
+ }
+
+ committed_items++;
+
+ std::unique_lock<std::mutex> lock(newItemsMutex);
+ newItems.emplace_back(item);
+ });
+
+ upq.Join();
+ }
+ }
+
+ itemsCount += committed_items;
+
+ completed_types.insert(type);
+
+#ifdef I2_DEBUG
+ if (committed_items > 0)
+ Log(LogDebug, "configitem")
+ << "Committed " << committed_items << " items of type '" << type->GetName() << "'.";
+#endif /* I2_DEBUG */
+
+ if (upq.HasExceptions())
+ return false;
+ }
+ }
+
+#ifdef I2_DEBUG
+ Log(LogDebug, "configitem")
+ << "Committed " << itemsCount << " items.";
+#endif /* I2_DEBUG */
+
+ completed_types.clear();
+
+ while (types.size() != completed_types.size()) {
+ for (const Type::Ptr& type : types) {
+ if (completed_types.find(type) != completed_types.end())
+ continue;
+
+ bool unresolved_dep = false;
+
+ /* skip this type (for now) if there are unresolved load dependencies */
+ for (auto pLoadDep : type->GetLoadDependencies()) {
+ if (types.find(pLoadDep) != types.end() && completed_types.find(pLoadDep) == completed_types.end()) {
+ unresolved_dep = true;
+ break;
+ }
+ }
+
+ if (unresolved_dep)
+ continue;
+
+ std::atomic<int> notified_items(0);
+
+ {
+ auto items (itemsByType.find(type.get()));
+
+ if (items != itemsByType.end()) {
+ upq.ParallelFor(items->second, [&notified_items](const ItemPair& ip) {
+ const ConfigItem::Ptr& item = ip.first;
+
+ if (!item->m_Object)
+ return;
+
+ try {
+ item->m_Object->OnAllConfigLoaded();
+ notified_items++;
+ } catch (const std::exception& ex) {
+ if (!item->m_IgnoreOnError)
+ throw;
+
+ Log(LogNotice, "ConfigObject")
+ << "Ignoring config object '" << item->m_Name << "' of type '" << item->m_Type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
+
+ item->Unregister();
+
+ {
+ std::unique_lock<std::mutex> lock(item->m_Mutex);
+ item->m_IgnoredItems.push_back(item->m_DebugInfo.Path);
+ }
+ }
+ });
+
+ upq.Join();
+ }
+ }
+
+ completed_types.insert(type);
+
+#ifdef I2_DEBUG
+ if (notified_items > 0)
+ Log(LogDebug, "configitem")
+ << "Sent OnAllConfigLoaded to " << notified_items << " items of type '" << type->GetName() << "'.";
+#endif /* I2_DEBUG */
+
+ if (upq.HasExceptions())
+ return false;
+
+ notified_items = 0;
+ for (auto loadDep : type->GetLoadDependencies()) {
+ auto items (itemsByType.find(loadDep));
+
+ if (items != itemsByType.end()) {
+ upq.ParallelFor(items->second, [&type, &notified_items](const ItemPair& ip) {
+ const ConfigItem::Ptr& item = ip.first;
+
+ if (!item->m_Object)
+ return;
+
+ ActivationScope ascope(item->m_ActivationContext);
+ item->m_Object->CreateChildObjects(type);
+ notified_items++;
+ });
+ }
+ }
+
+ upq.Join();
+
+#ifdef I2_DEBUG
+ if (notified_items > 0)
+ Log(LogDebug, "configitem")
+ << "Sent CreateChildObjects to " << notified_items << " items of type '" << type->GetName() << "'.";
+#endif /* I2_DEBUG */
+
+ if (upq.HasExceptions())
+ return false;
+
+ // Make sure to activate any additionally generated items
+ if (!CommitNewItems(context, upq, newItems))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfigItem::CommitItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems, bool silent)
+{
+ if (!silent)
+ Log(LogInformation, "ConfigItem", "Committing config item(s).");
+
+ if (!CommitNewItems(context, upq, newItems)) {
+ upq.ReportExceptions("config");
+
+ for (const ConfigItem::Ptr& item : newItems) {
+ item->Unregister();
+ }
+
+ return false;
+ }
+
+ ApplyRule::CheckMatches(silent);
+
+ if (!silent) {
+ /* log stats for external parsers */
+ typedef std::map<Type::Ptr, int> ItemCountMap;
+ ItemCountMap itemCounts;
+ for (const ConfigItem::Ptr& item : newItems) {
+ if (!item->m_Object)
+ continue;
+
+ itemCounts[item->m_Object->GetReflectionType()]++;
+ }
+
+ for (const ItemCountMap::value_type& kv : itemCounts) {
+ Log(LogInformation, "ConfigItem")
+ << "Instantiated " << kv.second << " " << (kv.second != 1 ? kv.first->GetPluralName() : kv.first->GetName()) << ".";
+ }
+ }
+
+ return true;
+}
+
+/**
+ * ActivateItems activates new config items.
+ *
+ * @param newItems Vector of items to be activated
+ * @param runtimeCreated Whether the objects were created by a runtime object
+ * @param mainConfigActivation Whether this is the call for activating the main configuration during startup
+ * @param withModAttrs Whether this call shall read the modified attributes file
+ * @param cookie Cookie for preventing message loops
+ * @return Whether the config activation was successful (in case of errors, exceptions are thrown)
+ */
+bool ConfigItem::ActivateItems(const std::vector<ConfigItem::Ptr>& newItems, bool runtimeCreated,
+ bool mainConfigActivation, bool withModAttrs, const Value& cookie)
+{
+ static std::mutex mtx;
+ std::unique_lock<std::mutex> lock(mtx);
+
+ if (withModAttrs) {
+ /* restore modified attributes */
+ if (Utility::PathExists(Configuration::ModAttrPath)) {
+ std::unique_ptr<Expression> expression = ConfigCompiler::CompileFile(Configuration::ModAttrPath);
+
+ if (expression) {
+ try {
+ ScriptFrame frame(true);
+ expression->Evaluate(frame);
+ } catch (const std::exception& ex) {
+ Log(LogCritical, "config", DiagnosticInformation(ex));
+ }
+ }
+ }
+ }
+
+ for (const ConfigItem::Ptr& item : newItems) {
+ if (!item->m_Object)
+ continue;
+
+ ConfigObject::Ptr object = item->m_Object;
+
+ if (object->IsActive())
+ continue;
+
+#ifdef I2_DEBUG
+ Log(LogDebug, "ConfigItem")
+ << "Setting 'active' to true for object '" << object->GetName() << "' of type '" << object->GetReflectionType()->GetName() << "'";
+#endif /* I2_DEBUG */
+
+ object->PreActivate();
+ }
+
+ if (mainConfigActivation)
+ Log(LogInformation, "ConfigItem", "Triggering Start signal for config items");
+
+ /* Activate objects in priority order. */
+ std::vector<Type::Ptr> types = Type::GetAllTypes();
+
+ std::sort(types.begin(), types.end(), [](const Type::Ptr& a, const Type::Ptr& b) {
+ if (a->GetActivationPriority() < b->GetActivationPriority())
+ return true;
+ return false;
+ });
+
+ /* Find the last logger type to be activated. */
+ Type::Ptr lastLoggerType = nullptr;
+ for (const Type::Ptr& type : types) {
+ if (Logger::TypeInstance->IsAssignableFrom(type)) {
+ lastLoggerType = type;
+ }
+ }
+
+ for (const Type::Ptr& type : types) {
+ for (const ConfigItem::Ptr& item : newItems) {
+ if (!item->m_Object)
+ continue;
+
+ ConfigObject::Ptr object = item->m_Object;
+ Type::Ptr objectType = object->GetReflectionType();
+
+ if (objectType != type)
+ continue;
+
+#ifdef I2_DEBUG
+ Log(LogDebug, "ConfigItem")
+ << "Activating object '" << object->GetName() << "' of type '"
+ << objectType->GetName() << "' with priority "
+ << objectType->GetActivationPriority();
+#endif /* I2_DEBUG */
+
+ object->Activate(runtimeCreated, cookie);
+ }
+
+ if (mainConfigActivation && type == lastLoggerType) {
+ /* Disable early logging configuration once the last logger type was activated. */
+ Logger::DisableEarlyLogging();
+ }
+ }
+
+ if (mainConfigActivation)
+ Log(LogInformation, "ConfigItem", "Activated all objects.");
+
+ return true;
+}
+
+bool ConfigItem::RunWithActivationContext(const Function::Ptr& function)
+{
+ ActivationScope scope;
+
+ if (!function)
+ BOOST_THROW_EXCEPTION(ScriptError("'function' argument must not be null."));
+
+ function->Invoke();
+
+ WorkQueue upq(25000, Configuration::Concurrency);
+ upq.SetName("ConfigItem::RunWithActivationContext");
+
+ std::vector<ConfigItem::Ptr> newItems;
+
+ if (!CommitItems(scope.GetContext(), upq, newItems, true))
+ return false;
+
+ if (!ActivateItems(newItems, false, false))
+ return false;
+
+ return true;
+}
+
+std::vector<ConfigItem::Ptr> ConfigItem::GetItems(const Type::Ptr& type)
+{
+ std::vector<ConfigItem::Ptr> items;
+
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ auto it = m_Items.find(type);
+
+ if (it == m_Items.end())
+ return items;
+
+ items.reserve(it->second.size());
+
+ for (const ItemMap::value_type& kv : it->second) {
+ items.push_back(kv.second);
+ }
+
+ return items;
+}
+
+std::vector<ConfigItem::Ptr> ConfigItem::GetDefaultTemplates(const Type::Ptr& type)
+{
+ std::vector<ConfigItem::Ptr> items;
+
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ auto it = m_DefaultTemplates.find(type);
+
+ if (it == m_DefaultTemplates.end())
+ return items;
+
+ items.reserve(it->second.size());
+
+ for (const ItemMap::value_type& kv : it->second) {
+ items.push_back(kv.second);
+ }
+
+ return items;
+}
+
+void ConfigItem::RemoveIgnoredItems(const String& allowedConfigPath)
+{
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ for (const String& path : m_IgnoredItems) {
+ if (path.Find(allowedConfigPath) == String::NPos)
+ continue;
+
+ Log(LogNotice, "ConfigItem")
+ << "Removing ignored item path '" << path << "'.";
+
+ (void) unlink(path.CStr());
+ }
+
+ m_IgnoredItems.clear();
+}
diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp
new file mode 100644
index 0000000..b99cd08
--- /dev/null
+++ b/lib/config/configitem.hpp
@@ -0,0 +1,106 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CONFIGITEM_H
+#define CONFIGITEM_H
+
+#include "config/i2-config.hpp"
+#include "config/expression.hpp"
+#include "config/activationcontext.hpp"
+#include "base/configobject.hpp"
+#include "base/workqueue.hpp"
+
+namespace icinga
+{
+
+
+/**
+ * A configuration item. Non-abstract configuration items can be used to
+ * create configuration objects at runtime.
+ *
+ * @ingroup config
+ */
+class ConfigItem final : public Object {
+public:
+ DECLARE_PTR_TYPEDEFS(ConfigItem);
+
+ ConfigItem(Type::Ptr type, String name, bool abstract,
+ Expression::Ptr exprl,
+ Expression::Ptr filter,
+ bool defaultTmpl, bool ignoreOnError, DebugInfo debuginfo,
+ Dictionary::Ptr scope, String zone,
+ String package);
+
+ Type::Ptr GetType() const;
+ String GetName() const;
+ bool IsAbstract() const;
+ bool IsDefaultTemplate() const;
+ bool IsIgnoreOnError() const;
+
+ std::vector<ConfigItem::Ptr> GetParents() const;
+
+ Expression::Ptr GetExpression() const;
+ Expression::Ptr GetFilter() const;
+
+ void Register();
+ void Unregister();
+
+ DebugInfo GetDebugInfo() const;
+ Dictionary::Ptr GetScope() const;
+
+ ConfigObject::Ptr GetObject() const;
+
+ static ConfigItem::Ptr GetByTypeAndName(const Type::Ptr& type,
+ const String& name);
+
+ static bool CommitItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems, bool silent = false);
+ static bool ActivateItems(const std::vector<ConfigItem::Ptr>& newItems, bool runtimeCreated = false,
+ bool mainConfigActivation = false, bool withModAttrs = false, const Value& cookie = Empty);
+
+ static bool RunWithActivationContext(const Function::Ptr& function);
+
+ static std::vector<ConfigItem::Ptr> GetItems(const Type::Ptr& type);
+ static std::vector<ConfigItem::Ptr> GetDefaultTemplates(const Type::Ptr& type);
+
+ static void RemoveIgnoredItems(const String& allowedConfigPath);
+
+private:
+ Type::Ptr m_Type; /**< The object type. */
+ String m_Name; /**< The name. */
+ bool m_Abstract; /**< Whether this is a template. */
+
+ Expression::Ptr m_Expression;
+ Expression::Ptr m_Filter;
+ bool m_DefaultTmpl;
+ bool m_IgnoreOnError;
+ DebugInfo m_DebugInfo; /**< Debug information. */
+ Dictionary::Ptr m_Scope; /**< variable scope. */
+ String m_Zone; /**< The zone. */
+ String m_Package;
+ ActivationContext::Ptr m_ActivationContext;
+
+ ConfigObject::Ptr m_Object;
+
+ static std::mutex m_Mutex;
+
+ typedef std::map<String, ConfigItem::Ptr> ItemMap;
+ typedef std::map<Type::Ptr, ItemMap> TypeMap;
+ static TypeMap m_Items; /**< All registered configuration items. */
+ static TypeMap m_DefaultTemplates;
+
+ typedef std::vector<ConfigItem::Ptr> ItemList;
+ static ItemList m_UnnamedItems;
+
+ typedef std::vector<String> IgnoredItemList;
+ static IgnoredItemList m_IgnoredItems;
+
+ static ConfigItem::Ptr GetObjectUnlocked(const String& type,
+ const String& name);
+
+ ConfigObject::Ptr Commit(bool discard = true);
+
+ static bool CommitNewItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems);
+};
+
+}
+
+#endif /* CONFIGITEM_H */
diff --git a/lib/config/configitembuilder.cpp b/lib/config/configitembuilder.cpp
new file mode 100644
index 0000000..f7a3ead
--- /dev/null
+++ b/lib/config/configitembuilder.cpp
@@ -0,0 +1,120 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/configitembuilder.hpp"
+#include "base/configtype.hpp"
+#include <sstream>
+
+using namespace icinga;
+
+ConfigItemBuilder::ConfigItemBuilder(const DebugInfo& debugInfo)
+ : m_Abstract(false), m_DefaultTmpl(false), m_IgnoreOnError(false)
+{
+ m_DebugInfo = debugInfo;
+}
+
+void ConfigItemBuilder::SetType(const Type::Ptr& type)
+{
+ ASSERT(type);
+ m_Type = type;
+}
+
+void ConfigItemBuilder::SetName(const String& name)
+{
+ m_Name = name;
+}
+
+void ConfigItemBuilder::SetAbstract(bool abstract)
+{
+ m_Abstract = abstract;
+}
+
+void ConfigItemBuilder::SetScope(const Dictionary::Ptr& scope)
+{
+ m_Scope = scope;
+}
+
+void ConfigItemBuilder::SetZone(const String& zone)
+{
+ m_Zone = zone;
+}
+
+void ConfigItemBuilder::SetPackage(const String& package)
+{
+ m_Package = package;
+}
+
+void ConfigItemBuilder::AddExpression(Expression *expr)
+{
+ m_Expressions.emplace_back(expr);
+}
+
+void ConfigItemBuilder::SetFilter(const Expression::Ptr& filter)
+{
+ m_Filter = filter;
+}
+
+void ConfigItemBuilder::SetDefaultTemplate(bool defaultTmpl)
+{
+ m_DefaultTmpl = defaultTmpl;
+}
+
+void ConfigItemBuilder::SetIgnoreOnError(bool ignoreOnError)
+{
+ m_IgnoreOnError = ignoreOnError;
+}
+
+ConfigItem::Ptr ConfigItemBuilder::Compile()
+{
+ if (!m_Type) {
+ std::ostringstream msgbuf;
+ msgbuf << "The type of an object must be specified";
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), m_DebugInfo));
+ }
+
+ auto *ctype = dynamic_cast<ConfigType *>(m_Type.get());
+
+ if (!ctype) {
+ std::ostringstream msgbuf;
+ msgbuf << "The type '" + m_Type->GetName() + "' cannot be used for config objects";
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), m_DebugInfo));
+ }
+
+ if (m_Name.FindFirstOf("!") != String::NPos) {
+ std::ostringstream msgbuf;
+ msgbuf << "Name for object '" << m_Name << "' of type '" << m_Type->GetName() << "' is invalid: Object names may not contain '!'";
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), m_DebugInfo));
+ }
+
+ std::vector<std::unique_ptr<Expression> > exprs;
+
+ Array::Ptr templateArray = new Array({ m_Name });
+
+ exprs.emplace_back(new SetExpression(MakeIndexer(ScopeThis, "templates"), OpSetAdd,
+ std::unique_ptr<LiteralExpression>(new LiteralExpression(templateArray)), m_DebugInfo));
+
+#ifdef I2_DEBUG
+ if (!m_Abstract) {
+ bool foundDefaultImport = false;
+
+ for (const std::unique_ptr<Expression>& expr : m_Expressions) {
+ if (dynamic_cast<ImportDefaultTemplatesExpression *>(expr.get())) {
+ foundDefaultImport = true;
+ break;
+ }
+ }
+
+ ASSERT(foundDefaultImport);
+ }
+#endif /* I2_DEBUG */
+
+ auto *dexpr = new DictExpression(std::move(m_Expressions), m_DebugInfo);
+ dexpr->MakeInline();
+ exprs.emplace_back(dexpr);
+
+ auto exprl = new DictExpression(std::move(exprs), m_DebugInfo);
+ exprl->MakeInline();
+
+ return new ConfigItem(m_Type, m_Name, m_Abstract, exprl, m_Filter,
+ m_DefaultTmpl, m_IgnoreOnError, m_DebugInfo, m_Scope, m_Zone, m_Package);
+}
+
diff --git a/lib/config/configitembuilder.hpp b/lib/config/configitembuilder.hpp
new file mode 100644
index 0000000..9d2e339
--- /dev/null
+++ b/lib/config/configitembuilder.hpp
@@ -0,0 +1,58 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CONFIGITEMBUILDER_H
+#define CONFIGITEMBUILDER_H
+
+#include "config/expression.hpp"
+#include "config/configitem.hpp"
+#include "base/debuginfo.hpp"
+#include "base/object.hpp"
+
+namespace icinga
+{
+
+/**
+ * Config item builder. Used to dynamically build configuration objects
+ * at runtime.
+ *
+ * @ingroup config
+ */
+class ConfigItemBuilder final
+{
+public:
+ DECLARE_PTR_TYPEDEFS(ConfigItemBuilder);
+
+ ConfigItemBuilder() = default;
+ explicit ConfigItemBuilder(const DebugInfo& debugInfo);
+
+ void SetType(const Type::Ptr& type);
+ void SetName(const String& name);
+ void SetAbstract(bool abstract);
+ void SetScope(const Dictionary::Ptr& scope);
+ void SetZone(const String& zone);
+ void SetPackage(const String& package);
+ void SetDefaultTemplate(bool defaultTmpl);
+ void SetIgnoreOnError(bool ignoreOnError);
+
+ void AddExpression(Expression *expr);
+ void SetFilter(const Expression::Ptr& filter);
+
+ ConfigItem::Ptr Compile();
+
+private:
+ Type::Ptr m_Type; /**< The object type. */
+ String m_Name; /**< The name. */
+ bool m_Abstract{false}; /**< Whether the item is abstract. */
+ std::vector<std::unique_ptr<Expression> > m_Expressions; /**< Expressions for this item. */
+ Expression::Ptr m_Filter; /**< Filter expression. */
+ DebugInfo m_DebugInfo; /**< Debug information. */
+ Dictionary::Ptr m_Scope; /**< variable scope. */
+ String m_Zone; /**< The zone. */
+ String m_Package; /**< The package name. */
+ bool m_DefaultTmpl{false};
+ bool m_IgnoreOnError{false}; /**< Whether the object should be ignored when an error occurs in one of the expressions. */
+};
+
+}
+
+#endif /* CONFIGITEMBUILDER */
diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp
new file mode 100644
index 0000000..a8e9986
--- /dev/null
+++ b/lib/config/expression.cpp
@@ -0,0 +1,1068 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/expression.hpp"
+#include "config/configitem.hpp"
+#include "config/configcompiler.hpp"
+#include "config/vmops.hpp"
+#include "base/array.hpp"
+#include "base/json.hpp"
+#include "base/object.hpp"
+#include "base/logger.hpp"
+#include "base/exception.hpp"
+#include "base/scriptglobal.hpp"
+#include "base/loader.hpp"
+#include "base/reference.hpp"
+#include "base/namespace.hpp"
+#include "base/defer.hpp"
+#include <boost/exception_ptr.hpp>
+#include <boost/exception/errinfo_nested_exception.hpp>
+
+using namespace icinga;
+
+boost::signals2::signal<void (ScriptFrame&, ScriptError *ex, const DebugInfo&)> Expression::OnBreakpoint;
+boost::thread_specific_ptr<bool> l_InBreakpointHandler;
+
+Expression::~Expression()
+{ }
+
+void Expression::ScriptBreakpoint(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di)
+{
+ bool *inHandler = l_InBreakpointHandler.get();
+ if (!inHandler || !*inHandler) {
+ inHandler = new bool(true);
+ l_InBreakpointHandler.reset(inHandler);
+ OnBreakpoint(frame, ex, di);
+ *inHandler = false;
+ }
+}
+
+ExpressionResult Expression::Evaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ try {
+#ifdef I2_DEBUG
+/* std::ostringstream msgbuf;
+ ShowCodeLocation(msgbuf, GetDebugInfo(), false);
+ Log(LogDebug, "Expression")
+ << "Executing:\n" << msgbuf.str();*/
+#endif /* I2_DEBUG */
+
+ frame.IncreaseStackDepth();
+
+ Defer decreaseStackDepth([&frame]{
+ frame.DecreaseStackDepth();
+ });
+
+ ExpressionResult result = DoEvaluate(frame, dhint);
+ return result;
+ } catch (ScriptError& ex) {
+ ScriptBreakpoint(frame, &ex, GetDebugInfo());
+ throw;
+ } catch (const std::exception& ex) {
+ BOOST_THROW_EXCEPTION(ScriptError("Error while evaluating expression: " + String(ex.what()), GetDebugInfo())
+ << boost::errinfo_nested_exception(boost::current_exception()));
+ }
+}
+
+bool Expression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const
+{
+ return false;
+}
+
+const DebugInfo& Expression::GetDebugInfo() const
+{
+ static DebugInfo debugInfo;
+ return debugInfo;
+}
+
+std::unique_ptr<Expression> icinga::MakeIndexer(ScopeSpecifier scopeSpec, const String& index)
+{
+ std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)};
+ return std::unique_ptr<Expression>(new IndexerExpression(std::move(scope), MakeLiteral(index)));
+}
+
+void DictExpression::MakeInline()
+{
+ m_Inline = true;
+}
+
+LiteralExpression::LiteralExpression(Value value)
+ : m_Value(std::move(value))
+{ }
+
+ExpressionResult LiteralExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ return m_Value;
+}
+
+const DebugInfo& DebuggableExpression::GetDebugInfo() const
+{
+ return m_DebugInfo;
+}
+
+VariableExpression::VariableExpression(String variable, std::vector<Expression::Ptr> imports, const DebugInfo& debugInfo)
+ : DebuggableExpression(debugInfo), m_Variable(std::move(variable)), m_Imports(std::move(imports))
+{
+ m_Imports.push_back(MakeIndexer(ScopeGlobal, "System").release());
+ m_Imports.push_back(new IndexerExpression(MakeIndexer(ScopeGlobal, "System"), MakeLiteral("Configuration")));
+ m_Imports.push_back(MakeIndexer(ScopeGlobal, "Types").release());
+ m_Imports.push_back(MakeIndexer(ScopeGlobal, "Icinga").release());
+}
+
+ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ Value value;
+
+ if (frame.Locals && frame.Locals->Get(m_Variable, &value))
+ return value;
+ else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get<Object::Ptr>() && frame.Self.Get<Object::Ptr>()->GetOwnField(m_Variable, &value))
+ return value;
+ else if (VMOps::FindVarImport(frame, m_Imports, m_Variable, &value, m_DebugInfo))
+ return value;
+ else
+ return ScriptGlobal::Get(m_Variable);
+}
+
+bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const
+{
+ *index = m_Variable;
+
+ if (frame.Locals && frame.Locals->Contains(m_Variable)) {
+ *parent = frame.Locals;
+
+ if (dhint)
+ *dhint = nullptr;
+ } else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get<Object::Ptr>() && frame.Self.Get<Object::Ptr>()->HasOwnField(m_Variable)) {
+ *parent = frame.Self;
+
+ if (dhint && *dhint)
+ *dhint = new DebugHint((*dhint)->GetChild(m_Variable));
+ } else if (VMOps::FindVarImportRef(frame, m_Imports, m_Variable, parent, m_DebugInfo)) {
+ return true;
+ } else if (ScriptGlobal::Exists(m_Variable)) {
+ *parent = ScriptGlobal::GetGlobals();
+
+ if (dhint)
+ *dhint = nullptr;
+ } else
+ *parent = frame.Self;
+
+ return true;
+}
+
+ExpressionResult RefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ Value parent;
+ String index;
+
+ if (!m_Operand->GetReference(frame, false, &parent, &index, &dhint))
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression.", m_DebugInfo));
+
+ if (!parent.IsObject())
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression because parent is not an object.", m_DebugInfo));
+
+ return new Reference(parent, index);
+}
+
+ExpressionResult DerefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand = m_Operand->Evaluate(frame);
+ CHECK_RESULT(operand);
+
+ Object::Ptr obj = operand.GetValue();
+ Reference::Ptr ref = dynamic_pointer_cast<Reference>(obj);
+
+ if (!ref)
+ BOOST_THROW_EXCEPTION(ScriptError("Invalid reference specified.", GetDebugInfo()));
+
+ return ref->Get();
+}
+
+bool DerefExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const
+{
+ ExpressionResult operand = m_Operand->Evaluate(frame);
+ if (operand.GetCode() != ResultOK)
+ return false;
+
+ Reference::Ptr ref = operand.GetValue();
+
+ *parent = ref->GetParent();
+ *index = ref->GetIndex();
+ return true;
+}
+
+ExpressionResult NegateExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand = m_Operand->Evaluate(frame);
+ CHECK_RESULT(operand);
+
+ return ~(long)operand.GetValue();
+}
+
+ExpressionResult LogicalNegateExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand = m_Operand->Evaluate(frame);
+ CHECK_RESULT(operand);
+
+ return !operand.GetValue().ToBool();
+}
+
+ExpressionResult AddExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() + operand2.GetValue();
+}
+
+ExpressionResult SubtractExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() - operand2.GetValue();
+}
+
+ExpressionResult MultiplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() * operand2.GetValue();
+}
+
+ExpressionResult DivideExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() / operand2.GetValue();
+}
+
+ExpressionResult ModuloExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() % operand2.GetValue();
+}
+
+ExpressionResult XorExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() ^ operand2.GetValue();
+}
+
+ExpressionResult BinaryAndExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() & operand2.GetValue();
+}
+
+ExpressionResult BinaryOrExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() | operand2.GetValue();
+}
+
+ExpressionResult ShiftLeftExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() << operand2.GetValue();
+}
+
+ExpressionResult ShiftRightExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() >> operand2.GetValue();
+}
+
+ExpressionResult EqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() == operand2.GetValue();
+}
+
+ExpressionResult NotEqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() != operand2.GetValue();
+}
+
+ExpressionResult LessThanExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() < operand2.GetValue();
+}
+
+ExpressionResult GreaterThanExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() > operand2.GetValue();
+}
+
+ExpressionResult LessThanOrEqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() <= operand2.GetValue();
+}
+
+ExpressionResult GreaterThanOrEqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand1.GetValue() >= operand2.GetValue();
+}
+
+ExpressionResult InExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ if (operand2.GetValue().IsEmpty())
+ return false;
+ else if (!operand2.GetValue().IsObjectType<Array>())
+ BOOST_THROW_EXCEPTION(ScriptError("Invalid right side argument for 'in' operator: " + JsonEncode(operand2.GetValue()), m_DebugInfo));
+
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1)
+
+ Array::Ptr arr = operand2.GetValue();
+ return arr->Contains(operand1.GetValue());
+}
+
+ExpressionResult NotInExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ if (operand2.GetValue().IsEmpty())
+ return true;
+ else if (!operand2.GetValue().IsObjectType<Array>())
+ BOOST_THROW_EXCEPTION(ScriptError("Invalid right side argument for 'in' operator: " + JsonEncode(operand2.GetValue()), m_DebugInfo));
+
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ Array::Ptr arr = operand2.GetValue();
+ return !arr->Contains(operand1.GetValue());
+}
+
+ExpressionResult LogicalAndExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ if (!operand1.GetValue().ToBool())
+ return operand1;
+ else {
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand2.GetValue();
+ }
+}
+
+ExpressionResult LogicalOrExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ CHECK_RESULT(operand1);
+
+ if (operand1.GetValue().ToBool())
+ return operand1;
+ else {
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ CHECK_RESULT(operand2);
+
+ return operand2.GetValue();
+ }
+}
+
+ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ Value self, vfunc;
+ String index;
+
+ if (m_FName->GetReference(frame, false, &self, &index))
+ vfunc = VMOps::GetField(self, index, frame.Sandboxed, m_DebugInfo);
+ else {
+ ExpressionResult vfuncres = m_FName->Evaluate(frame);
+ CHECK_RESULT(vfuncres);
+
+ vfunc = vfuncres.GetValue();
+ }
+
+ if (vfunc.IsObjectType<Type>()) {
+ std::vector<Value> arguments;
+ arguments.reserve(m_Args.size());
+ for (const auto& arg : m_Args) {
+ ExpressionResult argres = arg->Evaluate(frame);
+ CHECK_RESULT(argres);
+
+ arguments.push_back(argres.GetValue());
+ }
+
+ return VMOps::ConstructorCall(vfunc, arguments, m_DebugInfo);
+ }
+
+ if (!vfunc.IsObjectType<Function>())
+ BOOST_THROW_EXCEPTION(ScriptError("Argument is not a callable object.", m_DebugInfo));
+
+ Function::Ptr func = vfunc;
+
+ if (!func->IsSideEffectFree() && frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Function is not marked as safe for sandbox mode.", m_DebugInfo));
+
+ std::vector<Value> arguments;
+ arguments.reserve(m_Args.size());
+ for (const auto& arg : m_Args) {
+ ExpressionResult argres = arg->Evaluate(frame);
+ CHECK_RESULT(argres);
+
+ arguments.push_back(argres.GetValue());
+ }
+
+ return VMOps::FunctionCall(frame, self, func, arguments);
+}
+
+ExpressionResult ArrayExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ArrayData result;
+ result.reserve(m_Expressions.size());
+
+ for (const auto& aexpr : m_Expressions) {
+ ExpressionResult element = aexpr->Evaluate(frame);
+ CHECK_RESULT(element);
+
+ result.push_back(element.GetValue());
+ }
+
+ return new Array(std::move(result));
+}
+
+ExpressionResult DictExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ Value self;
+
+ if (!m_Inline) {
+ self = frame.Self;
+ frame.Self = new Dictionary();
+ }
+
+ Value result;
+
+ try {
+ for (const auto& aexpr : m_Expressions) {
+ ExpressionResult element = aexpr->Evaluate(frame, m_Inline ? dhint : nullptr);
+ CHECK_RESULT(element);
+ result = element.GetValue();
+ }
+ } catch (...) {
+ if (!m_Inline)
+ std::swap(self, frame.Self);
+ throw;
+ }
+
+ if (m_Inline)
+ return result;
+ else {
+ std::swap(self, frame.Self);
+ return self;
+ }
+}
+
+ExpressionResult GetScopeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (m_ScopeSpec == ScopeLocal)
+ return frame.Locals;
+ else if (m_ScopeSpec == ScopeThis)
+ return frame.Self;
+ else if (m_ScopeSpec == ScopeGlobal)
+ return ScriptGlobal::GetGlobals();
+ else
+ VERIFY(!"Invalid scope.");
+}
+
+static inline
+void WarnOnImplicitlySetGlobalVar(const std::unique_ptr<Expression>& setLhs, const Value& setLhsParent, CombinedSetOp setOp, const DebugInfo& debug)
+{
+ auto var (dynamic_cast<VariableExpression*>(setLhs.get()));
+
+ if (var && setLhsParent.IsObject()) {
+ auto ns (dynamic_pointer_cast<Namespace>(setLhsParent.Get<Object::Ptr>()));
+
+ if (ns && ns == ScriptGlobal::GetGlobals() && debug.Path.GetLength()) {
+ const char *opStr = nullptr;
+
+ switch (setOp) {
+ case OpSetLiteral:
+ opStr = "=";
+ break;
+ case OpSetAdd:
+ opStr = "+=";
+ break;
+ case OpSetSubtract:
+ opStr = "-=";
+ break;
+ case OpSetMultiply:
+ opStr = "*=";
+ break;
+ case OpSetDivide:
+ opStr = "/=";
+ break;
+ case OpSetModulo:
+ opStr = "%=";
+ break;
+ case OpSetXor:
+ opStr = "^=";
+ break;
+ case OpSetBinaryAnd:
+ opStr = "&=";
+ break;
+ case OpSetBinaryOr:
+ opStr = "|=";
+ break;
+ default:
+ VERIFY(!"Invalid opcode.");
+ }
+
+ auto varName (var->GetVariable());
+
+ Log(LogWarning, "config")
+ << "Global variable '" << varName << "' has been set implicitly via '" << varName << ' ' << opStr << " ...' " << debug << "."
+ " Please set it explicitly via 'globals." << varName << ' ' << opStr << " ...' instead.";
+ }
+ }
+}
+
+ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Assignments are not allowed in sandbox mode.", m_DebugInfo));
+
+ DebugHint *psdhint = dhint;
+
+ Value parent;
+ String index;
+
+ if (!m_Operand1->GetReference(frame, true, &parent, &index, &psdhint))
+ BOOST_THROW_EXCEPTION(ScriptError("Expression cannot be assigned to.", m_DebugInfo));
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame, dhint);
+ CHECK_RESULT(operand2);
+
+ if (m_Op != OpSetLiteral) {
+ Value object = VMOps::GetField(parent, index, frame.Sandboxed, m_DebugInfo);
+
+ switch (m_Op) {
+ case OpSetAdd:
+ operand2 = object + operand2;
+ break;
+ case OpSetSubtract:
+ operand2 = object - operand2;
+ break;
+ case OpSetMultiply:
+ operand2 = object * operand2;
+ break;
+ case OpSetDivide:
+ operand2 = object / operand2;
+ break;
+ case OpSetModulo:
+ operand2 = object % operand2;
+ break;
+ case OpSetXor:
+ operand2 = object ^ operand2;
+ break;
+ case OpSetBinaryAnd:
+ operand2 = object & operand2;
+ break;
+ case OpSetBinaryOr:
+ operand2 = object | operand2;
+ break;
+ default:
+ VERIFY(!"Invalid opcode.");
+ }
+ }
+
+ VMOps::SetField(parent, index, operand2.GetValue(), m_OverrideFrozen, m_DebugInfo);
+
+ if (psdhint) {
+ psdhint->AddMessage("=", m_DebugInfo);
+
+ if (psdhint != dhint)
+ delete psdhint;
+ }
+
+ WarnOnImplicitlySetGlobalVar(m_Operand1, parent, m_Op, m_DebugInfo);
+
+ return Empty;
+}
+
+void SetExpression::SetOverrideFrozen()
+{
+ m_OverrideFrozen = true;
+}
+
+ExpressionResult SetConstExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ auto globals = ScriptGlobal::GetGlobals();
+
+ ExpressionResult operandres = m_Operand->Evaluate(frame);
+ CHECK_RESULT(operandres);
+ Value operand = operandres.GetValue();
+
+ globals->Set(m_Name, operand, true);
+
+ return Empty;
+}
+
+ExpressionResult ConditionalExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult condition = m_Condition->Evaluate(frame, dhint);
+ CHECK_RESULT(condition);
+
+ if (condition.GetValue().ToBool())
+ return m_TrueBranch->Evaluate(frame, dhint);
+ else if (m_FalseBranch)
+ return m_FalseBranch->Evaluate(frame, dhint);
+
+ return Empty;
+}
+
+ExpressionResult WhileExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("While loops are not allowed in sandbox mode.", m_DebugInfo));
+
+ for (;;) {
+ ExpressionResult condition = m_Condition->Evaluate(frame, dhint);
+ CHECK_RESULT(condition);
+
+ if (!condition.GetValue().ToBool())
+ break;
+
+ ExpressionResult loop_body = m_LoopBody->Evaluate(frame, dhint);
+ CHECK_RESULT_LOOP(loop_body);
+ }
+
+ return Empty;
+}
+
+ExpressionResult ReturnExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand = m_Operand->Evaluate(frame);
+ CHECK_RESULT(operand);
+
+ return ExpressionResult(operand.GetValue(), ResultReturn);
+}
+
+ExpressionResult BreakExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ return ExpressionResult(Empty, ResultBreak);
+}
+
+ExpressionResult ContinueExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ return ExpressionResult(Empty, ResultContinue);
+}
+
+ExpressionResult IndexerExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame, dhint);
+ CHECK_RESULT(operand1);
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame, dhint);
+ CHECK_RESULT(operand2);
+
+ return VMOps::GetField(operand1.GetValue(), operand2.GetValue(), frame.Sandboxed, m_DebugInfo);
+}
+
+bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const
+{
+ Value vparent;
+ String vindex;
+ DebugHint *psdhint = nullptr;
+ bool free_psd = false;
+
+ if (dhint)
+ psdhint = *dhint;
+
+ if (frame.Sandboxed)
+ init_dict = false;
+
+ if (m_Operand1->GetReference(frame, init_dict, &vparent, &vindex, &psdhint)) {
+ if (init_dict) {
+ 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());
+ }
+
+ *parent = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_DebugInfo);
+ free_psd = true;
+ } else {
+ ExpressionResult operand1 = m_Operand1->Evaluate(frame);
+ *parent = operand1.GetValue();
+ }
+
+ ExpressionResult operand2 = m_Operand2->Evaluate(frame);
+ *index = operand2.GetValue();
+
+ if (dhint) {
+ if (psdhint)
+ *dhint = new DebugHint(psdhint->GetChild(*index));
+ else
+ *dhint = nullptr;
+ }
+
+ if (free_psd)
+ delete psdhint;
+
+ return true;
+}
+
+void IndexerExpression::SetOverrideFrozen()
+{
+ m_OverrideFrozen = true;
+}
+
+void icinga::BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec)
+{
+ auto *dexpr = dynamic_cast<DictExpression *>(expr.get());
+
+ if (dexpr) {
+ for (auto& expr : dexpr->m_Expressions)
+ BindToScope(expr, scopeSpec);
+
+ return;
+ }
+
+ auto *aexpr = dynamic_cast<SetExpression *>(expr.get());
+
+ if (aexpr) {
+ BindToScope(aexpr->m_Operand1, scopeSpec);
+
+ return;
+ }
+
+ auto *iexpr = dynamic_cast<IndexerExpression *>(expr.get());
+
+ if (iexpr) {
+ BindToScope(iexpr->m_Operand1, scopeSpec);
+ return;
+ }
+
+ auto *lexpr = dynamic_cast<LiteralExpression *>(expr.get());
+
+ if (lexpr && lexpr->GetValue().IsString()) {
+ std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)};
+ expr.reset(new IndexerExpression(std::move(scope), std::move(expr), lexpr->GetDebugInfo()));
+ }
+
+ auto *vexpr = dynamic_cast<VariableExpression *>(expr.get());
+
+ if (vexpr) {
+ std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)};
+ expr.reset(new IndexerExpression(std::move(scope), MakeLiteral(vexpr->GetVariable()), vexpr->GetDebugInfo()));
+ }
+}
+
+ExpressionResult ThrowExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ExpressionResult messageres = m_Message->Evaluate(frame);
+ CHECK_RESULT(messageres);
+ Value message = messageres.GetValue();
+ BOOST_THROW_EXCEPTION(ScriptError(message, m_DebugInfo, m_IncompleteExpr));
+}
+
+ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Imports are not allowed in sandbox mode.", m_DebugInfo));
+
+ String type = VMOps::GetField(frame.Self, "type", frame.Sandboxed, m_DebugInfo);
+ ExpressionResult nameres = m_Name->Evaluate(frame);
+ CHECK_RESULT(nameres);
+ Value name = nameres.GetValue();
+
+ if (!name.IsString())
+ BOOST_THROW_EXCEPTION(ScriptError("Template/object name must be a string", m_DebugInfo));
+
+ ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name);
+
+ if (!item)
+ BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo));
+
+ Dictionary::Ptr scope = item->GetScope();
+
+ if (scope)
+ scope->CopyTo(frame.Locals);
+
+ ExpressionResult result = item->GetExpression()->Evaluate(frame, dhint);
+ CHECK_RESULT(result);
+
+ return Empty;
+}
+
+ExpressionResult ImportDefaultTemplatesExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Imports are not allowed in sandbox mode.", m_DebugInfo));
+
+ String type = VMOps::GetField(frame.Self, "type", frame.Sandboxed, m_DebugInfo);
+ Type::Ptr ptype = Type::GetByName(type);
+
+ for (const ConfigItem::Ptr& item : ConfigItem::GetDefaultTemplates(ptype)) {
+ Dictionary::Ptr scope = item->GetScope();
+
+ if (scope)
+ scope->CopyTo(frame.Locals);
+
+ ExpressionResult result = item->GetExpression()->Evaluate(frame, dhint);
+ CHECK_RESULT(result);
+ }
+
+ return Empty;
+}
+
+ExpressionResult FunctionExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ return VMOps::NewFunction(frame, m_Name, m_Args, m_ClosedVars, m_Expression);
+}
+
+ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Apply rules are not allowed in sandbox mode.", m_DebugInfo));
+
+ ExpressionResult nameres = m_Name->Evaluate(frame);
+ CHECK_RESULT(nameres);
+
+ return VMOps::NewApply(frame, m_Type, m_Target, nameres.GetValue(), m_Filter,
+ m_Package, m_FKVar, m_FVVar, m_FTerm, m_ClosedVars, m_IgnoreOnError, m_Expression, m_DebugInfo);
+}
+
+ExpressionResult NamespaceExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ Namespace::Ptr ns = new Namespace(true);
+
+ ScriptFrame innerFrame(true, ns);
+ ExpressionResult result = m_Expression->Evaluate(innerFrame);
+ CHECK_RESULT(result);
+
+ return ns;
+}
+
+ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Object definitions are not allowed in sandbox mode.", m_DebugInfo));
+
+ ExpressionResult typeres = m_Type->Evaluate(frame, dhint);
+ CHECK_RESULT(typeres);
+ Type::Ptr type = typeres.GetValue();
+
+ String name;
+
+ if (m_Name) {
+ ExpressionResult nameres = m_Name->Evaluate(frame, dhint);
+ CHECK_RESULT(nameres);
+
+ name = nameres.GetValue();
+ }
+
+ return VMOps::NewObject(frame, m_Abstract, type, name, m_Filter, m_Zone,
+ m_Package, m_DefaultTmpl, m_IgnoreOnError, m_ClosedVars, m_Expression, m_DebugInfo);
+}
+
+ExpressionResult ForExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("For loops are not allowed in sandbox mode.", m_DebugInfo));
+
+ ExpressionResult valueres = m_Value->Evaluate(frame, dhint);
+ CHECK_RESULT(valueres);
+
+ return VMOps::For(frame, m_FKVar, m_FVVar, valueres.GetValue(), m_Expression, m_DebugInfo);
+}
+
+ExpressionResult LibraryExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Loading libraries is not allowed in sandbox mode.", m_DebugInfo));
+
+ ExpressionResult libres = m_Operand->Evaluate(frame, dhint);
+ CHECK_RESULT(libres);
+
+ Log(LogNotice, "config")
+ << "Ignoring explicit load request for library \"" << libres << "\".";
+
+ return Empty;
+}
+
+ExpressionResult IncludeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ if (frame.Sandboxed)
+ BOOST_THROW_EXCEPTION(ScriptError("Includes are not allowed in sandbox mode.", m_DebugInfo));
+
+ std::unique_ptr<Expression> expr;
+ String name, path, pattern;
+
+ switch (m_Type) {
+ case IncludeRegular:
+ {
+ ExpressionResult pathres = m_Path->Evaluate(frame, dhint);
+ CHECK_RESULT(pathres);
+ path = pathres.GetValue();
+ }
+
+ expr = ConfigCompiler::HandleInclude(m_RelativeBase, path, m_SearchIncludes, m_Zone, m_Package, m_DebugInfo);
+ break;
+
+ case IncludeRecursive:
+ {
+ ExpressionResult pathres = m_Path->Evaluate(frame, dhint);
+ CHECK_RESULT(pathres);
+ path = pathres.GetValue();
+ }
+
+ {
+ ExpressionResult patternres = m_Pattern->Evaluate(frame, dhint);
+ CHECK_RESULT(patternres);
+ pattern = patternres.GetValue();
+ }
+
+ expr = ConfigCompiler::HandleIncludeRecursive(m_RelativeBase, path, pattern, m_Zone, m_Package, m_DebugInfo);
+ break;
+
+ case IncludeZones:
+ {
+ ExpressionResult nameres = m_Name->Evaluate(frame, dhint);
+ CHECK_RESULT(nameres);
+ name = nameres.GetValue();
+ }
+
+ {
+ ExpressionResult pathres = m_Path->Evaluate(frame, dhint);
+ CHECK_RESULT(pathres);
+ path = pathres.GetValue();
+ }
+
+ {
+ ExpressionResult patternres = m_Pattern->Evaluate(frame, dhint);
+ CHECK_RESULT(patternres);
+ pattern = patternres.GetValue();
+ }
+
+ expr = ConfigCompiler::HandleIncludeZones(m_RelativeBase, name, path, pattern, m_Package, m_DebugInfo);
+ break;
+ }
+
+ ExpressionResult res(Empty);
+
+ try {
+ res = expr->Evaluate(frame, dhint);
+ } catch (const std::exception&) {
+ throw;
+ }
+
+ return res;
+}
+
+ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ScriptBreakpoint(frame, nullptr, GetDebugInfo());
+
+ return Empty;
+}
+
+ExpressionResult TryExceptExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ try {
+ ExpressionResult tryResult = m_TryBody->Evaluate(frame, dhint);
+ CHECK_RESULT(tryResult);
+ } catch (const std::exception&) {
+ ExpressionResult exceptResult = m_ExceptBody->Evaluate(frame, dhint);
+ CHECK_RESULT(exceptResult);
+ }
+
+ return Empty;
+}
+
diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp
new file mode 100644
index 0000000..644548d
--- /dev/null
+++ b/lib/config/expression.hpp
@@ -0,0 +1,986 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef EXPRESSION_H
+#define EXPRESSION_H
+
+#include "config/i2-config.hpp"
+#include "base/debuginfo.hpp"
+#include "base/array.hpp"
+#include "base/dictionary.hpp"
+#include "base/function.hpp"
+#include "base/exception.hpp"
+#include "base/scriptframe.hpp"
+#include "base/shared-object.hpp"
+#include "base/convert.hpp"
+#include <map>
+
+namespace icinga
+{
+
+struct DebugHint
+{
+public:
+ DebugHint(Dictionary::Ptr hints = nullptr)
+ : m_Hints(std::move(hints))
+ { }
+
+ DebugHint(Dictionary::Ptr&& hints)
+ : m_Hints(std::move(hints))
+ { }
+
+ void AddMessage(const String& message, const DebugInfo& di)
+ {
+ GetMessages()->Add(new Array({ message, di.Path, di.FirstLine, di.FirstColumn, di.LastLine, di.LastColumn }));
+ }
+
+ DebugHint GetChild(const String& name)
+ {
+ const Dictionary::Ptr& children = GetChildren();
+
+ Value vchild;
+ Dictionary::Ptr child;
+
+ if (!children->Get(name, &vchild)) {
+ child = new Dictionary();
+ children->Set(name, child);
+ } else
+ child = vchild;
+
+ return DebugHint(child);
+ }
+
+ Dictionary::Ptr ToDictionary() const
+ {
+ return m_Hints;
+ }
+
+private:
+ Dictionary::Ptr m_Hints;
+ Array::Ptr m_Messages;
+ Dictionary::Ptr m_Children;
+
+ const Array::Ptr& GetMessages()
+ {
+ if (m_Messages)
+ return m_Messages;
+
+ if (!m_Hints)
+ m_Hints = new Dictionary();
+
+ Value vmessages;
+
+ if (!m_Hints->Get("messages", &vmessages)) {
+ m_Messages = new Array();
+ m_Hints->Set("messages", m_Messages);
+ } else
+ m_Messages = vmessages;
+
+ return m_Messages;
+ }
+
+ const Dictionary::Ptr& GetChildren()
+ {
+ if (m_Children)
+ return m_Children;
+
+ if (!m_Hints)
+ m_Hints = new Dictionary();
+
+ Value vchildren;
+
+ if (!m_Hints->Get("properties", &vchildren)) {
+ m_Children = new Dictionary();
+ m_Hints->Set("properties", m_Children);
+ } else
+ m_Children = vchildren;
+
+ return m_Children;
+ }
+};
+
+enum CombinedSetOp
+{
+ OpSetLiteral,
+ OpSetAdd,
+ OpSetSubtract,
+ OpSetMultiply,
+ OpSetDivide,
+ OpSetModulo,
+ OpSetXor,
+ OpSetBinaryAnd,
+ OpSetBinaryOr
+};
+
+enum ScopeSpecifier
+{
+ ScopeLocal,
+ ScopeThis,
+ ScopeGlobal
+};
+
+typedef std::map<String, String> DefinitionMap;
+
+/**
+ * @ingroup config
+ */
+enum ExpressionResultCode
+{
+ ResultOK,
+ ResultReturn,
+ ResultContinue,
+ ResultBreak
+};
+
+/**
+ * @ingroup config
+ */
+struct ExpressionResult
+{
+public:
+ template<typename T>
+ ExpressionResult(T value, ExpressionResultCode code = ResultOK)
+ : m_Value(std::move(value)), m_Code(code)
+ { }
+
+ operator const Value&() const
+ {
+ return m_Value;
+ }
+
+ const Value& GetValue() const
+ {
+ return m_Value;
+ }
+
+ ExpressionResultCode GetCode() const
+ {
+ return m_Code;
+ }
+
+private:
+ Value m_Value;
+ ExpressionResultCode m_Code;
+};
+
+#define CHECK_RESULT(res) \
+ do { \
+ if (res.GetCode() != ResultOK) \
+ return res; \
+ } while (0);
+
+#define CHECK_RESULT_LOOP(res) \
+ if (res.GetCode() == ResultReturn) \
+ return res; \
+ if (res.GetCode() == ResultContinue) \
+ continue; \
+ if (res.GetCode() == ResultBreak) \
+ break; \
+
+/**
+ * @ingroup config
+ */
+class Expression : public SharedObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(Expression);
+
+ Expression() = default;
+ Expression(const Expression&) = delete;
+ virtual ~Expression();
+
+ Expression& operator=(const Expression&) = delete;
+
+ ExpressionResult Evaluate(ScriptFrame& frame, DebugHint *dhint = nullptr) const;
+ virtual bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint = nullptr) const;
+ virtual const DebugInfo& GetDebugInfo() const;
+
+ virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const = 0;
+
+ static boost::signals2::signal<void (ScriptFrame& frame, ScriptError *ex, const DebugInfo& di)> OnBreakpoint;
+
+ static void ScriptBreakpoint(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di);
+};
+
+std::unique_ptr<Expression> MakeIndexer(ScopeSpecifier scopeSpec, const String& index);
+
+class OwnedExpression final : public Expression
+{
+public:
+ OwnedExpression(Expression::Ptr expression)
+ : m_Expression(std::move(expression))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override
+ {
+ return m_Expression->DoEvaluate(frame, dhint);
+ }
+
+ const DebugInfo& GetDebugInfo() const override
+ {
+ return m_Expression->GetDebugInfo();
+ }
+
+private:
+ Expression::Ptr m_Expression;
+};
+
+class LiteralExpression final : public Expression
+{
+public:
+ LiteralExpression(Value value = Value());
+
+ const Value& GetValue() const
+ {
+ return m_Value;
+ }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ Value m_Value;
+};
+
+inline LiteralExpression *MakeLiteralRaw(const Value& literal = Value())
+{
+ return new LiteralExpression(literal);
+}
+
+inline std::unique_ptr<LiteralExpression> MakeLiteral(const Value& literal = Value())
+{
+ return std::unique_ptr<LiteralExpression>(MakeLiteralRaw(literal));
+}
+
+class DebuggableExpression : public Expression
+{
+public:
+ DebuggableExpression(DebugInfo debugInfo = DebugInfo())
+ : m_DebugInfo(std::move(debugInfo))
+ { }
+
+protected:
+ const DebugInfo& GetDebugInfo() const final;
+
+ DebugInfo m_DebugInfo;
+};
+
+class UnaryExpression : public DebuggableExpression
+{
+public:
+ UnaryExpression(std::unique_ptr<Expression> operand, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Operand(std::move(operand))
+ { }
+
+protected:
+ std::unique_ptr<Expression> m_Operand;
+};
+
+class BinaryExpression : public DebuggableExpression
+{
+public:
+ BinaryExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Operand1(std::move(operand1)), m_Operand2(std::move(operand2))
+ { }
+
+ inline const std::unique_ptr<Expression>& GetOperand1() const noexcept
+ {
+ return m_Operand1;
+ }
+
+ inline const std::unique_ptr<Expression>& GetOperand2() const noexcept
+ {
+ return m_Operand2;
+ }
+
+protected:
+ std::unique_ptr<Expression> m_Operand1;
+ std::unique_ptr<Expression> m_Operand2;
+};
+
+class VariableExpression final : public DebuggableExpression
+{
+public:
+ VariableExpression(String variable, std::vector<Expression::Ptr> imports, const DebugInfo& debugInfo = DebugInfo());
+
+ inline const String& GetVariable() const
+ {
+ return m_Variable;
+ }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+ bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const override;
+
+private:
+ String m_Variable;
+ std::vector<Expression::Ptr> m_Imports;
+
+ friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
+};
+
+class DerefExpression final : public UnaryExpression
+{
+public:
+ DerefExpression(std::unique_ptr<Expression> operand, const DebugInfo& debugInfo = DebugInfo())
+ : UnaryExpression(std::move(operand), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+ bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const override;
+};
+
+class RefExpression final : public UnaryExpression
+{
+public:
+ RefExpression(std::unique_ptr<Expression> operand, const DebugInfo& debugInfo = DebugInfo())
+ : UnaryExpression(std::move(operand), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class NegateExpression final : public UnaryExpression
+{
+public:
+ NegateExpression(std::unique_ptr<Expression> operand, const DebugInfo& debugInfo = DebugInfo())
+ : UnaryExpression(std::move(operand), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class LogicalNegateExpression final : public UnaryExpression
+{
+public:
+ LogicalNegateExpression(std::unique_ptr<Expression> operand, const DebugInfo& debugInfo = DebugInfo())
+ : UnaryExpression(std::move(operand), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class AddExpression final : public BinaryExpression
+{
+public:
+ AddExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class SubtractExpression final : public BinaryExpression
+{
+public:
+ SubtractExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class MultiplyExpression final : public BinaryExpression
+{
+public:
+ MultiplyExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class DivideExpression final : public BinaryExpression
+{
+public:
+ DivideExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class ModuloExpression final : public BinaryExpression
+{
+public:
+ ModuloExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class XorExpression final : public BinaryExpression
+{
+public:
+ XorExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class BinaryAndExpression final : public BinaryExpression
+{
+public:
+ BinaryAndExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class BinaryOrExpression final : public BinaryExpression
+{
+public:
+ BinaryOrExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class ShiftLeftExpression final : public BinaryExpression
+{
+public:
+ ShiftLeftExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class ShiftRightExpression final : public BinaryExpression
+{
+public:
+ ShiftRightExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class EqualExpression final : public BinaryExpression
+{
+public:
+ EqualExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class NotEqualExpression final : public BinaryExpression
+{
+public:
+ NotEqualExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class LessThanExpression final : public BinaryExpression
+{
+public:
+ LessThanExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class GreaterThanExpression final : public BinaryExpression
+{
+public:
+ GreaterThanExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class LessThanOrEqualExpression final : public BinaryExpression
+{
+public:
+ LessThanOrEqualExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class GreaterThanOrEqualExpression final : public BinaryExpression
+{
+public:
+ GreaterThanOrEqualExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class InExpression final : public BinaryExpression
+{
+public:
+ InExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class NotInExpression final : public BinaryExpression
+{
+public:
+ NotInExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class LogicalAndExpression final : public BinaryExpression
+{
+public:
+ LogicalAndExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class LogicalOrExpression final : public BinaryExpression
+{
+public:
+ LogicalOrExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class FunctionCallExpression final : public DebuggableExpression
+{
+public:
+ FunctionCallExpression(std::unique_ptr<Expression> fname, std::vector<std::unique_ptr<Expression> >&& args, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_FName(std::move(fname)), m_Args(std::move(args))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+public:
+ std::unique_ptr<Expression> m_FName;
+ std::vector<std::unique_ptr<Expression> > m_Args;
+};
+
+class ArrayExpression final : public DebuggableExpression
+{
+public:
+ ArrayExpression(std::vector<std::unique_ptr<Expression > >&& expressions, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Expressions(std::move(expressions))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::vector<std::unique_ptr<Expression> > m_Expressions;
+};
+
+class DictExpression final : public DebuggableExpression
+{
+public:
+ DictExpression(std::vector<std::unique_ptr<Expression> >&& expressions = {}, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Expressions(std::move(expressions))
+ { }
+
+ void MakeInline();
+
+ inline const std::vector<std::unique_ptr<Expression>>& GetExpressions() const noexcept
+ {
+ return m_Expressions;
+ }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::vector<std::unique_ptr<Expression> > m_Expressions;
+ bool m_Inline{false};
+
+ friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
+};
+
+class SetConstExpression final : public UnaryExpression
+{
+public:
+ SetConstExpression(const String& name, std::unique_ptr<Expression> 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:
+ SetExpression(std::unique_ptr<Expression> operand1, CombinedSetOp op, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo), m_Op(op)
+ { }
+
+ void SetOverrideFrozen();
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ CombinedSetOp m_Op;
+ bool m_OverrideFrozen{false};
+
+ friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
+};
+
+class ConditionalExpression final : public DebuggableExpression
+{
+public:
+ ConditionalExpression(std::unique_ptr<Expression> condition, std::unique_ptr<Expression> true_branch, std::unique_ptr<Expression> false_branch, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Condition(std::move(condition)), m_TrueBranch(std::move(true_branch)), m_FalseBranch(std::move(false_branch))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::unique_ptr<Expression> m_Condition;
+ std::unique_ptr<Expression> m_TrueBranch;
+ std::unique_ptr<Expression> m_FalseBranch;
+};
+
+class WhileExpression final : public DebuggableExpression
+{
+public:
+ WhileExpression(std::unique_ptr<Expression> condition, std::unique_ptr<Expression> loop_body, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Condition(std::move(condition)), m_LoopBody(std::move(loop_body))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::unique_ptr<Expression> m_Condition;
+ std::unique_ptr<Expression> m_LoopBody;
+};
+
+
+class ReturnExpression final : public UnaryExpression
+{
+public:
+ ReturnExpression(std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : UnaryExpression(std::move(expression), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class BreakExpression final : public DebuggableExpression
+{
+public:
+ BreakExpression(const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class ContinueExpression final : public DebuggableExpression
+{
+public:
+ ContinueExpression(const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class GetScopeExpression final : public Expression
+{
+public:
+ GetScopeExpression(ScopeSpecifier scopeSpec)
+ : m_ScopeSpec(scopeSpec)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ ScopeSpecifier m_ScopeSpec;
+};
+
+class IndexerExpression final : public BinaryExpression
+{
+public:
+ IndexerExpression(std::unique_ptr<Expression> operand1, std::unique_ptr<Expression> operand2, const DebugInfo& debugInfo = DebugInfo())
+ : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo)
+ { }
+
+ void SetOverrideFrozen();
+
+protected:
+ bool m_OverrideFrozen{false};
+
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+ bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const override;
+
+ friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
+};
+
+void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
+
+class ThrowExpression final : public DebuggableExpression
+{
+public:
+ ThrowExpression(std::unique_ptr<Expression> message, bool incompleteExpr, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Message(std::move(message)), m_IncompleteExpr(incompleteExpr)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::unique_ptr<Expression> m_Message;
+ bool m_IncompleteExpr;
+};
+
+class ImportExpression final : public DebuggableExpression
+{
+public:
+ ImportExpression(std::unique_ptr<Expression> name, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Name(std::move(name))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::unique_ptr<Expression> m_Name;
+};
+
+class ImportDefaultTemplatesExpression final : public DebuggableExpression
+{
+public:
+ ImportDefaultTemplatesExpression(const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class FunctionExpression final : public DebuggableExpression
+{
+public:
+ FunctionExpression(String name, std::vector<String> args,
+ std::map<String, std::unique_ptr<Expression> >&& closedVars, std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Name(std::move(name)), m_Args(std::move(args)), m_ClosedVars(std::move(closedVars)), m_Expression(expression.release())
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ String m_Name;
+ std::vector<String> m_Args;
+ std::map<String, std::unique_ptr<Expression> > m_ClosedVars;
+ Expression::Ptr m_Expression;
+};
+
+class ApplyExpression final : public DebuggableExpression
+{
+public:
+ ApplyExpression(String type, String target, std::unique_ptr<Expression> name,
+ std::unique_ptr<Expression> filter, String package, String fkvar, String fvvar,
+ std::unique_ptr<Expression> fterm, std::map<String, std::unique_ptr<Expression> >&& closedVars, bool ignoreOnError,
+ std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Type(std::move(type)), m_Target(std::move(target)),
+ m_Name(std::move(name)), m_Filter(filter.release()), m_Package(std::move(package)), m_FKVar(std::move(fkvar)), m_FVVar(std::move(fvvar)),
+ m_FTerm(fterm.release()), m_IgnoreOnError(ignoreOnError), m_ClosedVars(std::move(closedVars)),
+ m_Expression(expression.release())
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ String m_Type;
+ String m_Target;
+ std::unique_ptr<Expression> m_Name;
+ Expression::Ptr m_Filter;
+ String m_Package;
+ String m_FKVar;
+ String m_FVVar;
+ Expression::Ptr m_FTerm;
+ bool m_IgnoreOnError;
+ std::map<String, std::unique_ptr<Expression> > m_ClosedVars;
+ Expression::Ptr m_Expression;
+};
+
+class NamespaceExpression final : public DebuggableExpression
+{
+public:
+ NamespaceExpression(std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Expression(expression.release())
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ Expression::Ptr m_Expression;
+};
+
+class ObjectExpression final : public DebuggableExpression
+{
+public:
+ ObjectExpression(bool abstract, std::unique_ptr<Expression> type, std::unique_ptr<Expression> name, std::unique_ptr<Expression> filter,
+ String zone, String package, std::map<String, std::unique_ptr<Expression> >&& closedVars,
+ bool defaultTmpl, bool ignoreOnError, std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Abstract(abstract), m_Type(std::move(type)),
+ m_Name(std::move(name)), m_Filter(filter.release()), m_Zone(std::move(zone)), m_Package(std::move(package)), m_DefaultTmpl(defaultTmpl),
+ m_IgnoreOnError(ignoreOnError), m_ClosedVars(std::move(closedVars)), m_Expression(expression.release())
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ bool m_Abstract;
+ std::unique_ptr<Expression> m_Type;
+ std::unique_ptr<Expression> m_Name;
+ Expression::Ptr m_Filter;
+ String m_Zone;
+ String m_Package;
+ bool m_DefaultTmpl;
+ bool m_IgnoreOnError;
+ std::map<String, std::unique_ptr<Expression> > m_ClosedVars;
+ Expression::Ptr m_Expression;
+};
+
+class ForExpression final : public DebuggableExpression
+{
+public:
+ ForExpression(String fkvar, String fvvar, std::unique_ptr<Expression> value, std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_FKVar(std::move(fkvar)), m_FVVar(std::move(fvvar)), m_Value(std::move(value)), m_Expression(std::move(expression))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ String m_FKVar;
+ String m_FVVar;
+ std::unique_ptr<Expression> m_Value;
+ std::unique_ptr<Expression> m_Expression;
+};
+
+class LibraryExpression final : public UnaryExpression
+{
+public:
+ LibraryExpression(std::unique_ptr<Expression> expression, const DebugInfo& debugInfo = DebugInfo())
+ : UnaryExpression(std::move(expression), debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+enum IncludeType
+{
+ IncludeRegular,
+ IncludeRecursive,
+ IncludeZones
+};
+
+class IncludeExpression final : public DebuggableExpression
+{
+public:
+ IncludeExpression(String relativeBase, std::unique_ptr<Expression> path, std::unique_ptr<Expression> pattern, std::unique_ptr<Expression> name,
+ IncludeType type, bool searchIncludes, String zone, String package, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_RelativeBase(std::move(relativeBase)), m_Path(std::move(path)), m_Pattern(std::move(pattern)),
+ m_Name(std::move(name)), m_Type(type), m_SearchIncludes(searchIncludes), m_Zone(std::move(zone)), m_Package(std::move(package))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ String m_RelativeBase;
+ std::unique_ptr<Expression> m_Path;
+ std::unique_ptr<Expression> m_Pattern;
+ std::unique_ptr<Expression> m_Name;
+ IncludeType m_Type;
+ bool m_SearchIncludes;
+ String m_Zone;
+ String m_Package;
+};
+
+class BreakpointExpression final : public DebuggableExpression
+{
+public:
+ BreakpointExpression(const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo)
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
+class TryExceptExpression final : public DebuggableExpression
+{
+public:
+ TryExceptExpression(std::unique_ptr<Expression> tryBody, std::unique_ptr<Expression> exceptBody, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_TryBody(std::move(tryBody)), m_ExceptBody(std::move(exceptBody))
+ { }
+
+protected:
+ ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+
+private:
+ std::unique_ptr<Expression> m_TryBody;
+ std::unique_ptr<Expression> m_ExceptBody;
+};
+
+}
+
+#endif /* EXPRESSION_H */
diff --git a/lib/config/i2-config.hpp b/lib/config/i2-config.hpp
new file mode 100644
index 0000000..8c26287
--- /dev/null
+++ b/lib/config/i2-config.hpp
@@ -0,0 +1,16 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef I2CONFIG_H
+#define I2CONFIG_H
+
+/**
+ * @defgroup config Configuration library
+ *
+ * The configuration library implements a compiler for Icinga 2's configuration
+ * format. It also provides functionality for creating configuration objects
+ * at runtime.
+ */
+
+#include "base/i2-base.hpp"
+
+#endif /* I2CONFIG_H */
diff --git a/lib/config/objectrule.cpp b/lib/config/objectrule.cpp
new file mode 100644
index 0000000..6a74a40
--- /dev/null
+++ b/lib/config/objectrule.cpp
@@ -0,0 +1,18 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "config/objectrule.hpp"
+#include <set>
+
+using namespace icinga;
+
+ObjectRule::TypeSet ObjectRule::m_Types;
+
+void ObjectRule::RegisterType(const String& sourceType)
+{
+ m_Types.insert(sourceType);
+}
+
+bool ObjectRule::IsValidSourceType(const String& sourceType)
+{
+ return m_Types.find(sourceType) != m_Types.end();
+}
diff --git a/lib/config/objectrule.hpp b/lib/config/objectrule.hpp
new file mode 100644
index 0000000..d093c9f
--- /dev/null
+++ b/lib/config/objectrule.hpp
@@ -0,0 +1,33 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef OBJECTRULE_H
+#define OBJECTRULE_H
+
+#include "config/i2-config.hpp"
+#include "config/expression.hpp"
+#include "base/debuginfo.hpp"
+#include <set>
+
+namespace icinga
+{
+
+/**
+ * @ingroup config
+ */
+class ObjectRule
+{
+public:
+ typedef std::set<String> TypeSet;
+
+ static void RegisterType(const String& sourceType);
+ static bool IsValidSourceType(const String& sourceType);
+
+private:
+ ObjectRule();
+
+ static TypeSet m_Types;
+};
+
+}
+
+#endif /* OBJECTRULE_H */
diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp
new file mode 100644
index 0000000..ea30983
--- /dev/null
+++ b/lib/config/vmops.hpp
@@ -0,0 +1,274 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef VMOPS_H
+#define VMOPS_H
+
+#include "config/i2-config.hpp"
+#include "config/expression.hpp"
+#include "config/configitembuilder.hpp"
+#include "config/applyrule.hpp"
+#include "config/objectrule.hpp"
+#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"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include <map>
+#include <vector>
+
+namespace icinga
+{
+
+class VMOps
+{
+public:
+ static inline bool FindVarImportRef(ScriptFrame& frame, const std::vector<Expression::Ptr>& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
+ {
+ for (const auto& import : imports) {
+ ExpressionResult res = import->Evaluate(frame);
+ Object::Ptr obj = res.GetValue();
+ if (obj->HasOwnField(name)) {
+ *result = obj;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static inline bool FindVarImport(ScriptFrame& frame, const std::vector<Expression::Ptr>& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
+ {
+ Value parent;
+
+ if (FindVarImportRef(frame, imports, name, &parent, debugInfo)) {
+ *result = GetField(parent, name, frame.Sandboxed, debugInfo);
+ return true;
+ }
+
+ return false;
+ }
+
+ static inline Value ConstructorCall(const Type::Ptr& type, const std::vector<Value>& args, const DebugInfo& debugInfo = DebugInfo())
+ {
+ if (type->GetName() == "String") {
+ if (args.empty())
+ return "";
+ else if (args.size() == 1)
+ return Convert::ToString(args[0]);
+ else
+ BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
+ } else if (type->GetName() == "Number") {
+ if (args.empty())
+ return 0;
+ else if (args.size() == 1)
+ return Convert::ToDouble(args[0]);
+ else
+ BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
+ } else if (type->GetName() == "Boolean") {
+ if (args.empty())
+ return 0;
+ else if (args.size() == 1)
+ return Convert::ToBool(args[0]);
+ else
+ BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
+ } else if (args.size() == 1 && type->IsAssignableFrom(args[0].GetReflectionType()))
+ return args[0];
+ else
+ return type->Instantiate(args);
+ }
+
+ static inline Value FunctionCall(ScriptFrame& frame, const Value& self, const Function::Ptr& func, const std::vector<Value>& arguments)
+ {
+ if (!self.IsEmpty() || self.IsString())
+ return func->InvokeThis(self, arguments);
+ else
+ return func->Invoke(arguments);
+
+ }
+
+ static inline Value NewFunction(ScriptFrame& frame, const String& name, const std::vector<String>& argNames,
+ const std::map<String, std::unique_ptr<Expression> >& closedVars, const Expression::Ptr& expression)
+ {
+ auto evaluatedClosedVars = EvaluateClosedVars(frame, closedVars);
+
+ auto wrapper = [argNames, evaluatedClosedVars, expression](const std::vector<Value>& arguments) -> Value {
+ if (arguments.size() < argNames.size())
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Too few arguments for function"));
+
+ ScriptFrame *frame = ScriptFrame::GetCurrentFrame();
+
+ frame->Locals = new Dictionary();
+
+ if (evaluatedClosedVars)
+ evaluatedClosedVars->CopyTo(frame->Locals);
+
+ for (std::vector<Value>::size_type i = 0; i < std::min(arguments.size(), argNames.size()); i++)
+ frame->Locals->Set(argNames[i], arguments[i]);
+
+ return expression->Evaluate(*frame);
+ };
+
+ return new Function(name, wrapper, argNames);
+ }
+
+ static inline Value NewApply(ScriptFrame& frame, const String& type, const String& target, const String& name, const Expression::Ptr& filter,
+ const String& package, const String& fkvar, const String& fvvar, const Expression::Ptr& fterm, const std::map<String, std::unique_ptr<Expression> >& closedVars,
+ bool ignoreOnError, const Expression::Ptr& expression, const DebugInfo& debugInfo = DebugInfo())
+ {
+ ApplyRule::AddRule(type, target, name, expression, filter, package, fkvar,
+ fvvar, fterm, ignoreOnError, debugInfo, EvaluateClosedVars(frame, closedVars));
+
+ return Empty;
+ }
+
+ static inline Value NewObject(ScriptFrame& frame, bool abstract, const Type::Ptr& type, const String& name, const Expression::Ptr& filter,
+ const String& zone, const String& package, bool defaultTmpl, bool ignoreOnError, const std::map<String, std::unique_ptr<Expression> >& closedVars, const Expression::Ptr& expression, const DebugInfo& debugInfo = DebugInfo())
+ {
+ ConfigItemBuilder item{debugInfo};
+
+ String checkName = name;
+
+ if (!abstract) {
+ auto *nc = dynamic_cast<NameComposer *>(type.get());
+
+ if (nc)
+ checkName = nc->MakeName(name, nullptr);
+ }
+
+ if (!checkName.IsEmpty()) {
+ ConfigItem::Ptr oldItem = ConfigItem::GetByTypeAndName(type, checkName);
+
+ if (oldItem) {
+ std::ostringstream msgbuf;
+ msgbuf << "Object '" << name << "' of type '" << type->GetName() << "' re-defined: " << debugInfo << "; previous definition: " << oldItem->GetDebugInfo();
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debugInfo));
+ }
+ }
+
+ if (filter && !ObjectRule::IsValidSourceType(type->GetName())) {
+ std::ostringstream msgbuf;
+ msgbuf << "Object '" << name << "' of type '" << type->GetName() << "' must not have 'assign where' and 'ignore where' rules: " << debugInfo;
+ BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debugInfo));
+ }
+
+ item.SetType(type);
+ item.SetName(name);
+
+ if (!abstract)
+ item.AddExpression(new ImportDefaultTemplatesExpression());
+
+ item.AddExpression(new OwnedExpression(expression));
+ item.SetAbstract(abstract);
+ item.SetScope(EvaluateClosedVars(frame, closedVars));
+ item.SetZone(zone);
+ item.SetPackage(package);
+ item.SetFilter(filter);
+ item.SetDefaultTemplate(defaultTmpl);
+ item.SetIgnoreOnError(ignoreOnError);
+ item.Compile()->Register();
+
+ return Empty;
+ }
+
+ static inline ExpressionResult For(ScriptFrame& frame, const String& fkvar, const String& fvvar, const Value& value, const std::unique_ptr<Expression>& expression, const DebugInfo& debugInfo = DebugInfo())
+ {
+ if (value.IsObjectType<Array>()) {
+ if (!fvvar.IsEmpty())
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot use dictionary iterator for array.", debugInfo));
+
+ Array::Ptr arr = value;
+
+ for (Array::SizeType i = 0; i < arr->GetLength(); i++) {
+ frame.Locals->Set(fkvar, arr->Get(i));
+ ExpressionResult res = expression->Evaluate(frame);
+ CHECK_RESULT_LOOP(res);
+ }
+ } else if (value.IsObjectType<Dictionary>()) {
+ if (fvvar.IsEmpty())
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for dictionary.", debugInfo));
+
+ Dictionary::Ptr dict = value;
+ std::vector<String> keys;
+
+ {
+ ObjectLock olock(dict);
+ for (const Dictionary::Pair& kv : dict) {
+ keys.push_back(kv.first);
+ }
+ }
+
+ for (const String& key : keys) {
+ frame.Locals->Set(fkvar, key);
+ frame.Locals->Set(fvvar, dict->Get(key));
+ ExpressionResult res = expression->Evaluate(frame);
+ CHECK_RESULT_LOOP(res);
+ }
+ } else if (value.IsObjectType<Namespace>()) {
+ if (fvvar.IsEmpty())
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for namespace.", debugInfo));
+
+ Namespace::Ptr ns = value;
+ std::vector<String> 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));
+
+ return Empty;
+ }
+
+ static inline Value GetField(const Value& context, const String& field, bool sandboxed = false, const DebugInfo& debugInfo = DebugInfo())
+ {
+ if (BOOST_UNLIKELY(context.IsEmpty() && !context.IsString()))
+ return Empty;
+
+ if (BOOST_UNLIKELY(!context.IsObject()))
+ return GetPrototypeField(context, field, true, debugInfo);
+
+ Object::Ptr object = context;
+
+ return object->GetFieldByName(field, sandboxed, debugInfo);
+ }
+
+ static inline void SetField(const Object::Ptr& context, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo = DebugInfo())
+ {
+ if (!context)
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot set field '" + field + "' on a value that is not an object.", debugInfo));
+
+ return context->SetFieldByName(field, value, overrideFrozen, debugInfo);
+ }
+
+private:
+ static inline Dictionary::Ptr EvaluateClosedVars(ScriptFrame& frame, const std::map<String, std::unique_ptr<Expression> >& closedVars)
+ {
+ if (closedVars.empty())
+ return nullptr;
+
+ DictionaryData locals;
+
+ for (const auto& cvar : closedVars)
+ locals.emplace_back(cvar.first, cvar.second->Evaluate(frame));
+
+ return new Dictionary(std::move(locals));
+ }
+};
+
+}
+
+#endif /* VMOPS_H */