diff options
Diffstat (limited to '')
-rw-r--r-- | src/common/win32/service.cc | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/common/win32/service.cc b/src/common/win32/service.cc new file mode 100644 index 000000000..846c5d09d --- /dev/null +++ b/src/common/win32/service.cc @@ -0,0 +1,156 @@ +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2019 SUSE LINUX GmbH + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#define dout_context cct +#define dout_subsys ceph_subsys_ + +#include "common/debug.h" +#include "common/errno.h" +#include "common/win32/service.h" + +// Initialize the singleton service instance. +ServiceBase *ServiceBase::s_service = NULL; + +ServiceBase::ServiceBase(CephContext *cct_): cct(cct_) +{ + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwWin32ExitCode = NO_ERROR; + status.dwCheckPoint = 0; + /* The estimated time required for the stop operation in ms. */ + status.dwWaitHint = 0; +} + +/* Register service action callbacks */ +int ServiceBase::initialize(ServiceBase *service) +{ + s_service = service; + + SERVICE_TABLE_ENTRY service_table[] = { + {"", (LPSERVICE_MAIN_FUNCTION)run}, + {NULL, NULL} + }; + + /* StartServiceCtrlDispatcher blocks until the service is stopped. */ + if (!StartServiceCtrlDispatcher(service_table)) { + int err = GetLastError(); + lderr(service->cct) << "StartServiceCtrlDispatcher error: " + << err << dendl; + return -EINVAL; + } + return 0; +} + +void WINAPI ServiceBase::run() +{ + assert(s_service != NULL); + + /* Register the control handler. This function is called by the service + * manager to stop the service. The service name that we're passing here + * doesn't have to be valid as we're using SERVICE_WIN32_OWN_PROCESS. */ + s_service->hstatus = RegisterServiceCtrlHandler( + "", (LPHANDLER_FUNCTION)control_handler); + if (!s_service->hstatus) { + lderr(s_service->cct) << "Could not initialize service control handler. " + << "Error: " << GetLastError() << dendl; + return; + } + + s_service->set_status(SERVICE_START_PENDING); + + // TODO: should we expect exceptions? + ldout(s_service->cct, 5) << "Starting service." << dendl; + int err = s_service->run_hook(); + if (err) { + lderr(s_service->cct) << "Failed to start service. Error code: " + << err << dendl; + s_service->shutdown(true); + } else { + ldout(s_service->cct, 5) << "Successfully started service." << dendl; + s_service->set_status(SERVICE_RUNNING); + } +} + +void ServiceBase::shutdown(bool ignore_errors) +{ + DWORD original_state = status.dwCurrentState; + set_status(SERVICE_STOP_PENDING); + + int err = shutdown_hook(); + if (err) { + derr << "Shutdown service hook failed. Error code: " << err << dendl; + if (ignore_errors) { + derr << "Ignoring shutdown hook failure, marking the service as stopped." + << dendl; + set_status(SERVICE_STOPPED); + } else { + derr << "Reverting to original service state." << dendl; + set_status(original_state); + } + } else { + dout(5) << "Shutdown hook completed." << dendl; + set_status(SERVICE_STOPPED); + } +} + +void ServiceBase::stop() +{ + DWORD original_state = status.dwCurrentState; + set_status(SERVICE_STOP_PENDING); + + int err = stop_hook(); + if (err) { + derr << "Service stop hook failed. Error code: " << err << dendl; + set_status(original_state); + } else { + dout(5) << "Successfully stopped service." << dendl; + set_status(SERVICE_STOPPED); + } +} + +/* This function is registered with the Windows services manager through + * a call to RegisterServiceCtrlHandler() and will be called by the Windows + * service manager asynchronously to stop the service. */ +void ServiceBase::control_handler(DWORD request) +{ + switch (request) { + case SERVICE_CONTROL_STOP: + s_service->stop(); + break; + case SERVICE_CONTROL_SHUTDOWN: + s_service->shutdown(); + break; + default: + break; + } +} + +void ServiceBase::set_status(DWORD current_state, DWORD exit_code) { + static DWORD dwCheckPoint = 1; + if (current_state == SERVICE_RUNNING || current_state == SERVICE_STOPPED) { + status.dwCheckPoint = dwCheckPoint++; + } + + status.dwCurrentState = current_state; + status.dwWin32ExitCode = exit_code; + + if (hstatus) { + dout(5) << "Updating service service status (" << current_state + << ") and exit code(" << exit_code << ")." << dendl; + ::SetServiceStatus(hstatus, &status); + } else { + derr << "Service control handler not initialized. Cannot " + << "update service status (" << current_state + << ") and exit code(" << exit_code << ")." << dendl; + } +} |