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