diff --git a/doc/18-library-reference.md b/doc/18-library-reference.md index 592178664c..f76798a3bb 100644 --- a/doc/18-library-reference.md +++ b/doc/18-library-reference.md @@ -587,6 +587,65 @@ true false ``` +### regex\_match + +Signature: + +``` +function regex_match(pattern, value) +``` + +If the regular expression `pattern` matches the string `value`, +returns an array with the first match inside the whole string +and the submatches of the match declared with parentheses. + +Otherwise returns null. + +Examples: + +``` +$ icinga2 console +Icinga 2 (version: v2.13.0) +<1> => regex_match("foo", "bar") +null +<2> => regex_match("foo", "foo") +[ "foo" ] +<3> => regex_match("f(o)o", "foo") +[ "foo", "o" ] +``` + +``` +object Host "example.com" { + check_command = "passive" + + vars.http_urls = [ + "http://monitor.example.com/icingaweb2", + "https://logs.example.com", + "http://cloud.example.com:5000" + ] +} + +apply Service "http-" for (url in host.vars.http_urls) { + check_command = "http" + + var match = regex_match({{{http(s?)://([^:/]+)((?::\d+)?)((?:/.*)?)}}}, url) + + if (match[1]) { + vars.http_ssl = true + } + + vars.http_address = match[2] + + if (match[3]) { + vars.http_port = match[3].substr(1) + } + + if (match[4]) { + vars.http_uri = match[4] + } +} +``` + ### sleep Signature: diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index 3611799ffc..63920e2eef 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #ifdef _WIN32 #include #endif /* _WIN32 */ @@ -25,6 +26,7 @@ using namespace icinga; REGISTER_SAFE_FUNCTION(System, regex, &ScriptUtils::Regex, "pattern:text:mode"); +REGISTER_SAFE_FUNCTION(System, regex_match, &ScriptUtils::RegexMatch, "pattern:text"); REGISTER_SAFE_FUNCTION(System, match, &ScriptUtils::Match, "pattern:text:mode"); REGISTER_SAFE_FUNCTION(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode"); REGISTER_SAFE_FUNCTION(System, len, &ScriptUtils::Len, "value"); @@ -148,6 +150,27 @@ bool ScriptUtils::Regex(const std::vector& args) } } +Array::Ptr ScriptUtils::RegexMatch(const String& pattern, const String& text) +{ + boost::regex expr (pattern.GetData()); + boost::smatch what; + + if (!boost::regex_search(text.GetData(), what, expr)) { + return nullptr; + } + + Array::Ptr res = new Array(); + ObjectLock oLock (res); + + res->Reserve(what.size()); + + for (auto& submatch : what) { + res->Add(String(submatch.str())); + } + + return std::move(res); +} + bool ScriptUtils::Match(const std::vector& args) { if (args.size() < 2) diff --git a/lib/base/scriptutils.hpp b/lib/base/scriptutils.hpp index 7bd3e8b9d3..0890f7cb01 100644 --- a/lib/base/scriptutils.hpp +++ b/lib/base/scriptutils.hpp @@ -24,6 +24,7 @@ class ScriptUtils static double CastNumber(const Value& value); static bool CastBool(const Value& value); static bool Regex(const std::vector& args); + static Array::Ptr RegexMatch(const String& pattern, const String& text); static bool Match(const std::vector& args); static bool CidrMatch(const std::vector& args); static double Len(const Value& value); diff --git a/test/config-ops.cpp b/test/config-ops.cpp index dfbef2530f..3116bae1b6 100644 --- a/test/config-ops.cpp +++ b/test/config-ops.cpp @@ -158,6 +158,12 @@ BOOST_AUTO_TEST_CASE(advanced) expr = ConfigCompiler::CompileText("", R"(regex("^Hello", "Hello World"))"); BOOST_CHECK(expr->Evaluate(frame).GetValue()); + expr = ConfigCompiler::CompileText("", R"(regex_match("f(o)o", "foo") == [ "foo", "o" ])"); + BOOST_CHECK(expr->Evaluate(frame).GetValue()); + + expr = ConfigCompiler::CompileText("", R"(var nl = regex_match("f(o)o", "bar"); nl == null && typeof(nl) == Object)"); + BOOST_CHECK(expr->Evaluate(frame).GetValue()); + expr = ConfigCompiler::CompileText("", "__boost_test()"); BOOST_CHECK_THROW(expr->Evaluate(frame).GetValue(), ScriptError);