1 /******************************************************************************
3 * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
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. *
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. *
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 ******************************************************************************/
23 #include "check_service.h"
28 namespace po = boost::program_options;
32 INT wmain(INT argc, WCHAR **argv)
35 printInfoStruct printInfo;
37 INT ret = parseArguments(argc, argv, vm, printInfo);
41 if (vm.count("description"))
42 printInfo.service = GetServiceByDescription(vm["service"].as<std::wstring>());
44 if (printInfo.service.empty()) {
45 std::wcout << "Could not find service matching description\n";
49 printInfo.ServiceState = ServiceStatus(printInfo);
50 if (printInfo.ServiceState == -1)
53 return printOutput(printInfo);
56 INT parseArguments(INT ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
58 WCHAR namePath[MAX_PATH];
59 GetModuleFileName(NULL, namePath, MAX_PATH);
60 WCHAR *progName = PathFindFileName(namePath);
62 po::options_description desc;
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")
73 po::basic_command_line_parser<WCHAR> parser(ac, av);
80 po::command_line_style::unix_style |
81 po::command_line_style::allow_long_disguise)
86 catch (std::exception& e) {
87 std::cout << e.what() << '\n' << desc << '\n';
91 if (vm.count("help")) {
92 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
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);
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);
117 if (vm.count("version")) {
118 std::cout << "Version: " << VERSION << '\n';
122 if (!vm.count("service")) {
123 std::cout << "Argument \"service\" is required.\n" << desc << '\n';
127 printInfo.service = vm["service"].as<std::wstring>();
129 if (vm.count("warn")) {
130 printInfo.warn = true;
139 INT printOutput(CONST printInfoStruct& printInfo)
142 std::wcout << L"Constructing output string" << '\n';
147 if (!printInfo.ServiceState) {
148 std::wcout << L"SERVICE CRITICAL NOTFOUND | service=" << printInfo.ServiceState << ";;;1;7" << '\n';
152 if (printInfo.ServiceState != 0x04)
153 printInfo.warn ? state = WARNING : state = CRITICAL;
157 std::wcout << L"SERVICE \"" << printInfo.service << "\" OK RUNNING | service=4;;;1;7" << '\n';
160 std::wcout << L"SERVICE \"" << printInfo.service << "\"WARNING NOT RUNNING | service=" << printInfo.ServiceState << ";;;1;7" << '\n';
163 std::wcout << L"SERVICE \"" << printInfo.service << "\"CRITICAL NOT RUNNING | service=" << printInfo.ServiceState << ";;;1;7" << '\n';
170 std::wstring GetServiceByDescription(CONST std::wstring& description) {
171 SC_HANDLE hSCM = NULL;
174 DWORD lpServicesReturned = 0;
175 DWORD pcbBytesNeeded = 0;
176 DWORD lpResumeHandle = 0;;
179 std::wcout << L"Opening SC Manager" << '\n';
181 hSCM = OpenSCManager(NULL, NULL, GENERIC_READ);
186 std::wcout << L"Determining initially required memory" << '\n';
188 EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, NULL, 0,
189 &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle);
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
195 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER && GetLastError() != ERROR_MORE_DATA)
198 LPENUM_SERVICE_STATUSW lpServices = reinterpret_cast<LPENUM_SERVICE_STATUSW>(new BYTE[pcbBytesNeeded]);
201 std::wcout << L"Requesting Service Information. Entry point: " << lpResumeHandle << '\n';
203 EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, lpServices, pcbBytesNeeded,
204 &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle);
206 for (int index = 0; index < lpServicesReturned; index++) {
207 LPWSTR lpCurrent = lpServices[index].lpServiceName;
210 std::wcout << L"Opening Service \"" << lpServices[index].lpServiceName << L"\"\n";
213 SC_HANDLE hService = OpenService(hSCM, lpCurrent, SERVICE_QUERY_CONFIG);
214 if (hService == NULL)
217 DWORD dwBytesNeeded = 0;
219 std::wcout << "Accessing config\n";
221 if (!QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &dwBytesNeeded) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
224 LPSERVICE_DESCRIPTION lpsd = reinterpret_cast<LPSERVICE_DESCRIPTION>(new BYTE[dwBytesNeeded]);
226 if (!QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpsd, dwBytesNeeded, &dwBytesNeeded))
229 if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, L"") != 0) {
230 std::wstring desc(lpsd->lpDescription);
232 std::wcout << "Got description:\n" << desc << '\n';
233 size_t p = desc.find(description);
234 if (desc.find(description) != desc.npos)
238 std::wcout << "No description found\n";
241 CloseServiceHandle(hSCM);
248 CloseServiceHandle(hSCM);
254 DWORD ServiceStatus(CONST printInfoStruct& printInfo)
258 DWORD cbBufSize, lpServicesReturned, pcbBytesNeeded;
259 DWORD lpResumeHandle = 0;
263 std::wcout << L"Opening SC Manager" << '\n';
265 hSCM = OpenSCManager(NULL, NULL, GENERIC_READ);
269 hService = OpenService(hSCM, printInfo.service.c_str(), SERVICE_QUERY_STATUS);
270 if (hService == NULL)
273 QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, NULL, 0, &cbBufSize);
274 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
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;
286 CloseServiceHandle(hSCM);
288 CloseServiceHandle(hService);