]> granicus.if.org Git - icinga2/blob - lib/icinga/pluginutility.cpp
Fix set_if for non-numeric boolean values
[icinga2] / lib / icinga / pluginutility.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/pluginutility.hpp"
21 #include "icinga/macroprocessor.hpp"
22 #include "icinga/perfdatavalue.hpp"
23 #include "base/logger.hpp"
24 #include "base/utility.hpp"
25 #include "base/convert.hpp"
26 #include "base/process.hpp"
27 #include "base/objectlock.hpp"
28 #include <boost/algorithm/string/classification.hpp>
29 #include <boost/algorithm/string/split.hpp>
30 #include <boost/algorithm/string/trim.hpp>
31 #include <boost/foreach.hpp>
32
33 using namespace icinga;
34
35 struct CommandArgument
36 {
37         int Order;
38         bool SkipKey;
39         bool RepeatKey;
40         bool SkipValue;
41         String Key;
42         Value AValue;
43
44         CommandArgument(void)
45                 : Order(0), SkipKey(false), RepeatKey(true), SkipValue(false)
46         { }
47
48         bool operator<(const CommandArgument& rhs) const
49         {
50                 return Order < rhs.Order;
51         }
52 };
53
54 void PluginUtility::AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value, bool add_key, bool add_value)
55 {
56         if (add_key)
57                 args->Add(key);
58
59         if (add_value)
60                 args->Add(value);
61 }
62
63 Value PluginUtility::EscapeMacroShellArg(const Value& value)
64 {
65         String result;
66
67         if (value.IsObjectType<Array>()) {
68                 Array::Ptr arr = value;
69
70                 ObjectLock olock(arr);
71                 BOOST_FOREACH(const Value& arg, arr) {
72                         if (result.GetLength() > 0)
73                                 result += " ";
74
75                         result += Utility::EscapeShellArg(arg);
76                 }       
77         } else
78                 result = Utility::EscapeShellArg(value);
79
80         return result;
81 }
82
83 void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable,
84     const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers,
85     const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros,
86     const boost::function<void(const Value& commandLine, const ProcessResult&)>& callback)
87 {
88         Value raw_command = commandObj->GetCommandLine();
89         Dictionary::Ptr raw_arguments = commandObj->GetArguments();
90
91         Value command;
92         if (!raw_arguments || raw_command.IsObjectType<Array>())
93                 command = MacroProcessor::ResolveMacros(raw_command, macroResolvers, cr, NULL,
94                     PluginUtility::EscapeMacroShellArg, resolvedMacros, useResolvedMacros);
95         else {
96                 Array::Ptr arr = new Array();
97                 arr->Add(raw_command);
98                 command = arr;
99         }
100
101         if (raw_arguments) {
102                 std::vector<CommandArgument> args;
103
104                 ObjectLock olock(raw_arguments);
105                 BOOST_FOREACH(const Dictionary::Pair& kv, raw_arguments) {
106                         const Value& arginfo = kv.second;
107
108                         CommandArgument arg;
109                         arg.Key = kv.first;
110
111                         bool required = false;
112                         String argval;
113
114                         if (arginfo.IsObjectType<Dictionary>()) {
115                                 Dictionary::Ptr argdict = arginfo;
116                                 if (argdict->Contains("key"))
117                                         arg.Key = argdict->Get("key");
118                                 argval = argdict->Get("value");
119                                 if (argdict->Contains("required"))
120                                         required = argdict->Get("required");
121                                 arg.SkipKey = argdict->Get("skip_key");
122                                 if (argdict->Contains("repeat_key"))
123                                         arg.RepeatKey = argdict->Get("repeat_key");
124                                 arg.Order = argdict->Get("order");
125
126                                 String set_if = argdict->Get("set_if");
127
128                                 if (!set_if.IsEmpty()) {
129                                         String missingMacro;
130                                         String set_if_resolved = MacroProcessor::ResolveMacros(set_if, macroResolvers,
131                                             cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
132                                             useResolvedMacros);
133
134                                         if (!missingMacro.IsEmpty())
135                                                 continue;
136
137                                         int value;
138
139                                         if (set_if_resolved == "true")
140                                                 value = 1;
141                                         else if (set_if_resolved == "false")
142                                                 value = 0;
143                                         else {
144                                                 try {
145                                                         value = Convert::ToLong(set_if_resolved);
146                                                 } catch (const std::exception& ex) {
147                                                         /* tried to convert a string */
148                                                         Log(LogWarning, "PluginUtility")
149                                                             << "Error evaluating set_if value '" << set_if_resolved << "': " << ex.what();
150                                                         continue;
151                                                 }
152                                         }
153
154                                         if (!value)
155                                                 continue;
156                                 }
157                         }
158                         else
159                                 argval = arginfo;
160
161                         if (argval.IsEmpty())
162                                 arg.SkipValue = true;
163
164                         String missingMacro;
165                         arg.AValue = MacroProcessor::ResolveMacros(argval, macroResolvers,
166                             cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
167                             useResolvedMacros);
168
169                         if (!missingMacro.IsEmpty()) {
170                                 if (required) {
171                                         String message = "Non-optional macro '" + missingMacro + "' used in argument '" +
172                                             arg.Key + "' is missing while executing command '" + commandObj->GetName() +
173                                             "' for object '" + checkable->GetName() + "'";
174                                         Log(LogWarning, "PluginUtility", message);
175
176                                         if (callback) {
177                                                 ProcessResult pr;
178                                                 pr.PID = -1;
179                                                 pr.ExecutionStart = Utility::GetTime();
180                                                 pr.ExecutionStart = pr.ExecutionStart;
181                                                 pr.ExitStatus = 3; /* Unknown */
182                                                 pr.Output = message;
183                                                 callback(Empty, pr);
184                                         }
185
186                                         return;
187                                 }
188
189                                 continue;
190                         }
191
192                         args.push_back(arg);
193                 }
194
195                 std::sort(args.begin(), args.end());
196
197                 Array::Ptr command_arr = command;
198                 BOOST_FOREACH(const CommandArgument& arg, args) {
199                         Array::Ptr arr;
200
201                         if (arg.AValue.IsString())
202                                 AddArgumentHelper(command_arr, arg.Key, arg.AValue, !arg.SkipKey, !arg.SkipValue);
203                         else if (arg.AValue.IsObjectType<Array>())
204                                 arr = static_cast<Array::Ptr>(arg.AValue);
205                         else
206                                 continue;
207
208                         if (arr) {
209                                 bool first = true;
210
211                                 ObjectLock olock(arr);
212                                 BOOST_FOREACH(const Value& value, arr) {
213                                         bool add_key;
214
215                                         if (first) {
216                                                 first = false;
217                                                 add_key = !arg.SkipKey;
218                                         } else
219                                                 add_key = !arg.SkipKey && arg.RepeatKey;
220                                                 
221
222                                         AddArgumentHelper(command_arr, arg.Key, value, add_key, !arg.SkipValue);
223                                 }
224                         }
225                 }
226         }
227
228         Dictionary::Ptr envMacros = new Dictionary();
229
230         Dictionary::Ptr env = commandObj->GetEnv();
231
232         if (env) {
233                 ObjectLock olock(env);
234                 BOOST_FOREACH(const Dictionary::Pair& kv, env) {
235                         String name = kv.second;
236
237                         Value value = MacroProcessor::ResolveMacros(name, macroResolvers, cr,
238                             NULL, MacroProcessor::EscapeCallback(), resolvedMacros,
239                             useResolvedMacros);
240
241                         if (value.IsObjectType<Array>())
242                                 value = Utility::Join(value, ';');
243
244                         envMacros->Set(kv.first, value);
245                 }
246         }
247
248         if (resolvedMacros && !useResolvedMacros)
249                 return;
250
251         Process::Ptr process = new Process(Process::PrepareCommand(command), envMacros);
252         process->SetTimeout(commandObj->GetTimeout());
253         process->Run(boost::bind(callback, command, _1));
254 }
255
256 ServiceState PluginUtility::ExitStatusToState(int exitStatus)
257 {
258         switch (exitStatus) {
259                 case 0:
260                         return ServiceOK;
261                 case 1:
262                         return ServiceWarning;
263                 case 2:
264                         return ServiceCritical;
265                 default:
266                         return ServiceUnknown;
267         }
268 }
269
270 std::pair<String, String> PluginUtility::ParseCheckOutput(const String& output)
271 {
272         String text;
273         String perfdata;
274
275         std::vector<String> lines;
276         boost::algorithm::split(lines, output, boost::is_any_of("\r\n"));
277
278         BOOST_FOREACH (const String& line, lines) {
279                 size_t delim = line.FindFirstOf("|");
280
281                 if (!text.IsEmpty())
282                         text += "\n";
283
284                 if (delim != String::NPos) {
285                         text += line.SubStr(0, delim);
286
287                         if (!perfdata.IsEmpty())
288                                 perfdata += " ";
289
290                         perfdata += line.SubStr(delim + 1, line.GetLength());
291                 } else {
292                         text += line;
293                 }
294         }
295
296         boost::algorithm::trim(perfdata);
297
298         return std::make_pair(text, perfdata);
299 }
300
301 Array::Ptr PluginUtility::SplitPerfdata(const String& perfdata)
302 {
303         Array::Ptr result = new Array();
304
305         size_t begin = 0;
306         String multi_prefix;
307
308         for (;;) {
309                 size_t eqp = perfdata.FindFirstOf('=', begin);
310
311                 if (eqp == String::NPos)
312                         break;
313
314                 String label = perfdata.SubStr(begin, eqp - begin);
315
316                 if (label.GetLength() > 2 && label[0] == '\'' && label[label.GetLength() - 1] == '\'')
317                         label = label.SubStr(1, label.GetLength() - 2);
318
319                 size_t multi_index = label.RFind("::");
320
321                 if (multi_index != String::NPos)
322                         multi_prefix = "";
323
324                 size_t spq = perfdata.FindFirstOf(' ', eqp);
325
326                 if (spq == String::NPos)
327                         spq = perfdata.GetLength();
328
329                 String value = perfdata.SubStr(eqp + 1, spq - eqp - 1);
330
331                 if (!multi_prefix.IsEmpty())
332                         label = multi_prefix + "::" + label;
333
334                 String pdv;
335                 if (label.FindFirstOf(" ") != String::NPos)
336                         pdv = "'" + label + "'=" + value;
337                 else
338                         pdv = label + "=" + value;
339
340                 result->Add(pdv);
341
342                 if (multi_index != String::NPos)
343                         multi_prefix = label.SubStr(0, multi_index);
344
345                 begin = spq + 1;
346         }
347
348         return result;
349 }
350
351 String PluginUtility::FormatPerfdata(const Array::Ptr& perfdata)
352 {
353         if (!perfdata)
354                 return "";
355
356         std::ostringstream result;
357
358         ObjectLock olock(perfdata);
359
360         bool first = true;
361         BOOST_FOREACH(const Value& pdv, perfdata) {
362                 if (!first)
363                         result << " ";
364                 else
365                         first = false;
366
367                 if (pdv.IsObjectType<PerfdataValue>())
368                         result << static_cast<PerfdataValue::Ptr>(pdv)->Format();
369                 else
370                         result << pdv;
371         }
372
373         return result.str();
374 }