diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:34:54 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:34:54 +0000 |
commit | 0915b3ef56dfac3113cce55a59a5765dc94976be (patch) | |
tree | a8fea11d50b4f083e1bf0f90025ece7f0824784a /plugins | |
parent | Initial commit. (diff) | |
download | icinga2-0915b3ef56dfac3113cce55a59a5765dc94976be.tar.xz icinga2-0915b3ef56dfac3113cce55a59a5765dc94976be.zip |
Adding upstream version 2.13.6.upstream/2.13.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/CMakeLists.txt | 69 | ||||
-rw-r--r-- | plugins/check_disk.cpp | 443 | ||||
-rw-r--r-- | plugins/check_load.cpp | 244 | ||||
-rw-r--r-- | plugins/check_memory.cpp | 215 | ||||
-rw-r--r-- | plugins/check_network.cpp | 374 | ||||
-rw-r--r-- | plugins/check_nscp_api.cpp | 512 | ||||
-rw-r--r-- | plugins/check_perfmon.cpp | 387 | ||||
-rw-r--r-- | plugins/check_ping.cpp | 508 | ||||
-rw-r--r-- | plugins/check_procs.cpp | 325 | ||||
-rw-r--r-- | plugins/check_service.cpp | 284 | ||||
-rw-r--r-- | plugins/check_swap.cpp | 238 | ||||
-rw-r--r-- | plugins/check_update.cpp | 248 | ||||
-rw-r--r-- | plugins/check_uptime.cpp | 213 | ||||
-rw-r--r-- | plugins/check_users.cpp | 225 | ||||
-rw-r--r-- | plugins/thresholds.cpp | 276 | ||||
-rw-r--r-- | plugins/thresholds.hpp | 64 |
16 files changed, 4625 insertions, 0 deletions
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 0000000..27fddec --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,69 @@ +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ + +add_executable(check_nscp_api + check_nscp_api.cpp + ${base_OBJS} + $<TARGET_OBJECTS:config> + $<TARGET_OBJECTS:remote> +) +target_link_libraries(check_nscp_api ${base_DEPS}) +set_target_properties ( + check_nscp_api PROPERTIES + DEFINE_SYMBOL I2_PLUGINS_BUILD + FOLDER Plugins) + +# Prefer the PluginDir constant which is set to /sbin on Windows + +if(WIN32) + install(TARGETS check_nscp_api RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) +else() + install(TARGETS check_nscp_api RUNTIME DESTINATION ${ICINGA2_PLUGINDIR}) +endif() + +if (WIN32) + add_definitions(-DUNICODE -D_UNICODE) + + set(thresholds_SOURCES + thresholds.cpp thresholds.hpp + ) + + add_library(thresholds ${thresholds_SOURCES}) + + set_target_properties( + thresholds PROPERTIES + FOLDER Plugins + ) + + set(check_SOURCES + check_disk.cpp check_load.cpp check_memory.cpp check_network.cpp check_perfmon.cpp + check_ping.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}") + + add_executable(${check_OUT} ${source}) + target_link_libraries(${check_OUT} thresholds shlwapi.lib ${Boost_PROGRAM_OPTIONS_LIBRARY}) + + set_target_properties( + ${check_OUT} PROPERTIES + DEFINE_SYMBOL I2_PLUGINS_BUILD + FOLDER Plugins + ) + endforeach() + + target_link_libraries(check_load pdh.lib) + target_link_libraries(check_network pdh.lib iphlpapi.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_perfmon check_procs + check_ping check_service check_swap check_update check_uptime check_users + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} + ) +endif ( ) diff --git a/plugins/check_disk.cpp b/plugins/check_disk.cpp new file mode 100644 index 0000000..48f82ec --- /dev/null +++ b/plugins/check_disk.cpp @@ -0,0 +1,443 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <vector> +#include <windows.h> +#include <set> +#include <iostream> +#include <functional> +#include <shlwapi.h> +#include <math.h> + +#define VERSION 1.1 + +namespace po = boost::program_options; + +struct drive +{ + std::wstring name; + double cap; + double free; + double used; + + drive(std::wstring p) + : name(p) + { } +}; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + std::vector<std::wstring> drives; + std::vector<std::wstring> exclude_drives; + Bunit unit; + bool showUsed{false}; +}; + +static bool l_Debug; + +static int check_drives(std::vector<drive>& vDrives, std::vector<std::wstring>& vExclude_Drives) +{ + DWORD dwResult, dwSize = 0, dwVolumePathNamesLen = MAX_PATH + 1; + WCHAR szLogicalDrives[1024], szVolumeName[MAX_PATH], *szVolumePathNames = NULL; + HANDLE hVolume = NULL; + std::wstring wsLogicalDrives; + size_t volumeNameEnd = 0; + + std::set<std::wstring> sDrives; + + if (l_Debug) + std::wcout << "Getting logic drive string (includes network drives)\n"; + + dwResult = GetLogicalDriveStrings(MAX_PATH, szLogicalDrives); + if (dwResult > MAX_PATH) + goto die; + if (l_Debug) + std::wcout << "Splitting string into single drive names\n"; + + LPTSTR szSingleDrive = szLogicalDrives; + while (*szSingleDrive) { + std::wstring drname = szSingleDrive; + sDrives.insert(drname); + szSingleDrive += wcslen(szSingleDrive) + 1; + if (l_Debug) + std::wcout << "Got: " << drname << '\n'; + } + + if (l_Debug) + std::wcout << "Getting volume mountpoints (includes NTFS folders)\n" + << "Getting first volume\n"; + + hVolume = FindFirstVolume(szVolumeName, MAX_PATH); + if (hVolume == INVALID_HANDLE_VALUE) + goto die; + + if (l_Debug) + std::wcout << "Traversing through list of drives\n"; + + while (GetLastError() != ERROR_NO_MORE_FILES) { + if (l_Debug) + std::wcout << "Path name for " << szVolumeName << "= \""; + volumeNameEnd = wcslen(szVolumeName) - 1; + szVolumePathNames = new WCHAR[dwVolumePathNamesLen]; + + while (!GetVolumePathNamesForVolumeName(szVolumeName, szVolumePathNames, dwVolumePathNamesLen, + &dwVolumePathNamesLen)) { + if (GetLastError() != ERROR_MORE_DATA) + break; + delete[] szVolumePathNames; + szVolumePathNames = new WCHAR[dwVolumePathNamesLen]; + + } + if (l_Debug) + std::wcout << szVolumePathNames << "\"\n"; + + sDrives.insert(std::wstring(szVolumePathNames)); + FindNextVolume(hVolume, szVolumeName, MAX_PATH); + } + if (l_Debug) + std::wcout << "Creating vector from found volumes, ignoring CD drives etc.:\n"; + for (const auto& driveName : sDrives) { + unsigned int type = GetDriveType(driveName.c_str()); + if (type == DRIVE_FIXED || type == DRIVE_REMOTE) { + if (l_Debug) + std::wcout << "\t" << driveName << '\n'; + vDrives.push_back(drive(driveName)); + } + } + + FindVolumeClose(hVolume); + if (szVolumePathNames) + delete[] szVolumePathNames; + + if (l_Debug) + std::wcout << "Removing excluded drives\n"; + + for (const auto& driveName : vExclude_Drives) { + vDrives.erase(std::remove_if(vDrives.begin(), vDrives.end(), + [&driveName](const drive& d) { return d.name == driveName + L'\\'; }), + vDrives.end()); + } + + return -1; + +die: + if (hVolume) + FindVolumeClose(hVolume); + printErrorInfo(); + return 3; +} + +static int check_drives(std::vector<drive>& vDrives, printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << "Removing excluded drives from user input drives\n"; + + for (const auto& driveName : printInfo.exclude_drives) { + printInfo.drives.erase(std::remove(printInfo.drives.begin(), printInfo.drives.end(), driveName), + printInfo.drives.end()); + } + + if (l_Debug) + std::wcout << "Parsing user input drive names\n"; + + for (auto& driveName : printInfo.drives) { + if (driveName.at(driveName.length() - 1) != *L"\\") + driveName.append(L"\\"); + + if (std::wstring::npos == driveName.find(L":\\")) { + std::wcout << "A \":\" is required after the drive name of " << driveName << '\n'; + return 3; + } + + if (l_Debug) + std::wcout << "Added " << driveName << '\n'; + + vDrives.emplace_back(driveName); + } + + return -1; +} + +static bool getDriveSpaceValues(drive& drive, const Bunit& unit) +{ + if (l_Debug) + std::wcout << "Getting free and used disk space for drive " << drive.name << '\n'; + + ULARGE_INTEGER tempFree, tempTotal; + if (!GetDiskFreeSpaceEx(drive.name.c_str(), NULL, &tempTotal, &tempFree)) + return false; + + ULARGE_INTEGER tempUsed; + tempUsed.QuadPart = tempTotal.QuadPart - tempFree.QuadPart; + + if (l_Debug) + std::wcout << "\tcap: " << tempFree.QuadPart << '\n'; + + drive.cap = round((tempTotal.QuadPart / pow(1024.0, unit))); + + if (l_Debug) + std::wcout << "\tAfter conversion: " << drive.cap << '\n' + << "\tfree: " << tempFree.QuadPart << '\n'; + + drive.free = round((tempFree.QuadPart / pow(1024.0, unit))); + + if (l_Debug) + std::wcout << "\tAfter conversion: " << drive.free << '\n' + << "\tused: " << tempUsed.QuadPart << '\n'; + + drive.used = round((tempUsed.QuadPart / pow(1024.0, unit))); + + if (l_Debug) + std::wcout << "\tAfter conversion: " << drive.used << '\n' << '\n'; + + return true; +} + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc("Options"); + + desc.add_options() + ("help,h", "Print usage message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::wvalue<std::wstring>(), "Warning threshold") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold") + ("path,p", po::wvalue<std::vector<std::wstring>>()->multitoken(), "Declare explicitly which drives to check (default checks all)") + ("exclude_device,x", po::wvalue<std::vector<std::wstring>>()->multitoken(), "Exclude these drives from check") + ("exclude-type,X", po::wvalue<std::vector<std::wstring>>()->multitoken(), "Exclude partition types (ignored)") + ("iwarning,W", po::wvalue<std::wstring>(), "Warning threshold for inodes (ignored)") + ("icritical,K", po::wvalue<std::wstring>(), "Critical threshold for inodes (ignored)") + ("unit,u", po::wvalue<std::wstring>(), "Assign unit possible are: B, kB, MB, GB, TB") + ("show-used,U", "Show used space instead of the free space") + ("megabytes,m", "use megabytes, overridden by -unit") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines disk space usage.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tDISK WARNING 29GB | disk=29GB;50%%;5;0;120\n\n" + L"\"DISK\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"23.8304%%\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value.\n" + L"This program will also print out additional performance data disk by disk\n\n" + L"%s' exit codes denote the following:\n\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE < THRESHOLD\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE > THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too." + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) + std::cout << "Version: " << VERSION << '\n'; + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("path")) + printInfo.drives = vm["path"].as<std::vector<std::wstring>>(); + + if (vm.count("exclude_device")) + printInfo.exclude_drives = vm["exclude_device"].as<std::vector<std::wstring>>(); + + if (vm.count("unit")) { + try { + printInfo.unit = parseBUnit(vm["unit"].as<std::wstring>()); + } catch (const std::invalid_argument&) { + std::wcout << "Unknown unit Type " << vm["unit"].as<std::wstring>() << '\n'; + return 3; + } + } else { + if (vm.count("megabytes")) + printInfo.unit = BunitMB; + else + printInfo.unit = BunitB; + } + + printInfo.showUsed = vm.count("show-used") > 0; + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(printInfoStruct& printInfo, std::vector<drive>& vDrives) +{ + if (l_Debug) + std::wcout << "Constructing output string\n"; + + std::vector<std::wstring> wsDrives, wsPerf; + std::wstring unit = BunitStr(printInfo.unit); + + state state = OK; + + std::wstring output = L"DISK OK - free space:"; + + if (printInfo.showUsed) + output = L"DISK OK - used space:"; + + double tCap = 0, tFree = 0, tUsed = 0; + + for (std::vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); it++) { + tCap += it->cap; + tFree += it->free; + tUsed += it->used; + + if (printInfo.showUsed) { + wsDrives.push_back(it->name + L" " + removeZero(it->used) + L" " + unit + L" (" + + removeZero(std::round(it->used / it->cap * 100.0)) + L"%); "); + + wsPerf.push_back(L" " + it->name + L"=" + removeZero(it->used) + unit + L";" + + printInfo.warn.pString(it->cap) + L";" + printInfo.crit.pString(it->cap) + L";0;" + + removeZero(it->cap)); + + if (printInfo.crit.set && !printInfo.crit.rend(it->used, it->cap)) + state = CRITICAL; + + if (state == OK && printInfo.warn.set && !printInfo.warn.rend(it->used, it->cap)) + state = WARNING; + } else { + wsDrives.push_back(it->name + L" " + removeZero(it->free) + L" " + unit + L" (" + + removeZero(std::round(it->free / it->cap * 100.0)) + L"%); "); + + wsPerf.push_back(L" '" + it->name + L"'=" + removeZero(it->free) + unit + L";" + + printInfo.warn.pString(it->cap) + L";" + printInfo.crit.pString(it->cap) + L";0;" + + removeZero(it->cap)); + + if (printInfo.crit.rend(it->free, it->cap)) + state = CRITICAL; + + if (state == OK && printInfo.warn.rend(it->free, it->cap)) + state = WARNING; + } + } + + if (state == WARNING) { + output = L"DISK WARNING - free space:"; + + if (printInfo.showUsed) + output = L"DISK WARNING - used space:"; + } + + if (state == CRITICAL) { + output = L"DISK CRITICAL - free space:"; + + if (printInfo.showUsed) + output = L"DISK CRITICAL - used space:"; + } + + std::wcout << output; + + if (vDrives.size() > 1 && printInfo.showUsed) { + std::wcout << "Total " << (printInfo.showUsed ? tUsed : tFree) << unit + << " (" << removeZero(std::round(tUsed / tCap * 100.0)) << "%); "; + } + + for (const auto& driveName : wsDrives) + std::wcout << driveName; + + std::wcout << "|"; + + for (const auto& perf : wsPerf) + std::wcout << perf; + + std::wcout << '\n'; + + return state; +} + +int wmain(int argc, WCHAR **argv) +{ + std::vector<drive> vDrives; + printInfoStruct printInfo; + po::variables_map vm; + + int ret; + + ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + printInfo.warn.legal = !printInfo.warn.legal; + printInfo.crit.legal = !printInfo.crit.legal; + + if (printInfo.drives.empty()) + ret = check_drives(vDrives, printInfo.exclude_drives); + else + ret = check_drives(vDrives, printInfo); + + if (ret != -1) + return ret; + + for (std::vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); ++it) { + if (!getDriveSpaceValues(*it, printInfo.unit)) { + std::wcout << "Failed to access drive at " << it->name << '\n'; + return 3; + } + } + + return printOutput(printInfo, vDrives); +} diff --git a/plugins/check_load.cpp b/plugins/check_load.cpp new file mode 100644 index 0000000..563c347 --- /dev/null +++ b/plugins/check_load.cpp @@ -0,0 +1,244 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/classification.hpp> +#include <iostream> +#include <pdh.h> +#include <shlwapi.h> +#include <pdhmsg.h> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + double load; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + wchar_t namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + wchar_t *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print usage message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::wvalue<std::wstring>(), "Warning value (in percent)") + ("critical,c", po::wvalue<std::wstring>(), "Critical value (in percent)") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines CPU load.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tLOAD WARNING 67%% | load=67%%;50%%;90%%;0;100\n\n" + L"\"LOAD\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"67%%\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value.\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too." + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) + std::cout << "Version: " << VERSION << '\n'; + + if (vm.count("warning")) { + try { + std::wstring wthreshold = vm["warning"].as<std::wstring>(); + std::vector<std::wstring> tokens; + boost::algorithm::split(tokens, wthreshold, boost::algorithm::is_any_of(",")); + printInfo.warn = threshold(tokens[0]); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("critical")) { + try { + std::wstring cthreshold = vm["critical"].as<std::wstring>(); + std::vector<std::wstring> tokens; + boost::algorithm::split(tokens, cthreshold, boost::algorithm::is_any_of(",")); + printInfo.crit = threshold(tokens[0]); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + if (printInfo.warn.rend(printInfo.load)) + state = WARNING; + + if (printInfo.crit.rend(printInfo.load)) + state = CRITICAL; + + std::wcout << L"LOAD "; + + switch (state) { + case OK: + std::wcout << L"OK"; + break; + case WARNING: + std::wcout << L"WARNING"; + break; + case CRITICAL: + std::wcout << L"CRITICAL"; + break; + } + + std::wcout << " " << printInfo.load << L"% | 'load'=" << printInfo.load << L"%;" + << printInfo.warn.pString() << L";" + << printInfo.crit.pString() << L";0;100" << '\n'; + + return state; +} + +static int check_load(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Creating query and adding counter" << '\n'; + + PDH_HQUERY phQuery; + PDH_STATUS err = PdhOpenQuery(NULL, NULL, &phQuery); + if (!SUCCEEDED(err)) + goto die; + + PDH_HCOUNTER phCounter; + err = PdhAddEnglishCounter(phQuery, L"\\Processor(_Total)\\% Idle Time", NULL, &phCounter); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Collecting first batch of query data" << '\n'; + + err = PdhCollectQueryData(phQuery); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Sleep for one second" << '\n'; + + Sleep(1000); + + if (l_Debug) + std::wcout << L"Collecting second batch of query data" << '\n'; + + err = PdhCollectQueryData(phQuery); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Creating formatted counter array" << '\n'; + + DWORD CounterType; + PDH_FMT_COUNTERVALUE DisplayValue; + err = PdhGetFormattedCounterValue(phCounter, PDH_FMT_DOUBLE, &CounterType, &DisplayValue); + if (SUCCEEDED(err)) { + if (DisplayValue.CStatus == PDH_CSTATUS_VALID_DATA) { + if (l_Debug) + std::wcout << L"Recieved Value of " << DisplayValue.doubleValue << L" (idle)" << '\n'; + printInfo.load = 100.0 - DisplayValue.doubleValue; + } + else { + if (l_Debug) + std::wcout << L"Received data was not valid\n"; + goto die; + } + + if (l_Debug) + std::wcout << L"Finished collection. Cleaning up and returning" << '\n'; + + PdhCloseQuery(phQuery); + return -1; + } + +die: + printErrorInfo(); + if (phQuery) + PdhCloseQuery(phQuery); + return 3; +} + +int wmain(int argc, WCHAR **argv) +{ + printInfoStruct printInfo; + po::variables_map vm; + + int ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + ret = check_load(printInfo); + if (ret != -1) + return ret; + + return printOutput(printInfo); +} diff --git a/plugins/check_memory.cpp b/plugins/check_memory.cpp new file mode 100644 index 0000000..1461445 --- /dev/null +++ b/plugins/check_memory.cpp @@ -0,0 +1,215 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <shlwapi.h> +#include <winbase.h> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + double tRam; + double aRam; + double percentFree; + Bunit unit = BunitMB; + bool showUsed; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR ** av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::wvalue<std::wstring>(), "Warning threshold") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold") + ("unit,u", po::wvalue<std::wstring>(), "The unit to use for display (default MB)") + ("show-used,U", "Show used memory instead of the free memory") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines physical memory.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tMEMORY WARNING - 50%% free | memory=2024MB;3000;500;0;4096\n\n" + L"\"MEMORY\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"50%%\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. Performance data will only be displayed when\n" + L"you set at least one threshold\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too.\n" + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) + std::wcout << L"Version: " << VERSION << '\n'; + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + printInfo.warn.legal = !printInfo.warn.legal; + } + + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + printInfo.crit.legal = !printInfo.crit.legal; + } + + l_Debug = vm.count("debug") > 0; + + if (vm.count("unit")) { + try { + printInfo.unit = parseBUnit(vm["unit"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("show-used")) { + printInfo.showUsed = true; + printInfo.warn.legal = true; + printInfo.crit.legal = true; + } + + return -1; +} + +static int printOutput(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + std::wcout << L"MEMORY "; + + double currentValue; + + if (!printInfo.showUsed) + currentValue = printInfo.aRam; + else + currentValue = printInfo.tRam - printInfo.aRam; + + if (printInfo.warn.rend(currentValue, printInfo.tRam)) + state = WARNING; + + if (printInfo.crit.rend(currentValue, printInfo.tRam)) + state = CRITICAL; + + std::wcout << stateToString(state); + + if (!printInfo.showUsed) + std::wcout << " - " << printInfo.percentFree << L"% free"; + else + std::wcout << " - " << 100 - printInfo.percentFree << L"% used"; + + std::wcout << "| 'memory'=" << currentValue << BunitStr(printInfo.unit) << L";" + << printInfo.warn.pString(printInfo.tRam) << L";" << printInfo.crit.pString(printInfo.tRam) + << L";0;" << printInfo.tRam << '\n'; + + return state; +} + +static int check_memory(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Accessing memory statistics via MemoryStatus" << '\n'; + + MEMORYSTATUSEX memBuf; + memBuf.dwLength = sizeof(memBuf); + GlobalMemoryStatusEx(&memBuf); + + printInfo.tRam = round((memBuf.ullTotalPhys / pow(1024.0, printInfo.unit) * pow(10.0, printInfo.unit))) / pow(10.0, printInfo.unit); + printInfo.aRam = round((memBuf.ullAvailPhys / pow(1024.0, printInfo.unit) * pow(10.0, printInfo.unit))) / pow(10.0, printInfo.unit); + printInfo.percentFree = 100.0 * memBuf.ullAvailPhys / memBuf.ullTotalPhys; + + if (l_Debug) + std::wcout << L"Found memBuf.dwTotalPhys: " << memBuf.ullTotalPhys << '\n' + << L"Found memBuf.dwAvailPhys: " << memBuf.ullAvailPhys << '\n'; + + return -1; +} + +int wmain(int argc, WCHAR **argv) +{ + printInfoStruct printInfo = {}; + po::variables_map vm; + + int ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + ret = check_memory(printInfo); + if (ret != -1) + return ret; + + return printOutput(printInfo); +} diff --git a/plugins/check_network.cpp b/plugins/check_network.cpp new file mode 100644 index 0000000..e21607a --- /dev/null +++ b/plugins/check_network.cpp @@ -0,0 +1,374 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#define WIN32_LEAN_AND_MEAN + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <vector> +#include <map> +#include <windows.h> +#include <pdh.h> +#include <shlwapi.h> +#include <iostream> +#include <pdhmsg.h> +#include <winsock2.h> +#include <iphlpapi.h> + +#define VERSION 1.2 + +namespace po = boost::program_options; + +struct nInterface +{ + std::wstring name; + LONG BytesInSec, BytesOutSec; + nInterface(std::wstring p) + : name(p) + { } +}; + +struct printInfoStruct +{ + threshold warn; + threshold crit; +}; + +static bool l_Debug; +static bool l_NoISATAP; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc("Options"); + + desc.add_options() + ("help,h", "print usage and exit") + ("version,V", "print version and exit") + ("debug,d", "Verbose/Debug output") + ("noisatap,n", "Don't show ISATAP interfaces in output") + ("warning,w", po::wvalue<std::wstring>(), "warning value") + ("critical,c", po::wvalue<std::wstring>(), "critical value") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines network performance.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tNETWORK WARNING 1131B/s | network=1131B;1000;7000;0\n\n" + L"\"NETWORK\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"1131B/s\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. Performance data will only be displayed when\n" + L"you set at least one threshold\n\n" + L"This program will also print out additional performance data interface\n" + L"by interface\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"All of these options work with the critical threshold \"-c\" too." + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) + std::cout << "Version: " << VERSION << '\n'; + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + l_Debug = vm.count("debug") > 0; + l_NoISATAP = vm.count("noisatap") > 0; + + return -1; +} + +static int printOutput(printInfoStruct& printInfo, const std::vector<nInterface>& vInterfaces, const std::map<std::wstring, std::wstring>& mapNames) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + long tIn = 0, tOut = 0; + std::wstringstream tss; + state state = OK; + + std::map<std::wstring, std::wstring>::const_iterator mapIt; + std::wstring wsFriendlyName; + + for (std::vector<nInterface>::const_iterator it = vInterfaces.begin(); it != vInterfaces.end(); ++it) { + tIn += it->BytesInSec; + tOut += it->BytesOutSec; + if (l_Debug) + std::wcout << "Getting friendly name of " << it->name << '\n'; + mapIt = mapNames.find(it->name); + if (mapIt != mapNames.end()) { + if (l_Debug) + std::wcout << "\tIs " << mapIt->second << '\n'; + wsFriendlyName = mapIt->second; + } else { + if (l_Debug) + std::wcout << "\tNo friendly name found, using adapter name\n"; + wsFriendlyName = it->name; + } + if (wsFriendlyName.find(L"isatap") != std::wstring::npos && l_NoISATAP) { + if (l_Debug) + std::wcout << "\tSkipping isatap interface " << wsFriendlyName << "\n"; + continue; + } else { + boost::algorithm::replace_all(wsFriendlyName, "'", "''"); + tss << L"'" << wsFriendlyName << L"_in'=" << it->BytesInSec << L"B '" << wsFriendlyName << L"_out'=" << it->BytesOutSec << L"B "; + } + } + + if (printInfo.warn.rend(tIn + tOut)) + state = WARNING; + if (printInfo.crit.rend(tIn + tOut)) + state = CRITICAL; + + std::wcout << "NETWORK "; + + switch (state) { + case OK: + std::wcout << L"OK"; + break; + case WARNING: + std::wcout << L"WARNING"; + break; + case CRITICAL: + std::wcout << L"CRITICAL"; + break; + } + + std::wcout << " " << tIn + tOut << L"B/s | " + << L"'network'=" << tIn + tOut << L"B;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";" << L"0; " + << L"'network_in'=" << tIn << L"B 'network_out'=" << tOut << L"B " + << tss.str() << '\n'; + + return state; +} + +static int check_network(std::vector<nInterface>& vInterfaces) +{ + + if (l_Debug) + std::wcout << L"Creating Query and adding counters" << '\n'; + + PDH_FMT_COUNTERVALUE_ITEM *pDisplayValuesIn = NULL, *pDisplayValuesOut = NULL; + + PDH_HQUERY phQuery; + PDH_STATUS err = PdhOpenQuery(NULL, NULL, &phQuery); + if (!SUCCEEDED(err)) + goto die; + + const WCHAR *perfIn = L"\\Network Interface(*)\\Bytes Received/sec"; + PDH_HCOUNTER phCounterIn; + err = PdhAddEnglishCounter(phQuery, perfIn, NULL, &phCounterIn); + if (!SUCCEEDED(err)) + goto die; + + const WCHAR *perfOut = L"\\Network Interface(*)\\Bytes Sent/sec"; + PDH_HCOUNTER phCounterOut; + err = PdhAddEnglishCounter(phQuery, perfOut, NULL, &phCounterOut); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Collecting first batch of query data" << '\n'; + + err = PdhCollectQueryData(phQuery); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Sleep for one second" << '\n'; + + Sleep(1000); + + if (l_Debug) + std::wcout << L"Collecting second batch of query data" << '\n'; + + err = PdhCollectQueryData(phQuery); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Creating formatted counter arrays" << '\n'; + + DWORD dwItemCount; + DWORD dwBufferSizeIn = 0; + err = PdhGetFormattedCounterArray(phCounterIn, PDH_FMT_LONG, &dwBufferSizeIn, &dwItemCount, pDisplayValuesIn); + if (err == PDH_MORE_DATA || SUCCEEDED(err)) + pDisplayValuesIn = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwItemCount*dwBufferSizeIn]); + else + goto die; + + DWORD dwBufferSizeOut = 0; + err = PdhGetFormattedCounterArray(phCounterOut, PDH_FMT_LONG, &dwBufferSizeOut, &dwItemCount, pDisplayValuesOut); + if (err == PDH_MORE_DATA || SUCCEEDED(err)) + pDisplayValuesOut = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwItemCount*dwBufferSizeIn]); + else + goto die; + + err = PdhGetFormattedCounterArray(phCounterIn, PDH_FMT_LONG, &dwBufferSizeIn, &dwItemCount, pDisplayValuesIn); + if (!SUCCEEDED(err)) + goto die; + + err = PdhGetFormattedCounterArray(phCounterOut, PDH_FMT_LONG, &dwBufferSizeOut, &dwItemCount, pDisplayValuesOut); + if (!SUCCEEDED(err)) + goto die; + + if (l_Debug) + std::wcout << L"Going over counter array" << '\n'; + + for (DWORD i = 0; i < dwItemCount; i++) { + nInterface iface{pDisplayValuesIn[i].szName}; + iface.BytesInSec = pDisplayValuesIn[i].FmtValue.longValue; + iface.BytesOutSec = pDisplayValuesOut[i].FmtValue.longValue; + vInterfaces.push_back(iface); + + if (l_Debug) + std::wcout << L"Collected interface " << pDisplayValuesIn[i].szName << '\n'; + } + + if (l_Debug) + std::wcout << L"Finished collection. Cleaning up and returning" << '\n'; + + if (phQuery) + PdhCloseQuery(phQuery); + + delete reinterpret_cast<BYTE *>(pDisplayValuesIn); + delete reinterpret_cast<BYTE *>(pDisplayValuesOut); + + return -1; +die: + printErrorInfo(err); + if (phQuery) + PdhCloseQuery(phQuery); + + delete reinterpret_cast<BYTE *>(pDisplayValuesIn); + delete reinterpret_cast<BYTE *>(pDisplayValuesOut); + + return 3; +} + +static bool mapSystemNamesToFamiliarNames(std::map<std::wstring, std::wstring>& mapNames) +{ + /* + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL; + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL; + PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsServer = NULL; + PIP_ADAPTER_PREFIX pPrefix = NULL; + */ + ULONG outBufLen = 15000; //15KB as suggestet by msdn of GetAdaptersAddresses + + if (l_Debug) + std::wcout << "Mapping adapter system names to friendly names\n"; + + PIP_ADAPTER_ADDRESSES pAddresses; + + unsigned int Iterations = 0; + DWORD dwRetVal = 0; + + do { + pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(new BYTE[outBufLen]); + + dwRetVal = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + delete[]pAddresses; + pAddresses = NULL; + } else + break; + } while (++Iterations < 3); + + if (dwRetVal != NO_ERROR) { + std::wcout << "Failed to collect friendly adapter names\n"; + delete[]pAddresses; + return false; + } + + for (PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses; pCurrAddresses; pCurrAddresses = pCurrAddresses->Next) { + if (l_Debug) + std::wcout << "Got: " << pCurrAddresses->Description << " -- " << pCurrAddresses->FriendlyName << '\n'; + + mapNames[pCurrAddresses->Description] = pCurrAddresses->FriendlyName; + } + + delete[]pAddresses; + return true; +} + +int wmain(int argc, WCHAR **argv) +{ + std::vector<nInterface> vInterfaces; + std::map<std::wstring, std::wstring> mapNames; + printInfoStruct printInfo; + po::variables_map vm; + + int ret = parseArguments(argc, argv, vm, printInfo); + + if (ret != -1) + return ret; + + if (!mapSystemNamesToFamiliarNames(mapNames)) + return 3; + + ret = check_network(vInterfaces); + if (ret != -1) + return ret; + + return printOutput(printInfo, vInterfaces, mapNames); +} diff --git a/plugins/check_nscp_api.cpp b/plugins/check_nscp_api.cpp new file mode 100644 index 0000000..3f6843e --- /dev/null +++ b/plugins/check_nscp_api.cpp @@ -0,0 +1,512 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "icinga-version.h" /* include VERSION */ + +// ensure to include base first +#include "base/i2-base.hpp" +#include "base/application.hpp" +#include "base/json.hpp" +#include "base/string.hpp" +#include "base/logger.hpp" +#include "base/exception.hpp" +#include "base/utility.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" +#include "base/stream.hpp" +#include "base/tcpsocket.hpp" /* include global icinga::Connect */ +#include "base/tlsstream.hpp" +#include "base/base64.hpp" +#include "remote/url.hpp" +#include <remote/url-characters.hpp> +#include <boost/program_options.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/range/algorithm/remove_if.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/asio/ssl/context.hpp> +#include <boost/beast.hpp> +#include <cstddef> +#include <cstring> +#include <iostream> + +using namespace icinga; +namespace po = boost::program_options; + +static bool l_Debug; + +/** + * Prints an Icinga plugin API compliant output, including error handling. + * + * @param result + * + * @return Status code for exit() + */ +static int FormatOutput(const Dictionary::Ptr& result) +{ + if (!result) { + std::cerr << "UNKNOWN: No data received.\n"; + return 3; + } + + if (l_Debug) + std::cout << "\tJSON Body:\n" << result->ToString() << '\n'; + + Array::Ptr payloads = result->Get("payload"); + if (!payloads) { + std::cerr << "UNKNOWN: Answer format error: Answer is missing 'payload'.\n"; + return 3; + } + + if (payloads->GetLength() == 0) { + std::cerr << "UNKNOWN: Answer format error: 'payload' was empty.\n"; + return 3; + } + + if (payloads->GetLength() > 1) { + std::cerr << "UNKNOWN: Answer format error: Multiple payloads are not supported."; + return 3; + } + + Dictionary::Ptr payload; + + try { + payload = payloads->Get(0); + } catch (const std::exception&) { + std::cerr << "UNKNOWN: Answer format error: 'payload' was not a Dictionary.\n"; + return 3; + } + + Array::Ptr lines; + + try { + lines = payload->Get("lines"); + } catch (const std::exception&) { + std::cerr << "UNKNOWN: Answer format error: 'payload' is missing 'lines'.\n"; + return 3; + } + + if (!lines) { + std::cerr << "UNKNOWN: Answer format error: 'lines' is Null.\n"; + return 3; + } + + std::stringstream ssout; + + ObjectLock olock(lines); + + for (const Value& vline : lines) { + Dictionary::Ptr line; + + try { + line = vline; + } catch (const std::exception&) { + std::cerr << "UNKNOWN: Answer format error: 'lines' entry was not a Dictionary.\n"; + return 3; + } + + if (!line) { + std::cerr << "UNKNOWN: Answer format error: 'lines' entry was Null.\n"; + return 3; + } + + ssout << payload->Get("command") << ' ' << line->Get("message") << " | "; + + if (!line->Contains("perf")) { + ssout << '\n'; + break; + } + + Array::Ptr perfs = line->Get("perf"); + + ObjectLock olock(perfs); + + for (const Dictionary::Ptr& perf : perfs) { + ssout << "'" << perf->Get("alias") << "'="; + + Dictionary::Ptr values = perf->Get("float_value"); + + if (perf->Contains("int_value")) + values = perf->Get("int_value"); + + ssout << values->Get("value") << values->Get("unit") << ';' << values->Get("warning") << ';' << values->Get("critical"); + + if (values->Contains("minimum") || values->Contains("maximum")) { + ssout << ';'; + + if (values->Contains("minimum")) + ssout << values->Get("minimum"); + + if (values->Contains("maximum")) + ssout << ';' << values->Get("maximum"); + } + + ssout << ' '; + } + + ssout << '\n'; + } + + std::map<String, unsigned int> stateMap = { + { "OK", 0 }, + { "WARNING", 1}, + { "CRITICAL", 2}, + { "UNKNOWN", 3} + }; + + String state = static_cast<String>(payload->Get("result")).ToUpper(); + + auto it = stateMap.find(state); + + if (it == stateMap.end()) { + std::cerr << "UNKNOWN Answer format error: 'result' was not a known state.\n"; + return 3; + } + + std::cout << ssout.rdbuf(); + + return it->second; +} + +/** + * Connects to host:port and performs a TLS shandshake + * + * @param host To connect to. + * @param port To connect to. + * + * @returns AsioTlsStream pointer for future HTTP connections. + */ +static Shared<AsioTlsStream>::Ptr Connect(const String& host, const String& port) +{ + Shared<boost::asio::ssl::context>::Ptr sslContext; + + try { + sslContext = MakeAsioSslContext(Empty, Empty, Empty); //TODO: Add support for cert, key, ca parameters + } catch(const std::exception& ex) { + Log(LogCritical, "DebugConsole") + << "Cannot make SSL context: " << ex.what(); + throw; + } + + Shared<AsioTlsStream>::Ptr stream = Shared<AsioTlsStream>::Make(IoEngine::Get().GetIoContext(), *sslContext, host); + + try { + icinga::Connect(stream->lowest_layer(), host, port); + } catch (const std::exception& ex) { + Log(LogWarning, "DebugConsole") + << "Cannot connect to REST API on host '" << host << "' port '" << port << "': " << ex.what(); + throw; + } + + auto& tlsStream (stream->next_layer()); + + try { + tlsStream.handshake(tlsStream.client); + } catch (const std::exception& ex) { + Log(LogWarning, "DebugConsole") + << "TLS handshake with host '" << host << "' failed: " << ex.what(); + throw; + } + + return std::move(stream); +} + +static const char l_ReasonToInject[2] = {' ', 'X'}; + +template<class MutableBufferSequence> +static inline +boost::asio::mutable_buffer GetFirstNonZeroBuffer(const MutableBufferSequence& mbs) +{ + namespace asio = boost::asio; + + auto end (asio::buffer_sequence_end(mbs)); + + for (auto current (asio::buffer_sequence_begin(mbs)); current != end; ++current) { + asio::mutable_buffer buf (*current); + + if (buf.size() > 0u) { + return std::move(buf); + } + } + + return {}; +} + +/** + * Workaround for <https://github.com/mickem/nscp/issues/610>. + */ +template<class SyncReadStream> +class HttpResponseReasonInjector +{ +public: + inline HttpResponseReasonInjector(SyncReadStream& stream) + : m_Stream(stream), m_ReasonHasBeenInjected(false), m_StashedData(nullptr) + { + } + + HttpResponseReasonInjector(const HttpResponseReasonInjector&) = delete; + HttpResponseReasonInjector(HttpResponseReasonInjector&&) = delete; + HttpResponseReasonInjector& operator=(const HttpResponseReasonInjector&) = delete; + HttpResponseReasonInjector& operator=(HttpResponseReasonInjector&&) = delete; + + template<class MutableBufferSequence> + size_t read_some(const MutableBufferSequence& mbs) + { + boost::system::error_code ec; + size_t amount = read_some(mbs, ec); + + if (ec) { + throw boost::system::system_error(ec); + } + + return amount; + } + + template<class MutableBufferSequence> + size_t read_some(const MutableBufferSequence& mbs, boost::system::error_code& ec) + { + auto mb (GetFirstNonZeroBuffer(mbs)); + + if (m_StashedData) { + size_t amount = 0; + auto end ((char*)mb.data() + mb.size()); + + for (auto current ((char*)mb.data()); current < end; ++current) { + *current = *m_StashedData; + + ++m_StashedData; + ++amount; + + if (m_StashedData == (char*)m_StashedDataBuf + (sizeof(m_StashedDataBuf) / sizeof(m_StashedDataBuf[0]))) { + m_StashedData = nullptr; + break; + } + } + + return amount; + } + + size_t amount = m_Stream.read_some(mb, ec); + + if (!ec && !m_ReasonHasBeenInjected) { + auto end ((char*)mb.data() + amount); + + for (auto current ((char*)mb.data()); current < end; ++current) { + if (*current == '\r') { + auto last (end - 1); + + for (size_t i = sizeof(l_ReasonToInject) / sizeof(l_ReasonToInject[0]); i;) { + m_StashedDataBuf[--i] = *last; + + if (last > current) { + memmove(current + 1, current, last - current); + } + + *current = l_ReasonToInject[i]; + } + + m_ReasonHasBeenInjected = true; + m_StashedData = m_StashedDataBuf; + + break; + } + } + } + + return amount; + } + +private: + SyncReadStream& m_Stream; + bool m_ReasonHasBeenInjected; + char m_StashedDataBuf[sizeof(l_ReasonToInject) / sizeof(l_ReasonToInject[0])]; + char* m_StashedData; +}; + +/** + * Queries the given endpoint and host:port and retrieves data. + * + * @param host To connect to. + * @param port To connect to. + * @param password For auth header (required). + * @param endpoint Caller must construct the full endpoint including the command query. + * + * @return Dictionary de-serialized from JSON data. + */ + +static Dictionary::Ptr FetchData(const String& host, const String& port, const String& password, + const String& endpoint) +{ + namespace beast = boost::beast; + namespace http = beast::http; + + Shared<AsioTlsStream>::Ptr tlsStream; + + try { + tlsStream = Connect(host, port); + } catch (const std::exception& ex) { + std::cerr << "Connection error: " << ex.what(); + throw ex; + } + + Url::Ptr url; + + try { + url = new Url(endpoint); + } catch (const std::exception& ex) { + std::cerr << "URL error: " << ex.what(); + throw ex; + } + + url->SetScheme("https"); + url->SetHost(host); + url->SetPort(port); + + // NSClient++ uses `time=1m&time=5m` instead of `time[]=1m&time[]=5m` + url->SetArrayFormatUseBrackets(false); + + http::request<http::string_body> request (http::verb::get, std::string(url->Format(true)), 10); + + request.set(http::field::user_agent, "Icinga/check_nscp_api/" + String(VERSION)); + request.set(http::field::host, host + ":" + port); + + request.set(http::field::accept, "application/json"); + request.set("password", password); + + if (l_Debug) { + std::cout << "Sending request to " << url->Format(false, false) << "'.\n"; + } + + try { + http::write(*tlsStream, request); + tlsStream->flush(); + } catch (const std::exception& ex) { + std::cerr << "Cannot write HTTP request to REST API at URL '" << url->Format(false, false) << "': " << ex.what(); + throw ex; + } + + beast::flat_buffer buffer; + http::parser<false, http::string_body> p; + + try { + HttpResponseReasonInjector<decltype(*tlsStream)> reasonInjector (*tlsStream); + http::read(reasonInjector, buffer, p); + } catch (const std::exception &ex) { + BOOST_THROW_EXCEPTION(ScriptError(String("Error reading HTTP response data: ") + ex.what())); + } + + String body (std::move(p.get().body())); + + if (l_Debug) + std::cout << "Received body from NSCP: '" << body << "'." << std::endl; + + // Add some rudimentary error handling. + if (body.IsEmpty()) { + String message = "No body received. Ensure that connection parameters are good and check the NSCP logs."; + BOOST_THROW_EXCEPTION(ScriptError(message)); + } + + Dictionary::Ptr jsonResponse; + + try { + jsonResponse = JsonDecode(body); + } catch (const std::exception& ex) { + String message = "Cannot parse JSON response body '" + body + "', error: " + ex.what(); + BOOST_THROW_EXCEPTION(ScriptError(message)); + } + + return jsonResponse; +} + +/** + * Main function + * + * @param argc + * @param argv + * @return exit code + */ +int main(int argc, char **argv) +{ + po::variables_map vm; + po::options_description desc("Options"); + + desc.add_options() + ("help,h", "Print usage message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("host,H", po::value<std::string>()->required(), "REQUIRED: NSCP API Host") + ("port,P", po::value<std::string>()->default_value("8443"), "NSCP API Port (Default: 8443)") + ("password", po::value<std::string>()->required(), "REQUIRED: NSCP API Password") + ("query,q", po::value<std::string>()->required(), "REQUIRED: NSCP API Query endpoint") + ("arguments,a", po::value<std::vector<std::string>>()->multitoken(), "NSCP API Query arguments for the endpoint"); + + po::command_line_parser parser(argc, argv); + + try { + po::store( + parser + .options(desc) + .style( + po::command_line_style::unix_style | + po::command_line_style::allow_long_disguise) + .run(), + vm); + + if (vm.count("version")) { + std::cout << "Version: " << VERSION << '\n'; + Application::Exit(0); + } + + if (vm.count("help")) { + std::cout << argv[0] << " Help\n\tVersion: " << VERSION << '\n'; + std::cout << "check_nscp_api is a program used to query the NSClient++ API.\n"; + std::cout << desc; + std::cout << "For detailed information on possible queries and their arguments refer to the NSClient++ documentation.\n"; + Application::Exit(0); + } + + vm.notify(); + } catch (const std::exception& e) { + std::cout << e.what() << '\n' << desc << '\n'; + Application::Exit(3); + } + + l_Debug = vm.count("debug") > 0; + + // Initialize logger + if (l_Debug) + Logger::SetConsoleLogSeverity(LogDebug); + else + Logger::SetConsoleLogSeverity(LogWarning); + + // Create the URL string and escape certain characters since Url() follows RFC 3986 + String endpoint = "/query/" + vm["query"].as<std::string>(); + if (!vm.count("arguments")) + endpoint += '/'; + else { + endpoint += '?'; + for (const String& argument : vm["arguments"].as<std::vector<std::string>>()) { + String::SizeType pos = argument.FindFirstOf("="); + if (pos == String::NPos) + endpoint += Utility::EscapeString(argument, ACQUERY_ENCODE, false); + else { + String key = argument.SubStr(0, pos); + String val = argument.SubStr(pos + 1); + endpoint += Utility::EscapeString(key, ACQUERY_ENCODE, false) + "=" + Utility::EscapeString(val, ACQUERY_ENCODE, false); + } + endpoint += '&'; + } + } + + Dictionary::Ptr result; + + try { + result = FetchData(vm["host"].as<std::string>(), vm["port"].as<std::string>(), + vm["password"].as<std::string>(), endpoint); + } catch (const std::exception& ex) { + std::cerr << "UNKNOWN - " << ex.what(); + exit(3); + } + + // Application::Exit() is the clean way to exit after calling InitializeBase() + Application::Exit(FormatOutput(result)); + return 255; +} diff --git a/plugins/check_perfmon.cpp b/plugins/check_perfmon.cpp new file mode 100644 index 0000000..0f94b12 --- /dev/null +++ b/plugins/check_perfmon.cpp @@ -0,0 +1,387 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <vector> +#include <windows.h> +#include <pdh.h> +#include <pdhmsg.h> +#include <shlwapi.h> + +#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<std::wstring>(), "Warning thershold") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold") + ("performance-counter,P", po::wvalue<std::wstring>(), "The performance counter string to use") + ("performance-wait", po::value<DWORD>(), "Sleep in milliseconds between the two perfomance querries (Default: 1000ms)") + ("fmt-countertype", po::wvalue<std::wstring>(), "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<std::wstring>(), "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<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::wcout << e.what() << '\n'; + return false; + } + } + + if (vm.count("critical")) { + try { + printInfo.tCrit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::wcout << e.what() << '\n'; + return false; + } + } + + if (vm.count("fmt-countertype")) { + if (!vm["fmt-countertype"].as<std::wstring>().compare(L"int64")) + printInfo.dwRequestedType = PDH_FMT_LARGE; + else if (!vm["fmt-countertype"].as<std::wstring>().compare(L"long")) + printInfo.dwRequestedType = PDH_FMT_LONG; + else if (vm["fmt-countertype"].as<std::wstring>().compare(L"double")) { + std::wcout << "Unknown value type " << vm["fmt-countertype"].as<std::wstring>() << '\n'; + return false; + } + } + + if (vm.count("performance-counter")) + printInfo.wsFullPath = vm["performance-counter"].as<std::wstring>(); + + if (vm.count("performance-wait")) + printInfo.dwPerformanceWait = vm["performance-wait"].as<DWORD>(); + + return true; +} + +static bool getInstancesAndCountersOfObject(const std::wstring& wsObject, + std::vector<std::wstring>& vecInstances, std::vector<std::wstring>& 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<WCHAR> mszCounterList(dwCounterListLength + 1); + std::vector<WCHAR> 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<WCHAR> 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<std::wstring> 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<PDH_FMT_COUNTERVALUE_ITEM*>(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<std::wstring>() << "'="; + 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<std::wstring>() : 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<std::wstring>() : pi.wsFullPath) + << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; + return 1; + } + + std::wcout << "PERFMON OK for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : 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; +} diff --git a/plugins/check_ping.cpp b/plugins/check_ping.cpp new file mode 100644 index 0000000..c918d92 --- /dev/null +++ b/plugins/check_ping.cpp @@ -0,0 +1,508 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN //else winsock will be included with windows.h and conflict with winsock2 +#endif + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <winsock2.h> +#include <iphlpapi.h> +#include <icmpapi.h> +#include <shlwapi.h> +#include <ws2ipdef.h> +#include <mstcpip.h> +#include <ws2tcpip.h> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct response +{ + double avg; + unsigned int pMin = 0; + unsigned int pMax = 0; + unsigned int dropped = 0; +}; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + threshold wpl; + threshold cpl; + std::wstring host; + std::wstring ip; + bool ipv6 = false; + int timeout = 1000; + int num = 5; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print usage message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("host,H", po::wvalue<std::wstring>()->required(), "Target hostname or IP. If an IPv6 address is given, the '-6' option must be set") + (",4", "--Host is an IPv4 address or if it's a hostname: Resolve it to an IPv4 address (default)") + (",6", "--Host is an IPv6 address or if it's a hostname: Resolve it to an IPv6 address") + ("timeout,t", po::value<int>(), "Specify timeout for requests in ms (default=1000)") + ("packets,p", po::value<int>(), "Declare ping count (default=5)") + ("warning,w", po::wvalue<std::wstring>(), "Warning values: rtt,package loss") + ("critical,c", po::wvalue<std::wstring>(), "Critical values: rtt,package loss") + ; + + 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 & + ~po::command_line_style::allow_guessing + ) + .run(), + vm); + vm.notify(); + } catch (const std::exception& e) { + std::cout << e.what() << '\n' << desc << '\n'; + return 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to ping an ip4 address.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will take at least timeout times number of pings to run\n" + L"Then it will output a string looking something like this:\n\n" + L"\tPING WARNING RTA: 72ms Packet loss: 20%% | ping=72ms;40;80;71;77 pl=20%%;20;50;0;100\n\n" + L"\"PING\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"RTA: 72ms Packet loss: 20%%\" the relevant information.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. \n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too.", + progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) { + std::cout << progName << " Version: " << VERSION << '\n'; + return 0; + } + + if (vm.count("-4") && vm.count("-6")) { + std::cout << "Conflicting options \"4\" and \"6\"" << '\n'; + return 3; + } + + printInfo.ipv6 = vm.count("-6") > 0; + + if (vm.count("warning")) { + std::vector<std::wstring> sVec = splitMultiOptions(vm["warning"].as<std::wstring>()); + if (sVec.size() != 2) { + std::cout << "Wrong format for warning thresholds" << '\n'; + return 3; + } + try { + printInfo.warn = threshold(*sVec.begin()); + printInfo.wpl = threshold(sVec.back()); + if (!printInfo.wpl.perc) { + std::cout << "Packet loss must be percentage" << '\n'; + return 3; + } + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("critical")) { + std::vector<std::wstring> sVec = splitMultiOptions(vm["critical"].as<std::wstring>()); + if (sVec.size() != 2) { + std::cout << "Wrong format for critical thresholds" << '\n'; + return 3; + } + try { + printInfo.crit = threshold(*sVec.begin()); + printInfo.cpl = threshold(sVec.back()); + if (!printInfo.wpl.perc) { + std::cout << "Packet loss must be percentage" << '\n'; + return 3; + } + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("timeout")) + printInfo.timeout = vm["timeout"].as<int>(); + + if (vm.count("packets")) + printInfo.num = vm["packets"].as<int>(); + + printInfo.host = vm["host"].as<std::wstring>(); + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(printInfoStruct& printInfo, response& response) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + double plp = ((double)response.dropped / printInfo.num) * 100.0; + + if (printInfo.warn.rend(response.avg) || printInfo.wpl.rend(plp)) + state = WARNING; + + if (printInfo.crit.rend(response.avg) || printInfo.cpl.rend(plp)) + state = CRITICAL; + + std::wstringstream perf; + perf << L"rta=" << response.avg << L"ms;" << printInfo.warn.pString() << L";" + << printInfo.crit.pString() << L";0;" << " pl=" << removeZero(plp) << "%;" + << printInfo.wpl.pString() << ";" << printInfo.cpl.pString() << ";0;100"; + + if (response.dropped == printInfo.num) { + std::wcout << L"PING CRITICAL ALL CONNECTIONS DROPPED | " << perf.str() << '\n'; + return 2; + } + + std::wcout << L"PING "; + + switch (state) { + case OK: + std::wcout << L"OK"; + break; + case WARNING: + std::wcout << L"WARNING"; + break; + case CRITICAL: + std::wcout << L"CRITICAL"; + break; + } + + std::wcout << L" RTA: " << response.avg << L"ms Packet loss: " << removeZero(plp) << "% | " << perf.str() << '\n'; + + return state; +} + +static bool resolveHostname(const std::wstring& hostname, bool ipv6, std::wstring& ipaddr) +{ + ADDRINFOW hints; + ZeroMemory(&hints, sizeof(hints)); + + if (ipv6) + hints.ai_family = AF_INET6; + else + hints.ai_family = AF_INET; + + if (l_Debug) + std::wcout << L"Resolving hostname \"" << hostname << L"\"\n"; + + ADDRINFOW *result = NULL; + DWORD ret = GetAddrInfoW(hostname.c_str(), NULL, &hints, &result); + + if (ret) { + std::wcout << L"Failed to resolve hostname. Error " << ret << L": " << formatErrorInfo(ret) << L"\n"; + return false; + } + + wchar_t ipstringbuffer[46]; + + if (ipv6) { + struct sockaddr_in6 *address6 = (struct sockaddr_in6 *)result->ai_addr; + InetNtop(AF_INET6, &address6->sin6_addr, ipstringbuffer, 46); + } + else { + struct sockaddr_in *address4 = (struct sockaddr_in *)result->ai_addr; + InetNtop(AF_INET, &address4->sin_addr, ipstringbuffer, 46); + } + + if (l_Debug) + std::wcout << L"Resolved to \"" << ipstringbuffer << L"\"\n"; + + ipaddr = ipstringbuffer; + return true; +} + +static int check_ping4(const printInfoStruct& pi, response& response) +{ + if (l_Debug) + std::wcout << L"Parsing ip address" << '\n'; + + in_addr ipDest4; + LPCWSTR term; + if (RtlIpv4StringToAddress(pi.ip.c_str(), TRUE, &term, &ipDest4) == STATUS_INVALID_PARAMETER) { + std::wcout << pi.ip << " is not a valid ip address\n"; + return 3; + } + + if (*term != L'\0') { + std::wcout << pi.ip << " is not a valid ip address\n"; + return 3; + } + + if (l_Debug) + std::wcout << L"Creating Icmp File\n"; + + HANDLE hIcmp; + if ((hIcmp = IcmpCreateFile()) == INVALID_HANDLE_VALUE) + goto die; + + DWORD dwRepSize = sizeof(ICMP_ECHO_REPLY) + 8; + void *repBuf = reinterpret_cast<VOID *>(new BYTE[dwRepSize]); + + if (repBuf == NULL) + goto die; + + unsigned int rtt = 0; + int num = pi.num; + + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + do { + LARGE_INTEGER timer1; + QueryPerformanceCounter(&timer1); + + if (l_Debug) + std::wcout << L"Sending Icmp echo\n"; + + if (!IcmpSendEcho2(hIcmp, NULL, NULL, NULL, ipDest4.S_un.S_addr, + NULL, 0, NULL, repBuf, dwRepSize, pi.timeout)) { + response.dropped++; + if (l_Debug) + std::wcout << L"Dropped: Response was 0" << '\n'; + continue; + } + + if (l_Debug) + std::wcout << "Ping recieved" << '\n'; + + PICMP_ECHO_REPLY pEchoReply = static_cast<PICMP_ECHO_REPLY>(repBuf); + + if (pEchoReply->Status != IP_SUCCESS) { + response.dropped++; + if (l_Debug) + std::wcout << L"Dropped: echo reply status " << pEchoReply->Status << '\n'; + continue; + } + + if (l_Debug) + std::wcout << L"Recorded rtt of " << pEchoReply->RoundTripTime << '\n'; + + rtt += pEchoReply->RoundTripTime; + if (response.pMin == 0 || pEchoReply->RoundTripTime < response.pMin) + response.pMin = pEchoReply->RoundTripTime; + else if (pEchoReply->RoundTripTime > response.pMax) + response.pMax = pEchoReply->RoundTripTime; + + LARGE_INTEGER timer2; + QueryPerformanceCounter(&timer2); + + if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout) + Sleep((DWORD) (pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart))); + } while (--num); + + if (l_Debug) + std::wcout << L"All pings sent. Cleaning up and returning" << '\n'; + + if (hIcmp) + IcmpCloseHandle(hIcmp); + if (repBuf) + delete reinterpret_cast<VOID *>(repBuf); + + response.avg = ((double)rtt / pi.num); + + return -1; + +die: + printErrorInfo(); + if (hIcmp) + IcmpCloseHandle(hIcmp); + if (repBuf) + delete reinterpret_cast<VOID *>(repBuf); + + return 3; +} + +static int check_ping6(const printInfoStruct& pi, response& response) +{ + DWORD dwRepSize = sizeof(ICMPV6_ECHO_REPLY) + 8; + void *repBuf = reinterpret_cast<void *>(new BYTE[dwRepSize]); + + int num = pi.num; + unsigned int rtt = 0; + + if (l_Debug) + std::wcout << L"Parsing ip address" << '\n'; + + sockaddr_in6 ipDest6; + if (RtlIpv6StringToAddressEx(pi.ip.c_str(), &ipDest6.sin6_addr, &ipDest6.sin6_scope_id, &ipDest6.sin6_port)) { + std::wcout << pi.ip << " is not a valid ipv6 address" << '\n'; + return 3; + } + + ipDest6.sin6_family = AF_INET6; + + sockaddr_in6 ipSource6; + ipSource6.sin6_addr = in6addr_any; + ipSource6.sin6_family = AF_INET6; + ipSource6.sin6_flowinfo = 0; + ipSource6.sin6_port = 0; + + if (l_Debug) + std::wcout << L"Creating Icmp File" << '\n'; + + HANDLE hIcmp = Icmp6CreateFile(); + if (hIcmp == INVALID_HANDLE_VALUE) { + printErrorInfo(GetLastError()); + + if (hIcmp) + IcmpCloseHandle(hIcmp); + + if (repBuf) + delete reinterpret_cast<BYTE *>(repBuf); + + return 3; + } else { + IP_OPTION_INFORMATION ipInfo = { 30, 0, 0, 0, NULL }; + + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + do { + LARGE_INTEGER timer1; + QueryPerformanceCounter(&timer1); + + if (l_Debug) + std::wcout << L"Sending Icmp echo" << '\n'; + + if (!Icmp6SendEcho2(hIcmp, NULL, NULL, NULL, &ipSource6, &ipDest6, + NULL, 0, &ipInfo, repBuf, dwRepSize, pi.timeout)) { + response.dropped++; + if (l_Debug) + std::wcout << L"Dropped: Response was 0" << '\n'; + continue; + } + + if (l_Debug) + std::wcout << "Ping recieved" << '\n'; + + Icmp6ParseReplies(repBuf, dwRepSize); + + ICMPV6_ECHO_REPLY *pEchoReply = static_cast<ICMPV6_ECHO_REPLY *>(repBuf); + + if (pEchoReply->Status != IP_SUCCESS) { + response.dropped++; + if (l_Debug) + std::wcout << L"Dropped: echo reply status " << pEchoReply->Status << '\n'; + continue; + } + + rtt += pEchoReply->RoundTripTime; + + if (l_Debug) + std::wcout << L"Recorded rtt of " << pEchoReply->RoundTripTime << '\n'; + + if (response.pMin == 0 || pEchoReply->RoundTripTime < response.pMin) + response.pMin = pEchoReply->RoundTripTime; + else if (pEchoReply->RoundTripTime > response.pMax) + response.pMax = pEchoReply->RoundTripTime; + + LARGE_INTEGER timer2; + QueryPerformanceCounter(&timer2); + + if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout) + Sleep((DWORD) (pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart))); + } while (--num); + + if (l_Debug) + std::wcout << L"All pings sent. Cleaning up and returning" << '\n'; + + if (hIcmp) + IcmpCloseHandle(hIcmp); + + if (repBuf) + delete reinterpret_cast<BYTE *>(repBuf); + + response.avg = ((double)rtt / pi.num); + + return -1; + } +} + +int wmain(int argc, WCHAR **argv) +{ + WSADATA dat; + if (WSAStartup(MAKEWORD(2, 2), &dat)) { + std::cout << "WSAStartup failed\n"; + return 3; + } + + po::variables_map vm; + printInfoStruct printInfo; + if (parseArguments(argc, argv, vm, printInfo) != -1) + return 3; + + if (!resolveHostname(printInfo.host, printInfo.ipv6, printInfo.ip)) + return 3; + + response response; + + if (printInfo.ipv6) { + if (check_ping6(printInfo, response) != -1) + return 3; + } else { + if (check_ping4(printInfo, response) != -1) + return 3; + } + + WSACleanup(); + + return printOutput(printInfo, response); +} diff --git a/plugins/check_procs.cpp b/plugins/check_procs.cpp new file mode 100644 index 0000000..44e2483 --- /dev/null +++ b/plugins/check_procs.cpp @@ -0,0 +1,325 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <windows.h> +#include <shlwapi.h> +#include <tlhelp32.h> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + std::wstring user; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("user,u", po::wvalue<std::wstring>(), "Count only processes of user") + ("warning,w", po::wvalue<std::wstring>(), "Warning threshold") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines processes.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tPROCS WARNING 67 | load=67;50;90;0\n\n" + L"\"PROCS\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"67\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. Performance data will only be displayed when\n" + L"you set at least one threshold\n\n" + L"For \"-user\" option keep in mind you need root to see other users processes\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too." + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) { + std::wcout << "Version: " << VERSION << '\n'; + return 0; + } + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("user")) + printInfo.user = vm["user"].as<std::wstring>(); + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(const int numProcs, printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + if (printInfo.warn.rend(numProcs)) + state = WARNING; + + if (printInfo.crit.rend(numProcs)) + state = CRITICAL; + + std::wstring user; + if (!printInfo.user.empty()) + user = L" processes of user " + printInfo.user; + + std::wcout << L"PROCS "; + + switch (state) { + case OK: + std::wcout << L"OK"; + break; + case WARNING: + std::wcout << L"WARNING"; + break; + case CRITICAL: + std::wcout << L"CRITICAL"; + break; + } + + std::wcout << L" " << numProcs << user << L" | procs=" << numProcs << L";" + << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; + + return state; +} + +static int countProcs() +{ + if (l_Debug) + std::wcout << L"Counting all processes" << '\n'; + + if (l_Debug) + std::wcout << L"Creating snapshot" << '\n'; + + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) + return -1; + + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(PROCESSENTRY32); + + if (l_Debug) + std::wcout << L"Grabbing first proccess" << '\n'; + + if (!Process32First(hProcessSnap, &pe32)) { + CloseHandle(hProcessSnap); + return -1; + } + + if (l_Debug) + std::wcout << L"Counting processes..." << '\n'; + + int numProcs = 0; + + do { + ++numProcs; + } while (Process32Next(hProcessSnap, &pe32)); + + if (l_Debug) + std::wcout << L"Found " << numProcs << L" processes. Cleaning up udn returning" << '\n'; + + CloseHandle(hProcessSnap); + + return numProcs; +} + +static int countProcs(const std::wstring& user) +{ + if (l_Debug) + std::wcout << L"Counting all processes of user" << user << '\n'; + + const WCHAR *wuser = user.c_str(); + int numProcs = 0; + + HANDLE hProcessSnap, hProcess = NULL, hToken = NULL; + PROCESSENTRY32 pe32; + DWORD dwReturnLength, dwAcctName, dwDomainName; + PTOKEN_USER pSIDTokenUser = NULL; + SID_NAME_USE sidNameUse; + LPWSTR AcctName, DomainName; + + if (l_Debug) + std::wcout << L"Creating snapshot" << '\n'; + + hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) + goto die; + + pe32.dwSize = sizeof(PROCESSENTRY32); + + if (l_Debug) + std::wcout << L"Grabbing first proccess" << '\n'; + + if (!Process32First(hProcessSnap, &pe32)) + goto die; + + if (l_Debug) + std::wcout << L"Counting processes..." << '\n'; + + do { + if (l_Debug) + std::wcout << L"Getting process token" << '\n'; + + //get ProcessToken + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID); + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + //Won't count pid 0 (system idle) and 4/8 (Sytem) + continue; + + //Get dwReturnLength in first call + dwReturnLength = 1; + if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwReturnLength) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + continue; + + pSIDTokenUser = reinterpret_cast<PTOKEN_USER>(new BYTE[dwReturnLength]); + memset(pSIDTokenUser, 0, dwReturnLength); + + if (l_Debug) + std::wcout << L"Received token, saving information" << '\n'; + + //write Info in pSIDTokenUser + if (!GetTokenInformation(hToken, TokenUser, pSIDTokenUser, dwReturnLength, NULL)) + continue; + + AcctName = NULL; + DomainName = NULL; + dwAcctName = 1; + dwDomainName = 1; + + if (l_Debug) + std::wcout << L"Looking up SID" << '\n'; + + //get dwAcctName and dwDomainName size + if (!LookupAccountSid(NULL, pSIDTokenUser->User.Sid, AcctName, + (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &sidNameUse) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + continue; + + AcctName = reinterpret_cast<LPWSTR>(new WCHAR[dwAcctName]); + DomainName = reinterpret_cast<LPWSTR>(new WCHAR[dwDomainName]); + + if (!LookupAccountSid(NULL, pSIDTokenUser->User.Sid, AcctName, + (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &sidNameUse)) + continue; + + if (l_Debug) + std::wcout << L"Comparing " << AcctName << L" to " << wuser << '\n'; + if (!wcscmp(AcctName, wuser)) { + ++numProcs; + if (l_Debug) + std::wcout << L"Is process of " << wuser << L" (" << numProcs << L")" << '\n'; + } + + delete[] reinterpret_cast<LPWSTR>(AcctName); + delete[] reinterpret_cast<LPWSTR>(DomainName); + + } while (Process32Next(hProcessSnap, &pe32)); + +die: + if (hProcessSnap) + CloseHandle(hProcessSnap); + if (hProcess) + CloseHandle(hProcess); + if (hToken) + CloseHandle(hToken); + if (pSIDTokenUser) + delete[] reinterpret_cast<PTOKEN_USER>(pSIDTokenUser); + return numProcs; +} + +int wmain(int argc, WCHAR **argv) +{ + po::variables_map vm; + printInfoStruct printInfo = { }; + + int r = parseArguments(argc, argv, vm, printInfo); + + if (r != -1) + return r; + + if (!printInfo.user.empty()) + return printOutput(countProcs(printInfo.user), printInfo); + + return printOutput(countProcs(), printInfo); +} diff --git a/plugins/check_service.cpp b/plugins/check_service.cpp new file mode 100644 index 0000000..cd0cf14 --- /dev/null +++ b/plugins/check_service.cpp @@ -0,0 +1,284 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <windows.h> +#include <shlwapi.h> + +#define VERSION 1.1 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + bool warn; + DWORD ServiceState; + std::wstring service; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,D", "Verbose/Debug output") + ("service,s", po::wvalue<std::wstring>(), "Service name to check") + ("description,d", "Use \"service\" to match on description") + ("warn,w", "Return warning (1) instead of critical (2),\n when service is not running") + ; + + 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_guessing | + po::command_line_style::allow_long_disguise + ) + .run(), + vm); + vm.notify(); + } catch (const std::exception& e) { + std::cout << e.what() << '\n' << desc << '\n'; + return 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check the status of a service.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tSERVICE CRITICAL NOT_RUNNING | service=4;!4;!4;1;7\n\n" + L"\"SERVICE\" being the type of the check, \"CRITICAL\" the returned status\n" + L"and \"1\" is the returned value.\n" + L"A service is either running (Code 0x04) or not running (any other).\n" + L"For more information consult the msdn on service state transitions.\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"%s' thresholds work differently, since a service is either running or not\n" + L"all \"-w\" and \"-c\" do is say whether a not running service is a warning\n" + L"or critical state respectively.\n\n" + , progName, progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) { + std::cout << "Version: " << VERSION << '\n'; + return 0; + } + + if (!vm.count("service")) { + std::cout << "Argument \"service\" is required.\n" << desc << '\n'; + return 3; + } + + printInfo.service = vm["service"].as<std::wstring>(); + + printInfo.warn = vm.count("warn"); + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(const printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + std::wstring perf; + state state = OK; + + if (!printInfo.ServiceState) { + std::wcout << L"SERVICE CRITICAL NOT FOUND | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n'; + return 3; + } + + if (printInfo.ServiceState != 0x04) + printInfo.warn ? state = WARNING : state = CRITICAL; + + switch (state) { + case OK: + std::wcout << L"SERVICE \"" << printInfo.service << "\" OK RUNNING | 'service'=4;;;1;7" << '\n'; + break; + case WARNING: + std::wcout << L"SERVICE \"" << printInfo.service << "\" WARNING NOT RUNNING | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n'; + break; + case CRITICAL: + std::wcout << L"SERVICE \"" << printInfo.service << "\" CRITICAL NOT RUNNING | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n'; + break; + } + + return state; +} + +static std::wstring getServiceByDescription(const std::wstring& description) +{ + SC_HANDLE hSCM = NULL; + LPENUM_SERVICE_STATUSW lpServices = NULL; + LPBYTE lpBuf = NULL; + DWORD cbBufSize = 0; + DWORD lpServicesReturned = 0; + DWORD pcbBytesNeeded = 0; + DWORD lpResumeHandle = 0;; + + if (l_Debug) + std::wcout << L"Opening SC Manager" << '\n'; + + hSCM = OpenSCManager(NULL, NULL, GENERIC_READ); + if (hSCM == NULL) + goto die; + + if (l_Debug) + std::wcout << L"Determining initially required memory" << '\n'; + + EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, NULL, 0, + &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle); + + /* This should always be ERROR_INSUFFICIENT_BUFFER... But for some reason it is sometimes ERROR_MORE_DATA + * See the MSDN on EnumServiceStatus for a glimpse of despair + */ + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER && GetLastError() != ERROR_MORE_DATA) + goto die; + + lpServices = reinterpret_cast<LPENUM_SERVICE_STATUSW>(new BYTE[pcbBytesNeeded]); + + if (l_Debug) + std::wcout << L"Requesting Service Information. Entry point: " << lpResumeHandle << '\n'; + + EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, lpServices, pcbBytesNeeded, + &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle); + + for (decltype(lpServicesReturned) index = 0; index < lpServicesReturned; index++) { + LPWSTR lpCurrent = lpServices[index].lpServiceName; + + if (l_Debug) { + std::wcout << L"Opening Service \"" << lpServices[index].lpServiceName << L"\"\n"; + } + + SC_HANDLE hService = OpenService(hSCM, lpCurrent, SERVICE_QUERY_CONFIG); + if (!hService) + goto die; + + DWORD dwBytesNeeded = 0; + if (l_Debug) + std::wcout << "Accessing config\n"; + + if (!QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &dwBytesNeeded) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + continue; + + LPSERVICE_DESCRIPTION lpsd = reinterpret_cast<LPSERVICE_DESCRIPTION>(new BYTE[dwBytesNeeded]); + + if (!QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpsd, dwBytesNeeded, &dwBytesNeeded)) + continue; + + if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, L"") != 0) { + std::wstring desc(lpsd->lpDescription); + if (l_Debug) + std::wcout << "Got description:\n" << desc << '\n'; + size_t p = desc.find(description); + if (desc.find(description) != desc.npos) + return lpCurrent; + } + else if (l_Debug) + std::wcout << "No description found\n"; + } + + CloseServiceHandle(hSCM); + delete[] lpServices; + return L""; + +die: + printErrorInfo(); + if (hSCM) + CloseServiceHandle(hSCM); + if (lpServices) + delete[] lpServices; + return L""; +} + +static DWORD getServiceStatus(const printInfoStruct& printInfo) +{ + SC_HANDLE hSCM; + SC_HANDLE hService; + DWORD cbBufSize; + DWORD lpResumeHandle = 0; + LPBYTE lpBuf = NULL; + + if (l_Debug) + std::wcout << L"Opening SC Manager" << '\n'; + + hSCM = OpenSCManager(NULL, NULL, GENERIC_READ); + if (hSCM == NULL) + goto die; + + hService = OpenService(hSCM, printInfo.service.c_str(), SERVICE_QUERY_STATUS); + if (hService == NULL) + goto die; + + QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, NULL, 0, &cbBufSize); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto die; + + lpBuf = new BYTE[cbBufSize]; + if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, lpBuf, cbBufSize, &cbBufSize)) { + LPSERVICE_STATUS_PROCESS pInfo = (LPSERVICE_STATUS_PROCESS)lpBuf; + return pInfo->dwCurrentState; + } + +die: + printErrorInfo(); + if (hSCM) + CloseServiceHandle(hSCM); + if (hService) + CloseServiceHandle(hService); + if (lpBuf) + delete [] lpBuf; + + return -1; +} + +int wmain(int argc, WCHAR **argv) +{ + po::variables_map vm; + printInfoStruct printInfo; + + int ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + if (vm.count("description")) + printInfo.service = getServiceByDescription(vm["service"].as<std::wstring>()); + + if (printInfo.service.empty()) { + std::wcout << "Could not find service matching description\n"; + return 3; + } + + printInfo.ServiceState = getServiceStatus(printInfo); + if (printInfo.ServiceState == -1) + return 3; + + return printOutput(printInfo); +} + diff --git a/plugins/check_swap.cpp b/plugins/check_swap.cpp new file mode 100644 index 0000000..dc08f3b --- /dev/null +++ b/plugins/check_swap.cpp @@ -0,0 +1,238 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <shlwapi.h> +#include <Psapi.h> +#include <vector> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + double tSwap; + double aSwap; + double percentFree; + Bunit unit = BunitMB; + bool showUsed; +}; + +struct pageFileInfo +{ + SIZE_T totalSwap; + SIZE_T availableSpwap; +}; + +static bool l_Debug; + +BOOL EnumPageFilesProc(LPVOID pContext, PENUM_PAGE_FILE_INFORMATION pPageFileInfo, LPCWSTR lpFilename) { + std::vector<pageFileInfo>* pageFile = static_cast<std::vector<pageFileInfo>*>(pContext); + SYSTEM_INFO systemInfo; + + GetSystemInfo(&systemInfo); + + // pPageFileInfo output is in pages, we need to multiply it by the page size + pageFile->push_back({ pPageFileInfo->TotalSize * systemInfo.dwPageSize, (pPageFileInfo->TotalSize - pPageFileInfo->TotalInUse) * systemInfo.dwPageSize }); + + return TRUE; +} + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::wvalue<std::wstring>(), "Warning threshold") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold") + ("unit,u", po::wvalue<std::wstring>(), "The unit to use for display (default MB)") + ("show-used,U", "Show used swap instead of the free swap") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines swap in percent.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tSWAP WARNING - 20%% free | swap=2000B;3000;500;0;10000\n\n" + L"\"SWAP\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"20%%\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. Performance data will only be displayed when\n" + L"you set at least one threshold\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too.\n" + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) + std::wcout << L"Version: " << VERSION << '\n'; + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + printInfo.warn.legal = !printInfo.warn.legal; + } + + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + printInfo.crit.legal = !printInfo.crit.legal; + } + + l_Debug = vm.count("debug") > 0; + + if (vm.count("unit")) { + try { + printInfo.unit = parseBUnit(vm["unit"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("show-used")) { + printInfo.showUsed = true; + printInfo.warn.legal = true; + printInfo.crit.legal = true; + } + + return -1; +} + +static int printOutput(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + std::wcout << L"SWAP "; + + double currentValue; + + if (!printInfo.showUsed) + currentValue = printInfo.aSwap; + else + currentValue = printInfo.tSwap - printInfo.aSwap; + + if (printInfo.warn.rend(currentValue, printInfo.tSwap)) + state = WARNING; + + if (printInfo.crit.rend(currentValue, printInfo.tSwap)) + state = CRITICAL; + + std::wcout << stateToString(state) << " - "; + + if (!printInfo.showUsed) + std::wcout << printInfo.percentFree << L"% free "; + else + std::wcout << 100 - printInfo.percentFree << L"% used "; + + std::wcout << "| 'swap'=" << currentValue << BunitStr(printInfo.unit) << L";" + << printInfo.warn.pString(printInfo.tSwap) << L";" << printInfo.crit.pString(printInfo.tSwap) + << L";0;" << printInfo.tSwap << '\n'; + + return state; +} + +static int check_swap(printInfoStruct& printInfo) +{ + // Needs explicit cast: http://msinilo.pl/blog2/post/p1348/ + PENUM_PAGE_FILE_CALLBACKW pageFileCallback = (PENUM_PAGE_FILE_CALLBACKW)EnumPageFilesProc; + std::vector<pageFileInfo> pageFiles; + + if(!EnumPageFilesW(pageFileCallback, &pageFiles)) { + printErrorInfo(); + return 3; + } + + for (int i = 0; i < pageFiles.size(); i++) { + printInfo.tSwap += round(pageFiles.at(i).totalSwap / pow(1024.0, printInfo.unit)); + printInfo.aSwap += round(pageFiles.at(i).availableSpwap / pow(1024.0, printInfo.unit)); + } + + if (printInfo.aSwap > 0 && printInfo.tSwap > 0) + printInfo.percentFree = 100.0 * printInfo.aSwap / printInfo.tSwap; + else + printInfo.percentFree = 0; + + return -1; +} + +int wmain(int argc, WCHAR **argv) +{ + printInfoStruct printInfo = { }; + po::variables_map vm; + + int ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + ret = check_swap(printInfo); + if (ret != -1) + return ret; + + return printOutput(printInfo); +} diff --git a/plugins/check_update.cpp b/plugins/check_update.cpp new file mode 100644 index 0000000..2711d93 --- /dev/null +++ b/plugins/check_update.cpp @@ -0,0 +1,248 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <windows.h> +#include <shlwapi.h> +#include <wuapi.h> +#include <wuerror.h> + +#define VERSION 1.0 + +#define CRITERIA L"(IsInstalled = 0 and CategoryIDs contains '0fa1201d-4330-4fa8-8ae9-b877473b6441') or (IsInstalled = 0 and CategoryIDs contains 'E6CF1350-C01B-414D-A61F-263D14D133B4')" + +namespace po = boost::program_options; + +struct printInfoStruct +{ + int warn{0}; + int crit{0}; + LONG numUpdates{0}; + bool ignoreReboot{false}; + int reboot{0}; + bool careForCanRequest{false}; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::value<int>(), "Number of updates to trigger a warning.") + ("critical,c", po::value<int>(), "Number of updates to trigger a critical.") + ("possible-reboot", "Treat \"update may need reboot\" as \"update needs reboot\"") + ("no-reboot-critical", "Do not automatically return critical if an update requiring reboot is present.") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines required updates.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nAfter some time, it will then output a string like this one:\n\n" + L"\tUPDATE WARNING 8 | updates=8;1;1;0\n\n" + L"\"UPDATE\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"8\" is the number of important updates.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value.\n\n" + L"An update counts as important when it is part of the Security- or\n" + L"CriticalUpdates group.\n" + L"Consult the msdn on WSUS Classification GUIDs for more information.\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken or an update required reboot.\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"If a warning threshold is set but not a critical threshold, the critical\n" + L"threshold will be set to one greater than the set warning threshold.\n\n" + L"The \"possible-reboot\" option is not recommended since this true for nearly\n" + L"every update." + , progName); + std::cout << '\n'; + return 0; + } if (vm.count("version")) { + std::cout << "Version: " << VERSION << '\n'; + return 0; + } + if(vm.count("warning")) + printInfo.warn = vm["warning"].as<int>(); + if (vm.count("critical")) + printInfo.crit = vm["critical"].as<int>(); + else if (vm.count("warning")) + printInfo.crit = printInfo.warn + 1; + printInfo.careForCanRequest = vm.count("possible-reboot") > 0; + printInfo.ignoreReboot = vm.count("no-reboot-critical") > 0; + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(const printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + std::wstring output = L"UPDATE "; + + if (printInfo.numUpdates >= printInfo.warn && printInfo.warn) + state = WARNING; + + if ((printInfo.reboot && !printInfo.ignoreReboot) || (printInfo.numUpdates >= printInfo.crit && printInfo.crit)) + state = CRITICAL; + + switch (state) { + case OK: + output.append(L"OK "); + break; + case WARNING: + output.append(L"WARNING "); + break; + case CRITICAL: + output.append(L"CRITICAL "); + break; + } + output.append(std::to_wstring(printInfo.numUpdates)); + if (printInfo.reboot) { + output.append(L"; "); + output.append(std::to_wstring(printInfo.reboot)); + output.append(L" NEED REBOOT "); + } + std::wcout << output << L" | 'update'=" << printInfo.numUpdates << L";" + << printInfo.warn << L";" << printInfo.crit << L";0;" << '\n'; + + return state; +} + +static int check_update(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << "Initializing COM library" << '\n'; + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ISearchResult *pResult; + IUpdateSession *pSession; + IUpdateSearcher *pSearcher; + BSTR criteria = NULL; + + HRESULT err; + if (l_Debug) + std::wcout << "Creating UpdateSession and UpdateSearcher" << '\n'; + CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (void **)&pSession); + pSession->CreateUpdateSearcher(&pSearcher); + + /* + * IsInstalled = 0: All updates, including languagepacks and features + * BrowseOnly = 0: No features or languagepacks, security and unnamed + * BrowseOnly = 1: Nothing, broken + * RebootRequired = 1: Reboot required + */ + + criteria = SysAllocString(CRITERIA); + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa386526%28v=vs.85%29.aspx + // https://msdn.microsoft.com/en-us/library/ff357803%28v=vs.85%29.aspx + + if (l_Debug) + std::wcout << L"Querying updates from server" << '\n'; + + err = pSearcher->Search(criteria, &pResult); + if (!SUCCEEDED(err)) + goto die; + SysFreeString(criteria); + + IUpdateCollection *pCollection; + IUpdate *pUpdate; + + LONG updateSize; + pResult->get_Updates(&pCollection); + pCollection->get_Count(&updateSize); + + if (updateSize == 0) + return -1; + + printInfo.numUpdates = updateSize; + // printInfo.important = printInfo.warn; + + IInstallationBehavior *pIbehav; + InstallationRebootBehavior updateReboot; + + for (LONG i = 0; i < updateSize; i++) { + pCollection->get_Item(i, &pUpdate); + if (l_Debug) { + std::wcout << L"Checking reboot behaviour of update number " << i << '\n'; + } + pUpdate->get_InstallationBehavior(&pIbehav); + pIbehav->get_RebootBehavior(&updateReboot); + if (updateReboot == irbAlwaysRequiresReboot) { + printInfo.reboot++; + if (l_Debug) + std::wcout << L"It requires reboot" << '\n'; + continue; + } + if (printInfo.careForCanRequest && updateReboot == irbCanRequestReboot) { + if (l_Debug) + std::wcout << L"It requires reboot" << '\n'; + printInfo.reboot++; + } + } + + if (l_Debug) + std::wcout << L"Cleaning up and returning" << '\n'; + + SysFreeString(criteria); + CoUninitialize(); + return -1; + +die: + printErrorInfo(err); + CoUninitialize(); + if (criteria) + SysFreeString(criteria); + return 3; +} + +int wmain(int argc, WCHAR **argv) +{ + printInfoStruct printInfo; + po::variables_map vm; + + int ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + ret = check_update(printInfo); + if (ret != -1) + return ret; + + return printOutput(printInfo); +} diff --git a/plugins/check_uptime.cpp b/plugins/check_uptime.cpp new file mode 100644 index 0000000..93d540a --- /dev/null +++ b/plugins/check_uptime.cpp @@ -0,0 +1,213 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <boost/chrono.hpp> +#include <iostream> +#include <windows.h> +#include <shlwapi.h> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + long long time; + long long timeInSeconds; + Tunit unit; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::wvalue<std::wstring>(), "Warning threshold (Uses -unit)") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold (Uses -unit)") + ("unit,u", po::wvalue<std::wstring>(), "Unit to use:\nh\t- hours\nm\t- minutes\ns\t- seconds (default)\nms\t- milliseconds") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines uptime.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tUPTIME WARNING 712h | uptime=712h;700;1800;0\n\n" + L"\"UPTIME\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"712h\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. Performance data will only be displayed when\n" + L"you set at least one threshold\n\n" + L"Note that the returned time ins always rounded down,\n" + L"4 hours and 44 minutes will show as 4h.\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too.\n" + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) { + std::cout << VERSION << '\n'; + return 0; + } + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + if (vm.count("unit")) { + try { + printInfo.unit = parseTUnit(vm["unit"].as<std::wstring>()); + } catch (const std::invalid_argument&) { + std::wcout << L"Unknown unit type " << vm["unit"].as<std::wstring>() << '\n'; + return 3; + } + } else + printInfo.unit = TunitS; + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + if (printInfo.warn.rend((double) printInfo.time)) + state = WARNING; + if (printInfo.crit.rend((double) printInfo.time)) + state = CRITICAL; + + switch (state) { + case OK: + std::wcout << L"UPTIME OK " << printInfo.time << TunitStr(printInfo.unit) << L" | 'uptime'=" << printInfo.timeInSeconds + << "s" << L";" << printInfo.warn.toSeconds(printInfo.unit).pString() << L";" + << printInfo.crit.toSeconds(printInfo.unit).pString() << L";0;" << '\n'; + break; + case WARNING: + std::wcout << L"UPTIME WARNING " << printInfo.time << TunitStr(printInfo.unit) << L" | 'uptime'=" << printInfo.timeInSeconds + << "s" << L";" << printInfo.warn.toSeconds(printInfo.unit).pString() << L";" + << printInfo.crit.toSeconds(printInfo.unit).pString() << L";0;" << '\n'; + break; + case CRITICAL: + std::wcout << L"UPTIME CRITICAL " << printInfo.time << TunitStr(printInfo.unit) << L" | 'uptime'=" << printInfo.timeInSeconds + << "s" << L";" << printInfo.warn.toSeconds(printInfo.unit).pString() << L";" + << printInfo.crit.toSeconds(printInfo.unit).pString() << L";0;" << '\n'; + break; + } + + return state; +} + +static void getUptime(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Getting uptime in milliseconds" << '\n'; + + boost::chrono::milliseconds uptime = boost::chrono::milliseconds(GetTickCount64()); + + if (l_Debug) + std::wcout << L"Converting requested unit (default: seconds)" << '\n'; + + switch (printInfo.unit) { + case TunitH: + printInfo.time = boost::chrono::duration_cast<boost::chrono::hours>(uptime).count(); + break; + case TunitM: + printInfo.time = boost::chrono::duration_cast<boost::chrono::minutes>(uptime).count(); + break; + case TunitS: + printInfo.time = boost::chrono::duration_cast<boost::chrono::seconds>(uptime).count(); + break; + case TunitMS: + printInfo.time = uptime.count(); + break; + } + + // For the Performance Data we need the time in seconds + printInfo.timeInSeconds = boost::chrono::duration_cast<boost::chrono::seconds>(uptime).count(); +} + +int wmain(int argc, WCHAR **argv) +{ + po::variables_map vm; + printInfoStruct printInfo; + int ret = parseArguments(argc, argv, vm, printInfo); + + if (ret != -1) + return ret; + + getUptime(printInfo); + + return printOutput(printInfo); +} diff --git a/plugins/check_users.cpp b/plugins/check_users.cpp new file mode 100644 index 0000000..9193551 --- /dev/null +++ b/plugins/check_users.cpp @@ -0,0 +1,225 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/program_options.hpp> +#include <iostream> +#include <windows.h> +#include <shlwapi.h> +#include <wtsapi32.h> + +#define VERSION 1.0 + +namespace po = boost::program_options; + +struct printInfoStruct +{ + threshold warn; + threshold crit; + DOUBLE users; +}; + +static bool l_Debug; + +static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) +{ + WCHAR namePath[MAX_PATH]; + GetModuleFileName(NULL, namePath, MAX_PATH); + WCHAR *progName = PathFindFileName(namePath); + + po::options_description desc; + + desc.add_options() + ("help,h", "Print help message and exit") + ("version,V", "Print version and exit") + ("debug,d", "Verbose/Debug output") + ("warning,w", po::wvalue<std::wstring>(), "Warning threshold") + ("critical,c", po::wvalue<std::wstring>(), "Critical threshold") + ; + + 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 3; + } + + if (vm.count("help")) { + std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n'; + wprintf( + L"%s is a simple program to check a machines logged in users.\n" + L"You can use the following options to define its behaviour:\n\n", progName); + std::cout << desc; + wprintf( + L"\nIt will then output a string looking something like this:\n\n" + L"\tUSERS WARNING 48 | users=48;10;50;0\n\n" + L"\"USERS\" being the type of the check, \"WARNING\" the returned status\n" + L"and \"48\" is the returned value.\n" + L"The performance data is found behind the \"|\", in order:\n" + L"returned value, warning threshold, critical threshold, minimal value and,\n" + L"if applicable, the maximal value. Performance data will only be displayed when\n" + L"you set at least one threshold\n\n" + L"%s' exit codes denote the following:\n" + L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" + L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" + L"Threshold syntax:\n\n" + L"-w THRESHOLD\n" + L"warn if threshold is broken, which means VALUE > THRESHOLD\n" + L"(unless stated differently)\n\n" + L"-w !THRESHOLD\n" + L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n" + L"-w [THR1-THR2]\n" + L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" + L"-w ![THR1-THR2]\n" + L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" + L"-w THRESHOLD%%\n" + L"if the plugin accepts percentage based thresholds those will be used.\n" + L"Does nothing if the plugin does not accept percentages, or only uses\n" + L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" + L"to end with a percentage sign.\n\n" + L"All of these options work with the critical threshold \"-c\" too." + , progName); + std::cout << '\n'; + return 0; + } + + if (vm.count("version")) + std::wcout << L"Version: " << VERSION << '\n'; + + if (vm.count("warning")) { + try { + printInfo.warn = threshold(vm["warning"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + if (vm.count("critical")) { + try { + printInfo.crit = threshold(vm["critical"].as<std::wstring>()); + } catch (const std::invalid_argument& e) { + std::cout << e.what() << '\n'; + return 3; + } + } + + l_Debug = vm.count("debug") > 0; + + return -1; +} + +static int printOutput(printInfoStruct& printInfo) +{ + if (l_Debug) + std::wcout << L"Constructing output string" << '\n'; + + state state = OK; + + if (printInfo.warn.rend(printInfo.users)) + state = WARNING; + + if (printInfo.crit.rend(printInfo.users)) + state = CRITICAL; + + switch (state) { + case OK: + std::wcout << L"USERS OK " << printInfo.users << L" User(s) logged in | 'users'=" << printInfo.users << L";" + << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; + break; + case WARNING: + std::wcout << L"USERS WARNING " << printInfo.users << L" User(s) logged in | 'users'=" << printInfo.users << L";" + << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; + break; + case CRITICAL: + std::wcout << L"USERS CRITICAL " << printInfo.users << L" User(s) logged in | 'users'=" << printInfo.users << L";" + << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; + break; + } + + return state; +} + +static int check_users(printInfoStruct& printInfo) +{ + DOUBLE users = 0; + WTS_SESSION_INFOW *pSessionInfo = NULL; + DWORD count; + DWORD index; + + if (l_Debug) + std::wcout << L"Trying to enumerate terminal sessions" << '\n'; + + if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &count)) { + std::wcout << L"Failed to enumerate terminal sessions" << '\n'; + printErrorInfo(); + if (pSessionInfo) + WTSFreeMemory(pSessionInfo); + return 3; + } + + if (l_Debug) + std::wcout << L"Got all sessions (" << count << L"), traversing and counting active ones" << '\n'; + + for (index = 0; index < count; index++) { + LPWSTR name; + DWORD size; + int len; + + if (l_Debug) + std::wcout << L"Querrying session number " << index << '\n'; + + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, pSessionInfo[index].SessionId, + WTSUserName, &name, &size)) + continue; + + if (l_Debug) + std::wcout << L"Found \"" << name << L"\". Checking whether it's a real session" << '\n'; + + len = lstrlenW(name); + + WTSFreeMemory(name); + + if (!len) + continue; + + if (pSessionInfo[index].State == WTSActive || pSessionInfo[index].State == WTSDisconnected) { + users++; + if (l_Debug) + std::wcout << L"\"" << name << L"\" is a real session, counting it. Now " << users << '\n'; + } + } + + if (l_Debug) + std::wcout << "Finished coutning user sessions (" << users << "). Freeing memory and returning" << '\n'; + + WTSFreeMemory(pSessionInfo); + printInfo.users = users; + return -1; +} + +int wmain(int argc, WCHAR **argv) +{ + printInfoStruct printInfo = { }; + po::variables_map vm; + + int ret = parseArguments(argc, argv, vm, printInfo); + if (ret != -1) + return ret; + + ret = check_users(printInfo); + if (ret != -1) + return ret; + + return printOutput(printInfo); +} diff --git a/plugins/thresholds.cpp b/plugins/thresholds.cpp new file mode 100644 index 0000000..bdd67ee --- /dev/null +++ b/plugins/thresholds.cpp @@ -0,0 +1,276 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "plugins/thresholds.hpp" +#include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> + +using namespace boost::algorithm; + +threshold::threshold() + : set(false) +{} + +threshold::threshold(const double v, const double c, bool l , bool p ) { + lower = v; + upper = c; + legal = l; + perc = p; +} + +threshold::threshold(const std::wstring& stri) +{ + if (stri.empty()) + throw std::invalid_argument("Threshold must not be empty"); + + std::wstring str = stri; + + //kill whitespace + boost::algorithm::trim(str); + + bool low = (str.at(0) == L'!'); + if (low) + str = std::wstring(str.begin() + 1, str.end()); + + bool pc = false; + + if (str.at(0) == L'[' && str.at(str.length() - 1) == L']') {//is range + str = std::wstring(str.begin() + 1, str.end() - 1); + std::vector<std::wstring> svec; + boost::split(svec, str, boost::is_any_of(L"-")); + if (svec.size() != 2) + throw std::invalid_argument("Threshold range requires two arguments"); + std::wstring str1 = svec.at(0), str2 = svec.at(1); + + if (str1.at(str1.length() - 1) == L'%' && str2.at(str2.length() - 1) == L'%') { + pc = true; + str1 = std::wstring(str1.begin(), str1.end() - 1); + str2 = std::wstring(str2.begin(), str2.end() - 1); + } + + try { + boost::algorithm::trim(str1); + lower = boost::lexical_cast<DOUBLE>(str1); + boost::algorithm::trim(str2); + upper = boost::lexical_cast<DOUBLE>(str2); + legal = !low; perc = pc; set = true; + } catch (const boost::bad_lexical_cast&) { + throw std::invalid_argument("Unknown Threshold type"); + } + } else { //not range + if (str.at(str.length() - 1) == L'%') { + pc = true; + str = std::wstring(str.begin(), str.end() - 1); + } + try { + boost::algorithm::trim(str); + lower = upper = boost::lexical_cast<DOUBLE>(str); + legal = !low; perc = pc; set = true; + } catch (const boost::bad_lexical_cast&) { + throw std::invalid_argument("Unknown Threshold type"); + } + } +} + +//return TRUE if the threshold is broken +bool threshold::rend(const double val, const double max) +{ + double upperAbs = upper; + double lowerAbs = lower; + + if (perc) { + upperAbs = upper / 100.0 * max; + lowerAbs = lower / 100.0 * max; + } + + if (!set) + return set; + if (lowerAbs == upperAbs) + return val > upperAbs == legal; + else + return (val < lowerAbs || upperAbs < val) != legal; +} + +//returns a printable string of the threshold +std::wstring threshold::pString(const double max) +{ + if (!set) + return L""; + //transform percentages to abolute values + double lowerAbs = lower; + double upperAbs = upper; + if (perc) { + lowerAbs = lower / 100.0 * max; + upperAbs = upper / 100.0 * max; + } + + std::wstring s, lowerStr = removeZero(lowerAbs), + upperStr = removeZero(upperAbs); + + if (lower != upper) { + s.append(L"[").append(lowerStr).append(L"-") + .append(upperStr).append(L"]"); + } else + s.append(lowerStr); + + return s; +} + +threshold threshold::toSeconds(const Tunit& fromUnit) { + if (!set) + return *this; + + double lowerAbs = lower; + double upperAbs = upper; + + switch (fromUnit) { + case TunitMS: + lowerAbs = lowerAbs / 1000; + upperAbs = upperAbs / 1000; + break; + case TunitS: + lowerAbs = lowerAbs ; + upperAbs = upperAbs ; + break; + case TunitM: + lowerAbs = lowerAbs * 60; + upperAbs = upperAbs * 60; + break; + case TunitH: + lowerAbs = lowerAbs * 60 * 60; + upperAbs = upperAbs * 60 * 60; + break; + } + + return threshold(lowerAbs, upperAbs, legal, perc); +} + +std::wstring removeZero(double val) +{ + std::wstring ret = boost::lexical_cast<std::wstring>(val); + std::wstring::size_type pos = ret.length(); + if (ret.find_first_of(L".") == std::string::npos) + return ret; + for (std::wstring::reverse_iterator rit = ret.rbegin(); rit != ret.rend(); ++rit) { + if (*rit == L'.') { + return ret.substr(0, pos - 1); + } + if (*rit != L'0') { + return ret.substr(0, pos); + } + pos--; + } + return L"0"; +} + +std::vector<std::wstring> splitMultiOptions(const std::wstring& str) +{ + std::vector<std::wstring> sVec; + boost::split(sVec, str, boost::is_any_of(L",")); + return sVec; +} + +Bunit parseBUnit(const std::wstring& str) +{ + std::wstring wstr = to_upper_copy(str); + + if (wstr == L"B") + return BunitB; + if (wstr == L"KB") + return BunitkB; + if (wstr == L"MB") + return BunitMB; + if (wstr == L"GB") + return BunitGB; + if (wstr == L"TB") + return BunitTB; + + throw std::invalid_argument("Unknown unit type"); +} + +std::wstring BunitStr(const Bunit& unit) +{ + switch (unit) { + case BunitB: + return L"B"; + case BunitkB: + return L"kB"; + case BunitMB: + return L"MB"; + case BunitGB: + return L"GB"; + case BunitTB: + return L"TB"; + } + return NULL; +} + +Tunit parseTUnit(const std::wstring& str) { + std::wstring wstr = to_lower_copy(str); + + if (wstr == L"ms") + return TunitMS; + if (wstr == L"s") + return TunitS; + if (wstr == L"m") + return TunitM; + if (wstr == L"h") + return TunitH; + + throw std::invalid_argument("Unknown unit type"); +} + +std::wstring TunitStr(const Tunit& unit) +{ + switch (unit) { + case TunitMS: + return L"ms"; + case TunitS: + return L"s"; + case TunitM: + return L"m"; + case TunitH: + return L"h"; + } + return NULL; +} + +void printErrorInfo(unsigned long err) +{ + if (!err) + err = GetLastError(); + LPWSTR mBuf = NULL; + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&mBuf, 0, NULL)) + std::wcout << "Failed to format error message, last error was: " << err << '\n'; + else { + boost::trim_right(std::wstring(mBuf)); + std::wcout << mBuf << std::endl; + } +} + +std::wstring formatErrorInfo(unsigned long err) { + std::wostringstream out; + if (!err) + err = GetLastError(); + LPWSTR mBuf = NULL; + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&mBuf, 0, NULL)) + out << "Failed to format error message, last error was: " << err; + else { + std::wstring tempOut = std::wstring(mBuf); + boost::trim_right(tempOut); + out << tempOut; + } + + return out.str(); +} + +std::wstring stateToString(const state& state) { + switch (state) { + case OK: return L"OK"; + case WARNING: return L"WARNING"; + case CRITICAL: return L"CRITICAL"; + default: return L"UNKNOWN"; + } +}
\ No newline at end of file diff --git a/plugins/thresholds.hpp b/plugins/thresholds.hpp new file mode 100644 index 0000000..4c47ddb --- /dev/null +++ b/plugins/thresholds.hpp @@ -0,0 +1,64 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef THRESHOLDS_H +#define THRESHOLDS_H + +#include <string> +#include <vector> +#include <windows.h> + +enum Bunit +{ + BunitB = 0, BunitkB = 1, BunitMB = 2, BunitGB = 3, BunitTB = 4 +}; + +enum Tunit +{ + TunitMS, TunitS, TunitM, TunitH +}; + +enum state +{ + OK = 0, WARNING = 1, CRITICAL = 2 +}; + +class threshold +{ +public: + // doubles are always enough for ANY 64 bit value + double lower; + double upper; + // true means everything BELOW upper/outside [lower-upper] is fine + bool legal; + bool perc; + bool set; + + threshold(); + + threshold(const double v, const double c, bool l = true, bool p = false); + + threshold(const std::wstring&); + + // returns true if the threshold is broken + bool rend(const double val, const double max = 100.0); + + // returns a printable string of the threshold + std::wstring pString(const double max = 100.0); + + threshold toSeconds(const Tunit& fromUnit); +}; + +std::wstring removeZero(double); +std::vector<std::wstring> splitMultiOptions(const std::wstring&); + +Bunit parseBUnit(const std::wstring&); +std::wstring BunitStr(const Bunit&); +Tunit parseTUnit(const std::wstring&); +std::wstring TunitStr(const Tunit&); + +void printErrorInfo(unsigned long err = 0); +std::wstring formatErrorInfo(unsigned long err); + +std::wstring stateToString(const state&); + +#endif /* THRESHOLDS_H */ |