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