From 35c58d153e8711c2911bf69c031c20aa3357820b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 29 Aug 2019 17:32:22 +0200 Subject: [PATCH] DSL: introduce on_config_committed refs #3520 --- lib/cli/daemonutility.cpp | 4 +- lib/config/CMakeLists.txt | 1 + lib/config/commitcontext.cpp | 7 +++ lib/config/commitcontext.hpp | 86 ++++++++++++++++++++++++++++++++++++ lib/config/config_lexer.ll | 1 + lib/config/config_parser.yy | 6 +++ lib/config/expression.cpp | 17 +++++++ lib/config/expression.hpp | 14 ++++++ 8 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 lib/config/commitcontext.cpp create mode 100644 lib/config/commitcontext.hpp diff --git a/lib/cli/daemonutility.cpp b/lib/cli/daemonutility.cpp index 9e910f313d1..8985a974ca0 100644 --- a/lib/cli/daemonutility.cpp +++ b/lib/cli/daemonutility.cpp @@ -7,6 +7,7 @@ #include "base/logger.hpp" #include "base/application.hpp" #include "base/scriptglobal.hpp" +#include "config/commitcontext.hpp" #include "config/configcompiler.hpp" #include "config/configcompilercontext.hpp" #include "config/configitembuilder.hpp" @@ -240,6 +241,7 @@ bool DaemonUtility::LoadConfigFiles(const std::vector& configs, std::vector& newItems, const String& objectsFile, const String& varsfile) { + CommitContext cc; ActivationScope ascope; if (!DaemonUtility::ValidateConfigFiles(configs, objectsFile)) { @@ -254,7 +256,7 @@ bool DaemonUtility::LoadConfigFiles(const std::vector& configs, WorkQueue upq(25000, Configuration::Concurrency); upq.SetName("DaemonUtility::LoadConfigFiles"); - bool result = ConfigItem::CommitItems(ascope.GetContext(), upq, newItems); + bool result = ConfigItem::CommitItems(ascope.GetContext(), upq, newItems) && cc.RunOnConfigCommitted(); if (result) { try { diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 80b8c2c4420..e2091f41cfb 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -22,6 +22,7 @@ set(config_SOURCES i2-config.hpp activationcontext.cpp activationcontext.hpp applyrule.cpp applyrule-targeted.cpp applyrule.hpp + commitcontext.cpp commitcontext.hpp configcompiler.cpp configcompiler.hpp configcompilercontext.cpp configcompilercontext.hpp configfragment.hpp diff --git a/lib/config/commitcontext.cpp b/lib/config/commitcontext.cpp new file mode 100644 index 00000000000..53cdcf355d6 --- /dev/null +++ b/lib/config/commitcontext.cpp @@ -0,0 +1,7 @@ +/* Icinga 2 | (c) 2019 Icinga GmbH | GPLv2+ */ + +#include "config/commitcontext.hpp" + +using namespace icinga; + +thread_local CommitContext* CommitContext::m_Current = nullptr; diff --git a/lib/config/commitcontext.hpp b/lib/config/commitcontext.hpp new file mode 100644 index 00000000000..c6a1d388209 --- /dev/null +++ b/lib/config/commitcontext.hpp @@ -0,0 +1,86 @@ +/* Icinga 2 | (c) 2019 Icinga GmbH | GPLv2+ */ + +#ifndef COMMITCONTEXT_H +#define COMMITCONTEXT_H + +#include "base/debug.hpp" +#include "base/exception.hpp" +#include "base/logger.hpp" +#include "base/scriptframe.hpp" +#include "config/expression.hpp" +#include +#include +#include +#include + +namespace icinga +{ + +class CommitContext +{ +public: + static inline + CommitContext* GetCurrent() + { + return m_Current; + } + + inline CommitContext() : m_Started(false) + { + VERIFY(!m_Current); + + m_Current = this; + } + + CommitContext(const CommitContext&) = delete; + CommitContext(CommitContext&&) = delete; + CommitContext& operator=(const CommitContext&) = delete; + CommitContext& operator=(CommitContext&&) = delete; + + inline ~CommitContext() + { + VERIFY(m_Current); + + m_Current = nullptr; + } + + inline void RegisterOnConfigCommitted(std::shared_ptr expr) + { + m_OnConfigCommitted.emplace_back(std::move(expr)); + } + + inline bool RunOnConfigCommitted() + { + m_Started = true; + + for (auto& expr : m_OnConfigCommitted) { + if (!expr) + return false; + + try { + ScriptFrame frame(false); + expr->Evaluate(frame); + } catch (const std::exception& ex) { + Log(LogCritical, "config", DiagnosticInformation(ex)); + return false; + } + } + + return true; + } + + inline bool HasStarted() + { + return m_Started; + } + +private: + static thread_local CommitContext* m_Current; + + std::vector> m_OnConfigCommitted; + bool m_Started; +}; + +} + +#endif /* COMMITCONTEXT_H */ diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index abfdaffb7e8..629a61296ad 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -156,6 +156,7 @@ template return T_TEMPLATE; include return T_INCLUDE; include_recursive return T_INCLUDE_RECURSIVE; include_zones return T_INCLUDE_ZONES; +on_config_committed return T_ON_CONFIG_COMMITTED; library return T_LIBRARY; null return T_NULL; true { yylval->boolean = 1; return T_BOOLEAN; } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 939681e68fa..f3f7d535020 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -140,6 +140,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %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_ON_CONFIG_COMMITTED "on_config_committed (T_ON_CONFIG_COMMITTED)" %token T_LIBRARY "library (T_LIBRARY)" %token T_APPLY "apply (T_APPLY)" %token T_TO "to (T_TO)" @@ -195,6 +196,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %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_ON_CONFIG_COMMITTED %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 '?' ':' @@ -563,6 +565,10 @@ lterm: T_LIBRARY rterm $$ = MakeLiteralRaw(); } + | T_ON_CONFIG_COMMITTED rterm + { + $$ = new OnConfigCommittedExpression(std::shared_ptr($2), @$); + } | T_RETURN optional_rterm { UseFlowControl(context, FlowControlReturn, @$); diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index a8e99861a1f..134416554f9 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -1,6 +1,7 @@ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/expression.hpp" +#include "config/commitcontext.hpp" #include "config/configitem.hpp" #include "config/configcompiler.hpp" #include "config/vmops.hpp" @@ -1046,6 +1047,22 @@ ExpressionResult IncludeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh return res; } +ExpressionResult OnConfigCommittedExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("on_config_committed is not allowed in sandbox mode.", m_DebugInfo)); + + auto cc (CommitContext::GetCurrent()); + + if (!cc || cc->HasStarted()) { + BOOST_THROW_EXCEPTION(ScriptError("on_config_committed is not allowed after the config has been committed.", m_DebugInfo)); + } + + cc->RegisterOnConfigCommitted(m_Expr); + + return Empty; +} + ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { ScriptBreakpoint(frame, nullptr, GetDebugInfo()); diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 7be667a2b99..90cb7ae5564 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -950,6 +950,20 @@ class IncludeExpression final : public DebuggableExpression String m_Package; }; +class OnConfigCommittedExpression final : public DebuggableExpression +{ +public: + OnConfigCommittedExpression(std::shared_ptr expr, const DebugInfo& debugInfo = DebugInfo()) + : DebuggableExpression(debugInfo), m_Expr(std::move(expr)) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; + +private: + std::shared_ptr m_Expr; +}; + class BreakpointExpression final : public DebuggableExpression { public: