]> granicus.if.org Git - icinga2/blobdiff - plugins/check_network.cpp
Merge pull request #6882 from Icinga/bugfix/influxdb-gaps-6841
[icinga2] / plugins / check_network.cpp
index 30a4ef5aa32fa9ddfdeb36443a8eedc6e6e0956b..db0bb197052a5d4e7738cd59971a89ba6717ce6c 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  * Icinga 2                                                                   *
- * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+ * Copyright (C) 2012-2018 Icinga Development Team (https://icinga.com/)      *
  *                                                                            *
  * This program is free software; you can redistribute it and/or              *
  * modify it under the terms of the GNU General Public License                *
  * along with this program; if not, write to the Free Software Foundation     *
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
-#include <Windows.h>
-#include <Pdh.h>
-#include <Shlwapi.h>
+
+#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>
 
-#include "thresholds.h"
+#define VERSION 1.2
 
-#include "boost/program_options.hpp"
-
-#define VERSION 1.0
 namespace po = boost::program_options;
 
-using std::endl; using std::vector; using std::wstring;
-using std::wcout; using std::cout;
-
-static BOOL debug = FALSE;
-
-struct nInterface 
+struct nInterface
 {
-       wstring name;
-       long BytesInSec, BytesOutSec;
-       nInterface(wstring p)
+       std::wstring name;
+       LONG BytesInSec, BytesOutSec;
+       nInterface(std::wstring p)
                : name(p)
-       {}
+       { }
 };
 
-struct printInfoStruct 
+struct printInfoStruct
 {
-       threshold warn, crit;
+       threshold warn;
+       threshold crit;
 };
 
-static int parseArguments(int, TCHAR **, po::variables_map&, printInfoStruct&);
-static int printOutput(printInfoStruct&, const vector<nInterface>&);
-static int check_network(vector<nInterface>&);
-
-int wmain(int argc, wchar_t **argv) 
-{
-       vector<nInterface> vInterfaces;
-       printInfoStruct printInfo{ };
-       po::variables_map vm;
-       int ret = parseArguments(argc, argv, vm, printInfo);
-       if (ret != -1)
-               return ret;
-
-       ret = check_network(vInterfaces);
-       if (ret != -1)
-               return ret;
-               
-       return printOutput(printInfo, vInterfaces);
-}
+static bool l_Debug;
+static bool l_NoISATAP;
 
-int parseArguments(int ac, wchar_t **av, po::variables_map& vm, printInfoStruct& printInfo) 
+static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
 {
-       wchar_t namePath[MAX_PATH];
+       WCHAR namePath[MAX_PATH];
        GetModuleFileName(NULL, namePath, MAX_PATH);
-       wchar_t *progName = PathFindFileName(namePath);
+       WCHAR *progName = PathFindFileName(namePath);
 
        po::options_description desc("Options");
 
@@ -80,37 +66,38 @@ int parseArguments(int ac, wchar_t **av, po::variables_map& vm, printInfoStruct&
                ("help,h", "print usage and exit")
                ("version,V", "print version and exit")
                ("debug,d", "Verbose/Debug output")
-               ("warning,w", po::wvalue<wstring>(), "warning value")
-               ("critical,c", po::wvalue<wstring>(), "critical value")
+               ("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::basic_command_line_parser<wchar_t> parser(ac, av);
+       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::unix_style |
+                               po::command_line_style::allow_long_disguise)
                        .run(),
                        vm);
                vm.notify();
-       } catch (std::exception& e) {
-               cout << e.what() << endl << desc << endl;
+       } catch (const std::exception& e) {
+               std::cout << e.what() << '\n' << desc << '\n';
                return 3;
        }
 
        if (vm.count("help")) {
-               wcout << progName << " Help\n\tVersion: " << VERSION << endl;
+               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);
-               cout << desc;
+               std::cout << desc;
                wprintf(
                        L"\nIt will then output a string looking something like this:\n\n"
-                       L"\tNETWORK WARNING 1131B/s|network=1131B/s;1000;7000;0\n\n"
-                       L"\"DISK\" being the type of the check, \"WARNING\" the returned status\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"
@@ -140,127 +127,155 @@ int parseArguments(int ac, wchar_t **av, po::variables_map& vm, printInfoStruct&
                        L"to end with a percentage sign.\n\n"
                        L"All of these options work with the critical threshold \"-c\" too."
                        , progName);
-               cout << endl;
+               std::cout << '\n';
                return 0;
        }
 
        if (vm.count("version"))
-               cout << "Version: " << VERSION << endl;
+               std::cout << "Version: " << VERSION << '\n';
 
        if (vm.count("warning")) {
                try {
-                       printInfo.warn = threshold(vm["warning"].as<wstring>());
-               } catch (std::invalid_argument& e) {
-                       cout << e.what() << endl;
+                       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<wstring>());
-               } catch (std::invalid_argument& e) {
-                       cout << e.what() << endl;
+                       printInfo.crit = threshold(vm["critical"].as<std::wstring>());
+               } catch (const std::invalid_argument& e) {
+                       std::cout << e.what() << '\n';
                        return 3;
                }
        }
-       
-       if (vm.count("debug"))
-               debug = TRUE;
+
+       l_Debug = vm.count("debug") > 0;
+       l_NoISATAP = vm.count("noisatap") > 0;
 
        return -1;
 }
 
-int printOutput(printInfoStruct& printInfo, const vector<nInterface>& vInterfaces) 
+static int printOutput(printInfoStruct& printInfo, const std::vector<nInterface>& vInterfaces, const std::map<std::wstring, std::wstring>& mapNames)
 {
-       if (debug)
-               wcout << L"Constructing output string" << endl;
+       if (l_Debug)
+               std::wcout << L"Constructing output string" << '\n';
 
        long tIn = 0, tOut = 0;
-       std::wstringstream tss, perfDataFirst;
+       std::wstringstream tss;
        state state = OK;
 
-       for (vector<nInterface>::const_iterator it = vInterfaces.begin(); it != vInterfaces.end(); ++it) {
+       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;
-               tss << L"netI=\"" << it->name << L"\";in=" << it->BytesInSec << L"B/s;out=" << it->BytesOutSec << L"B/s ";
+               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;
-       
-       perfDataFirst << L"network=" << tIn + tOut << L"B/s;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";" << L"0; ";
+
+       std::wcout << "NETWORK ";
 
        switch (state) {
        case OK:
-               wcout << L"NETWORK OK " << tIn + tOut << L"B/s | " << perfDataFirst.str() << tss.str() << endl;
+               std::wcout << L"OK";
                break;
        case WARNING:
-               wcout << L"NETWORK WARNING " << tIn + tOut << L"B/s | " << perfDataFirst.str() << tss.str() << endl;
+               std::wcout << L"WARNING";
                break;
        case CRITICAL:
-               wcout << L"NETWORK CRITICAL " << tIn + tOut << L"B/s | " << perfDataFirst.str() << tss.str() << endl;
+               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; "
+               << tss.str() << '\n';
+
        return state;
 }
 
-int check_network(vector <nInterface>& vInterfaces) 
+static int check_network(std::vector<nInterface>& vInterfaces)
 {
-       const wchar_t *perfIn = L"\\Network Interface(*)\\Bytes Received/sec";
-       const wchar_t *perfOut = L"\\Network Interface(*)\\Bytes Sent/sec";
 
-       PDH_HQUERY phQuery = NULL;
-       PDH_HCOUNTER phCounterIn, phCounterOut;
-       DWORD dwBufferSizeIn = 0, dwBufferSizeOut = 0, dwItemCount = 0;
-       PDH_FMT_COUNTERVALUE_ITEM *pDisplayValuesIn = NULL, *pDisplayValuesOut = NULL;
-       PDH_STATUS err;
+       if (l_Debug)
+               std::wcout << L"Creating Query and adding counters" << '\n';
 
-       if (debug)
-               wcout << L"Creating Query and adding counters" << endl;
+       PDH_FMT_COUNTERVALUE_ITEM *pDisplayValuesIn = NULL, *pDisplayValuesOut = NULL;
 
-       err = PdhOpenQuery(NULL, NULL, &phQuery);
+       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)) 
+       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)) 
+       if (!SUCCEEDED(err))
                goto die;
-       
-       if (debug)
-               wcout << L"Collecting first batch of query data" << endl;
+
+       if (l_Debug)
+               std::wcout << L"Collecting first batch of query data" << '\n';
 
        err = PdhCollectQueryData(phQuery);
        if (!SUCCEEDED(err))
                goto die;
 
-       if (debug)
-               wcout << L"Sleep for one second" << endl;
+       if (l_Debug)
+               std::wcout << L"Sleep for one second" << '\n';
 
        Sleep(1000);
 
-       if (debug)
-               wcout << L"Collecting second batch of query data" << endl;
+       if (l_Debug)
+               std::wcout << L"Collecting second batch of query data" << '\n';
 
        err = PdhCollectQueryData(phQuery);
        if (!SUCCEEDED(err))
                goto die;
 
-       if (debug)
-               wcout << L"Creating formatted counter arrays" << endl;
-       
+       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]);
@@ -275,31 +290,106 @@ int check_network(vector <nInterface>& vInterfaces)
        if (!SUCCEEDED(err))
                goto die;
 
-       if (debug)
-               wcout << L"Going over counter array" << endl;
+       if (l_Debug)
+               std::wcout << L"Going over counter array" << '\n';
 
        for (DWORD i = 0; i < dwItemCount; i++) {
-               nInterface *iface = new nInterface(wstring(pDisplayValuesIn[i].szName));
-               iface->BytesInSec = pDisplayValuesIn[i].FmtValue.longValue;
-               iface->BytesOutSec = pDisplayValuesOut[i].FmtValue.longValue;
-               vInterfaces.push_back(*iface);
-               if (debug)
-                       wcout << L"Collected interface " << pDisplayValuesIn[i].szName << endl;
+               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 (debug)
-               wcout << L"Finished collection. Cleaning up and returning" << endl;
 
-       PdhCloseQuery(phQuery);
-       delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesIn);
-       delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesOut);
+       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:
-       die(err);
+       printErrorInfo(err);
        if (phQuery)
                PdhCloseQuery(phQuery);
-       if (pDisplayValuesIn)
-               delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesIn);
-       if (pDisplayValuesOut)
-               delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesOut);
+
+       delete reinterpret_cast<BYTE *>(pDisplayValuesIn);
+       delete reinterpret_cast<BYTE *>(pDisplayValuesOut);
+
        return 3;
-}
\ No newline at end of file
+}
+
+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);
+}