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