]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Ceph - scalable distributed file system | |
3 | * | |
4 | * Copyright (C) 2019 SUSE LINUX GmbH | |
5 | * | |
6 | * This is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License version 2.1, as published by the Free Software | |
9 | * Foundation. See file COPYING. | |
10 | * | |
11 | */ | |
12 | ||
13 | #define dout_context cct | |
14 | #define dout_subsys ceph_subsys_ | |
15 | ||
16 | #include "common/debug.h" | |
17 | #include "common/errno.h" | |
18 | #include "common/win32/service.h" | |
19 | ||
20 | // Initialize the singleton service instance. | |
21 | ServiceBase *ServiceBase::s_service = NULL; | |
22 | ||
23 | ServiceBase::ServiceBase(CephContext *cct_): cct(cct_) | |
24 | { | |
25 | status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; | |
26 | status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; | |
27 | status.dwCurrentState = SERVICE_START_PENDING; | |
28 | status.dwWin32ExitCode = NO_ERROR; | |
29 | status.dwCheckPoint = 0; | |
30 | /* The estimated time required for the stop operation in ms. */ | |
31 | status.dwWaitHint = 0; | |
32 | } | |
33 | ||
34 | /* Register service action callbacks */ | |
35 | int ServiceBase::initialize(ServiceBase *service) | |
36 | { | |
37 | s_service = service; | |
38 | ||
39 | SERVICE_TABLE_ENTRY service_table[] = { | |
40 | {"", (LPSERVICE_MAIN_FUNCTION)run}, | |
41 | {NULL, NULL} | |
42 | }; | |
43 | ||
44 | /* StartServiceCtrlDispatcher blocks until the service is stopped. */ | |
45 | if (!StartServiceCtrlDispatcher(service_table)) { | |
46 | int err = GetLastError(); | |
47 | lderr(service->cct) << "StartServiceCtrlDispatcher error: " | |
48 | << err << dendl; | |
49 | return -EINVAL; | |
50 | } | |
51 | return 0; | |
52 | } | |
53 | ||
54 | void WINAPI ServiceBase::run() | |
55 | { | |
56 | assert(s_service != NULL); | |
57 | ||
58 | /* Register the control handler. This function is called by the service | |
59 | * manager to stop the service. The service name that we're passing here | |
60 | * doesn't have to be valid as we're using SERVICE_WIN32_OWN_PROCESS. */ | |
61 | s_service->hstatus = RegisterServiceCtrlHandler( | |
62 | "", (LPHANDLER_FUNCTION)control_handler); | |
63 | if (!s_service->hstatus) { | |
64 | lderr(s_service->cct) << "Could not initialize service control handler. " | |
65 | << "Error: " << GetLastError() << dendl; | |
66 | return; | |
67 | } | |
68 | ||
69 | s_service->set_status(SERVICE_START_PENDING); | |
70 | ||
71 | // TODO: should we expect exceptions? | |
72 | ldout(s_service->cct, 5) << "Starting service." << dendl; | |
73 | int err = s_service->run_hook(); | |
74 | if (err) { | |
75 | lderr(s_service->cct) << "Failed to start service. Error code: " | |
76 | << err << dendl; | |
77 | s_service->shutdown(true); | |
78 | } else { | |
79 | ldout(s_service->cct, 5) << "Successfully started service." << dendl; | |
80 | s_service->set_status(SERVICE_RUNNING); | |
81 | } | |
82 | } | |
83 | ||
84 | void ServiceBase::shutdown(bool ignore_errors) | |
85 | { | |
86 | DWORD original_state = status.dwCurrentState; | |
87 | set_status(SERVICE_STOP_PENDING); | |
88 | ||
89 | int err = shutdown_hook(); | |
90 | if (err) { | |
91 | derr << "Shutdown service hook failed. Error code: " << err << dendl; | |
92 | if (ignore_errors) { | |
93 | derr << "Ignoring shutdown hook failure, marking the service as stopped." | |
94 | << dendl; | |
95 | set_status(SERVICE_STOPPED); | |
96 | } else { | |
97 | derr << "Reverting to original service state." << dendl; | |
98 | set_status(original_state); | |
99 | } | |
100 | } else { | |
101 | dout(5) << "Shutdown hook completed." << dendl; | |
102 | set_status(SERVICE_STOPPED); | |
103 | } | |
104 | } | |
105 | ||
106 | void ServiceBase::stop() | |
107 | { | |
108 | DWORD original_state = status.dwCurrentState; | |
109 | set_status(SERVICE_STOP_PENDING); | |
110 | ||
111 | int err = stop_hook(); | |
112 | if (err) { | |
113 | derr << "Service stop hook failed. Error code: " << err << dendl; | |
114 | set_status(original_state); | |
115 | } else { | |
116 | dout(5) << "Successfully stopped service." << dendl; | |
117 | set_status(SERVICE_STOPPED); | |
118 | } | |
119 | } | |
120 | ||
121 | /* This function is registered with the Windows services manager through | |
122 | * a call to RegisterServiceCtrlHandler() and will be called by the Windows | |
123 | * service manager asynchronously to stop the service. */ | |
124 | void ServiceBase::control_handler(DWORD request) | |
125 | { | |
126 | switch (request) { | |
127 | case SERVICE_CONTROL_STOP: | |
128 | s_service->stop(); | |
129 | break; | |
130 | case SERVICE_CONTROL_SHUTDOWN: | |
131 | s_service->shutdown(); | |
132 | break; | |
133 | default: | |
134 | break; | |
135 | } | |
136 | } | |
137 | ||
138 | void ServiceBase::set_status(DWORD current_state, DWORD exit_code) { | |
139 | static DWORD dwCheckPoint = 1; | |
140 | if (current_state == SERVICE_RUNNING || current_state == SERVICE_STOPPED) { | |
141 | status.dwCheckPoint = dwCheckPoint++; | |
142 | } | |
143 | ||
144 | status.dwCurrentState = current_state; | |
145 | status.dwWin32ExitCode = exit_code; | |
146 | ||
147 | if (hstatus) { | |
148 | dout(5) << "Updating service service status (" << current_state | |
149 | << ") and exit code(" << exit_code << ")." << dendl; | |
150 | ::SetServiceStatus(hstatus, &status); | |
151 | } else { | |
152 | derr << "Service control handler not initialized. Cannot " | |
153 | << "update service status (" << current_state | |
154 | << ") and exit code(" << exit_code << ")." << dendl; | |
155 | } | |
156 | } |