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