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