/****************************************************************************** * Icinga 2 * * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software Foundation * * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ #include "config/expression.hpp" #include "config/configitem.hpp" #include "config/configitembuilder.hpp" #include "config/applyrule.hpp" #include "config/objectrule.hpp" #include "base/array.hpp" #include "base/json.hpp" #include "base/scriptfunction.hpp" #include "base/scriptvariable.hpp" #include "base/utility.hpp" #include "base/objectlock.hpp" #include "base/object.hpp" #include "base/logger.hpp" #include "base/configerror.hpp" #include #include #include using namespace icinga; Expression::Expression(OpCallback op, const Value& operand1, const DebugInfo& di) : m_Operator(op), m_Operand1(operand1), m_Operand2(), m_DebugInfo(di) { } Expression::Expression(OpCallback op, const Value& operand1, const Value& operand2, const DebugInfo& di) : m_Operator(op), m_Operand1(operand1), m_Operand2(operand2), m_DebugInfo(di) { } Value Expression::Evaluate(const Dictionary::Ptr& locals, DebugHint *dhint) const { try { #ifdef _DEBUG if (m_Operator != &Expression::OpLiteral) { std::ostringstream msgbuf; ShowCodeFragment(msgbuf, m_DebugInfo, false); Log(LogDebug, "Expression") << "Executing:\n" << msgbuf.str(); } #endif /* _DEBUG */ return m_Operator(this, locals, dhint); } catch (const std::exception& ex) { if (boost::get_error_info(ex)) throw; else BOOST_THROW_EXCEPTION(ConfigError("Error while evaluating expression: " + String(ex.what())) << boost::errinfo_nested_exception(boost::current_exception()) << errinfo_debuginfo(m_DebugInfo)); } } void Expression::MakeInline(void) { if (m_Operator == &Expression::OpDict) m_Operand2 = true; } void Expression::DumpOperand(std::ostream& stream, const Value& operand, int indent) { if (operand.IsObjectType()) { Array::Ptr arr = operand; stream << String(indent, ' ') << "Array:\n"; ObjectLock olock(arr); BOOST_FOREACH(const Value& elem, arr) { DumpOperand(stream, elem, indent + 1); } } else if (operand.IsObjectType()) { Expression::Ptr left = operand; left->Dump(stream, indent); } else { stream << String(indent, ' ') << JsonEncode(operand) << "\n"; } } void Expression::Dump(std::ostream& stream, int indent) const { String sym = Utility::GetSymbolName(reinterpret_cast(m_Operator)); stream << String(indent, ' ') << "op: " << Utility::DemangleSymbolName(sym) << "\n"; stream << String(indent, ' ') << "left:\n"; DumpOperand(stream, m_Operand1, indent + 1); stream << String(indent, ' ') << "right:\n"; DumpOperand(stream, m_Operand2, indent + 1); } Value Expression::EvaluateOperand1(const Dictionary::Ptr& locals, DebugHint *dhint) const { return static_cast(m_Operand1)->Evaluate(locals, dhint); } Value Expression::EvaluateOperand2(const Dictionary::Ptr& locals, DebugHint *dhint) const { return static_cast(m_Operand2)->Evaluate(locals, dhint); } Value Expression::OpLiteral(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->m_Operand1; } Value Expression::OpVariable(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Dictionary::Ptr scope = locals; while (scope) { if (scope->Contains(expr->m_Operand1)) return scope->Get(expr->m_Operand1); scope = scope->Get("__parent"); } return ScriptVariable::Get(expr->m_Operand1); } Value Expression::OpNegate(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return ~(long)expr->EvaluateOperand1(locals); } Value Expression::OpLogicalNegate(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return !expr->EvaluateOperand1(locals).ToBool(); } Value Expression::OpAdd(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) + expr->EvaluateOperand2(locals); } Value Expression::OpSubtract(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) - expr->EvaluateOperand2(locals); } Value Expression::OpMultiply(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) * expr->EvaluateOperand2(locals); } Value Expression::OpDivide(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) / expr->EvaluateOperand2(locals); } Value Expression::OpBinaryAnd(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) & expr->EvaluateOperand2(locals); } Value Expression::OpBinaryOr(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) | expr->EvaluateOperand2(locals); } Value Expression::OpShiftLeft(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) << expr->EvaluateOperand2(locals); } Value Expression::OpShiftRight(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) >> expr->EvaluateOperand2(locals); } Value Expression::OpEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) == expr->EvaluateOperand2(locals); } Value Expression::OpNotEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) != expr->EvaluateOperand2(locals); } Value Expression::OpLessThan(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) < expr->EvaluateOperand2(locals); } Value Expression::OpGreaterThan(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) > expr->EvaluateOperand2(locals); } Value Expression::OpLessThanOrEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) <= expr->EvaluateOperand2(locals); } Value Expression::OpGreaterThanOrEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) >= expr->EvaluateOperand2(locals); } Value Expression::OpIn(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value right = expr->EvaluateOperand2(locals); if (right.IsEmpty()) return false; else if (!right.IsObjectType()) BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonEncode(right))); Value left = expr->EvaluateOperand1(locals); Array::Ptr arr = right; bool found = false; ObjectLock olock(arr); BOOST_FOREACH(const Value& value, arr) { if (value == left) { found = true; break; } } return found; } Value Expression::OpNotIn(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return !OpIn(expr, locals, dhint); } Value Expression::OpLogicalAnd(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals).ToBool() && expr->EvaluateOperand2(locals).ToBool(); } Value Expression::OpLogicalOr(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals).ToBool() || expr->EvaluateOperand2(locals).ToBool(); } Value Expression::OpFunctionCall(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value funcName = expr->EvaluateOperand1(locals); ScriptFunction::Ptr func; if (funcName.IsObjectType()) func = funcName; else func = ScriptFunction::GetByName(funcName); if (!func) BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist.")); Array::Ptr arr = expr->EvaluateOperand2(locals); std::vector arguments; for (Array::SizeType index = 0; index < arr->GetLength(); index++) { const Expression::Ptr& aexpr = arr->Get(index); arguments.push_back(aexpr->Evaluate(locals)); } return func->Invoke(arguments); } Value Expression::OpArray(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr arr = expr->m_Operand1; Array::Ptr result = make_shared(); if (arr) { for (Array::SizeType index = 0; index < arr->GetLength(); index++) { const Expression::Ptr& aexpr = arr->Get(index); result->Add(aexpr->Evaluate(locals)); } } return result; } Value Expression::OpDict(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr arr = expr->m_Operand1; bool in_place = expr->m_Operand2; Dictionary::Ptr result = make_shared(); result->Set("__parent", locals); if (arr) { for (Array::SizeType index = 0; index < arr->GetLength(); index++) { const Expression::Ptr& aexpr = arr->Get(index); Dictionary::Ptr alocals = in_place ? locals : result; aexpr->Evaluate(alocals, dhint); if (alocals->Contains("__result")) break; } } Dictionary::Ptr xresult = result->ShallowClone(); xresult->Remove("__parent"); return xresult; } Value Expression::OpSet(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); DebugHint *sdhint = NULL; if (dhint) sdhint = dhint->GetChild(index); Value right = expr->EvaluateOperand2(locals, sdhint); locals->Set(index, right); if (sdhint) sdhint->AddMessage("=", expr->m_DebugInfo); return right; } Value Expression::OpSetPlus(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); Expression::Ptr exp_right = expr->m_Operand2; Dictionary::Ptr xlocals = locals; if (exp_right->m_Operator == &Expression::OpDict) { xlocals = left; if (!xlocals) xlocals = make_shared(); xlocals->Set("__parent", locals); } DebugHint *sdhint = NULL; if (dhint) sdhint = dhint->GetChild(index); Value result = left + expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &Expression::OpDict) { Dictionary::Ptr dict = result; dict->Remove("__parent"); } locals->Set(index, result); if (sdhint) sdhint->AddMessage("+=", expr->m_DebugInfo); return result; } Value Expression::OpSetMinus(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); Expression::Ptr exp_right = expr->m_Operand2; Dictionary::Ptr xlocals = locals; if (exp_right->m_Operator == &Expression::OpDict) { xlocals = left; if (!xlocals) xlocals = make_shared(); xlocals->Set("__parent", locals); } DebugHint *sdhint = NULL; if (dhint) sdhint = dhint->GetChild(index); Value result = left - expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &Expression::OpDict) { Dictionary::Ptr dict = result; dict->Remove("__parent"); } locals->Set(index, result); if (sdhint) sdhint->AddMessage("-=", expr->m_DebugInfo); return result; } Value Expression::OpSetMultiply(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); Expression::Ptr exp_right = expr->m_Operand2; Dictionary::Ptr xlocals = locals; if (exp_right->m_Operator == &Expression::OpDict) { xlocals = left; if (!xlocals) xlocals = make_shared(); xlocals->Set("__parent", locals); } DebugHint *sdhint = NULL; if (dhint) sdhint = dhint->GetChild(index); Value result = left * expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &Expression::OpDict) { Dictionary::Ptr dict = result; dict->Remove("__parent"); } locals->Set(index, result); if (sdhint) sdhint->AddMessage("*=", expr->m_DebugInfo); return result; } Value Expression::OpSetDivide(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); Expression::Ptr exp_right = expr->m_Operand2; Dictionary::Ptr xlocals = locals; if (exp_right->m_Operator == &Expression::OpDict) { xlocals = left; if (!xlocals) xlocals = make_shared(); xlocals->Set("__parent", locals); } DebugHint *sdhint = NULL; if (dhint) sdhint = dhint->GetChild(index); Value result = left / expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &Expression::OpDict) { Dictionary::Ptr dict = result; dict->Remove("__parent"); } locals->Set(index, result); if (sdhint) sdhint->AddMessage("/=", expr->m_DebugInfo); return result; } Value Expression::OpIndexer(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value value = expr->EvaluateOperand1(locals); Value index = expr->EvaluateOperand2(locals); if (value.IsObjectType()) { Dictionary::Ptr dict = value; return dict->Get(index); } else if (value.IsObjectType()) { Array::Ptr arr = value; return arr->Get(index); } else if (value.IsObjectType()) { Object::Ptr object = value; const Type *type = object->GetReflectionType(); if (!type) BOOST_THROW_EXCEPTION(ConfigError("Dot operator applied to object which does not support reflection")); int field = type->GetFieldId(index); if (field == -1) BOOST_THROW_EXCEPTION(ConfigError("Tried to access invalid property '" + index + "'")); return object->GetField(field); } else if (value.IsEmpty()) { return Empty; } else { BOOST_THROW_EXCEPTION(ConfigError("Dot operator cannot be applied to type '" + value.GetTypeName() + "'")); } } Value Expression::OpImport(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value type = expr->EvaluateOperand1(locals); Value name = expr->EvaluateOperand2(locals); ConfigItem::Ptr item = ConfigItem::GetObject(type, name); if (!item) BOOST_THROW_EXCEPTION(ConfigError("Import references unknown template: '" + name + "'")); item->GetExpressionList()->Evaluate(locals, dhint); return Empty; } Value Expression::FunctionWrapper(const std::vector& arguments, const Array::Ptr& funcargs, const Expression::Ptr& expr, const Dictionary::Ptr& scope) { if (arguments.size() < funcargs->GetLength()) BOOST_THROW_EXCEPTION(ConfigError("Too few arguments for function")); Dictionary::Ptr locals = make_shared(); locals->Set("__parent", scope); for (std::vector::size_type i = 0; i < std::min(arguments.size(), funcargs->GetLength()); i++) locals->Set(funcargs->Get(i), arguments[i]); expr->Evaluate(locals); return locals->Get("__result"); } Value Expression::OpFunction(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; Expression::Ptr aexpr = left->Get(1); String name = left->Get(0); Array::Ptr funcargs = expr->m_Operand2; ScriptFunction::Ptr func = make_shared(boost::bind(&Expression::FunctionWrapper, _1, funcargs, aexpr, locals)); if (!name.IsEmpty()) ScriptFunction::Register(name, func); return func; } Value Expression::OpApply(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; Expression::Ptr exprl = expr->m_Operand2; String type = left->Get(0); String target = left->Get(1); Expression::Ptr aname = left->Get(2); Expression::Ptr filter = left->Get(3); String name = aname->Evaluate(locals, dhint); ApplyRule::AddRule(type, target, name, exprl, filter, expr->m_DebugInfo, locals); return Empty; } Value Expression::OpObject(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; Expression::Ptr exprl = expr->m_Operand2; bool abstract = left->Get(0); String type = left->Get(1); Expression::Ptr aname = left->Get(2); Expression::Ptr filter = left->Get(3); String zone = left->Get(4); String name = aname->Evaluate(locals, dhint); ConfigItemBuilder::Ptr item = make_shared(expr->m_DebugInfo); String checkName = name; if (!abstract) { const NameComposer *nc = dynamic_cast(Type::GetByName(type)); if (nc) checkName = nc->MakeName(name, Dictionary::Ptr()); } if (!checkName.IsEmpty()) { ConfigItem::Ptr oldItem = ConfigItem::GetObject(type, checkName); if (oldItem) { std::ostringstream msgbuf; msgbuf << "Object '" << name << "' of type '" << type << "' re-defined: " << expr->m_DebugInfo << "; previous definition: " << oldItem->GetDebugInfo(); BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(expr->m_DebugInfo)); } } item->SetType(type); if (name.FindFirstOf("!") != String::NPos) { std::ostringstream msgbuf; msgbuf << "Name for object '" << name << "' of type '" << type << "' is invalid: Object names may not contain '!'"; BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(expr->m_DebugInfo)); } item->SetName(name); item->AddExpression(exprl); item->SetAbstract(abstract); item->SetScope(locals); item->SetZone(zone); item->Compile()->Register(); ObjectRule::AddRule(type, name, exprl, filter, expr->m_DebugInfo, locals); return Empty; } Value Expression::OpFor(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; String varname = left->Get(0); Expression::Ptr aexpr = left->Get(1); Expression::Ptr ascope = expr->m_Operand2; Array::Ptr arr = aexpr->Evaluate(locals, dhint); ObjectLock olock(arr); BOOST_FOREACH(const Value& value, arr) { Dictionary::Ptr xlocals = make_shared(); xlocals->Set("__parent", locals); xlocals->Set(varname, value); ascope->Evaluate(xlocals, dhint); } return Empty; } Dictionary::Ptr DebugHint::ToDictionary(void) const { Dictionary::Ptr result = make_shared(); Array::Ptr messages = make_shared(); typedef std::pair MessageType; BOOST_FOREACH(const MessageType& message, Messages) { Array::Ptr amsg = make_shared(); amsg->Add(message.first); amsg->Add(message.second.Path); amsg->Add(message.second.FirstLine); amsg->Add(message.second.FirstColumn); amsg->Add(message.second.LastLine); amsg->Add(message.second.LastColumn); messages->Add(amsg); } result->Set("messages", messages); Dictionary::Ptr properties = make_shared(); typedef std::map::value_type ChildType; BOOST_FOREACH(const ChildType& kv, Children) { properties->Set(kv.first, kv.second.ToDictionary()); } result->Set("properties", properties); return result; }