/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include #include #include #include #include #include #include #define VERSION 1.0 namespace po = boost::program_options; struct printInfoStruct { threshold tWarn; threshold tCrit; std::wstring wsFullPath; double dValue; DWORD dwPerformanceWait = 1000; DWORD dwRequestedType = PDH_FMT_DOUBLE; }; static bool parseArguments(const int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) { WCHAR szNamePath[MAX_PATH + 1]; GetModuleFileName(NULL, szNamePath, MAX_PATH); WCHAR *szProgName = PathFindFileName(szNamePath); po::options_description desc("Options"); desc.add_options() ("help,h", "Print help page and exit") ("version,V", "Print version and exit") ("warning,w", po::wvalue(), "Warning thershold") ("critical,c", po::wvalue(), "Critical threshold") ("performance-counter,P", po::wvalue(), "The performance counter string to use") ("performance-wait", po::value(), "Sleep in milliseconds between the two perfomance querries (Default: 1000ms)") ("fmt-countertype", po::wvalue(), "Value type of counter: 'double'(default), 'long', 'int64'") ("print-objects", "Prints all available objects to console") ("print-object-info", "Prints all available instances and counters of --performance-counter, do not use a full perfomance counter string here") ("perf-syntax", po::wvalue(), "Use this string as name for the performance counter (graphite compatibility)") ; po::wcommand_line_parser parser(ac, av); try { po::store( parser .options(desc) .style( po::command_line_style::unix_style | po::command_line_style::allow_long_disguise) .run(), vm); vm.notify(); } catch (const std::exception& e) { std::cout << e.what() << '\n' << desc << '\n'; return false; } if (vm.count("version")) { std::wcout << "Version: " << VERSION << '\n'; return false; } if (vm.count("help")) { std::wcout << szProgName << " Help\n\tVersion: " << VERSION << '\n'; wprintf( L"%s runs a check against a performance counter.\n" L"You can use the following options to define its behaviour:\n\n", szProgName); std::cout << desc; wprintf( L"\nIt will then output a string looking something like this:\n\n" L"\tPERFMON CRITICAL \"\\Processor(_Total)\\%% Idle Time\" = 40.34 | " L"perfmon=40.34;20;40;; \"\\Processor(_Total)\\%% Idle Time\"=40.34\n\n" L"\"tPERFMON\" being the type of the check, \"CRITICAL\" the returned status\n" L"and \"40.34\" is the performance counters value.\n" L"%s' exit codes denote the following:\n" L" 0\tOK,\n\tNo Thresholds were exceeded\n" L" 1\tWARNING,\n\tThe warning was broken, but not the critical threshold\n" L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" L" 3\tUNKNOWN, \n\tNo check could be performed\n\n" , szProgName); return false; } if (vm.count("warning")) { try { printInfo.tWarn = threshold(vm["warning"].as()); } catch (const std::invalid_argument& e) { std::wcout << e.what() << '\n'; return false; } } if (vm.count("critical")) { try { printInfo.tCrit = threshold(vm["critical"].as()); } catch (const std::invalid_argument& e) { std::wcout << e.what() << '\n'; return false; } } if (vm.count("fmt-countertype")) { if (!vm["fmt-countertype"].as().compare(L"int64")) printInfo.dwRequestedType = PDH_FMT_LARGE; else if (!vm["fmt-countertype"].as().compare(L"long")) printInfo.dwRequestedType = PDH_FMT_LONG; else if (vm["fmt-countertype"].as().compare(L"double")) { std::wcout << "Unknown value type " << vm["fmt-countertype"].as() << '\n'; return false; } } if (vm.count("performance-counter")) printInfo.wsFullPath = vm["performance-counter"].as(); if (vm.count("performance-wait")) printInfo.dwPerformanceWait = vm["performance-wait"].as(); return true; } static bool getInstancesAndCountersOfObject(const std::wstring& wsObject, std::vector& vecInstances, std::vector& vecCounters) { DWORD dwCounterListLength = 0, dwInstanceListLength = 0; if (PdhEnumObjectItems(NULL, NULL, wsObject.c_str(), NULL, &dwCounterListLength, NULL, &dwInstanceListLength, PERF_DETAIL_WIZARD, 0) != PDH_MORE_DATA) return false; std::vector mszCounterList(dwCounterListLength + 1); std::vector mszInstanceList(dwInstanceListLength + 1); if (FAILED(PdhEnumObjectItems(NULL, NULL, wsObject.c_str(), mszCounterList.data(), &dwCounterListLength, mszInstanceList.data(), &dwInstanceListLength, PERF_DETAIL_WIZARD, 0))) { return false; } if (dwInstanceListLength) { std::wstringstream wssInstanceName; // XXX: is the "- 1" correct? for (DWORD c = 0; c < dwInstanceListLength - 1; ++c) { if (mszInstanceList[c]) wssInstanceName << mszInstanceList[c]; else { vecInstances.push_back(wssInstanceName.str()); wssInstanceName.str(L""); } } } if (dwCounterListLength) { std::wstringstream wssCounterName; // XXX: is the "- 1" correct? for (DWORD c = 0; c < dwCounterListLength - 1; ++c) { if (mszCounterList[c]) wssCounterName << mszCounterList[c]; else { vecCounters.push_back(wssCounterName.str()); wssCounterName.str(L""); } } } return true; } static void printPDHError(PDH_STATUS status) { HMODULE hPdhLibrary = NULL; LPWSTR pMessage = NULL; hPdhLibrary = LoadLibrary(L"pdh.dll"); if (!hPdhLibrary) { std::wcout << "LoadLibrary failed with " << GetLastError() << '\n'; return; } if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, hPdhLibrary, status, 0, (LPWSTR)&pMessage, 0, NULL)) { FreeLibrary(hPdhLibrary); std::wcout << "Format message failed with " << std::hex << GetLastError() << '\n'; return; } FreeLibrary(hPdhLibrary); std::wcout << pMessage << '\n'; LocalFree(pMessage); } static void printObjects() { DWORD dwBufferLength = 0; PDH_STATUS status = PdhEnumObjects(NULL, NULL, NULL, &dwBufferLength, PERF_DETAIL_WIZARD, FALSE); //HEX HEX! Only a Magicians gets all the info he wants, and only Microsoft knows what that means if (status != PDH_MORE_DATA) { printPDHError(status); return; } std::vector mszObjectList(dwBufferLength + 2); status = PdhEnumObjects(NULL, NULL, mszObjectList.data(), &dwBufferLength, PERF_DETAIL_WIZARD, FALSE); if (FAILED(status)) { printPDHError(status); return; } DWORD c = 0; while (++c < dwBufferLength) { if (mszObjectList[c] == '\0') std::wcout << '\n'; else std::wcout << mszObjectList[c]; } } static void printObjectInfo(const printInfoStruct& pI) { if (pI.wsFullPath.empty()) { std::wcout << "No object given!\n"; return; } std::vector vecInstances, vecCounters; if (!getInstancesAndCountersOfObject(pI.wsFullPath, vecInstances, vecCounters)) { std::wcout << "Could not enumerate instances and counters of " << pI.wsFullPath << '\n' << "Make sure it exists!\n"; return; } std::wcout << "Instances of " << pI.wsFullPath << ":\n"; if (vecInstances.empty()) std::wcout << "> Has no instances\n"; else { for (const auto& instance : vecInstances) std::wcout << "> " << instance << '\n'; } std::wcout << std::endl; std::wcout << "Performance Counters of " << pI.wsFullPath << ":\n"; if (vecCounters.empty()) std::wcout << "> Has no counters\n"; else { for (const auto& counter : vecCounters) std::wcout << "> " << counter << "\n"; } std::wcout << std::endl; } bool QueryPerfData(printInfoStruct& pI) { PDH_HQUERY hQuery = NULL; PDH_HCOUNTER hCounter = NULL; DWORD dwBufferSize = 0, dwItemCount = 0; if (pI.wsFullPath.empty()) { std::wcout << "No performance counter path given!\n"; return false; } PDH_FMT_COUNTERVALUE_ITEM *pDisplayValues = NULL; PDH_STATUS status = PdhOpenQuery(NULL, NULL, &hQuery); if (FAILED(status)) goto die; status = PdhAddEnglishCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter); if (FAILED(status)) status = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter); if (FAILED(status)) goto die; status = PdhCollectQueryData(hQuery); if (FAILED(status)) goto die; /* * Most counters need two queries to provide a value. * Those which need only one will return the second. */ Sleep(pI.dwPerformanceWait); status = PdhCollectQueryData(hQuery); if (FAILED(status)) goto die; status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, NULL); if (status != PDH_MORE_DATA) goto die; pDisplayValues = reinterpret_cast(new BYTE[dwBufferSize]); status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, pDisplayValues); if (FAILED(status)) goto die; switch (pI.dwRequestedType) { case (PDH_FMT_LONG): pI.dValue = pDisplayValues[0].FmtValue.longValue; break; case (PDH_FMT_LARGE): pI.dValue = (double) pDisplayValues[0].FmtValue.largeValue; break; default: pI.dValue = pDisplayValues[0].FmtValue.doubleValue; break; } delete[]pDisplayValues; return true; die: printPDHError(status); delete[]pDisplayValues; return false; } static int printOutput(const po::variables_map& vm, printInfoStruct& pi) { std::wstringstream wssPerfData; if (vm.count("perf-syntax")) wssPerfData << "'" << vm["perf-syntax"].as() << "'="; else wssPerfData << "'" << pi.wsFullPath << "'="; wssPerfData << pi.dValue << ';' << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";;"; if (pi.tCrit.rend(pi.dValue)) { std::wcout << "PERFMON CRITICAL for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; return 2; } if (pi.tWarn.rend(pi.dValue)) { std::wcout << "PERFMON WARNING for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; return 1; } std::wcout << "PERFMON OK for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; return 0; } int wmain(int argc, WCHAR **argv) { po::variables_map variables_map; printInfoStruct stPrintInfo; if (!parseArguments(argc, argv, variables_map, stPrintInfo)) return 3; if (variables_map.count("print-objects")) { printObjects(); return 0; } if (variables_map.count("print-object-info")) { printObjectInfo(stPrintInfo); return 0; } if (QueryPerfData(stPrintInfo)) return printOutput(variables_map, stPrintInfo); else return 3; }