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