Restore build on FreeBSD.
[getmangos.git] / dep / ACE_wrappers / ace / NT_Service.cpp
blob6323bb1e2d1dc0c576501411ab3c8fa43e32cbc8
1 // $Id: NT_Service.cpp 81862 2008-06-09 10:41:41Z sma $
3 #include "ace/config-all.h"
4 #if defined (ACE_WIN32) && !defined (ACE_LACKS_WIN32_SERVICES)
6 #include "ace/NT_Service.h"
8 #if !defined (__ACE_INLINE__)
9 #include "ace/NT_Service.inl"
10 #endif /* __ACE_INLINE__ */
12 #include "ace/Log_Msg.h"
13 #include "ace/Service_Object.h"
14 #include "ace/OS_NS_errno.h"
16 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
18 ACE_ALLOC_HOOK_DEFINE(ACE_NT_Service)
20 // ACE_NT_Service destructor.
22 ACE_NT_Service::~ACE_NT_Service (void)
24 if (this->svc_sc_handle_ != 0)
26 CloseServiceHandle (this->svc_sc_handle_);
27 this->svc_sc_handle_ = 0;
29 delete [] this->desc_;
30 delete [] this->name_;
31 delete [] this->host_;
34 // This default implementation of ACE_NT_Service::open sets the
35 // service's status to START_PENDING with the estimated time until
36 // STARTED set to the value given when this object was constructed.
37 // Then the svc function is called, which implements the guts of the
38 // service. Note that this function is running in a thread created by
39 // the OS, not by ACE_Thread_Manager. The thread manager does not
40 // know anything about this thread. The service can, however, use
41 // ACE_Thread_Manager to start more threads if desired. When the svc
42 // function returns, the service status is set to STOPPED, and exit
43 // codes set based on errno/GetLastError if the svc function returns
44 // -1.
46 // The svc function is expected to set the service status to SERVICE_RUNNING
47 // after it initializes.
49 // The handle_control function will be called for each time there is a
50 // request for the service. It is up to that function and svc to
51 // cooperate to both respond appropriately to the request (by at least
52 // updating the service's status) and to fulfill the request.
54 int
55 ACE_NT_Service::open (void *args)
57 ACE_UNUSED_ARG (args);
58 this->report_status (SERVICE_START_PENDING, 0);
60 int svc_return = this->svc ();
61 if (svc_return == 0)
63 this->svc_status_.dwWin32ExitCode = NO_ERROR;
64 this->svc_status_.dwServiceSpecificExitCode = 0;
66 else
68 if (errno == 0)
70 this->svc_status_.dwWin32ExitCode = GetLastError ();
72 else
74 this->svc_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
75 this->svc_status_.dwServiceSpecificExitCode = errno;
79 return svc_return;
83 int
84 ACE_NT_Service::fini (void)
86 return this->report_status (SERVICE_STOPPED, 0);
90 void
91 ACE_NT_Service::handle_control (DWORD control_code)
93 switch (control_code)
95 case SERVICE_CONTROL_SHUTDOWN:
96 case SERVICE_CONTROL_STOP:
97 this->stop_requested (control_code);
98 break;
100 case SERVICE_CONTROL_PAUSE:
101 this->pause_requested (control_code);
102 break;
104 case SERVICE_CONTROL_CONTINUE:
105 this->continue_requested (control_code);
106 break;
108 case SERVICE_CONTROL_INTERROGATE:
109 this->interrogate_requested (control_code);
110 break;
114 void
115 ACE_NT_Service::stop_requested (DWORD)
117 this->report_status (SERVICE_STOP_PENDING);
118 /* how to cancel? */
121 void
122 ACE_NT_Service::pause_requested (DWORD)
124 this->report_status (SERVICE_PAUSE_PENDING);
125 this->suspend ();
126 this->report_status (SERVICE_PAUSED);
129 void
130 ACE_NT_Service::continue_requested (DWORD)
132 this->report_status (SERVICE_CONTINUE_PENDING);
133 this->resume ();
134 this->report_status (SERVICE_RUNNING);
137 void
138 ACE_NT_Service::interrogate_requested (DWORD)
140 this->report_status (0);
143 void
144 ACE_NT_Service::name (const ACE_TCHAR *name, const ACE_TCHAR *desc)
146 delete [] this->desc_;
147 delete [] this->name_;
149 if (desc == 0)
150 desc = name;
152 this->name_ = ACE::strnew (name);
153 this->desc_ = ACE::strnew (desc);
156 void
157 ACE_NT_Service::host (const ACE_TCHAR *host)
159 delete [] this->host_;
161 if (this->svc_sc_handle_ != 0)
163 CloseServiceHandle (this->svc_sc_handle_);
164 this->svc_sc_handle_ = 0;
167 if (host == 0)
169 this->host_ = 0;
171 else
173 this->host_ = ACE::strnew (host);
178 ACE_NT_Service::insert (DWORD start_type,
179 DWORD error_control,
180 const ACE_TCHAR *exe_path,
181 const ACE_TCHAR *group_name,
182 LPDWORD tag_id,
183 const ACE_TCHAR *dependencies,
184 const ACE_TCHAR *account_name,
185 const ACE_TCHAR *password,
186 DWORD desired_access)
188 ACE_TCHAR this_exe[MAXPATHLEN + 2];
190 // Insure ACE_OS::last_error finds GetLastError unless we set errno.
191 errno = 0;
193 if (exe_path == 0)
195 if (ACE_TEXT_GetModuleFileName (0, this_exe + 1, MAXPATHLEN) == 0)
196 return -1;
197 // Make sure that this_exe is quoted
198 this_exe[0] = ACE_TEXT ('\"');
199 ACE_OS::strcat (this_exe, ACE_TEXT ("\""));
200 exe_path = this_exe;
203 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
205 SC_MANAGER_ALL_ACCESS);
206 if (sc_mgr == 0)
207 return -1;
209 SC_HANDLE sh = ACE_TEXT_CreateService (sc_mgr,
210 this->name (),
211 this->desc (),
212 desired_access,
213 this->svc_status_.dwServiceType,
214 start_type,
215 error_control,
216 exe_path,
217 group_name,
218 tag_id,
219 dependencies,
220 account_name,
221 password);
222 // If there was an error, stash GetLastError before CloseServiceHandle
223 // smashes it. ACE_OS::last_error will find the saved error value.
224 if (sh == 0)
225 ACE_OS::set_errno_to_last_error ();
227 CloseServiceHandle (sc_mgr);
229 if (sh == 0)
230 return -1;
232 if (this->svc_sc_handle_ != 0)
233 CloseServiceHandle (this->svc_sc_handle_);
234 this->svc_sc_handle_ = sh;
236 return 0;
241 ACE_NT_Service::remove (void)
243 if (this->svc_sc_handle () == 0)
244 return -1;
246 if (DeleteService (this->svc_sc_handle()) == 0
247 && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE)
248 return -1;
250 return 0;
253 // Sets the startup type for the service. Returns -1 on error, 0 on
254 // success.
256 ACE_NT_Service::startup (DWORD startup)
258 SC_HANDLE svc = this->svc_sc_handle ();
259 if (svc == 0)
260 return -1;
262 BOOL ok =
263 ChangeServiceConfig (svc,
264 (DWORD) SERVICE_NO_CHANGE,// No change to service type
265 startup, // New startup type
266 (DWORD) SERVICE_NO_CHANGE,// No change to error ctrl
267 0, // No change to pathname
268 0, // No change to load group
269 0, // No change to tag
270 0, // No change to dependencies
271 0, 0, // No change to acct/passwd
272 0); // No change to name
274 return ok ? 0 : -1;
277 // Returns the current startup type.
279 DWORD
280 ACE_NT_Service::startup (void)
282 // The query buffer will hold strings as well as the defined struct.
283 // The string pointers in the struct point to other areas in the
284 // passed memory area, so it has to be large enough to hold the
285 // struct plus all the strings.
286 char cfgbuff[1024];
287 LPQUERY_SERVICE_CONFIG cfg;
288 DWORD cfgsize, needed_size;
290 SC_HANDLE svc = this->svc_sc_handle ();
291 if (svc == 0)
293 // To distinguish this error from the QueryServiceConfig failure
294 // below, return the DWORD equivalent of -2, rather than -1.
295 return MAXDWORD - 1;
297 cfgsize = sizeof cfgbuff;
298 cfg = (LPQUERY_SERVICE_CONFIG) cfgbuff;
299 BOOL ok = QueryServiceConfig (svc, cfg, cfgsize, &needed_size);
300 if (ok)
301 return cfg->dwStartType;
302 // Zero is a valid return value for QueryServiceConfig, so if
303 // QueryServiceConfig fails, return the DWORD equivalent of -1.
304 return MAXDWORD;
309 void
310 ACE_NT_Service::capture_log_msg_attributes (void)
312 ACE_Log_Msg::init_hook (this->log_msg_attributes_);
315 void
316 ACE_NT_Service::inherit_log_msg_attributes (void)
318 // There's no thread descriptor involved with a NT-started
319 // thread, so the first arg is 0.
320 ACE_Log_Msg::inherit_hook (0, this->log_msg_attributes_);
325 ACE_NT_Service::start_svc (ACE_Time_Value *wait_time,
326 DWORD *svc_state,
327 DWORD argc, const ACE_TCHAR **argv)
329 SC_HANDLE svc = this->svc_sc_handle ();
330 if (svc == 0)
331 return -1;
333 if (!ACE_TEXT_StartService (svc, argc, argv))
334 return -1;
336 this->wait_for_service_state (SERVICE_RUNNING, wait_time);
337 if (svc_state != 0)
338 *svc_state = this->svc_status_.dwCurrentState;
340 return 0;
344 ACE_NT_Service::stop_svc (ACE_Time_Value *wait_time,
345 DWORD *svc_state)
347 SC_HANDLE svc = this->svc_sc_handle ();
348 if (svc == 0)
349 return -1;
351 if (!ControlService (svc,
352 SERVICE_CONTROL_STOP,
353 &this->svc_status_))
354 return -1;
356 this->wait_for_service_state (SERVICE_STOPPED,
357 wait_time);
358 if (svc_state != 0)
359 *svc_state = this->svc_status_.dwCurrentState;
361 return 0;
365 ACE_NT_Service::pause_svc (ACE_Time_Value *wait_time,
366 DWORD *svc_state)
368 SC_HANDLE svc = this->svc_sc_handle ();
369 if (svc == 0)
370 return -1;
372 if (!ControlService (svc,
373 SERVICE_CONTROL_PAUSE,
374 &this->svc_status_))
375 return -1;
377 this->wait_for_service_state (SERVICE_PAUSED,
378 wait_time);
379 if (svc_state != 0)
380 *svc_state = this->svc_status_.dwCurrentState;
382 return 0;
386 ACE_NT_Service::continue_svc (ACE_Time_Value *wait_time,
387 DWORD *svc_state)
389 SC_HANDLE svc = this->svc_sc_handle ();
390 if (svc == 0)
391 return -1;
393 if (!ControlService (svc,
394 SERVICE_CONTROL_CONTINUE,
395 &this->svc_status_))
396 return -1;
398 this->wait_for_service_state (SERVICE_RUNNING,
399 wait_time);
400 if (svc_state != 0)
401 *svc_state = this->svc_status_.dwCurrentState;
403 return 0;
406 DWORD
407 ACE_NT_Service::state (ACE_Time_Value *wait_hint)
409 DWORD curr_state;
411 if (this->state (&curr_state,
412 wait_hint) == -1)
413 return 0;
414 return curr_state;
418 ACE_NT_Service::state (DWORD *pstate,
419 ACE_Time_Value *wait_hint)
421 SC_HANDLE svc = this->svc_sc_handle ();
423 if (svc == 0)
424 return -1;
426 // Need to create a temporary copy of this variable since the
427 // QueryServiceStatus call will modify the setting depending on the
428 // current state of the Service. If the service is currently
429 // STOPPED, the value will be cleared.
430 DWORD controls_accepted = this->svc_status_.dwControlsAccepted;
432 if (QueryServiceStatus (svc,
433 &this->svc_status_) == 0)
434 return -1;
436 if (wait_hint != 0)
437 wait_hint->msec (static_cast<long> (this->svc_status_.dwWaitHint));
439 *pstate = this->svc_status_.dwCurrentState;
440 this->svc_status_.dwControlsAccepted = controls_accepted;
441 return 0;
444 // test_access
446 // Open a new handle, ignoring any handle open in svc_sc_handle_.
447 // This function's results are returned without leaving the handle
448 // open.
451 ACE_NT_Service::test_access (DWORD desired_access)
453 int status = -1; // Guilty until proven innocent
455 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
457 GENERIC_READ);
458 if (sc_mgr != 0)
460 SC_HANDLE handle = ACE_TEXT_OpenService (sc_mgr,
461 this->name (),
462 desired_access);
463 CloseServiceHandle (sc_mgr);
464 if (handle != 0)
466 status = 0;
467 CloseServiceHandle (handle);
471 return status;
474 // report_status
476 // Reports the current status. If new_status is not 0, it sets the
477 // status to the new value before reporting. NOTE - this assumes that
478 // no actual service status values have the value 0. This is true in
479 // WinNT 4. If the status is a 'pending' type, the supplied time hint
480 // is used unless it's 0, in which case the existing hint is used.
481 // The dwWaitHint is not updated by this function. The checkpoint is
482 // incremented by one after a pending report.
485 ACE_NT_Service::report_status (DWORD new_status,
486 DWORD time_hint)
488 int bump_checkpoint = 0;
489 int retval = 0;
490 DWORD save_controls = 0;
492 if (new_status != 0)
493 this->svc_status_.dwCurrentState = new_status;
494 switch (this->svc_status_.dwCurrentState)
496 case SERVICE_START_PENDING:
497 save_controls = this->svc_status_.dwControlsAccepted;
498 this->svc_status_.dwControlsAccepted = 0;
499 /* Fall through */
500 case SERVICE_STOP_PENDING:
501 case SERVICE_CONTINUE_PENDING:
502 case SERVICE_PAUSE_PENDING:
503 this->svc_status_.dwWaitHint = time_hint ? time_hint : this->start_time_;
504 bump_checkpoint = 1;
505 break;
507 default:
508 this->svc_status_.dwCheckPoint = 0;
511 retval = SetServiceStatus (this->svc_handle_,
512 &this->svc_status_) ? 0 : -1;
514 if (save_controls != 0)
515 this->svc_status_.dwControlsAccepted = save_controls;
517 if (bump_checkpoint)
518 ++this->svc_status_.dwCheckPoint;
520 return retval;
523 SC_HANDLE
524 ACE_NT_Service::svc_sc_handle (void)
526 if (this->svc_sc_handle_ == 0)
528 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
530 SC_MANAGER_ALL_ACCESS);
531 if (sc_mgr != 0)
533 this->svc_sc_handle_ = ACE_TEXT_OpenService (sc_mgr,
534 this->name (),
535 SERVICE_ALL_ACCESS);
536 if (this->svc_sc_handle_ == 0)
537 ACE_OS::set_errno_to_last_error ();
538 CloseServiceHandle (sc_mgr);
540 else
541 ACE_OS::set_errno_to_last_error ();
544 return this->svc_sc_handle_;
547 void
548 ACE_NT_Service::wait_for_service_state (DWORD desired_state,
549 ACE_Time_Value *wait_time)
551 DWORD last_state = 0;
552 DWORD last_check_point = 0;
553 int first_time = 1;
554 int service_ok;
556 ACE_Time_Value time_out = ACE_OS::gettimeofday ();
557 if (wait_time != 0)
558 time_out += *wait_time;
560 // Poll until the service reaches the desired state.
561 for (;;)
563 service_ok = 0 != QueryServiceStatus (this->svc_sc_handle_,
564 &this->svc_status_);
566 // If we cannot query the service, we are done.
567 if (!service_ok)
568 break;
570 // If the service has the desired state, we are done.
571 if (desired_state == this->svc_status_.dwCurrentState)
572 break;
574 // If we time-out, we are done
575 if (wait_time != 0 && ACE_OS::gettimeofday () > time_out )
577 errno = ETIME;
578 break;
581 if (first_time)
583 // remember the service state, the first time we wait
584 last_state = this->svc_status_.dwCurrentState;
585 last_check_point = this->svc_status_.dwCheckPoint;
586 first_time = 0;
588 else
590 // update the state change.
591 if (last_state != this->svc_status_.dwCurrentState)
593 last_state = this->svc_status_.dwCurrentState;
594 last_check_point = this->svc_status_.dwCheckPoint;
596 else
598 // The check-point should have increased
599 if (this->svc_status_.dwCheckPoint > last_check_point)
600 last_check_point = this->svc_status_.dwCheckPoint;
601 else
603 // Service control failure, we are done.
604 service_ok = 0;
605 break;
610 ::Sleep (this->svc_status_.dwWaitHint);
613 return;
616 ACE_END_VERSIONED_NAMESPACE_DECL
618 #endif /* ACE_WIN32 && !ACE_LACKS_WIN32_SERVICES */