Correct feature names
[ACE_TAO.git] / ACE / ace / NT_Service.cpp
blobfd88af02bea7de75f676068f02a8704fa81794c0
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
42 // -1.
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.
52 int
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 ();
59 if (svc_return == 0)
61 this->svc_status_.dwWin32ExitCode = NO_ERROR;
62 this->svc_status_.dwServiceSpecificExitCode = 0;
64 else
66 if (errno == 0)
68 this->svc_status_.dwWin32ExitCode = GetLastError ();
70 else
72 this->svc_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
73 this->svc_status_.dwServiceSpecificExitCode = errno;
77 return svc_return;
81 int
82 ACE_NT_Service::fini (void)
84 return this->report_status (SERVICE_STOPPED, 0);
88 void
89 ACE_NT_Service::handle_control (DWORD control_code)
91 switch (control_code)
93 case SERVICE_CONTROL_SHUTDOWN:
94 case SERVICE_CONTROL_STOP:
95 this->stop_requested (control_code);
96 break;
98 case SERVICE_CONTROL_PAUSE:
99 this->pause_requested (control_code);
100 break;
102 case SERVICE_CONTROL_CONTINUE:
103 this->continue_requested (control_code);
104 break;
106 case SERVICE_CONTROL_INTERROGATE:
107 this->interrogate_requested (control_code);
108 break;
112 void
113 ACE_NT_Service::stop_requested (DWORD)
115 this->report_status (SERVICE_STOP_PENDING);
116 /* how to cancel? */
119 void
120 ACE_NT_Service::pause_requested (DWORD)
122 this->report_status (SERVICE_PAUSE_PENDING);
123 this->suspend ();
124 this->report_status (SERVICE_PAUSED);
127 void
128 ACE_NT_Service::continue_requested (DWORD)
130 this->report_status (SERVICE_CONTINUE_PENDING);
131 this->resume ();
132 this->report_status (SERVICE_RUNNING);
135 void
136 ACE_NT_Service::interrogate_requested (DWORD)
138 this->report_status (0);
141 void
142 ACE_NT_Service::name (const ACE_TCHAR *name, const ACE_TCHAR *desc)
144 delete [] this->desc_;
145 delete [] this->name_;
147 if (desc == 0)
148 desc = name;
150 this->name_ = ACE::strnew (name);
151 this->desc_ = ACE::strnew (desc);
154 void
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;
165 if (host == 0)
167 this->host_ = 0;
169 else
171 this->host_ = ACE::strnew (host);
176 ACE_NT_Service::insert (DWORD start_type,
177 DWORD error_control,
178 const ACE_TCHAR *exe_path,
179 const ACE_TCHAR *group_name,
180 LPDWORD tag_id,
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.
189 errno = 0;
191 if (exe_path == 0)
193 if (ACE_TEXT_GetModuleFileName (0, this_exe + 1, MAXPATHLEN) == 0)
194 return -1;
195 // Make sure that this_exe is quoted
196 this_exe[0] = ACE_TEXT ('\"');
197 ACE_OS::strcat (this_exe, ACE_TEXT ("\""));
198 exe_path = this_exe;
201 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
203 SC_MANAGER_ALL_ACCESS);
204 if (sc_mgr == 0)
206 ACE_OS::set_errno_to_last_error();
207 return -1;
210 SC_HANDLE sh = ACE_TEXT_CreateService (sc_mgr,
211 this->name (),
212 this->desc (),
213 desired_access,
214 this->svc_status_.dwServiceType,
215 start_type,
216 error_control,
217 exe_path,
218 group_name,
219 tag_id,
220 dependencies,
221 account_name,
222 password);
223 // If there was an error, stash GetLastError before CloseServiceHandle
224 // smashes it. ACE_OS::last_error will find the saved error value.
225 if (sh == 0)
226 ACE_OS::set_errno_to_last_error ();
228 CloseServiceHandle (sc_mgr);
230 if (sh == 0)
231 return -1;
233 if (this->svc_sc_handle_ != 0)
234 CloseServiceHandle (this->svc_sc_handle_);
235 this->svc_sc_handle_ = sh;
237 return 0;
242 ACE_NT_Service::remove (void)
244 if (this->svc_sc_handle () == 0)
245 return -1;
247 if (DeleteService (this->svc_sc_handle()) == 0
248 && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE)
249 return -1;
251 return 0;
254 // Sets the startup type for the service. Returns -1 on error, 0 on
255 // success.
257 ACE_NT_Service::startup (DWORD startup)
259 SC_HANDLE svc = this->svc_sc_handle ();
260 if (svc == 0)
261 return -1;
263 BOOL ok =
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
275 return ok ? 0 : -1;
278 // Returns the current startup type.
280 DWORD
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.
287 char cfgbuff[1024];
288 LPQUERY_SERVICE_CONFIG cfg;
289 DWORD cfgsize, needed_size;
291 SC_HANDLE svc = this->svc_sc_handle ();
292 if (svc == 0)
294 // To distinguish this error from the QueryServiceConfig failure
295 // below, return the DWORD equivalent of -2, rather than -1.
296 return MAXDWORD - 1;
298 cfgsize = sizeof cfgbuff;
299 cfg = (LPQUERY_SERVICE_CONFIG) cfgbuff;
300 BOOL ok = QueryServiceConfig (svc, cfg, cfgsize, &needed_size);
301 if (ok)
302 return cfg->dwStartType;
303 // Zero is a valid return value for QueryServiceConfig, so if
304 // QueryServiceConfig fails, return the DWORD equivalent of -1.
305 return MAXDWORD;
310 void
311 ACE_NT_Service::capture_log_msg_attributes (void)
313 ACE_Log_Msg::init_hook (this->log_msg_attributes_);
316 void
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,
327 DWORD *svc_state,
328 DWORD argc, const ACE_TCHAR **argv)
330 SC_HANDLE svc = this->svc_sc_handle ();
331 if (svc == 0)
332 return -1;
334 if (!ACE_TEXT_StartService (svc, argc, argv))
335 return -1;
337 this->wait_for_service_state (SERVICE_RUNNING, wait_time);
338 if (svc_state != 0)
339 *svc_state = this->svc_status_.dwCurrentState;
341 return 0;
345 ACE_NT_Service::stop_svc (ACE_Time_Value *wait_time,
346 DWORD *svc_state)
348 SC_HANDLE svc = this->svc_sc_handle ();
349 if (svc == 0)
350 return -1;
352 if (!ControlService (svc,
353 SERVICE_CONTROL_STOP,
354 &this->svc_status_))
355 return -1;
357 this->wait_for_service_state (SERVICE_STOPPED,
358 wait_time);
359 if (svc_state != 0)
360 *svc_state = this->svc_status_.dwCurrentState;
362 return 0;
366 ACE_NT_Service::pause_svc (ACE_Time_Value *wait_time,
367 DWORD *svc_state)
369 SC_HANDLE svc = this->svc_sc_handle ();
370 if (svc == 0)
371 return -1;
373 if (!ControlService (svc,
374 SERVICE_CONTROL_PAUSE,
375 &this->svc_status_))
376 return -1;
378 this->wait_for_service_state (SERVICE_PAUSED,
379 wait_time);
380 if (svc_state != 0)
381 *svc_state = this->svc_status_.dwCurrentState;
383 return 0;
387 ACE_NT_Service::continue_svc (ACE_Time_Value *wait_time,
388 DWORD *svc_state)
390 SC_HANDLE svc = this->svc_sc_handle ();
391 if (svc == 0)
392 return -1;
394 if (!ControlService (svc,
395 SERVICE_CONTROL_CONTINUE,
396 &this->svc_status_))
397 return -1;
399 this->wait_for_service_state (SERVICE_RUNNING,
400 wait_time);
401 if (svc_state != 0)
402 *svc_state = this->svc_status_.dwCurrentState;
404 return 0;
407 DWORD
408 ACE_NT_Service::state (ACE_Time_Value *wait_hint)
410 DWORD curr_state;
412 if (this->state (&curr_state,
413 wait_hint) == -1)
414 return 0;
415 return curr_state;
419 ACE_NT_Service::state (DWORD *pstate,
420 ACE_Time_Value *wait_hint)
422 SC_HANDLE svc = this->svc_sc_handle ();
424 if (svc == 0)
425 return -1;
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)
435 return -1;
437 if (wait_hint != 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;
442 return 0;
445 // test_access
447 // Open a new handle, ignoring any handle open in svc_sc_handle_.
448 // This function's results are returned without leaving the handle
449 // open.
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 (),
458 GENERIC_READ);
459 if (sc_mgr != 0)
461 SC_HANDLE handle = ACE_TEXT_OpenService (sc_mgr,
462 this->name (),
463 desired_access);
464 CloseServiceHandle (sc_mgr);
465 if (handle != 0)
467 status = 0;
468 CloseServiceHandle (handle);
472 return status;
475 // report_status
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,
487 DWORD time_hint)
489 int bump_checkpoint = 0;
490 int retval = 0;
491 DWORD save_controls = 0;
493 if (new_status != 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;
500 /* Fall through */
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_;
505 bump_checkpoint = 1;
506 break;
508 default:
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;
518 if (bump_checkpoint)
519 ++this->svc_status_.dwCheckPoint;
521 return retval;
524 SC_HANDLE
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);
532 if (sc_mgr != 0)
534 this->svc_sc_handle_ = ACE_TEXT_OpenService (sc_mgr,
535 this->name (),
536 SERVICE_ALL_ACCESS);
537 if (this->svc_sc_handle_ == 0)
538 ACE_OS::set_errno_to_last_error ();
539 CloseServiceHandle (sc_mgr);
541 else
542 ACE_OS::set_errno_to_last_error ();
545 return this->svc_sc_handle_;
548 void
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;
554 int first_time = 1;
555 int service_ok;
557 ACE_Time_Value time_out = ACE_OS::gettimeofday ();
558 if (wait_time != 0)
559 time_out += *wait_time;
561 // Poll until the service reaches the desired state.
562 for (;;)
564 service_ok = 0 != QueryServiceStatus (this->svc_sc_handle_,
565 &this->svc_status_);
567 // If we cannot query the service, we are done.
568 if (!service_ok)
569 break;
571 // If the service has the desired state, we are done.
572 if (desired_state == this->svc_status_.dwCurrentState)
573 break;
575 // If we time-out, we are done
576 if (wait_time != 0 && ACE_OS::gettimeofday () > time_out )
578 errno = ETIME;
579 break;
582 if (first_time)
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;
587 first_time = 0;
589 else
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;
597 else
599 // The check-point should have increased
600 if (this->svc_status_.dwCheckPoint > last_check_point)
601 last_check_point = this->svc_status_.dwCheckPoint;
602 else
604 // Service control failure, we are done.
605 service_ok = 0;
606 break;
611 ::Sleep (this->svc_status_.dwWaitHint);
614 return;
617 ACE_END_VERSIONED_NAMESPACE_DECL
619 #endif /* ACE_WIN32 && !ACE_LACKS_WIN32_SERVICES */