diff options
Diffstat (limited to 'src/libs/zbxwinservice/service.c')
-rw-r--r-- | src/libs/zbxwinservice/service.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/src/libs/zbxwinservice/service.c b/src/libs/zbxwinservice/service.c new file mode 100644 index 00000000000..c83f3510185 --- /dev/null +++ b/src/libs/zbxwinservice/service.c @@ -0,0 +1,435 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "zbxwinservice.h" + +#include "zbxstr.h" +#include "cfg.h" +#include "log.h" +#include "zbxconf.h" + +#define EVENTLOG_REG_PATH TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\") + +static SERVICE_STATUS serviceStatus; +static SERVICE_STATUS_HANDLE serviceHandle; + +#define ZBX_APP_STOPPED 0 +#define ZBX_APP_RUNNING 1 +/* required for closing application from service */ +static int application_status = ZBX_APP_RUNNING; + +static zbx_on_exit_t zbx_on_exit_cb; + +int ZBX_IS_RUNNING(void) +{ + return application_status; +} + +void ZBX_DO_EXIT(void) +{ + application_status = ZBX_APP_STOPPED; +} +#undef ZBX_APP_STOPPED +#undef ZBX_APP_RUNNING + +/* free resources allocated by MAIN_ZABBIX_ENTRY() */ +void zbx_free_service_resources(int ret); + +static void parent_signal_handler(int sig) +{ + switch (sig) + { + case SIGINT: + case SIGTERM: + ZBX_DO_EXIT(); + zabbix_log(LOG_LEVEL_INFORMATION, "Got signal. Exiting ..."); + zbx_on_exit_cb(SUCCEED); + break; + } +} + +static VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode) +{ + serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwCurrentState = SERVICE_RUNNING; + serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + serviceStatus.dwWin32ExitCode = 0; + serviceStatus.dwServiceSpecificExitCode = 0; + serviceStatus.dwCheckPoint = 0; + serviceStatus.dwWaitHint = 0; + + switch (ctrlCode) + { + case SERVICE_CONTROL_STOP: + zabbix_log(LOG_LEVEL_INFORMATION, "Zabbix Agent received stop request."); + break; + case SERVICE_CONTROL_SHUTDOWN: + zabbix_log(LOG_LEVEL_INFORMATION, "Zabbix Agent received shutdown request."); + break; + default: + zabbix_log(LOG_LEVEL_DEBUG, "Zabbix Agent received request:%u.", ctrlCode); + break; + } + + switch (ctrlCode) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + serviceStatus.dwWaitHint = 4000; + SetServiceStatus(serviceHandle, &serviceStatus); + + /* notify other threads and allow them to terminate */ + ZBX_DO_EXIT(); + zbx_free_service_resources(SUCCEED); + + serviceStatus.dwCurrentState = SERVICE_STOPPED; + serviceStatus.dwWaitHint = 0; + serviceStatus.dwCheckPoint = 0; + serviceStatus.dwWin32ExitCode = 0; + + break; + default: + break; + } + + SetServiceStatus(serviceHandle, &serviceStatus); +} + +static VOID WINAPI ServiceEntry(DWORD argc, wchar_t **argv) +{ + wchar_t *wservice_name; + + ZBX_UNUSED(argc); + ZBX_UNUSED(argv); + + wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME); + serviceHandle = RegisterServiceCtrlHandler(wservice_name, ServiceCtrlHandler); + zbx_free(wservice_name); + + /* start service initialization */ + serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwCurrentState = SERVICE_START_PENDING; + serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + serviceStatus.dwWin32ExitCode = 0; + serviceStatus.dwServiceSpecificExitCode = 0; + serviceStatus.dwCheckPoint = 0; + serviceStatus.dwWaitHint = 2000; + + SetServiceStatus(serviceHandle, &serviceStatus); + + /* service is running */ + serviceStatus.dwCurrentState = SERVICE_RUNNING; + serviceStatus.dwWaitHint = 0; + SetServiceStatus(serviceHandle, &serviceStatus); + + MAIN_ZABBIX_ENTRY(0); +} + +void zbx_service_start(int flags) +{ + int ret; + static SERVICE_TABLE_ENTRY serviceTable[2]; + + if (0 != (flags & ZBX_TASK_FLAG_FOREGROUND)) + { + MAIN_ZABBIX_ENTRY(flags); + return; + } + + serviceTable[0].lpServiceName = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME); + serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceEntry; + serviceTable[1].lpServiceName = NULL; + serviceTable[1].lpServiceProc = NULL; + + ret = StartServiceCtrlDispatcher(serviceTable); + zbx_free(serviceTable[0].lpServiceName); + + if (0 == ret) + { + if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT == GetLastError()) + zbx_error("use foreground option to run Zabbix agent as console application"); + else + zbx_error("StartServiceCtrlDispatcher() failed: %s", strerror_from_system(GetLastError())); + } +} + +static int svc_OpenSCManager(SC_HANDLE *mgr) +{ + if (NULL != (*mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE))) + return SUCCEED; + + zbx_error("ERROR: cannot connect to Service Manager: %s", strerror_from_system(GetLastError())); + + return FAIL; +} + +static int svc_OpenService(SC_HANDLE mgr, SC_HANDLE *service, DWORD desired_access) +{ + wchar_t *wservice_name; + int ret = SUCCEED; + + wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME); + + if (NULL == (*service = OpenService(mgr, wservice_name, desired_access))) + { + zbx_error("ERROR: cannot open service [%s]: %s", + ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError())); + ret = FAIL; + } + + zbx_free(wservice_name); + + return ret; +} + +static void svc_get_fullpath(const char *path, wchar_t *fullpath, size_t max_fullpath) +{ + wchar_t *wpath; + + wpath = zbx_acp_to_unicode(path); + _wfullpath(fullpath, wpath, max_fullpath); + zbx_free(wpath); +} + +static void svc_get_command_line(const char *path, int multiple_agents, wchar_t *cmdLine, size_t max_cmdLine, + const char *config_file) +{ + wchar_t path1[MAX_PATH], path2[MAX_PATH]; + + svc_get_fullpath(path, path2, MAX_PATH); + + if (NULL == wcsstr(path2, TEXT(".exe"))) + StringCchPrintf(path1, MAX_PATH, TEXT("%s.exe"), path2); + else + StringCchPrintf(path1, MAX_PATH, path2); + + if (NULL != config_file) + { + svc_get_fullpath(config_file, path2, MAX_PATH); + StringCchPrintf(cmdLine, max_cmdLine, TEXT("\"%s\" %s--config \"%s\""), + path1, + (0 == multiple_agents) ? TEXT("") : TEXT("--multiple-agents "), + path2); + } + else + StringCchPrintf(cmdLine, max_cmdLine, TEXT("\"%s\""), path1); +} + +static int svc_install_event_source(const char *path) +{ + HKEY hKey; + DWORD dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + wchar_t execName[MAX_PATH]; + wchar_t regkey[256], *wevent_source; + + svc_get_fullpath(path, execName, MAX_PATH); + + wevent_source = zbx_utf8_to_unicode(ZABBIX_EVENT_SOURCE); + StringCchPrintf(regkey, ARRSIZE(regkey), EVENTLOG_REG_PATH TEXT("System\\%s"), wevent_source); + zbx_free(wevent_source); + + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, regkey, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, NULL, &hKey, NULL)) + { + zbx_error("unable to create registry key: %s", strerror_from_system(GetLastError())); + return FAIL; + } + + RegSetValueEx(hKey, TEXT("TypesSupported"), 0, REG_DWORD, (BYTE *)&dwTypes, sizeof(DWORD)); + RegSetValueEx(hKey, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (BYTE *)execName, + (DWORD)(wcslen(execName) + 1) * sizeof(wchar_t)); + RegCloseKey(hKey); + + zbx_error("event source [%s] installed successfully", ZABBIX_EVENT_SOURCE); + + return SUCCEED; +} + +int ZabbixCreateService(const char *path, int multiple_agents, const char *config_file) +{ + SC_HANDLE mgr, service; + SERVICE_DESCRIPTION sd; + wchar_t cmdLine[MAX_PATH]; + wchar_t *wservice_name; + DWORD code; + int ret = FAIL; + + if (FAIL == svc_OpenSCManager(&mgr)) + return ret; + + svc_get_command_line(path, multiple_agents, cmdLine, MAX_PATH, config_file); + + wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME); + + if (NULL == (service = CreateService(mgr, wservice_name, wservice_name, GENERIC_READ, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdLine, NULL, NULL, NULL, NULL, NULL))) + { + if (ERROR_SERVICE_EXISTS == (code = GetLastError())) + zbx_error("ERROR: service [%s] already exists", ZABBIX_SERVICE_NAME); + else + zbx_error("ERROR: cannot create service [%s]: %s", ZABBIX_SERVICE_NAME, strerror_from_system(code)); + } + else + { + zbx_error("service [%s] installed successfully", ZABBIX_SERVICE_NAME); + CloseServiceHandle(service); + ret = SUCCEED; + + /* update the service description */ + if (SUCCEED == svc_OpenService(mgr, &service, SERVICE_CHANGE_CONFIG)) + { + sd.lpDescription = TEXT("Provides system monitoring"); + if (0 == ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd)) + zbx_error("service description update failed: %s", strerror_from_system(GetLastError())); + CloseServiceHandle(service); + } + } + + zbx_free(wservice_name); + + CloseServiceHandle(mgr); + + if (SUCCEED == ret) + ret = svc_install_event_source(path); + + return ret; +} + +static int svc_RemoveEventSource() +{ + wchar_t regkey[256]; + wchar_t *wevent_source; + int ret = FAIL; + + wevent_source = zbx_utf8_to_unicode(ZABBIX_EVENT_SOURCE); + StringCchPrintf(regkey, ARRSIZE(regkey), EVENTLOG_REG_PATH TEXT("System\\%s"), wevent_source); + zbx_free(wevent_source); + + if (ERROR_SUCCESS == RegDeleteKey(HKEY_LOCAL_MACHINE, regkey)) + { + zbx_error("event source [%s] uninstalled successfully", ZABBIX_EVENT_SOURCE); + ret = SUCCEED; + } + else + { + zbx_error("unable to uninstall event source [%s]: %s", + ZABBIX_EVENT_SOURCE, strerror_from_system(GetLastError())); + } + + return ret; +} + +int ZabbixRemoveService(void) +{ + SC_HANDLE mgr, service; + int ret = FAIL; + + if (FAIL == svc_OpenSCManager(&mgr)) + return ret; + + if (SUCCEED == svc_OpenService(mgr, &service, DELETE)) + { + if (0 != DeleteService(service)) + { + zbx_error("service [%s] uninstalled successfully", ZABBIX_SERVICE_NAME); + ret = SUCCEED; + } + else + { + zbx_error("ERROR: cannot remove service [%s]: %s", + ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError())); + } + + CloseServiceHandle(service); + } + + CloseServiceHandle(mgr); + + if (SUCCEED == ret) + ret = svc_RemoveEventSource(); + + return ret; +} + +int ZabbixStartService(void) +{ + SC_HANDLE mgr, service; + int ret = FAIL; + + if (FAIL == svc_OpenSCManager(&mgr)) + return ret; + + if (SUCCEED == svc_OpenService(mgr, &service, SERVICE_START)) + { + if (0 != StartService(service, 0, NULL)) + { + zbx_error("service [%s] started successfully", ZABBIX_SERVICE_NAME); + ret = SUCCEED; + } + else + { + zbx_error("ERROR: cannot start service [%s]: %s", + ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError())); + } + + CloseServiceHandle(service); + } + + CloseServiceHandle(mgr); + + return ret; +} + +int ZabbixStopService(void) +{ + SC_HANDLE mgr, service; + SERVICE_STATUS status; + int ret = FAIL; + + if (FAIL == svc_OpenSCManager(&mgr)) + return ret; + + if (SUCCEED == svc_OpenService(mgr, &service, SERVICE_STOP)) + { + if (0 != ControlService(service, SERVICE_CONTROL_STOP, &status)) + { + zbx_error("service [%s] stopped successfully", ZABBIX_SERVICE_NAME); + ret = SUCCEED; + } + else + { + zbx_error("ERROR: cannot stop service [%s]: %s", + ZABBIX_SERVICE_NAME, strerror_from_system(GetLastError())); + } + + CloseServiceHandle(service); + } + + CloseServiceHandle(mgr); + + return ret; +} + +void zbx_set_parent_signal_handler(zbx_on_exit_t zbx_on_exit_cb_arg) +{ + zbx_on_exit_cb = zbx_on_exit_cb_arg; + signal(SIGINT, parent_signal_handler); + signal(SIGTERM, parent_signal_handler); +} |