return "Some text"
}}
+The `resolve_arguments` can be used to resolve a command and its arguments much in
+the same fashion Icinga does this for the `command` and `arguments` attributes for
+commands. The `by_ssh` command uses this functionality to let users specify a
+command and arguments that should be executed via SSH:
+
+ arguments = {
+ "-C" = {{
+ var command = macro("$by_ssh_command$")
+ var arguments = macro("$by_ssh_arguments$")
+
+ if (typeof(command) == String && !arguments) {
+ return command
+ }
+
+ var escaped_args = []
+ for (arg in resolve_arguments(command, arguments)) {
+ escaped_args.add(escape_shell_arg(arg))
+ }
+ return escaped_args.join(" ")
+ }}
+ ...
+ }
+
Acessing object attributes at runtime inside these functions is described in the
[advanced topics](5-advanced-topics.md#access-object-attributes-at-runtime) chapter.
----------------|--------------
by_ssh_address | **Optional.** The host's address. Defaults to "$address$" if the host's `address` attribute is set, "$address6$" otherwise.
by_ssh_port | **Optional.** The SSH port. Defaults to 22.
-by_ssh_command | **Optional.** The command that should be executed.
+by_ssh_command | **Required.** The command that should be executed. Can be an array if multiple arguments should be passed to `check_by_ssh`.
+by_ssh_arguments| **Optional.** A dictionary with arguments for the command. This works exactly like the 'arguments' dictionary for ordinary CheckCommands.
by_ssh_logname | **Optional.** The SSH username.
by_ssh_identity | **Optional.** The SSH identity.
by_ssh_quiet | **Optional.** Whether to suppress SSH warnings. Defaults to false.
arguments = {
"-H" = "$by_ssh_address$"
"-p" = "$by_ssh_port$"
- "-C" = "$by_ssh_command$"
+ "-C" = {{
+ var command = macro("$by_ssh_command$")
+ var arguments = macro("$by_ssh_arguments$")
+
+ if (typeof(command) == String && !arguments) {
+ return command
+ }
+
+ var escaped_args = []
+ for (arg in resolve_arguments(command, arguments)) {
+ escaped_args.add(escape_shell_arg(arg))
+ }
+ return escaped_args.join(" ")
+ }}
"-l" = "$by_ssh_logname$"
"-i" = "$by_ssh_identity$"
"-q" = {
#include "base/context.hpp"
#include "base/configobject.hpp"
#include "base/scriptframe.hpp"
+#include "base/convert.hpp"
+#include "base/exception.hpp"
#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
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;
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;
BOOST_FOREACH(const Value& arg, arr) {
/* Note: don't escape macros here. */
Value value = InternalResolveMacros(arg, resolvers, cr, missingMacro,
- EscapeCallback(), resolvedMacros, useResolvedMacros);
+ EscapeCallback(), resolvedMacros, useResolvedMacros, recursionLevel + 1);
if (value.IsObjectType<Array>())
resultArr->Add(Utility::Join(value, ';'));
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
/* Note: don't escape macros here. */
resultDict->Set(kv.first, InternalResolveMacros(kv.second, resolvers, cr, missingMacro,
- EscapeCallback(), resolvedMacros, useResolvedMacros));
+ EscapeCallback(), resolvedMacros, useResolvedMacros, recursionLevel + 1));
}
result = resultDict;
resolvedMacros, useResolvedMacros, recursionLevel);
}
+Value MacroProcessor::InternalResolveArgumentsShim(const std::vector<Value>& args, const ResolverList& resolvers,
+ const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros,
+ bool useResolvedMacros, int recursionLevel)
+{
+ 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);
+}
+
Value MacroProcessor::EvaluateFunction(const Function::Ptr& func, const ResolverList& resolvers,
const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel)
resolvers_this->Set("macro", new Function(boost::bind(&MacroProcessor::InternalResolveMacrosShim,
_1, boost::cref(resolvers), cr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros,
recursionLevel + 1)));
+ resolvers_this->Set("resolve_arguments", new Function(boost::bind(&MacroProcessor::InternalResolveArgumentsShim,
+ _1, boost::cref(resolvers), cr, resolvedMacros, useResolvedMacros,
+ recursionLevel + 1)));
ScriptFrame frame(resolvers_this);
return func->Invoke();
if (resolved_macro.IsObjectType<Function>()) {
resolved_macro = EvaluateFunction(resolved_macro, resolvers, cr, escapeFn,
- resolvedMacros, useResolvedMacros, recursionLevel);
+ resolvedMacros, useResolvedMacros, recursionLevel + 1);
}
if (!found) {
return true;
}
+
+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);
+ BOOST_FOREACH(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, NULL,
+ 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);
+ BOOST_FOREACH(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 << "': " << 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.push_back(arg);
+ }
+
+ std::sort(args.begin(), args.end());
+
+ Array::Ptr command_arr = resolvedCommand;
+ BOOST_FOREACH(const CommandArgument& arg, args) {
+
+ if (arg.AValue.IsObjectType<Dictionary>()) {
+ Log(LogWarning, "PluginUtility", "Tried to use dictionary in argument");
+ continue;
+ } else if (arg.AValue.IsObjectType<Array>()) {
+ bool first = true;
+ Array::Ptr arr = static_cast<Array::Ptr>(arg.AValue);
+
+ ObjectLock olock(arr);
+ BOOST_FOREACH(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;
+}
+
const CheckResult::Ptr& cr = CheckResult::Ptr(), String *missingMacro = NULL,
const EscapeCallback& escapeFn = EscapeCallback(),
const Dictionary::Ptr& resolvedMacros = Dictionary::Ptr(),
- bool useResolvedMacros = false);
+ bool useResolvedMacros = false, int recursionLevel = 0);
+
+ static Value ResolveArguments(const Value& command, const Dictionary::Ptr& arguments,
+ const MacroProcessor::ResolverList& resolvers, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel = 0);
static bool ValidateMacroString(const String& macro);
static Value InternalResolveMacrosShim(const std::vector<Value>& args, const ResolverList& resolvers,
const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel);
+ static Value InternalResolveArgumentsShim(const std::vector<Value>& args, const ResolverList& resolvers,
+ const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros,
+ bool useResolvedMacros, int recursionLevel);
static Value EvaluateFunction(const Function::Ptr& func, const ResolverList& resolvers,
const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel);
+ static void AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value,
+ bool add_key, bool add_value);
+ static Value EscapeMacroShellArg(const Value& value);
+
};
}
#include "base/convert.hpp"
#include "base/process.hpp"
#include "base/objectlock.hpp"
+#include "base/exception.hpp"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
using namespace icinga;
-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;
- }
-};
-
-void PluginUtility::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 PluginUtility::EscapeMacroShellArg(const Value& value)
-{
- String result;
-
- if (value.IsObjectType<Array>()) {
- Array::Ptr arr = value;
-
- ObjectLock olock(arr);
- BOOST_FOREACH(const Value& arg, arr) {
- if (result.GetLength() > 0)
- result += " ";
-
- result += Utility::EscapeShellArg(arg);
- }
- } else
- result = Utility::EscapeShellArg(value);
-
- return result;
-}
-
void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable,
const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros,
Dictionary::Ptr raw_arguments = commandObj->GetArguments();
Value command;
- if (!raw_arguments || raw_command.IsObjectType<Array>() || raw_command.IsObjectType<Function>())
- command = MacroProcessor::ResolveMacros(raw_command, macroResolvers, cr, NULL,
- PluginUtility::EscapeMacroShellArg, resolvedMacros, useResolvedMacros);
- else {
- Array::Ptr arr = new Array();
- arr->Add(raw_command);
- command = arr;
- }
- if (raw_arguments) {
- std::vector<CommandArgument> args;
-
- ObjectLock olock(raw_arguments);
- BOOST_FOREACH(const Dictionary::Pair& kv, raw_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, macroResolvers,
- cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
- useResolvedMacros);
-
- 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 << "': " << ex.what();
- continue;
- }
- }
-
- if (!value)
- continue;
- }
- }
- else
- argval = arginfo;
-
- if (argval.IsEmpty())
- arg.SkipValue = true;
-
- String missingMacro;
- arg.AValue = MacroProcessor::ResolveMacros(argval, macroResolvers,
- cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
- useResolvedMacros);
-
- if (!missingMacro.IsEmpty()) {
- if (required) {
- String message = "Non-optional macro '" + missingMacro + "' used in argument '" +
- arg.Key + "' is missing while executing command '" + commandObj->GetName() +
- "' for object '" + checkable->GetName() + "'";
- Log(LogWarning, "PluginUtility", message);
-
- if (callback) {
- ProcessResult pr;
- pr.PID = -1;
- pr.ExecutionStart = Utility::GetTime();
- pr.ExecutionEnd = pr.ExecutionStart;
- pr.ExitStatus = 3; /* Unknown */
- pr.Output = message;
- callback(Empty, pr);
- }
-
- return;
- }
-
- continue;
- }
-
- args.push_back(arg);
+ try {
+ command = MacroProcessor::ResolveArguments(raw_command, raw_arguments,
+ macroResolvers, cr, resolvedMacros, useResolvedMacros);
+ } catch (const std::exception& ex) {
+ String message = DiagnosticInformation(ex);
+
+ Log(LogWarning, "PluginUtility", message);
+
+ if (callback) {
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.ExecutionStart = Utility::GetTime();
+ pr.ExecutionEnd = pr.ExecutionStart;
+ pr.ExitStatus = 3; /* Unknown */
+ pr.Output = message;
+ callback(Empty, pr);
}
- std::sort(args.begin(), args.end());
-
- Array::Ptr command_arr = command;
- BOOST_FOREACH(const CommandArgument& arg, args) {
-
- if (arg.AValue.IsObjectType<Dictionary>()) {
- Log(LogWarning, "PluginUtility", "Tried to use dictionary in argument");
- continue;
- } else if (arg.AValue.IsObjectType<Array>()) {
- bool first = true;
- Array::Ptr arr = static_cast<Array::Ptr>(arg.AValue);
-
- ObjectLock olock(arr);
- BOOST_FOREACH(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;
}
Dictionary::Ptr envMacros = new Dictionary();
private:
PluginUtility(void);
-
- static void AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value, bool add_key, bool add_value);
- static Value EscapeMacroShellArg(const Value& value);
};
}