1 #include "ace/config-all.h"
2 #if defined (ACE_WIN32) && !defined (ACE_LACKS_WIN32_SERVICES)
4 #include "ace/NT_Service.h"
6 #if !defined (__ACE_INLINE__)
7 #include "ace/NT_Service.inl"
8 #endif /* __ACE_INLINE__ */
10 #include "ace/Log_Category.h"
11 #include "ace/Service_Object.h"
12 #include "ace/OS_NS_errno.h"
14 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
16 ACE_ALLOC_HOOK_DEFINE(ACE_NT_Service
)
18 // ACE_NT_Service destructor.
20 ACE_NT_Service::~ACE_NT_Service (void)
22 if (this->svc_sc_handle_
!= 0)
24 CloseServiceHandle (this->svc_sc_handle_
);
25 this->svc_sc_handle_
= 0;
27 delete [] this->desc_
;
28 delete [] this->name_
;
29 delete [] this->host_
;
32 // This default implementation of ACE_NT_Service::open sets the
33 // service's status to START_PENDING with the estimated time until
34 // STARTED set to the value given when this object was constructed.
35 // Then the svc function is called, which implements the guts of the
36 // service. Note that this function is running in a thread created by
37 // the OS, not by ACE_Thread_Manager. The thread manager does not
38 // know anything about this thread. The service can, however, use
39 // ACE_Thread_Manager to start more threads if desired. When the svc
40 // function returns, the service status is set to STOPPED, and exit
41 // codes set based on errno/GetLastError if the svc function returns
44 // The svc function is expected to set the service status to SERVICE_RUNNING
45 // after it initializes.
47 // The handle_control function will be called for each time there is a
48 // request for the service. It is up to that function and svc to
49 // cooperate to both respond appropriately to the request (by at least
50 // updating the service's status) and to fulfill the request.
53 ACE_NT_Service::open (void *args
)
55 ACE_UNUSED_ARG (args
);
56 this->report_status (SERVICE_START_PENDING
, 0);
58 int svc_return
= this->svc ();
61 this->svc_status_
.dwWin32ExitCode
= NO_ERROR
;
62 this->svc_status_
.dwServiceSpecificExitCode
= 0;
68 this->svc_status_
.dwWin32ExitCode
= GetLastError ();
72 this->svc_status_
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
73 this->svc_status_
.dwServiceSpecificExitCode
= errno
;
82 ACE_NT_Service::fini (void)
84 return this->report_status (SERVICE_STOPPED
, 0);
89 ACE_NT_Service::handle_control (DWORD control_code
)
93 case SERVICE_CONTROL_SHUTDOWN
:
94 case SERVICE_CONTROL_STOP
:
95 this->stop_requested (control_code
);
98 case SERVICE_CONTROL_PAUSE
:
99 this->pause_requested (control_code
);
102 case SERVICE_CONTROL_CONTINUE
:
103 this->continue_requested (control_code
);
106 case SERVICE_CONTROL_INTERROGATE
:
107 this->interrogate_requested (control_code
);
113 ACE_NT_Service::stop_requested (DWORD
)
115 this->report_status (SERVICE_STOP_PENDING
);
120 ACE_NT_Service::pause_requested (DWORD
)
122 this->report_status (SERVICE_PAUSE_PENDING
);
124 this->report_status (SERVICE_PAUSED
);
128 ACE_NT_Service::continue_requested (DWORD
)
130 this->report_status (SERVICE_CONTINUE_PENDING
);
132 this->report_status (SERVICE_RUNNING
);
136 ACE_NT_Service::interrogate_requested (DWORD
)
138 this->report_status (0);
142 ACE_NT_Service::name (const ACE_TCHAR
*name
, const ACE_TCHAR
*desc
)
144 delete [] this->desc_
;
145 delete [] this->name_
;
150 this->name_
= ACE::strnew (name
);
151 this->desc_
= ACE::strnew (desc
);
155 ACE_NT_Service::host (const ACE_TCHAR
*host
)
157 delete [] this->host_
;
159 if (this->svc_sc_handle_
!= 0)
161 CloseServiceHandle (this->svc_sc_handle_
);
162 this->svc_sc_handle_
= 0;
171 this->host_
= ACE::strnew (host
);
176 ACE_NT_Service::insert (DWORD start_type
,
178 const ACE_TCHAR
*exe_path
,
179 const ACE_TCHAR
*group_name
,
181 const ACE_TCHAR
*dependencies
,
182 const ACE_TCHAR
*account_name
,
183 const ACE_TCHAR
*password
,
184 DWORD desired_access
)
186 ACE_TCHAR this_exe
[MAXPATHLEN
+ 2];
188 // Insure ACE_OS::last_error finds GetLastError unless we set errno.
193 if (ACE_TEXT_GetModuleFileName (0, this_exe
+ 1, MAXPATHLEN
) == 0)
195 // Make sure that this_exe is quoted
196 this_exe
[0] = ACE_TEXT ('\"');
197 ACE_OS::strcat (this_exe
, ACE_TEXT ("\""));
201 SC_HANDLE sc_mgr
= ACE_TEXT_OpenSCManager (this->host (),
203 SC_MANAGER_ALL_ACCESS
);
206 ACE_OS::set_errno_to_last_error();
210 SC_HANDLE sh
= ACE_TEXT_CreateService (sc_mgr
,
214 this->svc_status_
.dwServiceType
,
223 // If there was an error, stash GetLastError before CloseServiceHandle
224 // smashes it. ACE_OS::last_error will find the saved error value.
226 ACE_OS::set_errno_to_last_error ();
228 CloseServiceHandle (sc_mgr
);
233 if (this->svc_sc_handle_
!= 0)
234 CloseServiceHandle (this->svc_sc_handle_
);
235 this->svc_sc_handle_
= sh
;
242 ACE_NT_Service::remove (void)
244 if (this->svc_sc_handle () == 0)
247 if (DeleteService (this->svc_sc_handle()) == 0
248 && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE
)
254 // Sets the startup type for the service. Returns -1 on error, 0 on
257 ACE_NT_Service::startup (DWORD startup
)
259 SC_HANDLE svc
= this->svc_sc_handle ();
264 ChangeServiceConfig (svc
,
265 (DWORD
) SERVICE_NO_CHANGE
,// No change to service type
266 startup
, // New startup type
267 (DWORD
) SERVICE_NO_CHANGE
,// No change to error ctrl
268 0, // No change to pathname
269 0, // No change to load group
270 0, // No change to tag
271 0, // No change to dependencies
272 0, 0, // No change to acct/passwd
273 0); // No change to name
278 // Returns the current startup type.
281 ACE_NT_Service::startup (void)
283 // The query buffer will hold strings as well as the defined struct.
284 // The string pointers in the struct point to other areas in the
285 // passed memory area, so it has to be large enough to hold the
286 // struct plus all the strings.
288 LPQUERY_SERVICE_CONFIG cfg
;
289 DWORD cfgsize
, needed_size
;
291 SC_HANDLE svc
= this->svc_sc_handle ();
294 // To distinguish this error from the QueryServiceConfig failure
295 // below, return the DWORD equivalent of -2, rather than -1.
298 cfgsize
= sizeof cfgbuff
;
299 cfg
= (LPQUERY_SERVICE_CONFIG
) cfgbuff
;
300 BOOL ok
= QueryServiceConfig (svc
, cfg
, cfgsize
, &needed_size
);
302 return cfg
->dwStartType
;
303 // Zero is a valid return value for QueryServiceConfig, so if
304 // QueryServiceConfig fails, return the DWORD equivalent of -1.
311 ACE_NT_Service::capture_log_msg_attributes (void)
313 ACE_Log_Msg::init_hook (this->log_msg_attributes_
);
317 ACE_NT_Service::inherit_log_msg_attributes (void)
319 // There's no thread descriptor involved with a NT-started
320 // thread, so the first arg is 0.
321 ACE_Log_Msg::inherit_hook (0, this->log_msg_attributes_
);
326 ACE_NT_Service::start_svc (ACE_Time_Value
*wait_time
,
328 DWORD argc
, const ACE_TCHAR
**argv
)
330 SC_HANDLE svc
= this->svc_sc_handle ();
334 if (!ACE_TEXT_StartService (svc
, argc
, argv
))
337 this->wait_for_service_state (SERVICE_RUNNING
, wait_time
);
339 *svc_state
= this->svc_status_
.dwCurrentState
;
345 ACE_NT_Service::stop_svc (ACE_Time_Value
*wait_time
,
348 SC_HANDLE svc
= this->svc_sc_handle ();
352 if (!ControlService (svc
,
353 SERVICE_CONTROL_STOP
,
357 this->wait_for_service_state (SERVICE_STOPPED
,
360 *svc_state
= this->svc_status_
.dwCurrentState
;
366 ACE_NT_Service::pause_svc (ACE_Time_Value
*wait_time
,
369 SC_HANDLE svc
= this->svc_sc_handle ();
373 if (!ControlService (svc
,
374 SERVICE_CONTROL_PAUSE
,
378 this->wait_for_service_state (SERVICE_PAUSED
,
381 *svc_state
= this->svc_status_
.dwCurrentState
;
387 ACE_NT_Service::continue_svc (ACE_Time_Value
*wait_time
,
390 SC_HANDLE svc
= this->svc_sc_handle ();
394 if (!ControlService (svc
,
395 SERVICE_CONTROL_CONTINUE
,
399 this->wait_for_service_state (SERVICE_RUNNING
,
402 *svc_state
= this->svc_status_
.dwCurrentState
;
408 ACE_NT_Service::state (ACE_Time_Value
*wait_hint
)
412 if (this->state (&curr_state
,
419 ACE_NT_Service::state (DWORD
*pstate
,
420 ACE_Time_Value
*wait_hint
)
422 SC_HANDLE svc
= this->svc_sc_handle ();
427 // Need to create a temporary copy of this variable since the
428 // QueryServiceStatus call will modify the setting depending on the
429 // current state of the Service. If the service is currently
430 // STOPPED, the value will be cleared.
431 DWORD controls_accepted
= this->svc_status_
.dwControlsAccepted
;
433 if (QueryServiceStatus (svc
,
434 &this->svc_status_
) == 0)
438 wait_hint
->msec (static_cast<long> (this->svc_status_
.dwWaitHint
));
440 *pstate
= this->svc_status_
.dwCurrentState
;
441 this->svc_status_
.dwControlsAccepted
= controls_accepted
;
447 // Open a new handle, ignoring any handle open in svc_sc_handle_.
448 // This function's results are returned without leaving the handle
452 ACE_NT_Service::test_access (DWORD desired_access
)
454 int status
= -1; // Guilty until proven innocent
456 SC_HANDLE sc_mgr
= ACE_TEXT_OpenSCManager (this->host (),
461 SC_HANDLE handle
= ACE_TEXT_OpenService (sc_mgr
,
464 CloseServiceHandle (sc_mgr
);
468 CloseServiceHandle (handle
);
477 // Reports the current status. If new_status is not 0, it sets the
478 // status to the new value before reporting. NOTE - this assumes that
479 // no actual service status values have the value 0. This is true in
480 // WinNT 4. If the status is a 'pending' type, the supplied time hint
481 // is used unless it's 0, in which case the existing hint is used.
482 // The dwWaitHint is not updated by this function. The checkpoint is
483 // incremented by one after a pending report.
486 ACE_NT_Service::report_status (DWORD new_status
,
489 int bump_checkpoint
= 0;
491 DWORD save_controls
= 0;
494 this->svc_status_
.dwCurrentState
= new_status
;
495 switch (this->svc_status_
.dwCurrentState
)
497 case SERVICE_START_PENDING
:
498 save_controls
= this->svc_status_
.dwControlsAccepted
;
499 this->svc_status_
.dwControlsAccepted
= 0;
501 case SERVICE_STOP_PENDING
:
502 case SERVICE_CONTINUE_PENDING
:
503 case SERVICE_PAUSE_PENDING
:
504 this->svc_status_
.dwWaitHint
= time_hint
? time_hint
: this->start_time_
;
509 this->svc_status_
.dwCheckPoint
= 0;
512 retval
= SetServiceStatus (this->svc_handle_
,
513 &this->svc_status_
) ? 0 : -1;
515 if (save_controls
!= 0)
516 this->svc_status_
.dwControlsAccepted
= save_controls
;
519 ++this->svc_status_
.dwCheckPoint
;
525 ACE_NT_Service::svc_sc_handle (void)
527 if (this->svc_sc_handle_
== 0)
529 SC_HANDLE sc_mgr
= ACE_TEXT_OpenSCManager (this->host (),
531 SC_MANAGER_ALL_ACCESS
);
534 this->svc_sc_handle_
= ACE_TEXT_OpenService (sc_mgr
,
537 if (this->svc_sc_handle_
== 0)
538 ACE_OS::set_errno_to_last_error ();
539 CloseServiceHandle (sc_mgr
);
542 ACE_OS::set_errno_to_last_error ();
545 return this->svc_sc_handle_
;
549 ACE_NT_Service::wait_for_service_state (DWORD desired_state
,
550 ACE_Time_Value
*wait_time
)
552 DWORD last_state
= 0;
553 DWORD last_check_point
= 0;
557 ACE_Time_Value time_out
= ACE_OS::gettimeofday ();
559 time_out
+= *wait_time
;
561 // Poll until the service reaches the desired state.
564 service_ok
= 0 != QueryServiceStatus (this->svc_sc_handle_
,
567 // If we cannot query the service, we are done.
571 // If the service has the desired state, we are done.
572 if (desired_state
== this->svc_status_
.dwCurrentState
)
575 // If we time-out, we are done
576 if (wait_time
!= 0 && ACE_OS::gettimeofday () > time_out
)
584 // remember the service state, the first time we wait
585 last_state
= this->svc_status_
.dwCurrentState
;
586 last_check_point
= this->svc_status_
.dwCheckPoint
;
591 // update the state change.
592 if (last_state
!= this->svc_status_
.dwCurrentState
)
594 last_state
= this->svc_status_
.dwCurrentState
;
595 last_check_point
= this->svc_status_
.dwCheckPoint
;
599 // The check-point should have increased
600 if (this->svc_status_
.dwCheckPoint
> last_check_point
)
601 last_check_point
= this->svc_status_
.dwCheckPoint
;
604 // Service control failure, we are done.
611 ::Sleep (this->svc_status_
.dwWaitHint
);
617 ACE_END_VERSIONED_NAMESPACE_DECL
619 #endif /* ACE_WIN32 && !ACE_LACKS_WIN32_SERVICES */