From: Lucian Petrut Date: Thu, 30 Apr 2020 07:08:11 +0000 (+0000) Subject: common: Add Windows service helpers X-Git-Tag: v16.1.0~543^2~9 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d2b5d8562419dbc69a1310875776daa254ceb303;p=ceph.git common: Add Windows service helpers We'll add a class that implements Windows service hooks. Subclasses must overide the start, stop and shutdown hooks. Signed-off-by: Lucian Petrut Signed-off-by: Alin Gabriel Serdean --- diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7fd47c95ec57..efd1b24440dd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -109,7 +109,8 @@ if(WIN32) dns_resolve_win32.cc SubProcess_win32.cc ifaddrs_win32.c - registry_win32.cc) + registry_win32.cc + service_win32.cc) else() list(APPEND common_srcs blkdev.cc diff --git a/src/common/service_win32.cc b/src/common/service_win32.cc new file mode 100644 index 000000000000..6297531d3de3 --- /dev/null +++ b/src/common/service_win32.cc @@ -0,0 +1,137 @@ +/* + * 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/service_win32.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) { + return; + } + + s_service->set_status(SERVICE_START_PENDING); + + // TODO: should we expect exceptions? + int err = s_service->run_hook(); + if (err) { + lderr(s_service->cct) << "Failed to start service. Error code: " + << err << dendl; + s_service->set_status(SERVICE_STOPPED); + } else { + s_service->set_status(SERVICE_RUNNING); + } +} + +void ServiceBase::shutdown() +{ + 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; + set_status(original_state); + } else { + 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 { + 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) { + ::SetServiceStatus(hstatus, &status); + } +} diff --git a/src/common/service_win32.h b/src/common/service_win32.h new file mode 100644 index 000000000000..5adcdea223f1 --- /dev/null +++ b/src/common/service_win32.h @@ -0,0 +1,49 @@ +/* + * 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. + * + */ + +#include "include/compat.h" +#include "common/ceph_context.h" + +class ServiceBase { + +public: + ServiceBase(CephContext *cct_); + virtual ~ServiceBase() {}; + + static int initialize(ServiceBase *service); +protected: + static void run(); + static void control_handler(DWORD request); + + void shutdown(); + void stop(); + + void set_status(DWORD current_state, DWORD exit_code = NO_ERROR); + + /* Subclasses should implement the following service hooks. */ + virtual int run_hook() = 0; + /* Invoked when the service is requested to stop. */ + virtual int stop_hook() = 0; + /* Invoked when the system is shutting down. */ + virtual int shutdown_hook() = 0; + + CephContext *cct; + +private: + /* A handle used when reporting the current status. */ + SERVICE_STATUS_HANDLE hstatus; + /* The current service status. */ + SERVICE_STATUS status; + + /* singleton service instance */ + static ServiceBase *s_service; +};