]> granicus.if.org Git - icinga2/blob - lib/icinga/macroprocessor.cpp
Resolve runtime macros recursively in *_url, notes similar to vars.
[icinga2] / lib / icinga / macroprocessor.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "icinga/macroprocessor.h"
21 #include "icinga/macroresolver.h"
22 #include "base/utility.h"
23 #include "base/array.h"
24 #include "base/objectlock.h"
25 #include "base/logger_fwd.h"
26 #include "base/context.h"
27 #include "base/dynamicobject.h"
28 #include <boost/foreach.hpp>
29 #include <boost/algorithm/string/split.hpp>
30 #include <boost/algorithm/string/join.hpp>
31 #include <boost/algorithm/string/classification.hpp>
32
33 using namespace icinga;
34
35 Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers,
36         const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn)
37 {
38         Value result;
39
40         if (str.IsEmpty())
41                 return Empty;
42
43         if (str.IsScalar()) {
44                 result = InternalResolveMacros(str, resolvers, cr, escapeFn);
45         } else if (str.IsObjectType<Array>()) {
46                 Array::Ptr resultArr = make_shared<Array>();
47                 Array::Ptr arr = str;
48
49                 ObjectLock olock(arr);
50
51                 BOOST_FOREACH(const Value& arg, arr) {
52                         /* Note: don't escape macros here. */
53                         resultArr->Add(InternalResolveMacros(arg, resolvers, cr, EscapeCallback()));
54                 }
55
56                 result = resultArr;
57         } else {
58                 BOOST_THROW_EXCEPTION(std::invalid_argument("Command is not a string or array."));
59         }
60
61         return result;
62 }
63
64 bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resolvers,
65     const CheckResult::Ptr& cr, String *result, bool *recursive_macro)
66 {
67         CONTEXT("Resolving macro '" + macro + "'");
68
69         *recursive_macro = false;
70
71         std::vector<String> tokens;
72         boost::algorithm::split(tokens, macro, boost::is_any_of("."));
73
74         String objName;
75         if (tokens.size() > 1) {
76                 objName = tokens[0];
77                 tokens.erase(tokens.begin());
78         }
79
80         BOOST_FOREACH(const ResolverSpec& resolver, resolvers) {
81                 if (!objName.IsEmpty() && objName != resolver.first)
82                         continue;
83
84                 if (objName.IsEmpty()) {
85                         DynamicObject::Ptr dobj = dynamic_pointer_cast<DynamicObject>(resolver.second);
86
87                         if (dobj) {
88                                 Dictionary::Ptr vars = dobj->GetVars();
89
90                                 if (vars && vars->Contains(macro)) {
91                                         *result = vars->Get(macro);
92                                         *recursive_macro = true;
93                                         return true;
94                                 }
95                         }
96                 }
97
98                 MacroResolver::Ptr mresolver = dynamic_pointer_cast<MacroResolver>(resolver.second);
99
100                 if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result))
101                         return true;
102
103                 Value ref = resolver.second;
104                 bool valid = true;
105
106                 BOOST_FOREACH(const String& token, tokens) {
107                         if (ref.IsObjectType<Dictionary>()) {
108                                 Dictionary::Ptr dict = ref;
109                                 if (dict->Contains(token)) {
110                                         ref = dict->Get(token);
111                                         continue;
112                                 } else {
113                                         valid = false;
114                                         break;
115                                 }
116                         } else if (ref.IsObject()) {
117                                 Object::Ptr object = ref;
118
119                                 const Type *type = object->GetReflectionType();
120
121                                 if (!type) {
122                                         valid = false;
123                                         break;
124                                 }
125
126                                 int field = type->GetFieldId(token);
127
128                                 if (field == -1) {
129                                         valid = false;
130                                         break;
131                                 }
132
133                                 ref = object->GetField(field);
134                         }
135                 }
136
137                 if (valid) {
138                         if (tokens[0] == "vars" ||
139                             tokens[0] == "action_url" ||
140                             tokens[0] == "notes_url" ||
141                             tokens[0] == "notes")
142                                 *recursive_macro = true;
143
144                         *result = ref;
145                         return true;
146                 }
147         }
148
149         return false;
150 }
151
152 String MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
153         const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn, int recursionLevel)
154 {
155         CONTEXT("Resolving macros for string '" + str + "'");
156
157         if (recursionLevel > 15)
158                 BOOST_THROW_EXCEPTION(std::runtime_error("Infinite recursion detected while resolving macros"));
159
160         size_t offset, pos_first, pos_second;
161         offset = 0;
162
163         String result = str;
164         while ((pos_first = result.FindFirstOf("$", offset)) != String::NPos) {
165                 pos_second = result.FindFirstOf("$", pos_first + 1);
166
167                 if (pos_second == String::NPos)
168                         BOOST_THROW_EXCEPTION(std::runtime_error("Closing $ not found in macro format string."));
169
170                 String name = result.SubStr(pos_first + 1, pos_second - pos_first - 1);
171
172                 String resolved_macro;
173                 bool recursive_macro;
174                 bool found = ResolveMacro(name, resolvers, cr, &resolved_macro, &recursive_macro);
175
176                 /* $$ is an escape sequence for $. */
177                 if (name.IsEmpty()) {
178                         resolved_macro = "$";
179                         found = true;
180                 }
181
182                 if (!found)
183                         Log(LogWarning, "icinga", "Macro '" + name + "' is not defined.");
184
185                 /* recursively resolve macros in the macro if it was a user macro */
186                 if (recursive_macro)
187                         resolved_macro = InternalResolveMacros(resolved_macro, resolvers, cr, EscapeCallback(), recursionLevel + 1);
188
189                 if (escapeFn)
190                         resolved_macro = escapeFn(resolved_macro);
191
192                 result.Replace(pos_first, pos_second - pos_first + 1, resolved_macro);
193                 offset = pos_first + resolved_macro.GetLength() + 1;
194         }
195
196         return result;
197 }