]> granicus.if.org Git - icinga2/blob - plugins/check_network.cpp
Merge pull request #6527 from Icinga/feature/acknowledgment-notifications-6047
[icinga2] / plugins / check_network.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #define WIN32_LEAN_AND_MEAN
21
22 #include "plugins/thresholds.hpp"
23 #include <boost/program_options.hpp>
24 #include <boost/algorithm/string/replace.hpp>
25 #include <vector>
26 #include <map>
27 #include <windows.h>
28 #include <pdh.h>
29 #include <shlwapi.h>
30 #include <iostream>
31 #include <pdhmsg.h>
32 #include <winsock2.h>
33 #include <iphlpapi.h>
34
35 #define VERSION 1.2
36
37 namespace po = boost::program_options;
38
39 struct nInterface
40 {
41         std::wstring name;
42         LONG BytesInSec, BytesOutSec;
43         nInterface(std::wstring p)
44                 : name(p)
45         { }
46 };
47
48 struct printInfoStruct
49 {
50         threshold warn;
51         threshold crit;
52 };
53
54 static bool l_Debug;
55 static bool l_NoISATAP;
56
57 static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
58 {
59         WCHAR namePath[MAX_PATH];
60         GetModuleFileName(NULL, namePath, MAX_PATH);
61         WCHAR *progName = PathFindFileName(namePath);
62
63         po::options_description desc("Options");
64
65         desc.add_options()
66                 ("help,h", "print usage and exit")
67                 ("version,V", "print version and exit")
68                 ("debug,d", "Verbose/Debug output")
69                 ("noisatap,n", "Don't show ISATAP interfaces in output")
70                 ("warning,w", po::wvalue<std::wstring>(), "warning value")
71                 ("critical,c", po::wvalue<std::wstring>(), "critical value")
72                 ;
73
74         po::wcommand_line_parser parser(ac, av);
75
76         try {
77                 po::store(
78                         parser
79                         .options(desc)
80                         .style(
81                                 po::command_line_style::unix_style |
82                                 po::command_line_style::allow_long_disguise)
83                         .run(),
84                         vm);
85                 vm.notify();
86         } catch (const std::exception& e) {
87                 std::cout << e.what() << '\n' << desc << '\n';
88                 return 3;
89         }
90
91         if (vm.count("help")) {
92                 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
93                 wprintf(
94                         L"%s is a simple program to check a machines network performance.\n"
95                         L"You can use the following options to define its behaviour:\n\n", progName);
96                 std::cout << desc;
97                 wprintf(
98                         L"\nIt will then output a string looking something like this:\n\n"
99                         L"\tNETWORK WARNING 1131B/s | network=1131B;1000;7000;0\n\n"
100                         L"\"NETWORK\" being the type of the check, \"WARNING\" the returned status\n"
101                         L"and \"1131B/s\" is the returned value.\n"
102                         L"The performance data is found behind the \"|\", in order:\n"
103                         L"returned value, warning threshold, critical threshold, minimal value and,\n"
104                         L"if applicable, the maximal value. Performance data will only be displayed when\n"
105                         L"you set at least one threshold\n\n"
106                         L"This program will also print out additional performance data interface\n"
107                         L"by interface\n\n"
108                         L"%s' exit codes denote the following:\n"
109                         L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
110                         L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
111                         L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
112                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
113                         L"Threshold syntax:\n\n"
114                         L"-w THRESHOLD\n"
115                         L"warn if threshold is broken, which means VALUE > THRESHOLD\n"
116                         L"(unless stated differently)\n\n"
117                         L"-w !THRESHOLD\n"
118                         L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n"
119                         L"-w [THR1-THR2]\n"
120                         L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
121                         L"-w ![THR1-THR2]\n"
122                         L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
123                         L"-w THRESHOLD%%\n"
124                         L"if the plugin accepts percentage based thresholds those will be used.\n"
125                         L"Does nothing if the plugin does not accept percentages, or only uses\n"
126                         L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
127                         L"to end with a percentage sign.\n\n"
128                         L"All of these options work with the critical threshold \"-c\" too."
129                         , progName);
130                 std::cout << '\n';
131                 return 0;
132         }
133
134         if (vm.count("version"))
135                 std::cout << "Version: " << VERSION << '\n';
136
137         if (vm.count("warning")) {
138                 try {
139                         printInfo.warn = threshold(vm["warning"].as<std::wstring>());
140                 } catch (const std::invalid_argument& e) {
141                         std::cout << e.what() << '\n';
142                         return 3;
143                 }
144         }
145         if (vm.count("critical")) {
146                 try {
147                         printInfo.crit = threshold(vm["critical"].as<std::wstring>());
148                 } catch (const std::invalid_argument& e) {
149                         std::cout << e.what() << '\n';
150                         return 3;
151                 }
152         }
153
154         l_Debug = vm.count("debug") > 0;
155         l_NoISATAP = vm.count("noisatap") > 0;
156
157         return -1;
158 }
159
160 static int printOutput(printInfoStruct& printInfo, const std::vector<nInterface>& vInterfaces, const std::map<std::wstring, std::wstring>& mapNames)
161 {
162         if (l_Debug)
163                 std::wcout << L"Constructing output string" << '\n';
164
165         long tIn = 0, tOut = 0;
166         std::wstringstream tss;
167         state state = OK;
168
169         std::map<std::wstring, std::wstring>::const_iterator mapIt;
170         std::wstring wsFriendlyName;
171
172         for (std::vector<nInterface>::const_iterator it = vInterfaces.begin(); it != vInterfaces.end(); ++it) {
173                 tIn += it->BytesInSec;
174                 tOut += it->BytesOutSec;
175                 if (l_Debug)
176                         std::wcout << "Getting friendly name of " << it->name << '\n';
177                 mapIt = mapNames.find(it->name);
178                 if (mapIt != mapNames.end()) {
179                         if (l_Debug)
180                                 std::wcout << "\tIs " << mapIt->second << '\n';
181                         wsFriendlyName = mapIt->second;
182                 } else {
183                         if (l_Debug)
184                                 std::wcout << "\tNo friendly name found, using adapter name\n";
185                         wsFriendlyName = it->name;
186                 }
187                 if (wsFriendlyName.find(L"isatap") != std::wstring::npos && l_NoISATAP) {
188                         if (l_Debug)
189                                 std::wcout << "\tSkipping isatap interface " << wsFriendlyName << "\n";
190                         continue;
191                 } else {
192                         boost::algorithm::replace_all(wsFriendlyName, "'", "''");
193                         tss << L"'" << wsFriendlyName << L"_in'=" << it->BytesInSec << L"B '" << wsFriendlyName << L"_out'=" << it->BytesOutSec << L"B ";
194                 }
195         }
196
197         if (printInfo.warn.rend(tIn + tOut))
198                 state = WARNING;
199         if (printInfo.crit.rend(tIn + tOut))
200                 state = CRITICAL;
201
202         std::wcout << "NETWORK ";
203
204         switch (state) {
205         case OK:
206                 std::wcout << L"OK";
207                 break;
208         case WARNING:
209                 std::wcout << L"WARNING";
210                 break;
211         case CRITICAL:
212                 std::wcout << L"CRITICAL";
213                 break;
214         }
215
216         std::wcout << " " << tIn + tOut << L"B/s | "
217                 << L"'network'=" << tIn + tOut << L"B;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";" << L"0; "
218                 << tss.str() << '\n';
219
220         return state;
221 }
222
223 static int check_network(std::vector<nInterface>& vInterfaces)
224 {
225
226         if (l_Debug)
227                 std::wcout << L"Creating Query and adding counters" << '\n';
228
229         PDH_FMT_COUNTERVALUE_ITEM *pDisplayValuesIn = NULL, *pDisplayValuesOut = NULL;
230
231         PDH_HQUERY phQuery;
232         PDH_STATUS err = PdhOpenQuery(NULL, NULL, &phQuery);
233         if (!SUCCEEDED(err))
234                 goto die;
235
236         const WCHAR *perfIn = L"\\Network Interface(*)\\Bytes Received/sec";
237         PDH_HCOUNTER phCounterIn;
238         err = PdhAddEnglishCounter(phQuery, perfIn, NULL, &phCounterIn);
239         if (!SUCCEEDED(err))
240                 goto die;
241
242         const WCHAR *perfOut = L"\\Network Interface(*)\\Bytes Sent/sec";
243         PDH_HCOUNTER phCounterOut;
244         err = PdhAddEnglishCounter(phQuery, perfOut, NULL, &phCounterOut);
245         if (!SUCCEEDED(err))
246                 goto die;
247
248         if (l_Debug)
249                 std::wcout << L"Collecting first batch of query data" << '\n';
250
251         err = PdhCollectQueryData(phQuery);
252         if (!SUCCEEDED(err))
253                 goto die;
254
255         if (l_Debug)
256                 std::wcout << L"Sleep for one second" << '\n';
257
258         Sleep(1000);
259
260         if (l_Debug)
261                 std::wcout << L"Collecting second batch of query data" << '\n';
262
263         err = PdhCollectQueryData(phQuery);
264         if (!SUCCEEDED(err))
265                 goto die;
266
267         if (l_Debug)
268                 std::wcout << L"Creating formatted counter arrays" << '\n';
269
270         DWORD dwItemCount;
271         DWORD dwBufferSizeIn = 0;
272         err = PdhGetFormattedCounterArray(phCounterIn, PDH_FMT_LONG, &dwBufferSizeIn, &dwItemCount, pDisplayValuesIn);
273         if (err == PDH_MORE_DATA || SUCCEEDED(err))
274                 pDisplayValuesIn = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwItemCount*dwBufferSizeIn]);
275         else
276                 goto die;
277
278         DWORD dwBufferSizeOut = 0;
279         err = PdhGetFormattedCounterArray(phCounterOut, PDH_FMT_LONG, &dwBufferSizeOut, &dwItemCount, pDisplayValuesOut);
280         if (err == PDH_MORE_DATA || SUCCEEDED(err))
281                 pDisplayValuesOut = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwItemCount*dwBufferSizeIn]);
282         else
283                 goto die;
284
285         err = PdhGetFormattedCounterArray(phCounterIn, PDH_FMT_LONG, &dwBufferSizeIn, &dwItemCount, pDisplayValuesIn);
286         if (!SUCCEEDED(err))
287                 goto die;
288
289         err = PdhGetFormattedCounterArray(phCounterOut, PDH_FMT_LONG, &dwBufferSizeOut, &dwItemCount, pDisplayValuesOut);
290         if (!SUCCEEDED(err))
291                 goto die;
292
293         if (l_Debug)
294                 std::wcout << L"Going over counter array" << '\n';
295
296         for (DWORD i = 0; i < dwItemCount; i++) {
297                 nInterface iface{pDisplayValuesIn[i].szName};
298                 iface.BytesInSec = pDisplayValuesIn[i].FmtValue.longValue;
299                 iface.BytesOutSec = pDisplayValuesOut[i].FmtValue.longValue;
300                 vInterfaces.push_back(iface);
301
302                 if (l_Debug)
303                         std::wcout << L"Collected interface " << pDisplayValuesIn[i].szName << '\n';
304         }
305
306         if (l_Debug)
307                 std::wcout << L"Finished collection. Cleaning up and returning" << '\n';
308
309         if (phQuery)
310                 PdhCloseQuery(phQuery);
311
312         delete reinterpret_cast<BYTE *>(pDisplayValuesIn);
313         delete reinterpret_cast<BYTE *>(pDisplayValuesOut);
314
315         return -1;
316 die:
317         printErrorInfo(err);
318         if (phQuery)
319                 PdhCloseQuery(phQuery);
320
321         delete reinterpret_cast<BYTE *>(pDisplayValuesIn);
322         delete reinterpret_cast<BYTE *>(pDisplayValuesOut);
323
324         return 3;
325 }
326
327 static bool mapSystemNamesToFamiliarNames(std::map<std::wstring, std::wstring>& mapNames)
328 {
329         /*
330         PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
331         PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
332         PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
333         PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsServer = NULL;
334         PIP_ADAPTER_PREFIX pPrefix = NULL;
335         */
336         ULONG outBufLen = 15000; //15KB as suggestet by msdn of GetAdaptersAddresses
337
338         if (l_Debug)
339                 std::wcout << "Mapping adapter system names to friendly names\n";
340
341         PIP_ADAPTER_ADDRESSES pAddresses;
342
343         unsigned int Iterations = 0;
344         DWORD dwRetVal = 0;
345
346         do {
347                 pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(new BYTE[outBufLen]);
348
349                 dwRetVal = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen);
350
351                 if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
352                         delete[]pAddresses;
353                         pAddresses = NULL;
354                 } else
355                         break;
356         } while (++Iterations < 3);
357
358         if (dwRetVal != NO_ERROR) {
359                 std::wcout << "Failed to collect friendly adapter names\n";
360                 delete[]pAddresses;
361                 return false;
362         }
363
364         for (PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses; pCurrAddresses; pCurrAddresses = pCurrAddresses->Next) {
365                 if (l_Debug)
366                         std::wcout << "Got: " << pCurrAddresses->Description << " -- " << pCurrAddresses->FriendlyName << '\n';
367
368                 mapNames[pCurrAddresses->Description] = pCurrAddresses->FriendlyName;
369         }
370
371         delete[]pAddresses;
372         return true;
373 }
374
375 int wmain(int argc, WCHAR **argv)
376 {
377         std::vector<nInterface> vInterfaces;
378         std::map<std::wstring, std::wstring> mapNames;
379         printInfoStruct printInfo;
380         po::variables_map vm;
381
382         int ret = parseArguments(argc, argv, vm, printInfo);
383
384         if (ret != -1)
385                 return ret;
386
387         if (!mapSystemNamesToFamiliarNames(mapNames))
388                 return 3;
389
390         ret = check_network(vInterfaces);
391         if (ret != -1)
392                 return ret;
393
394         return printOutput(printInfo, vInterfaces, mapNames);
395 }