summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/CMakeLists.txt69
-rw-r--r--plugins/check_disk.cpp443
-rw-r--r--plugins/check_load.cpp244
-rw-r--r--plugins/check_memory.cpp215
-rw-r--r--plugins/check_network.cpp374
-rw-r--r--plugins/check_nscp_api.cpp512
-rw-r--r--plugins/check_perfmon.cpp387
-rw-r--r--plugins/check_ping.cpp508
-rw-r--r--plugins/check_procs.cpp325
-rw-r--r--plugins/check_service.cpp284
-rw-r--r--plugins/check_swap.cpp238
-rw-r--r--plugins/check_update.cpp248
-rw-r--r--plugins/check_uptime.cpp213
-rw-r--r--plugins/check_users.cpp225
-rw-r--r--plugins/thresholds.cpp276
-rw-r--r--plugins/thresholds.hpp64
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..aef43fb
--- /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 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 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 */