]> granicus.if.org Git - icinga2/blob - lib/icinga/pluginutility.cpp
Fix null pointer deref in PluginUtility::FormatPerfdata
[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_fwd.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 SkipValue;
40         String Key;
41         String Value;
42
43         CommandArgument(void)
44                 : Order(0), SkipKey(false), SkipValue(false)
45         { }
46
47         bool operator<(const CommandArgument& rhs) const
48         {
49                 return Order < rhs.Order;
50         }
51 };
52
53 void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable,
54     const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers,
55     const boost::function<void(const Value& commandLine, const ProcessResult&)>& callback)
56 {
57         Value raw_command = commandObj->GetCommandLine();
58         Dictionary::Ptr raw_arguments = commandObj->GetArguments();
59
60         Value command;
61         if (!raw_arguments || raw_command.IsObjectType<Array>())
62                 command = MacroProcessor::ResolveMacros(raw_command, macroResolvers, cr, NULL, Utility::EscapeShellArg);
63         else {
64                 Array::Ptr arr = make_shared<Array>();
65                 arr->Add(raw_command);
66                 command = arr;
67         }
68
69         if (raw_arguments) {
70                 std::vector<CommandArgument> args;
71
72                 ObjectLock olock(raw_arguments);
73                 BOOST_FOREACH(const Dictionary::Pair& kv, raw_arguments) {
74                         const Value& arginfo = kv.second;
75
76                         CommandArgument arg;
77                         arg.Key = kv.first;
78
79                         bool required = false;
80                         String argval;
81
82                         if (arginfo.IsObjectType<Dictionary>()) {
83                                 Dictionary::Ptr argdict = arginfo;
84                                 argval = argdict->Get("value");
85                                 if (argdict->Contains("required"))
86                                         required = argdict->Get("required");
87                                 arg.SkipKey = argdict->Get("skip_key");
88                                 arg.Order = argdict->Get("order");
89
90                                 String set_if = argdict->Get("set_if");
91
92                                 if (!set_if.IsEmpty()) {
93                                         String missingMacro;
94                                         String set_if_resolved = MacroProcessor::ResolveMacros(set_if, macroResolvers,
95                                                 cr, &missingMacro);
96
97                                         if (!missingMacro.IsEmpty())
98                                                 continue;
99
100                                         try {
101                                                 if (!Convert::ToLong(set_if_resolved))
102                                                         continue;
103                                         } catch (const std::exception& ex) {
104                                                 /* tried to convert a string */
105                                                 Log(LogWarning, "PluginUtility", "Error evaluating set_if value '" + set_if_resolved + "': " + ex.what());
106                                                 continue;
107                                         }
108                                 }
109                         }
110                         else
111                                 argval = arginfo;
112
113                         if (argval.IsEmpty())
114                                 arg.SkipValue = true;
115
116                         String missingMacro;
117                         arg.Value = MacroProcessor::ResolveMacros(argval, macroResolvers,
118                             cr, &missingMacro);
119
120                         if (!missingMacro.IsEmpty()) {
121                                 if (required) {
122                                         String message = "Non-optional macro '" + missingMacro + "' used in argument '" +
123                                             arg.Key + "' is missing while executing command '" + commandObj->GetName() +
124                                             "' for object '" + checkable->GetName() + "'";
125                                         Log(LogWarning, "PluginUtility", message);
126
127                                         if (callback) {
128                                                 ProcessResult pr;
129                                                 pr.PID = -1;
130                                                 pr.ExecutionStart = Utility::GetTime();
131                                                 pr.ExecutionStart = pr.ExecutionStart;
132                                                 pr.ExitStatus = 3; /* Unknown */
133                                                 pr.Output = message;
134                                                 callback(Empty, pr);
135                                         }
136
137                                         return;
138                                 }
139
140                                 continue;
141                         }
142
143                         args.push_back(arg);
144                 }
145
146                 std::sort(args.begin(), args.end());
147
148                 Array::Ptr command_arr = command;
149                 BOOST_FOREACH(const CommandArgument& arg, args) {
150                         if (!arg.SkipKey)
151                                 command_arr->Add(arg.Key);
152
153                         if (!arg.SkipValue)
154                                 command_arr->Add(arg.Value);
155                 }
156         }
157
158         Dictionary::Ptr envMacros = make_shared<Dictionary>();
159
160         Dictionary::Ptr env = commandObj->GetEnv();
161
162         if (env) {
163                 ObjectLock olock(env);
164                 BOOST_FOREACH(const Dictionary::Pair& kv, env) {
165                         String name = kv.second;
166
167                         Value value = MacroProcessor::ResolveMacros(name, macroResolvers, cr);
168
169                         envMacros->Set(kv.first, value);
170                 }
171         }
172
173         Process::Ptr process = make_shared<Process>(Process::PrepareCommand(command), envMacros);
174         process->SetTimeout(commandObj->GetTimeout());
175         process->Run(boost::bind(callback, command, _1));
176 }
177
178 ServiceState PluginUtility::ExitStatusToState(int exitStatus)
179 {
180         switch (exitStatus) {
181                 case 0:
182                         return ServiceOK;
183                 case 1:
184                         return ServiceWarning;
185                 case 2:
186                         return ServiceCritical;
187                 default:
188                         return ServiceUnknown;
189         }
190 }
191
192 std::pair<String, String> PluginUtility::ParseCheckOutput(const String& output)
193 {
194         String text;
195         String perfdata;
196
197         std::vector<String> lines;
198         boost::algorithm::split(lines, output, boost::is_any_of("\r\n"));
199
200         BOOST_FOREACH (const String& line, lines) {
201                 size_t delim = line.FindFirstOf("|");
202
203                 if (!text.IsEmpty())
204                         text += "\n";
205
206                 if (delim != String::NPos) {
207                         text += line.SubStr(0, delim);
208
209                         if (!perfdata.IsEmpty())
210                                 perfdata += " ";
211
212                         perfdata += line.SubStr(delim + 1, line.GetLength());
213                 } else {
214                         text += line;
215                 }
216         }
217
218         boost::algorithm::trim(perfdata);
219
220         return std::make_pair(text, perfdata);
221 }
222
223 Array::Ptr PluginUtility::SplitPerfdata(const String& perfdata)
224 {
225         Array::Ptr result = make_shared<Array>();
226
227         size_t begin = 0;
228         String multi_prefix;
229
230         for (;;) {
231                 size_t eqp = perfdata.FindFirstOf('=', begin);
232
233                 if (eqp == String::NPos)
234                         break;
235
236                 String label = perfdata.SubStr(begin, eqp - begin);
237
238                 if (label.GetLength() > 2 && label[0] == '\'' && label[label.GetLength() - 1] == '\'')
239                         label = label.SubStr(1, label.GetLength() - 2);
240
241                 size_t multi_index = label.RFind("::");
242
243                 if (multi_index != String::NPos)
244                         multi_prefix = "";
245
246                 size_t spq = perfdata.FindFirstOf(' ', eqp);
247
248                 if (spq == String::NPos)
249                         spq = perfdata.GetLength();
250
251                 String value = perfdata.SubStr(eqp + 1, spq - eqp - 1);
252
253                 if (!multi_prefix.IsEmpty())
254                         label = multi_prefix + "::" + label;
255
256                 String pdv;
257                 if (label.FindFirstOf(" ") != String::NPos)
258                         pdv = "'" + label + "'=" + value;
259                 else
260                         pdv = label + "=" + value;
261
262                 result->Add(pdv);
263
264                 if (multi_index != String::NPos)
265                         multi_prefix = label.SubStr(0, multi_index);
266
267                 begin = spq + 1;
268         }
269
270         return result;
271 }
272
273 String PluginUtility::FormatPerfdata(const Array::Ptr& perfdata)
274 {
275         if (!perfdata)
276                 return "";
277
278         std::ostringstream result;
279
280         ObjectLock olock(perfdata);
281
282         bool first = true;
283         BOOST_FOREACH(const Value& pdv, perfdata) {
284                 if (!first)
285                         result << " ";
286                 else
287                         first = false;
288
289                 if (pdv.IsObjectType<PerfdataValue>())
290                         result << static_cast<PerfdataValue::Ptr>(pdv)->Format();
291                 else
292                         result << pdv;
293         }
294
295         return result.str();
296 }