]> granicus.if.org Git - icinga2/blob - lib/icinga/pluginutility.cpp
Fix missing command arguments
[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
200                         if (arg.AValue.IsObjectType<Dictionary>()) {
201                                 Log(LogWarning, "PluginUtility", "Tried to use dictionary in argument");
202                                 continue;
203                         } else if (arg.AValue.IsObjectType<Array>()) {
204                                 bool first = true;
205                                 Array::Ptr arr = static_cast<Array::Ptr>(arg.AValue);
206
207                                 ObjectLock olock(arr);
208                                 BOOST_FOREACH(const Value& value, arr) {
209                                         bool add_key;
210
211                                         if (first) {
212                                                 first = false;
213                                                 add_key = !arg.SkipKey;
214                                         } else
215                                                 add_key = !arg.SkipKey && arg.RepeatKey;
216
217                                         AddArgumentHelper(command_arr, arg.Key, value, add_key, !arg.SkipValue);
218                                 }
219                         } else
220                                 AddArgumentHelper(command_arr, arg.Key, arg.AValue, !arg.SkipKey, !arg.SkipValue);
221                 }
222         }
223
224         Dictionary::Ptr envMacros = new Dictionary();
225
226         Dictionary::Ptr env = commandObj->GetEnv();
227
228         if (env) {
229                 ObjectLock olock(env);
230                 BOOST_FOREACH(const Dictionary::Pair& kv, env) {
231                         String name = kv.second;
232
233                         Value value = MacroProcessor::ResolveMacros(name, macroResolvers, cr,
234                             NULL, MacroProcessor::EscapeCallback(), resolvedMacros,
235                             useResolvedMacros);
236
237                         if (value.IsObjectType<Array>())
238                                 value = Utility::Join(value, ';');
239
240                         envMacros->Set(kv.first, value);
241                 }
242         }
243
244         if (resolvedMacros && !useResolvedMacros)
245                 return;
246
247         Process::Ptr process = new Process(Process::PrepareCommand(command), envMacros);
248         process->SetTimeout(commandObj->GetTimeout());
249         process->Run(boost::bind(callback, command, _1));
250 }
251
252 ServiceState PluginUtility::ExitStatusToState(int exitStatus)
253 {
254         switch (exitStatus) {
255                 case 0:
256                         return ServiceOK;
257                 case 1:
258                         return ServiceWarning;
259                 case 2:
260                         return ServiceCritical;
261                 default:
262                         return ServiceUnknown;
263         }
264 }
265
266 std::pair<String, String> PluginUtility::ParseCheckOutput(const String& output)
267 {
268         String text;
269         String perfdata;
270
271         std::vector<String> lines;
272         boost::algorithm::split(lines, output, boost::is_any_of("\r\n"));
273
274         BOOST_FOREACH (const String& line, lines) {
275                 size_t delim = line.FindFirstOf("|");
276
277                 if (!text.IsEmpty())
278                         text += "\n";
279
280                 if (delim != String::NPos) {
281                         text += line.SubStr(0, delim);
282
283                         if (!perfdata.IsEmpty())
284                                 perfdata += " ";
285
286                         perfdata += line.SubStr(delim + 1, line.GetLength());
287                 } else {
288                         text += line;
289                 }
290         }
291
292         boost::algorithm::trim(perfdata);
293
294         return std::make_pair(text, perfdata);
295 }
296
297 Array::Ptr PluginUtility::SplitPerfdata(const String& perfdata)
298 {
299         Array::Ptr result = new Array();
300
301         size_t begin = 0;
302         String multi_prefix;
303
304         for (;;) {
305                 size_t eqp = perfdata.FindFirstOf('=', begin);
306
307                 if (eqp == String::NPos)
308                         break;
309
310                 String label = perfdata.SubStr(begin, eqp - begin);
311
312                 if (label.GetLength() > 2 && label[0] == '\'' && label[label.GetLength() - 1] == '\'')
313                         label = label.SubStr(1, label.GetLength() - 2);
314
315                 size_t multi_index = label.RFind("::");
316
317                 if (multi_index != String::NPos)
318                         multi_prefix = "";
319
320                 size_t spq = perfdata.FindFirstOf(' ', eqp);
321
322                 if (spq == String::NPos)
323                         spq = perfdata.GetLength();
324
325                 String value = perfdata.SubStr(eqp + 1, spq - eqp - 1);
326
327                 if (!multi_prefix.IsEmpty())
328                         label = multi_prefix + "::" + label;
329
330                 String pdv;
331                 if (label.FindFirstOf(" ") != String::NPos)
332                         pdv = "'" + label + "'=" + value;
333                 else
334                         pdv = label + "=" + value;
335
336                 result->Add(pdv);
337
338                 if (multi_index != String::NPos)
339                         multi_prefix = label.SubStr(0, multi_index);
340
341                 begin = spq + 1;
342         }
343
344         return result;
345 }
346
347 String PluginUtility::FormatPerfdata(const Array::Ptr& perfdata)
348 {
349         if (!perfdata)
350                 return "";
351
352         std::ostringstream result;
353
354         ObjectLock olock(perfdata);
355
356         bool first = true;
357         BOOST_FOREACH(const Value& pdv, perfdata) {
358                 if (!first)
359                         result << " ";
360                 else
361                         first = false;
362
363                 if (pdv.IsObjectType<PerfdataValue>())
364                         result << static_cast<PerfdataValue::Ptr>(pdv)->Format();
365                 else
366                         result << pdv;
367         }
368
369         return result.str();
370 }