]> granicus.if.org Git - icinga2/blob - plugins/check_perfmon.cpp
Merge pull request #7190 from Icinga/bugfix/check-service-ambiguous-parameter
[icinga2] / plugins / check_perfmon.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "plugins/thresholds.hpp"
4 #include <boost/program_options.hpp>
5 #include <iostream>
6 #include <vector>
7 #include <windows.h>
8 #include <pdh.h>
9 #include <pdhmsg.h>
10 #include <shlwapi.h>
11
12 #define VERSION 1.0
13
14 namespace po = boost::program_options;
15
16 struct printInfoStruct
17 {
18         threshold tWarn;
19         threshold tCrit;
20         std::wstring wsFullPath;
21         double dValue;
22         DWORD dwPerformanceWait = 1000;
23         DWORD dwRequestedType = PDH_FMT_DOUBLE;
24 };
25
26 static bool parseArguments(const int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
27 {
28         WCHAR szNamePath[MAX_PATH + 1];
29         GetModuleFileName(NULL, szNamePath, MAX_PATH);
30         WCHAR *szProgName = PathFindFileName(szNamePath);
31
32         po::options_description desc("Options");
33         desc.add_options()
34                 ("help,h", "Print help page and exit")
35                 ("version,V", "Print version and exit")
36                 ("warning,w", po::wvalue<std::wstring>(), "Warning thershold")
37                 ("critical,c", po::wvalue<std::wstring>(), "Critical threshold")
38                 ("performance-counter,P", po::wvalue<std::wstring>(), "The performance counter string to use")
39                 ("performance-wait", po::value<DWORD>(), "Sleep in milliseconds between the two perfomance querries (Default: 1000ms)")
40                 ("fmt-countertype", po::wvalue<std::wstring>(), "Value type of counter: 'double'(default), 'long', 'int64'")
41                 ("print-objects", "Prints all available objects to console")
42                 ("print-object-info", "Prints all available instances and counters of --performance-counter, do not use a full perfomance counter string here")
43                 ("perf-syntax", po::wvalue<std::wstring>(), "Use this string as name for the performance counter (graphite compatibility)")
44                 ;
45
46         po::wcommand_line_parser parser(ac, av);
47
48         try {
49                 po::store(
50                         parser
51                         .options(desc)
52                         .style(
53                                 po::command_line_style::unix_style |
54                                 po::command_line_style::allow_long_disguise)
55                         .run(),
56                         vm);
57                 vm.notify();
58         } catch (const std::exception& e) {
59                 std::cout << e.what() << '\n' << desc << '\n';
60                 return false;
61         }
62
63         if (vm.count("version")) {
64                 std::wcout << "Version: " << VERSION << '\n';
65                 return false;
66         }
67
68         if (vm.count("help")) {
69                 std::wcout << szProgName << " Help\n\tVersion: " << VERSION << '\n';
70                 wprintf(
71                         L"%s runs a check against a performance counter.\n"
72                         L"You can use the following options to define its behaviour:\n\n", szProgName);
73                 std::cout << desc;
74                 wprintf(
75                         L"\nIt will then output a string looking something like this:\n\n"
76                         L"\tPERFMON CRITICAL \"\\Processor(_Total)\\%% Idle Time\" = 40.34 | "
77                         L"perfmon=40.34;20;40;; \"\\Processor(_Total)\\%% Idle Time\"=40.34\n\n"
78                         L"\"tPERFMON\" being the type of the check, \"CRITICAL\" the returned status\n"
79                         L"and \"40.34\" is the performance counters value.\n"
80                         L"%s' exit codes denote the following:\n"
81                         L" 0\tOK,\n\tNo Thresholds were exceeded\n"
82                         L" 1\tWARNING,\n\tThe warning was broken, but not the critical threshold\n"
83                         L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
84                         L" 3\tUNKNOWN, \n\tNo check could be performed\n\n"
85                         , szProgName);
86                 return false;
87         }
88
89         if (vm.count("warning")) {
90                 try {
91                         printInfo.tWarn = threshold(vm["warning"].as<std::wstring>());
92                 } catch (const std::invalid_argument& e) {
93                         std::wcout << e.what() << '\n';
94                         return false;
95                 }
96         }
97
98         if (vm.count("critical")) {
99                 try {
100                         printInfo.tCrit = threshold(vm["critical"].as<std::wstring>());
101                 } catch (const std::invalid_argument& e) {
102                         std::wcout << e.what() << '\n';
103                         return false;
104                 }
105         }
106
107         if (vm.count("fmt-countertype")) {
108                 if (!vm["fmt-countertype"].as<std::wstring>().compare(L"int64"))
109                         printInfo.dwRequestedType = PDH_FMT_LARGE;
110                 else if (!vm["fmt-countertype"].as<std::wstring>().compare(L"long"))
111                         printInfo.dwRequestedType = PDH_FMT_LONG;
112                 else if (vm["fmt-countertype"].as<std::wstring>().compare(L"double")) {
113                         std::wcout << "Unknown value type " << vm["fmt-countertype"].as<std::wstring>() << '\n';
114                         return false;
115                 }
116         }
117
118         if (vm.count("performance-counter"))
119                 printInfo.wsFullPath = vm["performance-counter"].as<std::wstring>();
120
121         if (vm.count("performance-wait"))
122                 printInfo.dwPerformanceWait = vm["performance-wait"].as<DWORD>();
123
124         return true;
125 }
126
127 static bool getInstancesAndCountersOfObject(const std::wstring& wsObject,
128         std::vector<std::wstring>& vecInstances, std::vector<std::wstring>& vecCounters)
129 {
130         DWORD dwCounterListLength = 0, dwInstanceListLength = 0;
131
132         if (PdhEnumObjectItems(NULL, NULL, wsObject.c_str(),
133                 NULL, &dwCounterListLength, NULL,
134                 &dwInstanceListLength, PERF_DETAIL_WIZARD, 0) != PDH_MORE_DATA)
135                 return false;
136
137         std::vector<WCHAR> mszCounterList(dwCounterListLength + 1);
138         std::vector<WCHAR> mszInstanceList(dwInstanceListLength + 1);
139
140         if (FAILED(PdhEnumObjectItems(NULL, NULL, wsObject.c_str(),
141                 mszCounterList.data(), &dwCounterListLength, mszInstanceList.data(),
142                 &dwInstanceListLength, PERF_DETAIL_WIZARD, 0))) {
143                 return false;
144         }
145
146         if (dwInstanceListLength) {
147                 std::wstringstream wssInstanceName;
148
149                 // XXX: is the "- 1" correct?
150                 for (DWORD c = 0; c < dwInstanceListLength - 1; ++c) {
151                         if (mszInstanceList[c])
152                                 wssInstanceName << mszInstanceList[c];
153                         else {
154                                 vecInstances.push_back(wssInstanceName.str());
155                                 wssInstanceName.str(L"");
156                         }
157                 }
158         }
159
160         if (dwCounterListLength) {
161                 std::wstringstream wssCounterName;
162
163                 // XXX: is the "- 1" correct?
164                 for (DWORD c = 0; c < dwCounterListLength - 1; ++c) {
165                         if (mszCounterList[c])
166                                 wssCounterName << mszCounterList[c];
167                         else {
168                                 vecCounters.push_back(wssCounterName.str());
169                                 wssCounterName.str(L"");
170                         }
171                 }
172         }
173
174         return true;
175 }
176
177 static void printPDHError(PDH_STATUS status)
178 {
179         HMODULE hPdhLibrary = NULL;
180         LPWSTR pMessage = NULL;
181
182         hPdhLibrary = LoadLibrary(L"pdh.dll");
183         if (!hPdhLibrary) {
184                 std::wcout << "LoadLibrary failed with " << GetLastError() << '\n';
185                 return;
186         }
187
188         if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
189                 hPdhLibrary, status, 0, (LPWSTR)&pMessage, 0, NULL)) {
190                 FreeLibrary(hPdhLibrary);
191                 std::wcout << "Format message failed with " << std::hex << GetLastError() << '\n';
192                 return;
193         }
194
195         FreeLibrary(hPdhLibrary);
196
197         std::wcout << pMessage << '\n';
198         LocalFree(pMessage);
199 }
200
201 static void printObjects()
202 {
203         DWORD dwBufferLength = 0;
204         PDH_STATUS status =     PdhEnumObjects(NULL, NULL, NULL,
205                 &dwBufferLength, PERF_DETAIL_WIZARD, FALSE);
206         //HEX HEX! Only a Magicians gets all the info he wants, and only Microsoft knows what that means
207
208         if (status != PDH_MORE_DATA) {
209                 printPDHError(status);
210                 return;
211         }
212
213         std::vector<WCHAR> mszObjectList(dwBufferLength + 2);
214         status = PdhEnumObjects(NULL, NULL, mszObjectList.data(),
215                 &dwBufferLength, PERF_DETAIL_WIZARD, FALSE);
216
217         if (FAILED(status)) {
218                 printPDHError(status);
219                 return;
220         }
221
222         DWORD c = 0;
223
224         while (++c < dwBufferLength) {
225                 if (mszObjectList[c] == '\0')
226                         std::wcout << '\n';
227                 else
228                         std::wcout << mszObjectList[c];
229         }
230 }
231
232 static void printObjectInfo(const printInfoStruct& pI)
233 {
234         if (pI.wsFullPath.empty()) {
235                 std::wcout << "No object given!\n";
236                 return;
237         }
238
239         std::vector<std::wstring> vecInstances, vecCounters;
240
241         if (!getInstancesAndCountersOfObject(pI.wsFullPath, vecInstances, vecCounters)) {
242                 std::wcout << "Could not enumerate instances and counters of " << pI.wsFullPath << '\n'
243                         << "Make sure it exists!\n";
244                 return;
245         }
246
247         std::wcout << "Instances of " << pI.wsFullPath << ":\n";
248         if (vecInstances.empty())
249                 std::wcout << "> Has no instances\n";
250         else {
251                 for (const auto& instance : vecInstances)
252                         std::wcout << "> " << instance << '\n';
253         }
254         std::wcout << std::endl;
255
256         std::wcout << "Performance Counters of " << pI.wsFullPath << ":\n";
257         if (vecCounters.empty())
258                 std::wcout << "> Has no counters\n";
259         else {
260                 for (const auto& counter : vecCounters)
261                         std::wcout << "> " << counter << "\n";
262         }
263         std::wcout << std::endl;
264 }
265
266 bool QueryPerfData(printInfoStruct& pI)
267 {
268         PDH_HQUERY hQuery = NULL;
269         PDH_HCOUNTER hCounter = NULL;
270         DWORD dwBufferSize = 0, dwItemCount = 0;
271
272         if (pI.wsFullPath.empty()) {
273                 std::wcout << "No performance counter path given!\n";
274                 return false;
275         }
276
277         PDH_FMT_COUNTERVALUE_ITEM *pDisplayValues = NULL;
278
279         PDH_STATUS status = PdhOpenQuery(NULL, NULL, &hQuery);
280         if (FAILED(status))
281                 goto die;
282
283         status = PdhAddEnglishCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter);
284
285         if (FAILED(status))
286                 status = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter);
287
288         if (FAILED(status))
289                 goto die;
290
291         status = PdhCollectQueryData(hQuery);
292         if (FAILED(status))
293                 goto die;
294
295         /*
296         * Most counters need two queries to provide a value.
297         * Those which need only one will return the second.
298         */
299         Sleep(pI.dwPerformanceWait);
300
301         status = PdhCollectQueryData(hQuery);
302         if (FAILED(status))
303                 goto die;
304
305         status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, NULL);
306         if (status != PDH_MORE_DATA)
307                 goto die;
308
309         pDisplayValues = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwBufferSize]);
310         status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, pDisplayValues);
311
312         if (FAILED(status))
313                 goto die;
314
315         switch (pI.dwRequestedType) {
316         case (PDH_FMT_LONG):
317                 pI.dValue = pDisplayValues[0].FmtValue.longValue;
318                 break;
319         case (PDH_FMT_LARGE):
320                 pI.dValue = (double) pDisplayValues[0].FmtValue.largeValue;
321                 break;
322         default:
323                 pI.dValue = pDisplayValues[0].FmtValue.doubleValue;
324                 break;
325         }
326
327         delete[]pDisplayValues;
328
329         return true;
330
331 die:
332         printPDHError(status);
333         delete[]pDisplayValues;
334         return false;
335 }
336
337 static int printOutput(const po::variables_map& vm, printInfoStruct& pi)
338 {
339         std::wstringstream wssPerfData;
340
341         if (vm.count("perf-syntax"))
342                 wssPerfData << "'" << vm["perf-syntax"].as<std::wstring>() << "'=";
343         else
344                 wssPerfData << "'" << pi.wsFullPath << "'=";
345
346         wssPerfData << pi.dValue << ';' << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";;";
347
348         if (pi.tCrit.rend(pi.dValue)) {
349                 std::wcout << "PERFMON CRITICAL for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
350                         << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
351                 return 2;
352         }
353
354         if (pi.tWarn.rend(pi.dValue)) {
355                 std::wcout << "PERFMON WARNING for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
356                         << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
357                 return 1;
358         }
359
360         std::wcout << "PERFMON OK for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
361                 << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
362
363         return 0;
364 }
365
366 int wmain(int argc, WCHAR **argv)
367 {
368         po::variables_map variables_map;
369         printInfoStruct stPrintInfo;
370         if (!parseArguments(argc, argv, variables_map, stPrintInfo))
371                 return 3;
372
373         if (variables_map.count("print-objects")) {
374                 printObjects();
375                 return 0;
376         }
377
378         if (variables_map.count("print-object-info")) {
379                 printObjectInfo(stPrintInfo);
380                 return 0;
381         }
382
383         if (QueryPerfData(stPrintInfo))
384                 return printOutput(variables_map, stPrintInfo);
385         else
386                 return 3;
387 }