From 9575491100371ce5a50b3cd3c3dd3971547652a3 Mon Sep 17 00:00:00 2001 From: Jean Flach Date: Mon, 23 Mar 2015 13:09:29 +0100 Subject: [PATCH] Add check_perfmon fixes #8809 --- plugins/CMakeLists.txt | 9 +- plugins/check_perfmon.cpp | 403 ++++++++++++++++++++++++++++++++++++++ plugins/check_perfmon.h | 48 +++++ 3 files changed, 456 insertions(+), 4 deletions(-) create mode 100644 plugins/check_perfmon.cpp create mode 100644 plugins/check_perfmon.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 050b71b6c..049cabad3 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -27,8 +27,8 @@ if ( WIN32 ) ) list( APPEND check_SOURCES - check_disk.cpp check_load.cpp check_memory.cpp check_network.cpp check_ping.cpp check_procs.cpp - check_service.cpp check_swap.cpp check_update.cpp check_uptime.cpp check_users.cpp ) + check_disk.cpp check_load.cpp check_memory.cpp check_network.cpp check_ping.cpp check_perfmon.cpp + check_procs.cpp check_service.cpp check_swap.cpp check_update.cpp check_uptime.cpp check_users.cpp ) foreach ( source ${check_SOURCES} ) string ( REGEX REPLACE ".cpp\$" "" check_OUT "${source}" ) @@ -45,14 +45,15 @@ if ( WIN32 ) target_link_libraries( check_load Pdh.lib ) target_link_libraries( check_network Pdh.lib ) + target_link_libraries ( check_perfmon Pdh.lib ) target_link_libraries( check_ping Ntdll.lib iphlpapi.lib Ws2_32.lib ) target_link_libraries( check_procs Pdh.lib ) target_link_libraries( check_uptime ${Boost_SYSTEM_LIBRARY} ) target_link_libraries( check_users wtsapi32.lib ) install ( - TARGETS check_disk check_load check_memory check_network check_procs check_ping - check_service check_swap check_update check_uptime check_users + TARGETS check_disk check_load check_memory check_network check_procs check_perfmon + check_ping check_service check_swap check_update check_uptime check_users RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} ) endif() diff --git a/plugins/check_perfmon.cpp b/plugins/check_perfmon.cpp new file mode 100644 index 000000000..a068b117f --- /dev/null +++ b/plugins/check_perfmon.cpp @@ -0,0 +1,403 @@ +/****************************************************************************** +* Icinga 2 * +* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software Foundation * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * +******************************************************************************/ + +#include +#include +#include + +#include "check_perfmon.h" + +#define VERSION 1.0 + +namespace po = boost::program_options; + +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; +} + +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") + ; + + po::basic_command_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 (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 0; + } + + if (vm.count("warning")) { + try { + printInfo.tWarn = threshold(vm["warning"].as()); + } catch (std::invalid_argument& e) { + std::wcout << e.what() << '\n'; + return FALSE; + } + } + + if (vm.count("critical")) { + try { + printInfo.tCrit = threshold(vm["critical"].as()); + } catch (std::invalid_argument& e) { + std::wcout << e.what() << '\n'; + return FALSE; + } + } + + if (vm.count("fmt-countertype")) { + if (vm["fmt-countertype"].as().compare(L"double")) + printInfo.dwRequestedType = PDH_FMT_DOUBLE; + else 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 { + 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; +} + +BOOL GetIntstancesAndCountersOfObject(CONST std::wstring wsObject, + std::vector& vecInstances, + std::vector& vecCounters) +{ + LPWSTR szDataSource = NULL, szMachineName = NULL, + mszCounterList = NULL, mszInstanceList = NULL; + DWORD dwCounterListLength = 0, dwInstanceListLength = 0; + + std::wstringstream wssInstanceName, wssCounterName; + LPWSTR szObjectName = new WCHAR[wsObject.length() + 1]; + StrCpyW(szObjectName, wsObject.c_str()); + + PDH_STATUS status = + PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, + mszCounterList, &dwCounterListLength, mszInstanceList, + &dwInstanceListLength, PERF_DETAIL_WIZARD, 0); + + if (status != PDH_MORE_DATA) { + delete[]szObjectName; + return FALSE; + } + + mszCounterList = new WCHAR[dwCounterListLength + 1]; + mszInstanceList = new WCHAR[dwInstanceListLength + 1]; + + status = PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, + mszCounterList, &dwCounterListLength, mszInstanceList, + &dwInstanceListLength, PERF_DETAIL_WIZARD, 0); + + if (FAILED(status)) { + delete[]mszCounterList; + delete[]mszInstanceList; + delete[]szObjectName; + return FALSE; + } + + if (dwInstanceListLength) { + 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) { + for (DWORD c = 0; c < dwCounterListLength-1; ++c) { + if (mszCounterList[c]) { + wssCounterName << mszCounterList[c]; + } else { + vecCounters.push_back(wssCounterName.str()); + wssCounterName.str(L""); + } + } + } + + delete[]mszCounterList; + delete[]mszInstanceList; + delete[]szObjectName; + + return TRUE; +} + +VOID PrintObjects() +{ + LPWSTR szDataSource = NULL, szMachineName = NULL, mszObjectList = NULL; + DWORD dwBufferLength = 0; + PDH_STATUS status = + PdhEnumObjects(szDataSource, szMachineName, mszObjectList, + &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) + goto die; + + mszObjectList = new WCHAR[dwBufferLength + 2]; + status = PdhEnumObjects(szDataSource, szMachineName, mszObjectList, + &dwBufferLength, PERF_DETAIL_WIZARD, FALSE); + + if (FAILED(status)) + goto die; + + DWORD c = 0; + + while (++c < dwBufferLength) { + if (mszObjectList[c] == '\0') + std::wcout << '\n'; + else + std::wcout << mszObjectList[c]; + } + + delete[]mszObjectList; + return; + +die: + FormatPDHError(status); + delete[]mszObjectList; +} + +VOID PrintObjectInfo(CONST printInfoStruct& pI) +{ + if (pI.wsFullPath.empty()) { + std::wcout << "No object given!\n"; + return; + } + + std::vector vecInstances, vecCounters; + + if (!GetIntstancesAndCountersOfObject(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 (std::vector::iterator it = vecInstances.begin(); + it != vecInstances.end(); ++it) { + std::wcout << "> " << *it << '\n'; + } + } + std::wcout << std::endl; + + std::wcout << "Performance Counters of " << pI.wsFullPath << ":\n"; + if (vecCounters.empty()) + std::wcout << "> Has no counters\n"; + else { + for (std::vector::iterator it = vecCounters.begin(); + it != vecCounters.end(); ++it) { + std::wcout << "> " << *it << '\n'; + } + } + std::wcout << std::endl; +} + +BOOL QueryPerfData(printInfoStruct& pI) +{ + PDH_HQUERY hQuery = NULL; + PDH_HCOUNTER hCounter = NULL; + PDH_FMT_COUNTERVALUE_ITEM *pDisplayValues = NULL; + DWORD dwBufferSize = 0, dwItemCount = 0; + + if (pI.wsFullPath.empty()) { + std::wcout << "No performance counter path given!\n"; + return FALSE; + } + + PDH_STATUS status = PdhOpenQuery(NULL, NULL, &hQuery); + if (FAILED(status)) + goto die; + + 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, pDisplayValues); + 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; + case (PDH_FMT_LARGE) : + pI.dValue = pDisplayValues[0].FmtValue.largeValue; + default: + pI.dValue = pDisplayValues[0].FmtValue.doubleValue; + } + + delete[]pDisplayValues; + + return TRUE; + +die: + FormatPDHError(status); + delete[]pDisplayValues; + return FALSE; +} + +INT PrintOutput(CONST po::variables_map& vm, printInfoStruct& pi) +{ + std::wstringstream wssPerfData; + wssPerfData << "perfmon=" << pi.dValue << ';' + << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";; " + << '"' << pi.wsFullPath << "\"=" << pi.dValue; + + if (pi.tCrit.rend(pi.dValue)) { + std::wcout << "PERFMON CRITICAL \"" << pi.wsFullPath << "\" = " + << pi.dValue << " | " << wssPerfData.str() << '\n'; + return 2; + } + + if (pi.tWarn.rend(pi.dValue)) { + std::wcout << "PERFMON WARNING \"" << pi.wsFullPath << "\" = " + << pi.dValue << " | " << wssPerfData.str() << '\n'; + return 1; + } + + std::wcout << "PERFMON OK \"" << pi.wsFullPath << "\" = " + << pi.dValue << " | " << wssPerfData.str() << '\n'; + return 0; +} + +VOID FormatPDHError(PDH_STATUS status) +{ + HANDLE hPdhLibrary = NULL; + LPWSTR pMessage = NULL; + + hPdhLibrary = LoadLibrary(L"pdh.dll"); + if (NULL == 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)) { + std::wcout << "Format message failed with " << std::hex << GetLastError() << '\n'; + return; + } + + std::wcout << pMessage << '\n'; + LocalFree(pMessage); +} \ No newline at end of file diff --git a/plugins/check_perfmon.h b/plugins/check_perfmon.h new file mode 100644 index 000000000..d97389c10 --- /dev/null +++ b/plugins/check_perfmon.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef CHECK_PERFMON_H +#define CHECK_PERFMON_H + +#include +#include +#include + +#include "thresholds.h" + +#include "boost/program_options.hpp" + +struct printInfoStruct +{ + threshold tWarn, tCrit; + std::wstring wsFullPath; + DOUBLE dValue; + DWORD dwPerformanceWait = 1000, + dwRequestedType = PDH_FMT_DOUBLE; +}; + +BOOL ParseArguments(CONST INT, WCHAR **, boost::program_options::variables_map&, printInfoStruct&); +BOOL GetIntstancesAndCountersOfObject(CONST std::wstring, std::vector&, std::vector&); +VOID PrintObjects(); +VOID PrintObjectInfo(CONST printInfoStruct&); +INT QueryPerfData(printInfoStruct&); +INT PrintOutput(CONST boost::program_options::variables_map&, printInfoStruct&); +VOID FormatPDHError(PDH_STATUS); + +#endif // !CHECK_PERFMON_H \ No newline at end of file