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