icinga2/lib/config/expression.cpp
Julian Brost 0ebcd2662d No longer allow overriding the frozen attribute of containers
The Array, Dictionary, and Namespace types provide a Freeze() method that makes
them read-only. So far, there was the possibility to call some methods with
`overrideFrozen=true` which would then bypass the corresponding check and allow
modification of the data structures nonetheless.

With 24b57f0d3a222835178e88489eabd595755ed883, this possibility was already
removed from the Namespace type. However, for interface compatibility, it kept
the parameter and just ignores it, throwing an exception on any modification on
a frozen instance.

The only place using `overrideFrozen` was processing of the `-D`/`--define`
command line flag that allows setting additional variables in the DSL. At the
time it is evaluated, there are no user-created data structures yet that could
be frozen, so the only frozen objects that could be encountered are Namespaces
(Icinga doesn't freeze other types by itself) and for these, `overrideFrozen`
already has no effect.

Hence, there is no harm in removing `overrideFrozen` altogether. This
simplifies the code and also means that frozen objects are now indeed read-only
without exceptions, allowing further optimizations regarding locking in the
future.
2025-07-08 14:16:20 +02:00

1058 lines
29 KiB
C++

/* 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_DebugInfo);
if (psdhint) {
psdhint->AddMessage("=", m_DebugInfo);
if (psdhint != dhint)
delete psdhint;
}
WarnOnImplicitlySetGlobalVar(m_Operand1, parent, m_Op, m_DebugInfo);
return Empty;
}
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_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 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;
}