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