]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
common: Add Windows service helpers
authorLucian Petrut <lpetrut@cloudbasesolutions.com>
Thu, 30 Apr 2020 07:08:11 +0000 (07:08 +0000)
committerLucian Petrut <lpetrut@cloudbasesolutions.com>
Wed, 18 Nov 2020 09:23:51 +0000 (09:23 +0000)
We'll add a class that implements Windows service hooks. Subclasses
must overide the start, stop and shutdown hooks.

Signed-off-by: Lucian Petrut <lpetrut@cloudbasesolutions.com>
Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
src/common/CMakeLists.txt
src/common/service_win32.cc [new file with mode: 0644]
src/common/service_win32.h [new file with mode: 0644]

index 7fd47c95ec57f8c3021f8034314937e475501e23..efd1b24440dd66a7dea57464b704de56f03aa66f 100644 (file)
@@ -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 (file)
index 0000000..6297531
--- /dev/null
@@ -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 (file)
index 0000000..5adcdea
--- /dev/null
@@ -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;
+};