1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "base/scriptutils.hpp"
4 #include "base/function.hpp"
5 #include "base/scriptframe.hpp"
6 #include "base/exception.hpp"
7 #include "base/utility.hpp"
8 #include "base/convert.hpp"
9 #include "base/json.hpp"
10 #include "base/logger.hpp"
11 #include "base/objectlock.hpp"
12 #include "base/configtype.hpp"
13 #include "base/application.hpp"
14 #include "base/dependencygraph.hpp"
15 #include "base/initialize.hpp"
16 #include "base/namespace.hpp"
17 #include <boost/regex.hpp>
24 using namespace icinga;
26 REGISTER_SAFE_FUNCTION(System, regex, &ScriptUtils::Regex, "pattern:text:mode");
27 REGISTER_SAFE_FUNCTION(System, match, &ScriptUtils::Match, "pattern:text:mode");
28 REGISTER_SAFE_FUNCTION(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode");
29 REGISTER_SAFE_FUNCTION(System, len, &ScriptUtils::Len, "value");
30 REGISTER_SAFE_FUNCTION(System, union, &ScriptUtils::Union, "");
31 REGISTER_SAFE_FUNCTION(System, intersection, &ScriptUtils::Intersection, "");
32 REGISTER_FUNCTION(System, log, &ScriptUtils::Log, "severity:facility:value");
33 REGISTER_FUNCTION(System, range, &ScriptUtils::Range, "start:end:increment");
34 REGISTER_FUNCTION(System, exit, &Application::Exit, "status");
35 REGISTER_SAFE_FUNCTION(System, typeof, &ScriptUtils::TypeOf, "value");
36 REGISTER_SAFE_FUNCTION(System, keys, &ScriptUtils::Keys, "value");
37 REGISTER_SAFE_FUNCTION(System, random, &Utility::Random, "");
38 REGISTER_SAFE_FUNCTION(System, get_object, &ScriptUtils::GetObject, "type:name");
39 REGISTER_SAFE_FUNCTION(System, get_objects, &ScriptUtils::GetObjects, "type");
40 REGISTER_FUNCTION(System, assert, &ScriptUtils::Assert, "value");
41 REGISTER_SAFE_FUNCTION(System, string, &ScriptUtils::CastString, "value");
42 REGISTER_SAFE_FUNCTION(System, number, &ScriptUtils::CastNumber, "value");
43 REGISTER_SAFE_FUNCTION(System, bool, &ScriptUtils::CastBool, "value");
44 REGISTER_SAFE_FUNCTION(System, get_time, &Utility::GetTime, "");
45 REGISTER_SAFE_FUNCTION(System, basename, &Utility::BaseName, "path");
46 REGISTER_SAFE_FUNCTION(System, dirname, &Utility::DirName, "path");
47 REGISTER_SAFE_FUNCTION(System, getenv, &ScriptUtils::GetEnv, "value");
48 REGISTER_SAFE_FUNCTION(System, msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim, "component");
49 REGISTER_SAFE_FUNCTION(System, track_parents, &ScriptUtils::TrackParents, "child");
50 REGISTER_SAFE_FUNCTION(System, escape_shell_cmd, &Utility::EscapeShellCmd, "cmd");
51 REGISTER_SAFE_FUNCTION(System, escape_shell_arg, &Utility::EscapeShellArg, "arg");
53 REGISTER_SAFE_FUNCTION(System, escape_create_process_arg, &Utility::EscapeCreateProcessArg, "arg");
55 REGISTER_FUNCTION(System, ptr, &ScriptUtils::Ptr, "object");
56 REGISTER_FUNCTION(System, sleep, &Utility::Sleep, "interval");
57 REGISTER_FUNCTION(System, path_exists, &Utility::PathExists, "path");
58 REGISTER_FUNCTION(System, glob, &ScriptUtils::Glob, "pathspec:callback:type");
59 REGISTER_FUNCTION(System, glob_recursive, &ScriptUtils::GlobRecursive, "pathspec:callback:type");
61 INITIALIZE_ONCE(&ScriptUtils::StaticInitialize);
69 void ScriptUtils::StaticInitialize()
71 ScriptGlobal::Set("System.MatchAll", MatchAll, true);
72 ScriptGlobal::Set("System.MatchAny", MatchAny, true);
74 ScriptGlobal::Set("System.GlobFile", GlobFile, true);
75 ScriptGlobal::Set("System.GlobDirectory", GlobDirectory, true);
78 String ScriptUtils::CastString(const Value& value)
83 double ScriptUtils::CastNumber(const Value& value)
88 bool ScriptUtils::CastBool(const Value& value)
90 return value.ToBool();
93 bool ScriptUtils::Regex(const std::vector<Value>& args)
96 BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified for regex()."));
98 String pattern = args[0];
99 const Value& argTexts = args[1];
101 if (argTexts.IsObjectType<Dictionary>())
102 BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by regex()."));
107 mode = static_cast<MatchType>(static_cast<int>(args[2]));
111 boost::regex expr(pattern.GetData());
115 if (argTexts.IsObject())
119 ObjectLock olock(texts);
121 if (texts->GetLength() == 0)
124 for (const String& text : texts) {
128 res = boost::regex_search(text.GetData(), what, expr);
129 } catch (boost::exception&) {
130 res = false; /* exception means something went terribly wrong */
133 if (mode == MatchAny && res)
135 else if (mode == MatchAll && !res)
139 /* MatchAny: Nothing matched. MatchAll: Everything matched. */
140 return mode == MatchAll;
142 String text = argTexts;
144 return boost::regex_search(text.GetData(), what, expr);
148 bool ScriptUtils::Match(const std::vector<Value>& args)
151 BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified for match()."));
153 String pattern = args[0];
154 const Value& argTexts = args[1];
156 if (argTexts.IsObjectType<Dictionary>())
157 BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by match()."));
162 mode = static_cast<MatchType>(static_cast<int>(args[2]));
168 if (argTexts.IsObject())
172 ObjectLock olock(texts);
174 if (texts->GetLength() == 0)
177 for (const String& text : texts) {
178 bool res = Utility::Match(pattern, text);
180 if (mode == MatchAny && res)
182 else if (mode == MatchAll && !res)
186 /* MatchAny: Nothing matched. MatchAll: Everything matched. */
187 return mode == MatchAll;
189 String text = argTexts;
190 return Utility::Match(pattern, argTexts);
194 bool ScriptUtils::CidrMatch(const std::vector<Value>& args)
197 BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified for cidr_match()."));
199 String pattern = args[0];
200 const Value& argIps = args[1];
202 if (argIps.IsObjectType<Dictionary>())
203 BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by cidr_match()."));
208 mode = static_cast<MatchType>(static_cast<int>(args[2]));
214 if (argIps.IsObject())
218 ObjectLock olock(ips);
220 if (ips->GetLength() == 0)
223 for (const String& ip : ips) {
224 bool res = Utility::CidrMatch(pattern, ip);
226 if (mode == MatchAny && res)
228 else if (mode == MatchAll && !res)
232 /* MatchAny: Nothing matched. MatchAll: Everything matched. */
233 return mode == MatchAll;
236 return Utility::CidrMatch(pattern, ip);
240 double ScriptUtils::Len(const Value& value)
242 if (value.IsObjectType<Dictionary>()) {
243 Dictionary::Ptr dict = value;
244 return dict->GetLength();
245 } else if (value.IsObjectType<Array>()) {
246 Array::Ptr array = value;
247 return array->GetLength();
248 } else if (value.IsString()) {
249 return Convert::ToString(value).GetLength();
255 Array::Ptr ScriptUtils::Union(const std::vector<Value>& arguments)
257 std::set<Value> values;
259 for (const Value& varr : arguments) {
260 Array::Ptr arr = varr;
263 ObjectLock olock(arr);
264 for (const Value& value : arr) {
265 values.insert(value);
270 return Array::FromSet(values);
273 Array::Ptr ScriptUtils::Intersection(const std::vector<Value>& arguments)
275 if (arguments.size() == 0)
278 Array::Ptr result = new Array();
280 Array::Ptr arg1 = arguments[0];
285 Array::Ptr arr1 = arg1->ShallowClone();
287 for (std::vector<Value>::size_type i = 1; i < arguments.size(); i++) {
289 ObjectLock olock(arr1);
290 std::sort(arr1->Begin(), arr1->End());
293 Array::Ptr arg2 = arguments[i];
298 Array::Ptr arr2 = arg2->ShallowClone();
300 ObjectLock olock(arr2);
301 std::sort(arr2->Begin(), arr2->End());
304 result->Resize(std::max(arr1->GetLength(), arr2->GetLength()));
307 ObjectLock olock(arr1), xlock(arr2), ylock(result);
308 auto it = std::set_intersection(arr1->Begin(), arr1->End(), arr2->Begin(), arr2->End(), result->Begin());
309 len = it - result->Begin();
318 void ScriptUtils::Log(const std::vector<Value>& arguments)
320 if (arguments.size() != 1 && arguments.size() != 3)
321 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for log()"));
323 LogSeverity severity;
327 if (arguments.size() == 1) {
328 severity = LogInformation;
330 message = arguments[0];
332 auto sval = static_cast<int>(arguments[0]);
333 severity = static_cast<LogSeverity>(sval);
334 facility = arguments[1];
335 message = arguments[2];
338 if (message.IsString() || (!message.IsObjectType<Array>() && !message.IsObjectType<Dictionary>()))
339 ::Log(severity, facility, message);
341 ::Log(severity, facility, JsonEncode(message));
344 Array::Ptr ScriptUtils::Range(const std::vector<Value>& arguments)
346 double start, end, increment;
348 switch (arguments.size()) {
355 start = arguments[0];
360 start = arguments[0];
362 increment = arguments[2];
365 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for range()"));
370 if ((start < end && increment <= 0) ||
371 (start > end && increment >= 0))
374 for (double i = start; (increment > 0 ? i < end : i > end); i += increment)
377 return new Array(std::move(result));
380 Type::Ptr ScriptUtils::TypeOf(const Value& value)
382 return value.GetReflectionType();
385 Array::Ptr ScriptUtils::Keys(const Object::Ptr& obj)
389 Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(obj);
392 ObjectLock olock(dict);
393 for (const Dictionary::Pair& kv : dict) {
394 result.push_back(kv.first);
398 Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(obj);
401 ObjectLock olock(ns);
402 for (const Namespace::Pair& kv : ns) {
403 result.push_back(kv.first);
407 return new Array(std::move(result));
410 ConfigObject::Ptr ScriptUtils::GetObject(const Value& vtype, const String& name)
414 if (vtype.IsObjectType<Type>())
417 ptype = Type::GetByName(vtype);
419 auto *ctype = dynamic_cast<ConfigType *>(ptype.get());
424 return ctype->GetObject(name);
427 Array::Ptr ScriptUtils::GetObjects(const Type::Ptr& type)
430 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type: Must not be null"));
432 auto *ctype = dynamic_cast<ConfigType *>(type.get());
435 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type: Type must inherit from 'ConfigObject'"));
439 for (const ConfigObject::Ptr& object : ctype->GetObjects())
440 result.push_back(object);
442 return new Array(std::move(result));
445 void ScriptUtils::Assert(const Value& arg)
448 BOOST_THROW_EXCEPTION(std::runtime_error("Assertion failed"));
451 String ScriptUtils::MsiGetComponentPathShim(const String& component)
454 TCHAR productCode[39];
455 if (MsiGetProductCode(component.CStr(), productCode) != ERROR_SUCCESS)
458 DWORD szPath = sizeof(path);
460 MsiGetComponentPath(productCode, component.CStr(), path, &szPath);
467 Array::Ptr ScriptUtils::TrackParents(const Object::Ptr& child)
469 return Array::FromVector(DependencyGraph::GetParents(child));
472 double ScriptUtils::Ptr(const Object::Ptr& object)
474 return reinterpret_cast<intptr_t>(object.get());
477 static void GlobCallbackHelper(std::vector<String>& paths, const String& path)
479 paths.push_back(path);
482 Value ScriptUtils::Glob(const std::vector<Value>& args)
485 BOOST_THROW_EXCEPTION(std::invalid_argument("Path must be specified."));
487 String pathSpec = args[0];
488 int type = GlobFile | GlobDirectory;
493 std::vector<String> paths;
494 Utility::Glob(pathSpec, std::bind(&GlobCallbackHelper, std::ref(paths), _1), type);
496 return Array::FromVector(paths);
499 Value ScriptUtils::GlobRecursive(const std::vector<Value>& args)
502 BOOST_THROW_EXCEPTION(std::invalid_argument("Path and pattern must be specified."));
504 String path = args[0];
505 String pattern = args[1];
507 int type = GlobFile | GlobDirectory;
512 std::vector<String> paths;
513 Utility::GlobRecursive(path, pattern, std::bind(&GlobCallbackHelper, std::ref(paths), _1), type);
515 return Array::FromVector(paths);
518 String ScriptUtils::GetEnv(const String& key)
520 return Utility::GetFromEnvironment(key);