]> granicus.if.org Git - icinga2/blob - plugins/check_service.cpp
Merge pull request #6348 from Mikesch-mp/itl_db2_health
[icinga2] / plugins / check_service.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
26 #define VERSION 1.1
27
28 namespace po = boost::program_options;
29
30 struct printInfoStruct
31 {
32         bool warn;
33         DWORD ServiceState;
34         std::wstring service;
35 };
36
37 static bool l_Debug;
38
39 static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
40 {
41         WCHAR namePath[MAX_PATH];
42         GetModuleFileName(NULL, namePath, MAX_PATH);
43         WCHAR *progName = PathFindFileName(namePath);
44
45         po::options_description desc;
46
47         desc.add_options()
48                 ("help,h", "Print help message and exit")
49                 ("version,V", "Print version and exit")
50                 ("debug,D", "Verbose/Debug output")
51                 ("service,s", po::wvalue<std::wstring>(), "Service name to check")
52                 ("description,d", "Use \"service\" to match on description")
53                 ("warn,w", "Return warning (1) instead of critical (2),\n when service is not running")
54                 ;
55
56         po::wcommand_line_parser parser(ac, av);
57
58         try {
59                 po::store(
60                         parser
61                         .options(desc)
62                         .style(
63                         po::command_line_style::unix_style |
64                         po::command_line_style::allow_long_disguise)
65                         .run(),
66                         vm);
67                 vm.notify();
68         } catch (const std::exception& e) {
69                 std::cout << e.what() << '\n' << desc << '\n';
70                 return 3;
71         }
72
73         if (vm.count("help")) {
74                 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
75                 wprintf(
76                         L"%s is a simple program to check the status of a service.\n"
77                         L"You can use the following options to define its behaviour:\n\n", progName);
78                 std::cout << desc;
79                 wprintf(
80                         L"\nIt will then output a string looking something like this:\n\n"
81                         L"\tSERVICE CRITICAL NOT_RUNNING | service=4;!4;!4;1;7\n\n"
82                         L"\"SERVICE\" being the type of the check, \"CRITICAL\" the returned status\n"
83                         L"and \"1\" is the returned value.\n"
84                         L"A service is either running (Code 0x04) or not running (any other).\n"
85                         L"For more information consult the msdn on service state transitions.\n\n"
86                         L"%s' exit codes denote the following:\n"
87                         L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
88                         L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
89                         L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
90                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
91                         L"%s' thresholds work differently, since a service is either running or not\n"
92                         L"all \"-w\" and \"-c\" do is say whether a not running service is a warning\n"
93                         L"or critical state respectively.\n\n"
94                         , progName, progName);
95                 std::cout << '\n';
96                 return 0;
97         }
98
99         if (vm.count("version")) {
100                 std::cout << "Version: " << VERSION << '\n';
101                 return 0;
102         }
103
104         if (!vm.count("service")) {
105                 std::cout << "Argument \"service\" is required.\n" << desc << '\n';
106                 return 3;
107         }
108
109         printInfo.service = vm["service"].as<std::wstring>();
110
111         printInfo.warn = vm.count("warn");
112
113         l_Debug = vm.count("debug") > 0;
114
115         return -1;
116 }
117
118 static int printOutput(const printInfoStruct& printInfo)
119 {
120         if (l_Debug)
121                 std::wcout << L"Constructing output string" << '\n';
122
123         std::wstring perf;
124         state state = OK;
125
126         if (!printInfo.ServiceState) {
127                 std::wcout << L"SERVICE CRITICAL NOT FOUND | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n';
128                 return 3;
129         }
130
131         if (printInfo.ServiceState != 0x04)
132                 printInfo.warn ? state = WARNING : state = CRITICAL;
133
134         switch (state) {
135         case OK:
136                 std::wcout << L"SERVICE \"" << printInfo.service << "\" OK RUNNING | 'service'=4;;;1;7" << '\n';
137                 break;
138         case WARNING:
139                 std::wcout << L"SERVICE \"" << printInfo.service << "\" WARNING NOT RUNNING | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n';
140                 break;
141         case CRITICAL:
142                 std::wcout << L"SERVICE \"" << printInfo.service << "\" CRITICAL NOT RUNNING | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n';
143                 break;
144         }
145
146         return state;
147 }
148
149 static std::wstring getServiceByDescription(const std::wstring& description)
150 {
151         SC_HANDLE hSCM = NULL;
152         LPENUM_SERVICE_STATUSW lpServices = NULL;
153         LPBYTE lpBuf = NULL;
154         DWORD cbBufSize = 0;
155         DWORD lpServicesReturned = 0;
156         DWORD pcbBytesNeeded = 0;
157         DWORD lpResumeHandle = 0;;
158
159         if (l_Debug)
160                 std::wcout << L"Opening SC Manager" << '\n';
161
162         hSCM = OpenSCManager(NULL, NULL, GENERIC_READ);
163         if (hSCM == NULL)
164                 goto die;
165
166         if (l_Debug)
167                 std::wcout << L"Determining initially required memory" << '\n';
168
169         EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, NULL, 0,
170                 &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle);
171
172         /* This should always be ERROR_INSUFFICIENT_BUFFER... But for some reason it is sometimes ERROR_MORE_DATA
173         * See the MSDN on EnumServiceStatus for a glimpse of despair
174         */
175
176         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER && GetLastError() != ERROR_MORE_DATA)
177                 goto die;
178
179         lpServices = reinterpret_cast<LPENUM_SERVICE_STATUSW>(new BYTE[pcbBytesNeeded]);
180
181         if (l_Debug)
182                 std::wcout << L"Requesting Service Information. Entry point: " << lpResumeHandle << '\n';
183
184         EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, lpServices, pcbBytesNeeded,
185                 &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle);
186
187         for (int index = 0; index < lpServicesReturned; index++) {
188                 LPWSTR lpCurrent = lpServices[index].lpServiceName;
189
190                 if (l_Debug) {
191                         std::wcout << L"Opening Service \"" << lpServices[index].lpServiceName << L"\"\n";
192                 }
193
194                 SC_HANDLE hService = OpenService(hSCM, lpCurrent, SERVICE_QUERY_CONFIG);
195                 if (!hService)
196                         goto die;
197
198                 DWORD dwBytesNeeded = 0;
199                 if (l_Debug)
200                         std::wcout << "Accessing config\n";
201
202                 if (!QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &dwBytesNeeded) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
203                         continue;
204
205                 LPSERVICE_DESCRIPTION lpsd = reinterpret_cast<LPSERVICE_DESCRIPTION>(new BYTE[dwBytesNeeded]);
206
207                 if (!QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpsd, dwBytesNeeded, &dwBytesNeeded))
208                         continue;
209
210                 if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, L"") != 0) {
211                         std::wstring desc(lpsd->lpDescription);
212                         if (l_Debug)
213                                 std::wcout << "Got description:\n" << desc << '\n';
214                         size_t p = desc.find(description);
215                         if (desc.find(description) != desc.npos)
216                                 return lpCurrent;
217                 }
218                 else if (l_Debug)
219                         std::wcout << "No description found\n";
220         }
221
222         CloseServiceHandle(hSCM);
223         delete[] lpServices;
224         return L"";
225
226 die:
227         printErrorInfo();
228         if (hSCM)
229                 CloseServiceHandle(hSCM);
230         if (lpServices)
231                 delete[] lpServices;
232         return L"";
233 }
234
235 static DWORD getServiceStatus(const printInfoStruct& printInfo)
236 {
237         SC_HANDLE hSCM;
238         SC_HANDLE hService;
239         DWORD cbBufSize;
240         DWORD lpResumeHandle = 0;
241         LPBYTE lpBuf = NULL;
242
243         if (l_Debug)
244                 std::wcout << L"Opening SC Manager" << '\n';
245
246         hSCM = OpenSCManager(NULL, NULL, GENERIC_READ);
247         if (hSCM == NULL)
248                 goto die;
249
250         hService = OpenService(hSCM, printInfo.service.c_str(), SERVICE_QUERY_STATUS);
251         if (hService == NULL)
252                 goto die;
253
254         QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, NULL, 0, &cbBufSize);
255         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
256                 goto die;
257
258         lpBuf = new BYTE[cbBufSize];
259         if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, lpBuf, cbBufSize, &cbBufSize)) {
260                 LPSERVICE_STATUS_PROCESS pInfo = (LPSERVICE_STATUS_PROCESS)lpBuf;
261                 return pInfo->dwCurrentState;
262         }
263
264 die:
265         printErrorInfo();
266         if (hSCM)
267                 CloseServiceHandle(hSCM);
268         if (hService)
269                 CloseServiceHandle(hService);
270         if (lpBuf)
271                 delete [] lpBuf;
272
273         return -1;
274 }
275
276 int wmain(int argc, WCHAR **argv)
277 {
278         po::variables_map vm;
279         printInfoStruct printInfo;
280
281         int ret = parseArguments(argc, argv, vm, printInfo);
282         if (ret != -1)
283                 return ret;
284
285         if (vm.count("description"))
286                 printInfo.service = getServiceByDescription(vm["service"].as<std::wstring>());
287
288         if (printInfo.service.empty()) {
289                 std::wcout << "Could not find service matching description\n";
290                 return 3;
291         }
292
293         printInfo.ServiceState = getServiceStatus(printInfo);
294         if (printInfo.ServiceState == -1)
295                 return 3;
296
297         return printOutput(printInfo);
298 }
299