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