Electroneum
service_impl_base.h
Go to the documentation of this file.
1 // Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of the Andrey N. Sabelnikov nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 //
26 
27 
28 #ifndef _SERVICE_IMPL_BASE_H_
29 #define _SERVICE_IMPL_BASE_H_
30 
31 #pragma comment(lib, "advapi32.lib")
32 
33 
34 namespace epee
35 {
37  public:
39  virtual ~service_impl_base();
40 
41  virtual const char *get_name() = 0;
42  virtual const char *get_caption() = 0;
43  virtual const char *get_description() = 0;
44 
45  bool run_service();
46  virtual bool install();
47  virtual bool remove();
48  virtual bool init();
49  void set_control_accepted(unsigned controls);
50  void set_status(unsigned state, unsigned pending = 0);
51  unsigned get_control_accepted();
52 
53  private:
54  virtual void service_main() = 0;
55  virtual unsigned service_handler(unsigned control, unsigned event_code,
56  void *pdata) = 0;
57  //-------------------------------------------------------------------------
58  static service_impl_base*& instance();
59  //-------------------------------------------------------------------------
60  static DWORD __stdcall _service_handler(DWORD control, DWORD event,
61  void *pdata, void *pcontext);
62  static void __stdcall service_entry(DWORD argc, char **pargs);
63  virtual SERVICE_FAILURE_ACTIONSA* get_failure_actions();
64 
65  private:
66  SC_HANDLE m_manager;
67  SC_HANDLE m_service;
68  SERVICE_STATUS_HANDLE m_status_handle;
69  DWORD m_accepted_control;
70 };
71 
73  m_manager = 0;
74  m_service = 0;
75  m_status_handle = 0;
76  m_accepted_control = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN
77  | SERVICE_ACCEPT_PAUSE_CONTINUE;
78 
79  instance() = this;
80 }
81 //-----------------------------------------------------------------------------
83  if (m_service) {
84  ::CloseServiceHandle(m_service);
85  }
86  m_service = 0;
87  if (m_manager) {
88  ::CloseServiceHandle(m_manager);
89  }
90  m_manager = 0;
91  instance() = 0;
92 }
93 //-----------------------------------------------------------------------------
94 inline service_impl_base*& service_impl_base::instance() {
95  static service_impl_base *pservice = NULL;
96  return pservice;
97 }
98 //-----------------------------------------------------------------------------
99 inline
101  CHECK_AND_ASSERT(!m_service, false);
102  const char *psz_descr = get_description();
103  SERVICE_FAILURE_ACTIONSA* fail_acts = get_failure_actions();
104 
105  char sz_path[MAX_PATH];
106  ::GetModuleFileNameA(0, sz_path, sizeof(sz_path));
107  ::GetShortPathNameA(sz_path, sz_path, sizeof(sz_path));
108 
109  while (TRUE) {
110  if (!m_manager) {
111  m_manager = ::OpenSCManager(NULL, NULL, GENERIC_ALL);
112  if (!m_manager) {
113  int err = GetLastError();
114  LOG_ERROR(
115  "Failed to OpenSCManager(), last err="
116  << log_space::get_win32_err_descr(err));
117  break;
118  }
119  }
120  m_service = ::CreateServiceA(m_manager, get_name(), get_caption(),
121  SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
122  SERVICE_ERROR_IGNORE, sz_path, 0, 0, 0, 0, 0);
123  if (!m_service) {
124  int err = GetLastError();
125  LOG_ERROR(
126  "Failed to CreateService(), last err="
127  << log_space::get_win32_err_descr(err));
128  break;
129  }
130 
131  if (psz_descr) {
132  SERVICE_DESCRIPTIONA sd = { (char*) psz_descr };
133  if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_DESCRIPTION,
134  &sd)) {
135  int err = GetLastError();
136  LOG_ERROR(
137  "Failed to ChangeServiceConfig2(SERVICE_CONFIG_DESCRIPTION), last err="
138  << log_space::get_win32_err_descr(err));
139  break;
140  }
141  }
142 
143  if (fail_acts) {
144  if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_FAILURE_ACTIONS,
145  fail_acts)) {
146  int err = GetLastError();
147  LOG_ERROR(
148  "Failed to ChangeServiceConfig2(SERVICE_CONFIG_FAILURE_ACTIONS), last err="
149  << log_space::get_win32_err_descr(err));
150  break;
151  }
152  }
153  LOG_PRINT("Installed succesfully.", LOG_LEVEL_0);
154  return true;
155  }
156  LOG_PRINT("Failed to install.", LOG_LEVEL_0);
157  return false;
158 }
159 //-----------------------------------------------------------------------------
160 inline
162  CHECK_AND_ASSERT(!m_service, false);
163 
164  while (TRUE) {
165  if (!m_manager) {
166  m_manager = ::OpenSCManager(0, 0, GENERIC_ALL);
167  if (!m_manager) {
168  int err = GetLastError();
169  LOG_ERROR(
170  "Failed to OpenSCManager(), last err="
171  << log_space::get_win32_err_descr(err));
172  break;
173  }
174  }
175 
176  if (!m_service) {
177  m_service = ::OpenServiceA(m_manager, get_name(), SERVICE_STOP | DELETE);
178  if (!m_service) {
179  int err = GetLastError();
180  LOG_ERROR(
181  "Failed to OpenService(), last err="
182  << log_space::get_win32_err_descr(err));
183  break;
184  }
185  }
186 
187  SERVICE_STATUS status = { };
188  if (!::ControlService(m_service, SERVICE_CONTROL_STOP, &status)) {
189  int err = ::GetLastError();
190  if (err == ERROR_SHUTDOWN_IN_PROGRESS)
191  continue;
192  else if (err != ERROR_SERVICE_NOT_ACTIVE) {
193  LOG_ERROR(
194  "Failed to ControlService(SERVICE_CONTROL_STOP), last err="
195  << log_space::get_win32_err_descr(err));
196  break;
197  }
198  }
199 
200  if (!::DeleteService(m_service)) {
201  int err = ::GetLastError();
202  LOG_ERROR(
203  "Failed to ControlService(SERVICE_CONTROL_STOP), last err="
204  << log_space::get_win32_err_descr(err));
205  break;
206  }
207 
208  LOG_PRINT("Removed successfully.", LOG_LEVEL_0);
209  break;
210  }
211 
212  return true;
213 }
214 //-----------------------------------------------------------------------------
215 inline
217  return true;
218 }
219 //-----------------------------------------------------------------------------
220 inline
222  CHECK_AND_ASSERT(!m_service, false);
223 
224  long error_code = 0;
225 
226  SERVICE_TABLE_ENTRYA service_table[2];
227  ZeroMemory(&service_table, sizeof(service_table));
228 
229  service_table->lpServiceName = (char*) get_name();
230  service_table->lpServiceProc = service_entry;
231 
232  LOG_PRINT("[+] Start service control dispatcher for \"" << get_name() << "\"",
233  LOG_LEVEL_1);
234 
235  error_code = 1;
236  BOOL res = ::StartServiceCtrlDispatcherA(service_table);
237  if (!res) {
238  int err = GetLastError();
239  LOG_PRINT(
240  "[+] Error starting service control dispatcher, err="
241  << log_space::get_win32_err_descr(err), LOG_LEVEL_1);
242  return false;
243  } else {
244  LOG_PRINT("[+] End service control dispatcher for \"" << get_name() << "\"",
245  LOG_LEVEL_1);
246  }
247  return true;
248 }
249 //-----------------------------------------------------------------------------
250 inline DWORD __stdcall service_impl_base::_service_handler(DWORD control,
251  DWORD event, void *pdata, void *pcontext) {
252  CHECK_AND_ASSERT(pcontext, ERROR_CALL_NOT_IMPLEMENTED);
253 
254  service_impl_base *pservice = (service_impl_base*) pcontext;
255  return pservice->service_handler(control, event, pdata);
256 }
257 //-----------------------------------------------------------------------------
258 inline
259 void __stdcall service_impl_base::service_entry(DWORD argc, char **pargs) {
260  service_impl_base *pme = instance();
261  LOG_PRINT("instance: " << pme, LOG_LEVEL_4);
262  if (!pme) {
263  LOG_ERROR("Error: at service_entry() pme = NULL");
264  return;
265  }
266  pme->m_status_handle = ::RegisterServiceCtrlHandlerExA(pme->get_name(),
267  _service_handler, pme);
268 
269  pme->set_status(SERVICE_RUNNING);
270  pme->service_main();
271  pme->set_status(SERVICE_STOPPED);
272 }
273 //-----------------------------------------------------------------------------
274 inline
275 void service_impl_base::set_status(unsigned state, unsigned pending) {
276  if (!m_status_handle)
277  return;
278 
279  SERVICE_STATUS status = { 0 };
280  status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
281  status.dwCurrentState = state;
282  status.dwControlsAccepted = m_accepted_control;
283  /*status.dwWin32ExitCode = NO_ERROR;
284  status.dwServiceSpecificExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
285  status.dwCheckPoint = 0;
286  status.dwWaitHint = 0;
287 
288  status.dwCurrentState = state;*/
289 
290  if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING
291  || state == SERVICE_CONTINUE_PENDING || state == SERVICE_PAUSE_PENDING) {
292  status.dwWaitHint = 2000;
293  status.dwCheckPoint = pending;
294  }
295  ::SetServiceStatus(m_status_handle, &status);
296 }
297 //-----------------------------------------------------------------------------------------
298 inline
300  m_accepted_control = controls;
301 }
302 //-----------------------------------------------------------------------------------------
303 inline
305  return m_accepted_control;
306 }
307 //-----------------------------------------------------------------------------------------
308 inline SERVICE_FAILURE_ACTIONSA* service_impl_base::get_failure_actions() {
309  // first 3 failures in 30 minutes. Service will be restarted.
310  // do nothing for next failures
311  static SC_ACTION sa[] = { { SC_ACTION_RESTART, 3 * 1000 }, {
312  SC_ACTION_RESTART, 3 * 1000 }, { SC_ACTION_RESTART, 3 * 1000 }, {
313  SC_ACTION_NONE, 0 } };
314 
315  static SERVICE_FAILURE_ACTIONSA sfa = { 1800, // interval for failures counter - 30 min
316  "", NULL, 4, (SC_ACTION*) &sa };
317 
318  // TODO: refactor this code, really unsafe!
319  return &sfa;
320 }
321 }
322 
323 #endif //_SERVICE_IMPL_BASE_H_
const char * res
Definition: hmac_keccak.cpp:41
void set_control_accepted(unsigned controls)
virtual const char * get_description()=0
#define CHECK_AND_ASSERT(expr, fail_ret_val)
Definition: misc_log_ex.h:177
virtual const char * get_caption()=0
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
Definition: blake256.h:37
virtual const char * get_name()=0
void set_status(unsigned state, unsigned pending=0)