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