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