]> granicus.if.org Git - icinga2/blob - lib/base/scriptutils.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / scriptutils.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
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>
18 #include <algorithm>
19 #include <set>
20 #ifdef _WIN32
21 #include <msi.h>
22 #endif /* _WIN32 */
23
24 using namespace icinga;
25
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");
52 #ifdef _WIN32
53 REGISTER_SAFE_FUNCTION(System, escape_create_process_arg, &Utility::EscapeCreateProcessArg, "arg");
54 #endif /* _WIN32 */
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");
60
61 INITIALIZE_ONCE(&ScriptUtils::StaticInitialize);
62
63 enum MatchType
64 {
65         MatchAll,
66         MatchAny
67 };
68
69 void ScriptUtils::StaticInitialize()
70 {
71         ScriptGlobal::Set("System.MatchAll", MatchAll, true);
72         ScriptGlobal::Set("System.MatchAny", MatchAny, true);
73
74         ScriptGlobal::Set("System.GlobFile", GlobFile, true);
75         ScriptGlobal::Set("System.GlobDirectory", GlobDirectory, true);
76 }
77
78 String ScriptUtils::CastString(const Value& value)
79 {
80         return value;
81 }
82
83 double ScriptUtils::CastNumber(const Value& value)
84 {
85         return value;
86 }
87
88 bool ScriptUtils::CastBool(const Value& value)
89 {
90         return value.ToBool();
91 }
92
93 bool ScriptUtils::Regex(const std::vector<Value>& args)
94 {
95         if (args.size() < 2)
96                 BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified for regex()."));
97
98         String pattern = args[0];
99         const Value& argTexts = args[1];
100
101         if (argTexts.IsObjectType<Dictionary>())
102                 BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by regex()."));
103
104         MatchType mode;
105
106         if (args.size() > 2)
107                 mode = static_cast<MatchType>(static_cast<int>(args[2]));
108         else
109                 mode = MatchAll;
110
111         boost::regex expr(pattern.GetData());
112
113         Array::Ptr texts;
114
115         if (argTexts.IsObject())
116                 texts = argTexts;
117
118         if (texts) {
119                 ObjectLock olock(texts);
120
121                 if (texts->GetLength() == 0)
122                         return false;
123
124                 for (const String& text : texts) {
125                         bool res = false;
126                         try {
127                                 boost::smatch what;
128                                 res = boost::regex_search(text.GetData(), what, expr);
129                         } catch (boost::exception&) {
130                                 res = false; /* exception means something went terribly wrong */
131                         }
132
133                         if (mode == MatchAny && res)
134                                 return true;
135                         else if (mode == MatchAll && !res)
136                                 return false;
137                 }
138
139                 /* MatchAny: Nothing matched. MatchAll: Everything matched. */
140                 return mode == MatchAll;
141         } else {
142                 String text = argTexts;
143                 boost::smatch what;
144                 return boost::regex_search(text.GetData(), what, expr);
145         }
146 }
147
148 bool ScriptUtils::Match(const std::vector<Value>& args)
149 {
150         if (args.size() < 2)
151                 BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified for match()."));
152
153         String pattern = args[0];
154         const Value& argTexts = args[1];
155
156         if (argTexts.IsObjectType<Dictionary>())
157                 BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by match()."));
158
159         MatchType mode;
160
161         if (args.size() > 2)
162                 mode = static_cast<MatchType>(static_cast<int>(args[2]));
163         else
164                 mode = MatchAll;
165
166         Array::Ptr texts;
167
168         if (argTexts.IsObject())
169                 texts = argTexts;
170
171         if (texts) {
172                 ObjectLock olock(texts);
173
174                 if (texts->GetLength() == 0)
175                         return false;
176
177                 for (const String& text : texts) {
178                         bool res = Utility::Match(pattern, text);
179
180                         if (mode == MatchAny && res)
181                                 return true;
182                         else if (mode == MatchAll && !res)
183                                 return false;
184                 }
185
186                 /* MatchAny: Nothing matched. MatchAll: Everything matched. */
187                 return mode == MatchAll;
188         } else {
189                 String text = argTexts;
190                 return Utility::Match(pattern, argTexts);
191         }
192 }
193
194 bool ScriptUtils::CidrMatch(const std::vector<Value>& args)
195 {
196         if (args.size() < 2)
197                 BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified for cidr_match()."));
198
199         String pattern = args[0];
200         const Value& argIps = args[1];
201
202         if (argIps.IsObjectType<Dictionary>())
203                 BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by cidr_match()."));
204
205         MatchType mode;
206
207         if (args.size() > 2)
208                 mode = static_cast<MatchType>(static_cast<int>(args[2]));
209         else
210                 mode = MatchAll;
211
212         Array::Ptr ips;
213
214         if (argIps.IsObject())
215                 ips = argIps;
216
217         if (ips) {
218                 ObjectLock olock(ips);
219
220                 if (ips->GetLength() == 0)
221                         return false;
222
223                 for (const String& ip : ips) {
224                         bool res = Utility::CidrMatch(pattern, ip);
225
226                         if (mode == MatchAny && res)
227                                 return true;
228                         else if (mode == MatchAll && !res)
229                                 return false;
230                 }
231
232                 /* MatchAny: Nothing matched. MatchAll: Everything matched. */
233                 return mode == MatchAll;
234         } else {
235                 String ip = argIps;
236                 return Utility::CidrMatch(pattern, ip);
237         }
238 }
239
240 double ScriptUtils::Len(const Value& value)
241 {
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();
250         } else {
251                 return 0;
252         }
253 }
254
255 Array::Ptr ScriptUtils::Union(const std::vector<Value>& arguments)
256 {
257         std::set<Value> values;
258
259         for (const Value& varr : arguments) {
260                 Array::Ptr arr = varr;
261
262                 if (arr) {
263                         ObjectLock olock(arr);
264                         for (const Value& value : arr) {
265                                 values.insert(value);
266                         }
267                 }
268         }
269
270         return Array::FromSet(values);
271 }
272
273 Array::Ptr ScriptUtils::Intersection(const std::vector<Value>& arguments)
274 {
275         if (arguments.size() == 0)
276                 return new Array();
277
278         Array::Ptr result = new Array();
279
280         Array::Ptr arg1 = arguments[0];
281
282         if (!arg1)
283                 return result;
284
285         Array::Ptr arr1 = arg1->ShallowClone();
286
287         for (std::vector<Value>::size_type i = 1; i < arguments.size(); i++) {
288                 {
289                         ObjectLock olock(arr1);
290                         std::sort(arr1->Begin(), arr1->End());
291                 }
292
293                 Array::Ptr arg2 = arguments[i];
294
295                 if (!arg2)
296                         return result;
297
298                 Array::Ptr arr2 = arg2->ShallowClone();
299                 {
300                         ObjectLock olock(arr2);
301                         std::sort(arr2->Begin(), arr2->End());
302                 }
303
304                 result->Resize(std::max(arr1->GetLength(), arr2->GetLength()));
305                 Array::SizeType len;
306                 {
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();
310                 }
311                 result->Resize(len);
312                 arr1 = result;
313         }
314
315         return result;
316 }
317
318 void ScriptUtils::Log(const std::vector<Value>& arguments)
319 {
320         if (arguments.size() != 1 && arguments.size() != 3)
321                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for log()"));
322
323         LogSeverity severity;
324         String facility;
325         Value message;
326
327         if (arguments.size() == 1) {
328                 severity = LogInformation;
329                 facility = "config";
330                 message = arguments[0];
331         } else {
332                 auto sval = static_cast<int>(arguments[0]);
333                 severity = static_cast<LogSeverity>(sval);
334                 facility = arguments[1];
335                 message = arguments[2];
336         }
337
338         if (message.IsString() || (!message.IsObjectType<Array>() && !message.IsObjectType<Dictionary>()))
339                 ::Log(severity, facility, message);
340         else
341                 ::Log(severity, facility, JsonEncode(message));
342 }
343
344 Array::Ptr ScriptUtils::Range(const std::vector<Value>& arguments)
345 {
346         double start, end, increment;
347
348         switch (arguments.size()) {
349                 case 1:
350                         start = 0;
351                         end = arguments[0];
352                         increment = 1;
353                         break;
354                 case 2:
355                         start = arguments[0];
356                         end = arguments[1];
357                         increment = 1;
358                         break;
359                 case 3:
360                         start = arguments[0];
361                         end = arguments[1];
362                         increment = arguments[2];
363                         break;
364                 default:
365                         BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for range()"));
366         }
367
368         ArrayData result;
369
370         if ((start < end && increment <= 0) ||
371                 (start > end && increment >= 0))
372                 return new Array();
373
374         for (double i = start; (increment > 0 ? i < end : i > end); i += increment)
375                 result.push_back(i);
376
377         return new Array(std::move(result));
378 }
379
380 Type::Ptr ScriptUtils::TypeOf(const Value& value)
381 {
382         return value.GetReflectionType();
383 }
384
385 Array::Ptr ScriptUtils::Keys(const Object::Ptr& obj)
386 {
387         ArrayData result;
388
389         Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(obj);
390
391         if (dict) {
392                 ObjectLock olock(dict);
393                 for (const Dictionary::Pair& kv : dict) {
394                         result.push_back(kv.first);
395                 }
396         }
397
398         Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(obj);
399
400         if (ns) {
401                 ObjectLock olock(ns);
402                 for (const Namespace::Pair& kv : ns) {
403                         result.push_back(kv.first);
404                 }
405         }
406
407         return new Array(std::move(result));
408 }
409
410 ConfigObject::Ptr ScriptUtils::GetObject(const Value& vtype, const String& name)
411 {
412         Type::Ptr ptype;
413
414         if (vtype.IsObjectType<Type>())
415                 ptype = vtype;
416         else
417                 ptype = Type::GetByName(vtype);
418
419         auto *ctype = dynamic_cast<ConfigType *>(ptype.get());
420
421         if (!ctype)
422                 return nullptr;
423
424         return ctype->GetObject(name);
425 }
426
427 Array::Ptr ScriptUtils::GetObjects(const Type::Ptr& type)
428 {
429         if (!type)
430                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type: Must not be null"));
431
432         auto *ctype = dynamic_cast<ConfigType *>(type.get());
433
434         if (!ctype)
435                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type: Type must inherit from 'ConfigObject'"));
436
437         ArrayData result;
438
439         for (const ConfigObject::Ptr& object : ctype->GetObjects())
440                 result.push_back(object);
441
442         return new Array(std::move(result));
443 }
444
445 void ScriptUtils::Assert(const Value& arg)
446 {
447         if (!arg.ToBool())
448                 BOOST_THROW_EXCEPTION(std::runtime_error("Assertion failed"));
449 }
450
451 String ScriptUtils::MsiGetComponentPathShim(const String& component)
452 {
453 #ifdef _WIN32
454         TCHAR productCode[39];
455         if (MsiGetProductCode(component.CStr(), productCode) != ERROR_SUCCESS)
456                 return "";
457         TCHAR path[2048];
458         DWORD szPath = sizeof(path);
459         path[0] = '\0';
460         MsiGetComponentPath(productCode, component.CStr(), path, &szPath);
461         return path;
462 #else /* _WIN32 */
463         return String();
464 #endif /* _WIN32 */
465 }
466
467 Array::Ptr ScriptUtils::TrackParents(const Object::Ptr& child)
468 {
469         return Array::FromVector(DependencyGraph::GetParents(child));
470 }
471
472 double ScriptUtils::Ptr(const Object::Ptr& object)
473 {
474         return reinterpret_cast<intptr_t>(object.get());
475 }
476
477 static void GlobCallbackHelper(std::vector<String>& paths, const String& path)
478 {
479         paths.push_back(path);
480 }
481
482 Value ScriptUtils::Glob(const std::vector<Value>& args)
483 {
484         if (args.size() < 1)
485                 BOOST_THROW_EXCEPTION(std::invalid_argument("Path must be specified."));
486
487         String pathSpec = args[0];
488         int type = GlobFile | GlobDirectory;
489
490         if (args.size() > 1)
491                 type = args[1];
492
493         std::vector<String> paths;
494         Utility::Glob(pathSpec, std::bind(&GlobCallbackHelper, std::ref(paths), _1), type);
495
496         return Array::FromVector(paths);
497 }
498
499 Value ScriptUtils::GlobRecursive(const std::vector<Value>& args)
500 {
501         if (args.size() < 2)
502                 BOOST_THROW_EXCEPTION(std::invalid_argument("Path and pattern must be specified."));
503
504         String path = args[0];
505         String pattern = args[1];
506
507         int type = GlobFile | GlobDirectory;
508
509         if (args.size() > 2)
510                 type = args[2];
511
512         std::vector<String> paths;
513         Utility::GlobRecursive(path, pattern, std::bind(&GlobCallbackHelper, std::ref(paths), _1), type);
514
515         return Array::FromVector(paths);
516 }
517
518 String ScriptUtils::GetEnv(const String& key)
519 {
520         return Utility::GetFromEnvironment(key);
521 }