1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (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
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "service.h"
22 
23 #include "cfg.h"
24 #include "log.h"
25 #include "alias.h"
26 #include "zbxconf.h"
27 #include "perfmon.h"
28 
29 #define EVENTLOG_REG_PATH TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\")
30 
31 static	SERVICE_STATUS		serviceStatus;
32 static	SERVICE_STATUS_HANDLE	serviceHandle;
33 
34 int	application_status = ZBX_APP_RUNNING;
35 
36 /* free resources allocated by MAIN_ZABBIX_ENTRY() */
37 void	zbx_free_service_resources(int ret);
38 
parent_signal_handler(int sig)39 static void	parent_signal_handler(int sig)
40 {
41 	switch (sig)
42 	{
43 		case SIGINT:
44 		case SIGTERM:
45 			ZBX_DO_EXIT();
46 			zabbix_log(LOG_LEVEL_INFORMATION, "Got signal. Exiting ...");
47 			zbx_on_exit(SUCCEED);
48 			break;
49 	}
50 }
51 
ServiceCtrlHandler(DWORD ctrlCode)52 static VOID WINAPI	ServiceCtrlHandler(DWORD ctrlCode)
53 {
54 	serviceStatus.dwServiceType		= SERVICE_WIN32_OWN_PROCESS;
55 	serviceStatus.dwCurrentState		= SERVICE_RUNNING;
56 	serviceStatus.dwControlsAccepted	= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
57 	serviceStatus.dwWin32ExitCode		= 0;
58 	serviceStatus.dwServiceSpecificExitCode	= 0;
59 	serviceStatus.dwCheckPoint		= 0;
60 	serviceStatus.dwWaitHint		= 0;
61 
62 	switch (ctrlCode)
63 	{
64 		case SERVICE_CONTROL_STOP:
65 		case SERVICE_CONTROL_SHUTDOWN:
66 			serviceStatus.dwCurrentState	= SERVICE_STOP_PENDING;
67 			serviceStatus.dwWaitHint	= 4000;
68 			SetServiceStatus(serviceHandle, &serviceStatus);
69 
70 			/* notify other threads and allow them to terminate */
71 			ZBX_DO_EXIT();
72 			zbx_free_service_resources(SUCCEED);
73 
74 			serviceStatus.dwCurrentState	= SERVICE_STOPPED;
75 			serviceStatus.dwWaitHint	= 0;
76 			serviceStatus.dwCheckPoint	= 0;
77 			serviceStatus.dwWin32ExitCode	= 0;
78 
79 			break;
80 		default:
81 			break;
82 	}
83 
84 	SetServiceStatus(serviceHandle, &serviceStatus);
85 }
86 
ServiceEntry(DWORD argc,wchar_t ** argv)87 static VOID WINAPI	ServiceEntry(DWORD argc, wchar_t **argv)
88 {
89 	wchar_t	*wservice_name;
90 
91 	wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME);
92 	serviceHandle = RegisterServiceCtrlHandler(wservice_name, ServiceCtrlHandler);
93 	zbx_free(wservice_name);
94 
95 	/* start service initialization */
96 	serviceStatus.dwServiceType		= SERVICE_WIN32_OWN_PROCESS;
97 	serviceStatus.dwCurrentState		= SERVICE_START_PENDING;
98 	serviceStatus.dwControlsAccepted	= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
99 	serviceStatus.dwWin32ExitCode		= 0;
100 	serviceStatus.dwServiceSpecificExitCode	= 0;
101 	serviceStatus.dwCheckPoint		= 0;
102 	serviceStatus.dwWaitHint		= 2000;
103 
104 	SetServiceStatus(serviceHandle, &serviceStatus);
105 
106 	/* service is running */
107 	serviceStatus.dwCurrentState	= SERVICE_RUNNING;
108 	serviceStatus.dwWaitHint	= 0;
109 	SetServiceStatus(serviceHandle, &serviceStatus);
110 
111 	MAIN_ZABBIX_ENTRY(0);
112 }
113 
service_start(int flags)114 void	service_start(int flags)
115 {
116 	int				ret;
117 	static SERVICE_TABLE_ENTRY	serviceTable[2];
118 
119 	if (0 != (flags & ZBX_TASK_FLAG_FOREGROUND))
120 	{
121 		MAIN_ZABBIX_ENTRY(flags);
122 		return;
123 	}
124 
125 	serviceTable[0].lpServiceName = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME);
126 	serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceEntry;
127 	serviceTable[1].lpServiceName = NULL;
128 	serviceTable[1].lpServiceProc = NULL;
129 
130 	ret = StartServiceCtrlDispatcher(serviceTable);
131 	zbx_free(serviceTable[0].lpServiceName);
132 
133 	if (0 == ret)
134 	{
135 		if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT == GetLastError())
136 			zbx_error("use foreground option to run Zabbix agent as console application");
137 		else
138 			zbx_error("StartServiceCtrlDispatcher() failed: %s", strerror_from_system(GetLastError()));
139 	}
140 }
141 
svc_OpenSCManager(SC_HANDLE * mgr)142 static int	svc_OpenSCManager(SC_HANDLE *mgr)
143 {
144 	if (NULL != (*mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE)))
145 		return SUCCEED;
146 
147 	zbx_error("ERROR: cannot connect to Service Manager: %s", strerror_from_system(GetLastError()));
148 
149 	return FAIL;
150 }
151 
svc_OpenService(SC_HANDLE mgr,SC_HANDLE * service,DWORD desired_access)152 static int	svc_OpenService(SC_HANDLE mgr, SC_HANDLE *service, DWORD desired_access)
153 {
154 	wchar_t	*wservice_name;
155 	int	ret = SUCCEED;
156 
157 	wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME);
158 
159 	if (NULL == (*service = OpenService(mgr, wservice_name, desired_access)))
160 	{
161 		zbx_error("ERROR: cannot open service [%s]: %s",
162 				ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError()));
163 		ret = FAIL;
164 	}
165 
166 	zbx_free(wservice_name);
167 
168 	return ret;
169 }
170 
svc_get_fullpath(const char * path,wchar_t * fullpath,size_t max_fullpath)171 static void	svc_get_fullpath(const char *path, wchar_t *fullpath, size_t max_fullpath)
172 {
173 	wchar_t	*wpath;
174 
175 	wpath = zbx_acp_to_unicode(path);
176 	_wfullpath(fullpath, wpath, max_fullpath);
177 	zbx_free(wpath);
178 }
179 
svc_get_command_line(const char * path,int multiple_agents,wchar_t * cmdLine,size_t max_cmdLine)180 static void	svc_get_command_line(const char *path, int multiple_agents, wchar_t *cmdLine, size_t max_cmdLine)
181 {
182 	wchar_t	path1[MAX_PATH], path2[MAX_PATH];
183 
184 	svc_get_fullpath(path, path2, MAX_PATH);
185 
186 	if (NULL == wcsstr(path2, TEXT(".exe")))
187 		StringCchPrintf(path1, MAX_PATH, TEXT("%s.exe"), path2);
188 	else
189 		StringCchPrintf(path1, MAX_PATH, path2);
190 
191 	if (NULL != CONFIG_FILE)
192 	{
193 		svc_get_fullpath(CONFIG_FILE, path2, MAX_PATH);
194 		StringCchPrintf(cmdLine, max_cmdLine, TEXT("\"%s\" %s--config \"%s\""),
195 				path1,
196 				(0 == multiple_agents) ? TEXT("") : TEXT("--multiple-agents "),
197 				path2);
198 	}
199 	else
200 		StringCchPrintf(cmdLine, max_cmdLine, TEXT("\"%s\""), path1);
201 }
202 
svc_install_event_source(const char * path)203 static int	svc_install_event_source(const char *path)
204 {
205 	HKEY	hKey;
206 	DWORD	dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
207 	wchar_t	execName[MAX_PATH];
208 	wchar_t	regkey[256], *wevent_source;
209 
210 	svc_get_fullpath(path, execName, MAX_PATH);
211 
212 	wevent_source = zbx_utf8_to_unicode(ZABBIX_EVENT_SOURCE);
213 	StringCchPrintf(regkey, ARRSIZE(regkey), EVENTLOG_REG_PATH TEXT("System\\%s"), wevent_source);
214 	zbx_free(wevent_source);
215 
216 	if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, regkey, 0, NULL, REG_OPTION_NON_VOLATILE,
217 			KEY_SET_VALUE, NULL, &hKey, NULL))
218 	{
219 		zbx_error("unable to create registry key: %s", strerror_from_system(GetLastError()));
220 		return FAIL;
221 	}
222 
223 	RegSetValueEx(hKey, TEXT("TypesSupported"), 0, REG_DWORD, (BYTE *)&dwTypes, sizeof(DWORD));
224 	RegSetValueEx(hKey, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (BYTE *)execName,
225 			(DWORD)(wcslen(execName) + 1) * sizeof(wchar_t));
226 	RegCloseKey(hKey);
227 
228 	zbx_error("event source [%s] installed successfully", ZABBIX_EVENT_SOURCE);
229 
230 	return SUCCEED;
231 }
232 
ZabbixCreateService(const char * path,int multiple_agents)233 int	ZabbixCreateService(const char *path, int multiple_agents)
234 {
235 	SC_HANDLE		mgr, service;
236 	SERVICE_DESCRIPTION	sd;
237 	wchar_t			cmdLine[MAX_PATH];
238 	wchar_t			*wservice_name;
239 	DWORD			code;
240 	int			ret = FAIL;
241 
242 	if (FAIL == svc_OpenSCManager(&mgr))
243 		return ret;
244 
245 	svc_get_command_line(path, multiple_agents, cmdLine, MAX_PATH);
246 
247 	wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME);
248 
249 	if (NULL == (service = CreateService(mgr, wservice_name, wservice_name, GENERIC_READ, SERVICE_WIN32_OWN_PROCESS,
250 			SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdLine, NULL, NULL, NULL, NULL, NULL)))
251 	{
252 		if (ERROR_SERVICE_EXISTS == (code = GetLastError()))
253 			zbx_error("ERROR: service [%s] already exists", ZABBIX_SERVICE_NAME);
254 		else
255 			zbx_error("ERROR: cannot create service [%s]: %s", ZABBIX_SERVICE_NAME, strerror_from_system(code));
256 	}
257 	else
258 	{
259 		zbx_error("service [%s] installed successfully", ZABBIX_SERVICE_NAME);
260 		CloseServiceHandle(service);
261 		ret = SUCCEED;
262 
263 		/* update the service description */
264 		if (SUCCEED == svc_OpenService(mgr, &service, SERVICE_CHANGE_CONFIG))
265 		{
266 			sd.lpDescription = TEXT("Provides system monitoring");
267 			if (0 == ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd))
268 				zbx_error("service description update failed: %s", strerror_from_system(GetLastError()));
269 			CloseServiceHandle(service);
270 		}
271 	}
272 
273 	zbx_free(wservice_name);
274 
275 	CloseServiceHandle(mgr);
276 
277 	if (SUCCEED == ret)
278 		ret = svc_install_event_source(path);
279 
280 	return ret;
281 }
282 
svc_RemoveEventSource()283 static int	svc_RemoveEventSource()
284 {
285 	wchar_t	regkey[256];
286 	wchar_t	*wevent_source;
287 	int	ret = FAIL;
288 
289 	wevent_source = zbx_utf8_to_unicode(ZABBIX_EVENT_SOURCE);
290 	StringCchPrintf(regkey, ARRSIZE(regkey), EVENTLOG_REG_PATH TEXT("System\\%s"), wevent_source);
291 	zbx_free(wevent_source);
292 
293 	if (ERROR_SUCCESS == RegDeleteKey(HKEY_LOCAL_MACHINE, regkey))
294 	{
295 		zbx_error("event source [%s] uninstalled successfully", ZABBIX_EVENT_SOURCE);
296 		ret = SUCCEED;
297 	}
298 	else
299 	{
300 		zbx_error("unable to uninstall event source [%s]: %s",
301 				ZABBIX_EVENT_SOURCE, strerror_from_system(GetLastError()));
302 	}
303 
304 	return ret;
305 }
306 
ZabbixRemoveService(void)307 int	ZabbixRemoveService(void)
308 {
309 	SC_HANDLE	mgr, service;
310 	int		ret = FAIL;
311 
312 	if (FAIL == svc_OpenSCManager(&mgr))
313 		return ret;
314 
315 	if (SUCCEED == svc_OpenService(mgr, &service, DELETE))
316 	{
317 		if (0 != DeleteService(service))
318 		{
319 			zbx_error("service [%s] uninstalled successfully", ZABBIX_SERVICE_NAME);
320 			ret = SUCCEED;
321 		}
322 		else
323 		{
324 			zbx_error("ERROR: cannot remove service [%s]: %s",
325 					ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError()));
326 		}
327 
328 		CloseServiceHandle(service);
329 	}
330 
331 	CloseServiceHandle(mgr);
332 
333 	if (SUCCEED == ret)
334 		ret = svc_RemoveEventSource();
335 
336 	return ret;
337 }
338 
ZabbixStartService(void)339 int	ZabbixStartService(void)
340 {
341 	SC_HANDLE	mgr, service;
342 	int		ret = FAIL;
343 
344 	if (FAIL == svc_OpenSCManager(&mgr))
345 		return ret;
346 
347 	if (SUCCEED == svc_OpenService(mgr, &service, SERVICE_START))
348 	{
349 		if (0 != StartService(service, 0, NULL))
350 		{
351 			zbx_error("service [%s] started successfully", ZABBIX_SERVICE_NAME);
352 			ret = SUCCEED;
353 		}
354 		else
355 		{
356 			zbx_error("ERROR: cannot start service [%s]: %s",
357 					ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError()));
358 		}
359 
360 		CloseServiceHandle(service);
361 	}
362 
363 	CloseServiceHandle(mgr);
364 
365 	return ret;
366 }
367 
ZabbixStopService(void)368 int	ZabbixStopService(void)
369 {
370 	SC_HANDLE	mgr, service;
371 	SERVICE_STATUS	status;
372 	int		ret = FAIL;
373 
374 	if (FAIL == svc_OpenSCManager(&mgr))
375 		return ret;
376 
377 	if (SUCCEED == svc_OpenService(mgr, &service, SERVICE_STOP))
378 	{
379 		if (0 != ControlService(service, SERVICE_CONTROL_STOP, &status))
380 		{
381 			zbx_error("service [%s] stopped successfully", ZABBIX_SERVICE_NAME);
382 			ret = SUCCEED;
383 		}
384 		else
385 		{
386 			zbx_error("ERROR: cannot stop service [%s]: %s",
387 					ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError()));
388 		}
389 
390 		CloseServiceHandle(service);
391 	}
392 
393 	CloseServiceHandle(mgr);
394 
395 	return ret;
396 }
397 
set_parent_signal_handler(void)398 void	set_parent_signal_handler(void)
399 {
400 	signal(SIGINT, parent_signal_handler);
401 	signal(SIGTERM, parent_signal_handler);
402 }
403 
404