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