diff --git a/doc/18-library-reference.md b/doc/18-library-reference.md index 0a3f91225..4c82ab4c1 100644 --- a/doc/18-library-reference.md +++ b/doc/18-library-reference.md @@ -16,7 +16,10 @@ Signature: function regex(pattern, text) -Returns true if the regular expression matches the text, false otherwise. +Returns true if the regular expression matches the text, false otherwise. The mode argument is +optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny +(in which case at least one element has to match). The default mode is MatchAll. + **Tip**: In case you are looking for regular expression tests try [regex101](https://regex101.com). Example: @@ -34,9 +37,11 @@ Example: Signature: - function match(pattern, text) + function match(pattern, text, mode) -Returns true if the wildcard (`?*`) pattern matches the text, false otherwise. +Returns true if the wildcard (`?*`) pattern matches the text, false otherwise. The mode argument is +optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny +(in which case at least one element has to match). The default mode is MatchAll. Example: @@ -59,7 +64,10 @@ Signature: Returns true if the CIDR pattern matches the IP address, false otherwise. IPv4 addresses are converted to IPv4-mapped IPv6 addresses before being -matched against the pattern. +matched against the pattern. The mode argument is optional and can be +either MatchAll (in which case all elements for an array have to match) or MatchAny +(in which case at least one element has to match). The default mode is MatchAll. + Example: @@ -1148,6 +1156,24 @@ Signature: Returns a copy of the array containing only the elements for which `func(element)` is true. +### Array#any + +Signature: + + function any(func); + +Returns true if the array contains at least one element for which `func(element)` +is true, false otherwise. + +### Array#all + +Signature: + + function all(func); + +Returns true if the array contains only elements for which `func(element)` +is true, false otherwise. + ### Array#unique Signature: @@ -1220,6 +1246,14 @@ Signature: Returns a list of keys for all items that are currently in the dictionary. +### Dictionary#values + +Signature: + + function values(); + +Returns a list of values for all items that are currently in the dictionary. + ## Function type Inherits methods from the [Object type](18-library-reference.md#object-type). diff --git a/lib/base/array-script.cpp b/lib/base/array-script.cpp index 03ae2cae8..568037d9e 100644 --- a/lib/base/array-script.cpp +++ b/lib/base/array-script.cpp @@ -207,6 +207,43 @@ static Array::Ptr ArrayFilter(const Function::Ptr& function) return result; } +static bool ArrayAny(const Function::Ptr& function) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Array::Ptr self = static_cast(vframe->Self); + + if (vframe->Sandboxed && !function->IsSideEffectFree()) + BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free.")); + + ObjectLock olock(self); + for (const Value& item : self) { + std::vector args; + args.push_back(item); + if (function->Invoke(args)) + return true; + } + + return false; +} + +static bool ArrayAll(const Function::Ptr& function) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Array::Ptr self = static_cast(vframe->Self); + + if (vframe->Sandboxed && !function->IsSideEffectFree()) + BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free.")); + + ObjectLock olock(self); + for (const Value& item : self) { + std::vector args; + args.push_back(item); + if (!function->Invoke(args)) + return false; + } + + return true; +} static Array::Ptr ArrayUnique(void) { ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); @@ -242,6 +279,8 @@ Object::Ptr Array::GetPrototype(void) prototype->Set("map", new Function("Array#map", WrapFunction(ArrayMap), { "func" }, true)); prototype->Set("reduce", new Function("Array#reduce", WrapFunction(ArrayReduce), { "reduce" }, true)); prototype->Set("filter", new Function("Array#filter", WrapFunction(ArrayFilter), { "func" }, true)); + prototype->Set("any", new Function("Array#any", WrapFunction(ArrayAny), { "func" }, true)); + prototype->Set("all", new Function("Array#all", WrapFunction(ArrayAll), { "func" }, true)); prototype->Set("unique", new Function("Array#unique", WrapFunction(ArrayUnique), {}, true)); } diff --git a/lib/base/dictionary-script.cpp b/lib/base/dictionary-script.cpp index 908b719b2..66aa26cbd 100644 --- a/lib/base/dictionary-script.cpp +++ b/lib/base/dictionary-script.cpp @@ -79,6 +79,18 @@ static Array::Ptr DictionaryKeys(void) return keys; } +static Array::Ptr DictionaryValues(void) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Dictionary::Ptr self = static_cast(vframe->Self); + Array::Ptr keys = new Array(); + ObjectLock olock(self); + for (const Dictionary::Pair& kv : self) { + keys->Add(kv.second); + } + return keys; +} + Object::Ptr Dictionary::GetPrototype(void) { static Dictionary::Ptr prototype; @@ -92,6 +104,7 @@ Object::Ptr Dictionary::GetPrototype(void) prototype->Set("contains", new Function("Dictionary#contains", WrapFunction(DictionaryContains), { "key" }, true)); prototype->Set("shallow_clone", new Function("Dictionary#shallow_clone", WrapFunction(DictionaryShallowClone), {}, true)); prototype->Set("keys", new Function("Dictionary#keys", WrapFunction(DictionaryKeys), {}, true)); + prototype->Set("values", new Function("Dictionary#values", WrapFunction(DictionaryValues), {}, true)); } return prototype; diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index 9cbdc1c5f..ef70eccbf 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -27,6 +27,7 @@ #include "base/configtype.hpp" #include "base/application.hpp" #include "base/dependencygraph.hpp" +#include "base/initialize.hpp" #include #include #include @@ -36,9 +37,9 @@ using namespace icinga; -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &Utility::Match, "pattern:text"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &Utility::CidrMatch, "pattern:ip"); +REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text:mode"); +REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &ScriptUtils::Match, "pattern:text:mode"); +REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode"); REGISTER_SAFE_SCRIPTFUNCTION_NS(System, len, &ScriptUtils::Len, "value"); REGISTER_SAFE_SCRIPTFUNCTION_NS(System, union, &ScriptUtils::Union, ""); REGISTER_SAFE_SCRIPTFUNCTION_NS(System, intersection, &ScriptUtils::Intersection, ""); @@ -67,6 +68,20 @@ REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_create_process_arg, &Utility::Esc REGISTER_SCRIPTFUNCTION_NS(System, ptr, &ScriptUtils::Ptr, "object"); REGISTER_SCRIPTFUNCTION_NS(System, sleep, &Utility::Sleep, "interval"); +INITIALIZE_ONCE(&ScriptUtils::StaticInitialize); + +enum MatchType +{ + MatchAll, + MatchAny +}; + +void ScriptUtils::StaticInitialize(void) +{ + ScriptGlobal::Set("MatchAll", MatchAll); + ScriptGlobal::Set("MatchAny", MatchAny); +} + String ScriptUtils::CastString(const Value& value) { return value; @@ -81,18 +96,120 @@ bool ScriptUtils::CastBool(const Value& value) { return value.ToBool(); } -bool ScriptUtils::Regex(const String& pattern, const String& text) + +bool ScriptUtils::Regex(const std::vector& args) { - bool res = false; - try { - boost::regex expr(pattern.GetData()); - boost::smatch what; - res = boost::regex_search(text.GetData(), what, expr); - } catch (boost::exception&) { - res = false; /* exception means something went terribly wrong */ + if (args.size() < 2) + BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified.")); + + Array::Ptr texts = new Array(); + + String pattern = args[0]; + Value argTexts = args[1]; + MatchType mode; + + if (args.size() > 2) + mode = static_cast(static_cast(args[2])); + else + mode = MatchAll; + + if (argTexts.IsObjectType()) + texts = argTexts; + else { + texts = new Array(); + texts->Add(argTexts); } - return res; + ObjectLock olock(texts); + for (const String& text : texts) { + bool res = false; + try { + boost::regex expr(pattern.GetData()); + boost::smatch what; + res = boost::regex_search(text.GetData(), what, expr); + } catch (boost::exception&) { + res = false; /* exception means something went terribly wrong */ + } + + if (mode == MatchAny && res) + return true; + else if (mode == MatchAll && !res) + return false; + } + + return mode == MatchAll; +} + +bool ScriptUtils::Match(const std::vector& args) +{ + if (args.size() < 2) + BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified.")); + + Array::Ptr texts = new Array(); + + String pattern = args[0]; + Value argTexts = args[1]; + MatchType mode; + + if (args.size() > 2) + mode = static_cast(static_cast(args[2])); + else + mode = MatchAll; + + if (argTexts.IsObjectType()) + texts = argTexts; + else { + texts = new Array(); + texts->Add(argTexts); + } + + ObjectLock olock(texts); + for (const String& text : texts) { + bool res = Utility::Match(pattern, text); + + if (mode == MatchAny && res) + return true; + else if (mode == MatchAll && !res) + return false; + } + + return mode == MatchAll; +} + +bool ScriptUtils::CidrMatch(const std::vector& args) +{ + if (args.size() < 2) + BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified.")); + + Array::Ptr ips = new Array(); + + String pattern = args[0]; + Value argIps = args[1]; + MatchType mode; + + if (args.size() > 2) + mode = static_cast(static_cast(args[2])); + else + mode = MatchAll; + + if (argIps.IsObjectType()) + ips = argIps; + else { + ips = new Array(); + ips->Add(argIps); + } + + ObjectLock olock(ips); + for (const String& ip : ips) { + bool res = Utility::CidrMatch(pattern, ip); + + if (mode == MatchAny && res) + return true; + else if (mode == MatchAll && !res) + return false; + } + + return mode == MatchAll; } double ScriptUtils::Len(const Value& value) diff --git a/lib/base/scriptutils.hpp b/lib/base/scriptutils.hpp index fd4333241..e605be5fa 100644 --- a/lib/base/scriptutils.hpp +++ b/lib/base/scriptutils.hpp @@ -36,10 +36,13 @@ namespace icinga class I2_BASE_API ScriptUtils { public: + static void StaticInitialize(void); static String CastString(const Value& value); static double CastNumber(const Value& value); static bool CastBool(const Value& value); - static bool Regex(const String& pattern, const String& text); + static bool Regex(const std::vector& args); + static bool Match(const std::vector& args); + static bool CidrMatch(const std::vector& args); static double Len(const Value& value); static Array::Ptr Union(const std::vector& arguments); static Array::Ptr Intersection(const std::vector& arguments);