]> granicus.if.org Git - icinga2/blob - plugins/check_perfmon.cpp
Merge pull request #6412 from Icinga/fix/plugin-output
[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 "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 = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter);
301         if (FAILED(status))
302                 goto die;
303
304         status = PdhCollectQueryData(hQuery);
305         if (FAILED(status))
306                 goto die;
307
308         /*
309         * Most counters need two queries to provide a value.
310         * Those which need only one will return the second.
311         */
312         Sleep(pI.dwPerformanceWait);
313
314         status = PdhCollectQueryData(hQuery);
315         if (FAILED(status))
316                 goto die;
317
318         status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, NULL);
319         if (status != PDH_MORE_DATA)
320                 goto die;
321
322         pDisplayValues = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwBufferSize]);
323         status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, pDisplayValues);
324
325         if (FAILED(status))
326                 goto die;
327
328         switch (pI.dwRequestedType) {
329         case (PDH_FMT_LONG):
330                 pI.dValue = pDisplayValues[0].FmtValue.longValue;
331                 break;
332         case (PDH_FMT_LARGE):
333                 pI.dValue = pDisplayValues[0].FmtValue.largeValue;
334                 break;
335         default:
336                 pI.dValue = pDisplayValues[0].FmtValue.doubleValue;
337                 break;
338         }
339
340         delete[]pDisplayValues;
341
342         return true;
343
344 die:
345         printPDHError(status);
346         delete[]pDisplayValues;
347         return false;
348 }
349
350 static int printOutput(const po::variables_map& vm, printInfoStruct& pi)
351 {
352         std::wstringstream wssPerfData;
353
354         if (vm.count("perf-syntax"))
355                 wssPerfData << "'" << vm["perf-syntax"].as<std::wstring>() << "'=";
356         else
357                 wssPerfData << "'" << pi.wsFullPath << "'=";
358
359         wssPerfData << pi.dValue << ';' << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";;";
360
361         if (pi.tCrit.rend(pi.dValue)) {
362                 std::wcout << "PERFMON CRITICAL for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
363                         << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
364                 return 2;
365         }
366
367         if (pi.tWarn.rend(pi.dValue)) {
368                 std::wcout << "PERFMON WARNING for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
369                         << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
370                 return 1;
371         }
372
373         std::wcout << "PERFMON OK for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
374                 << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
375
376         return 0;
377 }
378
379 int wmain(int argc, WCHAR **argv)
380 {
381         po::variables_map variables_map;
382         printInfoStruct stPrintInfo;
383         if (!parseArguments(argc, argv, variables_map, stPrintInfo))
384                 return 3;
385
386         if (variables_map.count("print-objects")) {
387                 printObjects();
388                 return 0;
389         }
390
391         if (variables_map.count("print-object-info")) {
392                 printObjectInfo(stPrintInfo);
393                 return 0;
394         }
395
396         if (QueryPerfData(stPrintInfo))
397                 return printOutput(variables_map, stPrintInfo);
398         else
399                 return 3;
400 }