]> granicus.if.org Git - icinga2/blob - plugins/check_perfmon.cpp
Merge pull request #5933 from mcktr/fix/copyright-for-2018
[icinga2] / plugins / check_perfmon.cpp
1 /******************************************************************************
2 * Icinga 2                                                                   *
3 * Copyright (C) 2012-2018 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, 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, &dwBufferSize, &dwItemCount, pDisplayValues);
327         if (status != PDH_MORE_DATA)
328                 goto die;
329
330         pDisplayValues = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwBufferSize]);
331         status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, pDisplayValues);
332
333         if (FAILED(status))
334                 goto die;
335
336         switch (pI.dwRequestedType)
337         {
338         case (PDH_FMT_LONG):
339                 pI.dValue = pDisplayValues[0].FmtValue.longValue;
340                 break;
341         case (PDH_FMT_LARGE):
342                 pI.dValue = pDisplayValues[0].FmtValue.largeValue;
343                 break;
344         default:
345                 pI.dValue = pDisplayValues[0].FmtValue.doubleValue;
346                 break;
347         }
348
349         delete[]pDisplayValues;
350
351         return TRUE;
352
353 die:
354         FormatPDHError(status);
355         delete[]pDisplayValues;
356         return FALSE;
357 }
358
359 INT PrintOutput(CONST po::variables_map& vm, printInfoStruct& pi)
360 {
361         std::wstringstream wssPerfData;
362
363         if (vm.count("perf-syntax")) {
364                 wssPerfData << "\"" << vm["perf-syntax"].as<std::wstring>() << "\"=";
365         } else {
366                 wssPerfData << "\"" << pi.wsFullPath << "\"=";
367         }
368
369         wssPerfData << pi.dValue << ';' << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";;";
370
371         if (pi.tCrit.rend(pi.dValue)) {
372                 std::wcout << "PERFMON CRITICAL \"" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
373                         << "\" = " << pi.dValue << " | " << wssPerfData.str() << '\n';
374                 return 2;
375         }
376
377         if (pi.tWarn.rend(pi.dValue)) {
378                 std::wcout << "PERFMON WARNING \"" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
379                         << "\" = " << pi.dValue << " | " << wssPerfData.str() << '\n';
380                 return 1;
381         }
382
383         std::wcout << "PERFMON OK \"" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
384                 << "\" = " << pi.dValue << " | " << wssPerfData.str() << '\n';
385         return 0;
386 }
387
388 VOID FormatPDHError(PDH_STATUS status)
389 {
390         HANDLE hPdhLibrary = NULL;
391         LPWSTR pMessage = NULL;
392
393         hPdhLibrary = LoadLibrary(L"pdh.dll");
394         if (NULL == hPdhLibrary) {
395                 std::wcout << "LoadLibrary failed with " << GetLastError() << '\n';
396                 return;
397         }
398
399         if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
400                 hPdhLibrary, status, 0, (LPWSTR)&pMessage, 0, NULL)) {
401                 std::wcout << "Format message failed with " << std::hex << GetLastError() << '\n';
402                 return;
403         }
404
405         std::wcout << pMessage << '\n';
406         LocalFree(pMessage);
407 }