]> granicus.if.org Git - icinga2/blob - plugins/check_procs.cpp
Fix check_memory thresholds in 'used' mode
[icinga2] / plugins / check_procs.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 #include "plugins/thresholds.hpp"
21 #include <boost/program_options.hpp>
22 #include <iostream>
23 #include <windows.h>
24 #include <shlwapi.h>
25 #include <tlhelp32.h>
26
27 #define VERSION 1.0
28
29 namespace po = boost::program_options;
30
31 struct printInfoStruct
32 {
33         threshold warn;
34         threshold crit;
35         std::wstring user;
36 };
37
38 static bool l_Debug;
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;
47
48         desc.add_options()
49                 ("help,h", "Print help message and exit")
50                 ("version,V", "Print version and exit")
51                 ("debug,d", "Verbose/Debug output")
52                 ("user,u", po::wvalue<std::wstring>(), "Count only processes of user")
53                 ("warning,w", po::wvalue<std::wstring>(), "Warning threshold")
54                 ("critical,c", po::wvalue<std::wstring>(), "Critical threshold")
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 processes.\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"\tPROCS WARNING 67 | load=67;50;90;0\n\n"
83                         L"\"PROCS\" being the type of the check, \"WARNING\" the returned status\n"
84                         L"and \"67\" 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"For \"-user\" option keep in mind you need root to see other users processes\n\n"
90                         L"%s' exit codes denote the following:\n"
91                         L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
92                         L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
93                         L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
94                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
95                         L"Threshold syntax:\n\n"
96                         L"-w THRESHOLD\n"
97                         L"warn if threshold is broken, which means VALUE > THRESHOLD\n"
98                         L"(unless stated differently)\n\n"
99                         L"-w !THRESHOLD\n"
100                         L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n"
101                         L"-w [THR1-THR2]\n"
102                         L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
103                         L"-w ![THR1-THR2]\n"
104                         L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
105                         L"-w THRESHOLD%%\n"
106                         L"if the plugin accepts percentage based thresholds those will be used.\n"
107                         L"Does nothing if the plugin does not accept percentages, or only uses\n"
108                         L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
109                         L"to end with a percentage sign.\n\n"
110                         L"All of these options work with the critical threshold \"-c\" too."
111                         , progName);
112                 std::cout << '\n';
113                 return 0;
114         }
115
116         if (vm.count("version")) {
117                 std::wcout << "Version: " << VERSION << '\n';
118                 return 0;
119         }
120
121         if (vm.count("warning")) {
122                 try {
123                         printInfo.warn = threshold(vm["warning"].as<std::wstring>());
124                 } catch (const std::invalid_argument& e) {
125                         std::cout << e.what() << '\n';
126                         return 3;
127                 }
128         }
129         if (vm.count("critical")) {
130                 try {
131                         printInfo.crit = threshold(vm["critical"].as<std::wstring>());
132                 } catch (const std::invalid_argument& e) {
133                         std::cout << e.what() << '\n';
134                         return 3;
135                 }
136         }
137
138         if (vm.count("user"))
139                 printInfo.user = vm["user"].as<std::wstring>();
140
141         l_Debug = vm.count("debug") > 0;
142
143         return -1;
144 }
145
146 static int printOutput(const int numProcs, printInfoStruct& printInfo)
147 {
148         if (l_Debug)
149                 std::wcout << L"Constructing output string" << '\n';
150
151         state state = OK;
152
153         if (printInfo.warn.rend(numProcs))
154                 state = WARNING;
155
156         if (printInfo.crit.rend(numProcs))
157                 state = CRITICAL;
158
159         std::wstring user;
160         if (!printInfo.user.empty())
161                 user = L" processes of user " + printInfo.user;
162
163         std::wcout << L"PROCS ";
164
165         switch (state) {
166         case OK:
167                 std::wcout << L"OK";
168                 break;
169         case WARNING:
170                 std::wcout << L"WARNING";
171                 break;
172         case CRITICAL:
173                 std::wcout << L"CRITICAL";
174                 break;
175         }
176
177         std::wcout << L" " << numProcs << user << L" | procs=" << numProcs << L";"
178                 << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n';
179
180         return state;
181 }
182
183 static int countProcs()
184 {
185         if (l_Debug)
186                 std::wcout << L"Counting all processes" << '\n';
187
188         if (l_Debug)
189                 std::wcout << L"Creating snapshot" << '\n';
190
191         HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
192         if (hProcessSnap == INVALID_HANDLE_VALUE)
193                 return -1;
194
195         PROCESSENTRY32 pe32;
196         pe32.dwSize = sizeof(PROCESSENTRY32);
197
198         if (l_Debug)
199                 std::wcout << L"Grabbing first proccess" << '\n';
200
201         if (!Process32First(hProcessSnap, &pe32)) {
202                 CloseHandle(hProcessSnap);
203                 return -1;
204         }
205
206         if (l_Debug)
207                 std::wcout << L"Counting processes..." << '\n';
208
209         int numProcs = 0;
210
211         do {
212                 ++numProcs;
213         } while (Process32Next(hProcessSnap, &pe32));
214
215         if (l_Debug)
216                 std::wcout << L"Found " << numProcs << L" processes. Cleaning up udn returning" << '\n';
217
218         CloseHandle(hProcessSnap);
219
220         return numProcs;
221 }
222
223 static int countProcs(const std::wstring& user)
224 {
225         if (l_Debug)
226                 std::wcout << L"Counting all processes of user" << user << '\n';
227
228         const WCHAR *wuser = user.c_str();
229         int numProcs = 0;
230
231         HANDLE hProcessSnap, hProcess = NULL, hToken = NULL;
232         PROCESSENTRY32 pe32;
233         DWORD dwReturnLength, dwAcctName, dwDomainName;
234         PTOKEN_USER pSIDTokenUser = NULL;
235         SID_NAME_USE sidNameUse;
236         LPWSTR AcctName, DomainName;
237
238         if (l_Debug)
239                 std::wcout << L"Creating snapshot" << '\n';
240
241         hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
242         if (hProcessSnap == INVALID_HANDLE_VALUE)
243                 goto die;
244
245         pe32.dwSize = sizeof(PROCESSENTRY32);
246
247         if (l_Debug)
248                 std::wcout << L"Grabbing first proccess" << '\n';
249
250         if (!Process32First(hProcessSnap, &pe32))
251                 goto die;
252
253         if (l_Debug)
254                 std::wcout << L"Counting processes..." << '\n';
255
256         do {
257                 if (l_Debug)
258                         std::wcout << L"Getting process token" << '\n';
259
260                 //get ProcessToken
261                 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
262                 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
263                         //Won't count pid 0 (system idle) and 4/8 (Sytem)
264                         continue;
265
266                 //Get dwReturnLength in first call
267                 dwReturnLength = 1;
268                 if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwReturnLength)
269                         && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
270                         continue;
271
272                 pSIDTokenUser = reinterpret_cast<PTOKEN_USER>(new BYTE[dwReturnLength]);
273                 memset(pSIDTokenUser, 0, dwReturnLength);
274
275                 if (l_Debug)
276                         std::wcout << L"Received token, saving information" << '\n';
277
278                 //write Info in pSIDTokenUser
279                 if (!GetTokenInformation(hToken, TokenUser, pSIDTokenUser, dwReturnLength, NULL))
280                         continue;
281
282                 AcctName = NULL;
283                 DomainName = NULL;
284                 dwAcctName = 1;
285                 dwDomainName = 1;
286
287                 if (l_Debug)
288                         std::wcout << L"Looking up SID" << '\n';
289
290                 //get dwAcctName and dwDomainName size
291                 if (!LookupAccountSid(NULL, pSIDTokenUser->User.Sid, AcctName,
292                         (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &sidNameUse)
293                         && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
294                         continue;
295
296                 AcctName = reinterpret_cast<LPWSTR>(new WCHAR[dwAcctName]);
297                 DomainName = reinterpret_cast<LPWSTR>(new WCHAR[dwDomainName]);
298
299                 if (!LookupAccountSid(NULL, pSIDTokenUser->User.Sid, AcctName,
300                         (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &sidNameUse))
301                         continue;
302
303                 if (l_Debug)
304                         std::wcout << L"Comparing " << AcctName << L" to " << wuser << '\n';
305                 if (!wcscmp(AcctName, wuser)) {
306                         ++numProcs;
307                         if (l_Debug)
308                                 std::wcout << L"Is process of " << wuser << L" (" << numProcs << L")" << '\n';
309                 }
310
311                 delete[] reinterpret_cast<LPWSTR>(AcctName);
312                 delete[] reinterpret_cast<LPWSTR>(DomainName);
313
314         } while (Process32Next(hProcessSnap, &pe32));
315
316 die:
317         if (hProcessSnap)
318                 CloseHandle(hProcessSnap);
319         if (hProcess)
320                 CloseHandle(hProcess);
321         if (hToken)
322                 CloseHandle(hToken);
323         if (pSIDTokenUser)
324                 delete[] reinterpret_cast<PTOKEN_USER>(pSIDTokenUser);
325         return numProcs;
326 }
327
328 int wmain(int argc, WCHAR **argv)
329 {
330         po::variables_map vm;
331         printInfoStruct printInfo = { };
332
333         int r = parseArguments(argc, argv, vm, printInfo);
334
335         if (r != -1)
336                 return r;
337
338         if (!printInfo.user.empty())
339                 return printOutput(countProcs(printInfo.user), printInfo);
340
341         return printOutput(countProcs(), printInfo);
342 }