]> granicus.if.org Git - icinga2/blobdiff - lib/icinga/macroprocessor.cpp
Workaround for GCC bug 61321
[icinga2] / lib / icinga / macroprocessor.cpp
index e8e6259ba5f108eb9f023676ed4e9831cce992bc..9789313f7601dceefa437105e2623041b5e94a24 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  * Icinga 2                                                                   *
- * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+ * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
  *                                                                            *
  * This program is free software; you can redistribute it and/or              *
  * modify it under the terms of the GNU General Public License                *
 #include "base/objectlock.hpp"
 #include "base/logger.hpp"
 #include "base/context.hpp"
-#include "base/dynamicobject.hpp"
+#include "base/configobject.hpp"
 #include "base/scriptframe.hpp"
-#include <boost/foreach.hpp>
+#include "base/convert.hpp"
+#include "base/exception.hpp"
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/join.hpp>
 #include <boost/algorithm/string/classification.hpp>
@@ -36,7 +37,7 @@ using namespace icinga;
 Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers,
     const CheckResult::Ptr& cr, String *missingMacro,
     const MacroProcessor::EscapeCallback& escapeFn, const Dictionary::Ptr& resolvedMacros,
-    bool useResolvedMacros)
+    bool useResolvedMacros, int recursionLevel)
 {
        Value result;
 
@@ -45,22 +46,40 @@ Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolv
 
        if (str.IsScalar()) {
                result = InternalResolveMacros(str, resolvers, cr, missingMacro, escapeFn,
-                   resolvedMacros, useResolvedMacros);
+                   resolvedMacros, useResolvedMacros, recursionLevel + 1);
        } else if (str.IsObjectType<Array>()) {
                Array::Ptr resultArr = new Array();
                Array::Ptr arr = str;
 
                ObjectLock olock(arr);
 
-               BOOST_FOREACH(const Value& arg, arr) {
+               for (const Value& arg : arr) {
                        /* Note: don't escape macros here. */
-                       resultArr->Add(InternalResolveMacros(arg, resolvers, cr, missingMacro,
-                           EscapeCallback(), resolvedMacros, useResolvedMacros));
+                       Value value = InternalResolveMacros(arg, resolvers, cr, missingMacro,
+                           EscapeCallback(), resolvedMacros, useResolvedMacros, recursionLevel + 1);
+
+                       if (value.IsObjectType<Array>())
+                               resultArr->Add(Utility::Join(value, ';'));
+                       else
+                               resultArr->Add(value);
                }
 
                result = resultArr;
+       } else if (str.IsObjectType<Dictionary>()) {
+               Dictionary::Ptr resultDict = new Dictionary();
+               Dictionary::Ptr dict = str;
+
+               ObjectLock olock(dict);
+
+               for (const Dictionary::Pair& kv : dict) {
+                       /* Note: don't escape macros here. */
+                       resultDict->Set(kv.first, InternalResolveMacros(kv.second, resolvers, cr, missingMacro,
+                           EscapeCallback(), resolvedMacros, useResolvedMacros, recursionLevel + 1));
+               }
+
+               result = resultDict;
        } else if (str.IsObjectType<Function>()) {
-               result = EvaluateFunction(str, resolvers, cr, missingMacro, escapeFn, resolvedMacros, useResolvedMacros, 0);
+               result = EvaluateFunction(str, resolvers, cr, escapeFn, resolvedMacros, useResolvedMacros, 0);
        } else {
                BOOST_THROW_EXCEPTION(std::invalid_argument("Macro is not a string or array."));
        }
@@ -84,7 +103,7 @@ bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resol
                tokens.erase(tokens.begin());
        }
 
-       BOOST_FOREACH(const ResolverSpec& resolver, resolvers) {
+       for (const ResolverSpec& resolver : resolvers) {
                if (!objName.IsEmpty() && objName != resolver.first)
                        continue;
 
@@ -110,7 +129,7 @@ bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resol
                Value ref = resolver.second;
                bool valid = true;
 
-               BOOST_FOREACH(const String& token, tokens) {
+               for (const String& token : tokens) {
                        if (ref.IsObjectType<Dictionary>()) {
                                Dictionary::Ptr dict = ref;
                                if (dict->Contains(token)) {
@@ -138,6 +157,11 @@ bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resol
                                }
 
                                ref = object->GetField(field);
+
+                               Field fieldInfo = type->GetFieldInfo(field);
+
+                               if (strcmp(fieldInfo.TypeName, "Timestamp") == 0)
+                                       ref = static_cast<long>(ref);
                        }
                }
 
@@ -156,36 +180,39 @@ bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resol
        return false;
 }
 
-Value MacroProcessor::InternalResolveMacrosShim(const std::vector<Value>& args, const ResolverList& resolvers,
-    const CheckResult::Ptr& cr, String *missingMacro,
-    const MacroProcessor::EscapeCallback& escapeFn, const Dictionary::Ptr& resolvedMacros,
-    bool useResolvedMacros, int recursionLevel)
-{
-       if (args.size() < 1)
-               BOOST_THROW_EXCEPTION(std::invalid_argument("Too few arguments for function"));
-
-       return MacroProcessor::InternalResolveMacros(args[0], resolvers, cr, missingMacro, escapeFn,
-           resolvedMacros, useResolvedMacros, recursionLevel);
-}
-
 Value MacroProcessor::EvaluateFunction(const Function::Ptr& func, const ResolverList& resolvers,
-    const CheckResult::Ptr& cr, String *missingMacro,
-    const MacroProcessor::EscapeCallback& escapeFn, const Dictionary::Ptr& resolvedMacros,
-    bool useResolvedMacros, int recursionLevel)
+    const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn,
+    const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel)
 {
        Dictionary::Ptr resolvers_this = new Dictionary();
 
-       BOOST_FOREACH(const ResolverSpec& resolver, resolvers) {
+       for (const ResolverSpec& resolver : resolvers) {
                resolvers_this->Set(resolver.first, resolver.second);
        }
 
-       resolvers_this->Set("macro", new Function(boost::bind(&MacroProcessor::InternalResolveMacrosShim,
-           _1, boost::cref(resolvers), cr, missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros,
-           recursionLevel)));
+       auto internalResolveMacrosShim = [resolvers, cr, resolvedMacros, useResolvedMacros, recursionLevel](const std::vector<Value>& args) {
+               if (args.size() < 1)
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Too few arguments for function"));
+
+               String missingMacro;
 
-       ScriptFrame frame(resolvers_this);
-       std::vector<Value> args;
-       return func->Invoke(args);
+               return MacroProcessor::InternalResolveMacros(args[0], resolvers, cr, &missingMacro, MacroProcessor::EscapeCallback(),
+                   resolvedMacros, useResolvedMacros, recursionLevel);
+       };
+
+       resolvers_this->Set("macro", new Function("macro (temporary)", internalResolveMacrosShim, { "str" }));
+
+       auto internalResolveArgumentsShim = [resolvers, cr, resolvedMacros, useResolvedMacros, recursionLevel](const std::vector<Value>& args) {
+               if (args.size() < 2)
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Too few arguments for function"));
+
+               return MacroProcessor::ResolveArguments(args[0], args[1], resolvers, cr,
+                   resolvedMacros, useResolvedMacros, recursionLevel + 1);
+       };
+
+       resolvers_this->Set("resolve_arguments", new Function("resolve_arguments (temporary)", internalResolveArgumentsShim, { "command", "args" }));
+
+       return func->InvokeThis(resolvers_this);
 }
 
 Value MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
@@ -232,8 +259,8 @@ Value MacroProcessor::InternalResolveMacros(const String& str, const ResolverLis
                }
 
                if (resolved_macro.IsObjectType<Function>()) {
-                       resolved_macro = EvaluateFunction(resolved_macro, resolvers, cr, missingMacro, escapeFn,
-                           resolvedMacros, useResolvedMacros, recursionLevel);
+                       resolved_macro = EvaluateFunction(resolved_macro, resolvers, cr, escapeFn,
+                           resolvedMacros, useResolvedMacros, recursionLevel + 1);
                }
 
                if (!found) {
@@ -251,20 +278,21 @@ Value MacroProcessor::InternalResolveMacros(const String& str, const ResolverLis
                                Array::Ptr resolved_arr = new Array();
 
                                ObjectLock olock(arr);
-                               BOOST_FOREACH(const Value& value, arr) {
+                               for (const Value& value : arr) {
                                        if (value.IsScalar()) {
                                                resolved_arr->Add(InternalResolveMacros(value,
-                                                       resolvers, cr, missingMacro, EscapeCallback(), Dictionary::Ptr(),
+                                                       resolvers, cr, missingMacro, EscapeCallback(), nullptr,
                                                        false, recursionLevel + 1));
                                        } else
                                                resolved_arr->Add(value);
                                }
 
                                resolved_macro = resolved_arr;
-                       } else
+                       } else if (resolved_macro.IsString()) {
                                resolved_macro = InternalResolveMacros(resolved_macro,
-                                       resolvers, cr, missingMacro, EscapeCallback(), Dictionary::Ptr(),
+                                       resolvers, cr, missingMacro, EscapeCallback(), nullptr,
                                        false, recursionLevel + 1);
+                       }
                }
 
                if (!useResolvedMacros && found && resolvedMacros)
@@ -273,7 +301,12 @@ Value MacroProcessor::InternalResolveMacros(const String& str, const ResolverLis
                if (escapeFn)
                        resolved_macro = escapeFn(resolved_macro);
 
-               /* we're done if the value is an array */
+               /* we're done if this is the only macro and there are no other non-macro parts in the string */
+               if (pos_first == 0 && pos_second == str.GetLength() - 1)
+                       return resolved_macro;
+               else if (resolved_macro.IsObjectType<Array>())
+                               BOOST_THROW_EXCEPTION(std::invalid_argument("Mixing both strings and non-strings in macros is not allowed."));
+
                if (resolved_macro.IsObjectType<Array>()) {
                        /* don't allow mixing strings and arrays in macro strings */
                        if (pos_first != 0 || pos_second != str.GetLength() - 1)
@@ -290,3 +323,247 @@ Value MacroProcessor::InternalResolveMacros(const String& str, const ResolverLis
 
        return result;
 }
+
+
+bool MacroProcessor::ValidateMacroString(const String& macro)
+{
+       if (macro.IsEmpty())
+               return true;
+
+       size_t pos_first, pos_second, offset;
+       offset = 0;
+
+       while ((pos_first = macro.FindFirstOf("$", offset)) != String::NPos) {
+               pos_second = macro.FindFirstOf("$", pos_first + 1);
+
+               if (pos_second == String::NPos)
+                       return false;
+
+               offset = pos_second + 1;
+       }
+
+       return true;
+}
+
+void MacroProcessor::ValidateCustomVars(const ConfigObject::Ptr& object, const Dictionary::Ptr& value)
+{
+       if (!value)
+               return;
+
+       /* string, array, dictionary */
+       ObjectLock olock(value);
+       for (const Dictionary::Pair& kv : value) {
+               const Value& varval = kv.second;
+
+               if (varval.IsObjectType<Dictionary>()) {
+                       /* only one dictonary level */
+                       Dictionary::Ptr varval_dict = varval;
+
+                       ObjectLock xlock(varval_dict);
+                       for (const Dictionary::Pair& kv_var : varval_dict) {
+                               if (!kv_var.second.IsString())
+                                       continue;
+
+                               if (!ValidateMacroString(kv_var.second))
+                                       BOOST_THROW_EXCEPTION(ValidationError(object.get(), { "vars", kv.first, kv_var.first }, "Closing $ not found in macro format string '" + kv_var.second + "'."));
+                       }
+               } else if (varval.IsObjectType<Array>()) {
+                       /* check all array entries */
+                       Array::Ptr varval_arr = varval;
+
+                       ObjectLock ylock (varval_arr);
+                       for (const Value& arrval : varval_arr) {
+                               if (!arrval.IsString())
+                                       continue;
+
+                               if (!ValidateMacroString(arrval)) {
+                                       BOOST_THROW_EXCEPTION(ValidationError(object.get(), { "vars", kv.first }, "Closing $ not found in macro format string '" + arrval + "'."));
+                               }
+                       }
+               } else {
+                       if (!varval.IsString())
+                               continue;
+
+                       if (!ValidateMacroString(varval))
+                               BOOST_THROW_EXCEPTION(ValidationError(object.get(), { "vars", kv.first }, "Closing $ not found in macro format string '" + varval + "'."));
+               }
+       }
+}
+
+void MacroProcessor::AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value,
+    bool add_key, bool add_value)
+{
+       if (add_key)
+               args->Add(key);
+
+       if (add_value)
+               args->Add(value);
+}
+
+Value MacroProcessor::EscapeMacroShellArg(const Value& value)
+{
+       String result;
+
+       if (value.IsObjectType<Array>()) {
+               Array::Ptr arr = value;
+
+               ObjectLock olock(arr);
+               for (const Value& arg : arr) {
+                       if (result.GetLength() > 0)
+                               result += " ";
+
+                       result += Utility::EscapeShellArg(arg);
+               }
+       } else
+               result = Utility::EscapeShellArg(value);
+
+       return result;
+}
+
+struct CommandArgument
+{
+       int Order;
+       bool SkipKey;
+       bool RepeatKey;
+       bool SkipValue;
+       String Key;
+       Value AValue;
+
+       CommandArgument(void)
+               : Order(0), SkipKey(false), RepeatKey(true), SkipValue(false)
+       { }
+
+       bool operator<(const CommandArgument& rhs) const
+       {
+               return Order < rhs.Order;
+       }
+};
+
+Value MacroProcessor::ResolveArguments(const Value& command, const Dictionary::Ptr& arguments,
+    const MacroProcessor::ResolverList& resolvers, const CheckResult::Ptr& cr,
+    const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel)
+{
+       Value resolvedCommand;
+       if (!arguments || command.IsObjectType<Array>() || command.IsObjectType<Function>())
+               resolvedCommand = MacroProcessor::ResolveMacros(command, resolvers, cr, nullptr,
+                   EscapeMacroShellArg, resolvedMacros, useResolvedMacros, recursionLevel + 1);
+       else {
+               Array::Ptr arr = new Array();
+               arr->Add(command);
+               resolvedCommand = arr;
+       }
+
+       if (arguments) {
+               std::vector<CommandArgument> args;
+
+               ObjectLock olock(arguments);
+               for (const Dictionary::Pair& kv : arguments) {
+                       const Value& arginfo = kv.second;
+
+                       CommandArgument arg;
+                       arg.Key = kv.first;
+
+                       bool required = false;
+                       Value argval;
+
+                       if (arginfo.IsObjectType<Dictionary>()) {
+                               Dictionary::Ptr argdict = arginfo;
+                               if (argdict->Contains("key"))
+                                       arg.Key = argdict->Get("key");
+                               argval = argdict->Get("value");
+                               if (argdict->Contains("required"))
+                                       required = argdict->Get("required");
+                               arg.SkipKey = argdict->Get("skip_key");
+                               if (argdict->Contains("repeat_key"))
+                                       arg.RepeatKey = argdict->Get("repeat_key");
+                               arg.Order = argdict->Get("order");
+
+                               Value set_if = argdict->Get("set_if");
+
+                               if (!set_if.IsEmpty()) {
+                                       String missingMacro;
+                                       Value set_if_resolved = MacroProcessor::ResolveMacros(set_if, resolvers,
+                                           cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
+                                           useResolvedMacros, recursionLevel + 1);
+
+                                       if (!missingMacro.IsEmpty())
+                                               continue;
+
+                                       int value;
+
+                                       if (set_if_resolved == "true")
+                                               value = 1;
+                                       else if (set_if_resolved == "false")
+                                               value = 0;
+                                       else {
+                                               try {
+                                                       value = Convert::ToLong(set_if_resolved);
+                                               } catch (const std::exception& ex) {
+                                                       /* tried to convert a string */
+                                                       Log(LogWarning, "PluginUtility")
+                                                           << "Error evaluating set_if value '" << set_if_resolved
+                                                           << "' used in argument '" << arg.Key << "': " << ex.what();
+                                                       continue;
+                                               }
+                                       }
+
+                                       if (!value)
+                                               continue;
+                               }
+                       }
+                       else
+                               argval = arginfo;
+
+                       if (argval.IsEmpty())
+                               arg.SkipValue = true;
+
+                       String missingMacro;
+                       arg.AValue = MacroProcessor::ResolveMacros(argval, resolvers,
+                           cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
+                           useResolvedMacros, recursionLevel + 1);
+
+                       if (!missingMacro.IsEmpty()) {
+                               if (required) {
+                                       BOOST_THROW_EXCEPTION(ScriptError("Non-optional macro '" + missingMacro + "' used in argument '" +
+                                           arg.Key + "' is missing."));
+                               }
+
+                               continue;
+                       }
+
+                       args.emplace_back(std::move(arg));
+               }
+
+               std::sort(args.begin(), args.end());
+
+               Array::Ptr command_arr = resolvedCommand;
+               for (const CommandArgument& arg : args) {
+
+                       if (arg.AValue.IsObjectType<Dictionary>()) {
+                               Log(LogWarning, "PluginUtility")
+                                   << "Tried to use dictionary in argument '" << arg.Key << "'.";
+                               continue;
+                       } else if (arg.AValue.IsObjectType<Array>()) {
+                               bool first = true;
+                               Array::Ptr arr = static_cast<Array::Ptr>(arg.AValue);
+
+                               ObjectLock olock(arr);
+                               for (const Value& value : arr) {
+                                       bool add_key;
+
+                                       if (first) {
+                                               first = false;
+                                               add_key = !arg.SkipKey;
+                                       } else
+                                               add_key = !arg.SkipKey && arg.RepeatKey;
+
+                                       AddArgumentHelper(command_arr, arg.Key, value, add_key, !arg.SkipValue);
+                               }
+                       } else
+                               AddArgumentHelper(command_arr, arg.Key, arg.AValue, !arg.SkipKey, !arg.SkipValue);
+               }
+       }
+
+       return resolvedCommand;
+}
+