Changes to attempt to silence bcc64x
[ACE_TAO.git] / ACE / ace / NT_Service.cpp
blob66c8432ce10ad3a11ebb951e05fa1cc10fbe9bd7
1 #include "ace/config-all.h"
2 #if defined (ACE_WIN32)
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 ()
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;
80 int
81 ACE_NT_Service::fini ()
83 return this->report_status (SERVICE_STOPPED, 0);
86 void
87 ACE_NT_Service::handle_control (DWORD control_code)
89 switch (control_code)
91 case SERVICE_CONTROL_SHUTDOWN:
92 case SERVICE_CONTROL_STOP:
93 this->stop_requested (control_code);
94 break;
96 case SERVICE_CONTROL_PAUSE:
97 this->pause_requested (control_code);
98 break;
100 case SERVICE_CONTROL_CONTINUE:
101 this->continue_requested (control_code);
102 break;
104 case SERVICE_CONTROL_INTERROGATE:
105 this->interrogate_requested (control_code);
106 break;
110 void
111 ACE_NT_Service::stop_requested (DWORD)
113 this->report_status (SERVICE_STOP_PENDING);
114 /* how to cancel? */
117 void
118 ACE_NT_Service::pause_requested (DWORD)
120 this->report_status (SERVICE_PAUSE_PENDING);
121 this->suspend ();
122 this->report_status (SERVICE_PAUSED);
125 void
126 ACE_NT_Service::continue_requested (DWORD)
128 this->report_status (SERVICE_CONTINUE_PENDING);
129 this->resume ();
130 this->report_status (SERVICE_RUNNING);
133 void
134 ACE_NT_Service::interrogate_requested (DWORD)
136 this->report_status (0);
139 void
140 ACE_NT_Service::name (const ACE_TCHAR *name, const ACE_TCHAR *desc)
142 delete [] this->desc_;
143 delete [] this->name_;
145 if (desc == 0)
146 desc = name;
148 this->name_ = ACE::strnew (name);
149 this->desc_ = ACE::strnew (desc);
152 void
153 ACE_NT_Service::host (const ACE_TCHAR *host)
155 delete [] this->host_;
157 if (this->svc_sc_handle_ != 0)
159 CloseServiceHandle (this->svc_sc_handle_);
160 this->svc_sc_handle_ = 0;
163 if (host == 0)
165 this->host_ = 0;
167 else
169 this->host_ = ACE::strnew (host);
174 ACE_NT_Service::insert (DWORD start_type,
175 DWORD error_control,
176 const ACE_TCHAR *exe_path,
177 const ACE_TCHAR *group_name,
178 LPDWORD tag_id,
179 const ACE_TCHAR *dependencies,
180 const ACE_TCHAR *account_name,
181 const ACE_TCHAR *password,
182 DWORD desired_access)
184 ACE_TCHAR this_exe[MAXPATHLEN + 2];
186 // Insure ACE_OS::last_error finds GetLastError unless we set errno.
187 errno = 0;
189 if (exe_path == 0)
191 if (ACE_TEXT_GetModuleFileName (0, this_exe + 1, MAXPATHLEN) == 0)
192 return -1;
193 // Make sure that this_exe is quoted
194 this_exe[0] = ACE_TEXT ('\"');
195 ACE_OS::strcat (this_exe, ACE_TEXT ("\""));
196 exe_path = this_exe;
199 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
201 SC_MANAGER_ALL_ACCESS);
202 if (sc_mgr == 0)
204 ACE_OS::set_errno_to_last_error();
205 return -1;
208 SC_HANDLE sh = ACE_TEXT_CreateService (sc_mgr,
209 this->name (),
210 this->desc (),
211 desired_access,
212 this->svc_status_.dwServiceType,
213 start_type,
214 error_control,
215 exe_path,
216 group_name,
217 tag_id,
218 dependencies,
219 account_name,
220 password);
221 // If there was an error, stash GetLastError before CloseServiceHandle
222 // smashes it. ACE_OS::last_error will find the saved error value.
223 if (sh == 0)
224 ACE_OS::set_errno_to_last_error ();
226 CloseServiceHandle (sc_mgr);
228 if (sh == 0)
229 return -1;
231 if (this->svc_sc_handle_ != 0)
232 CloseServiceHandle (this->svc_sc_handle_);
233 this->svc_sc_handle_ = sh;
235 return 0;
239 ACE_NT_Service::remove ()
241 if (this->svc_sc_handle () == 0)
242 return -1;
244 if (DeleteService (this->svc_sc_handle()) == 0
245 && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE)
246 return -1;
248 return 0;
251 // Sets the startup type for the service. Returns -1 on error, 0 on
252 // success.
254 ACE_NT_Service::startup (DWORD startup)
256 SC_HANDLE svc = this->svc_sc_handle ();
257 if (svc == 0)
258 return -1;
260 BOOL ok =
261 ChangeServiceConfig (svc,
262 (DWORD) SERVICE_NO_CHANGE,// No change to service type
263 startup, // New startup type
264 (DWORD) SERVICE_NO_CHANGE,// No change to error ctrl
265 0, // No change to pathname
266 0, // No change to load group
267 0, // No change to tag
268 0, // No change to dependencies
269 0, 0, // No change to acct/passwd
270 0); // No change to name
272 return ok ? 0 : -1;
275 // Returns the current startup type.
277 DWORD
278 ACE_NT_Service::startup ()
280 // The query buffer will hold strings as well as the defined struct.
281 // The string pointers in the struct point to other areas in the
282 // passed memory area, so it has to be large enough to hold the
283 // struct plus all the strings.
284 char cfgbuff[1024];
285 LPQUERY_SERVICE_CONFIG cfg;
286 DWORD cfgsize, needed_size;
288 SC_HANDLE svc = this->svc_sc_handle ();
289 if (svc == 0)
291 // To distinguish this error from the QueryServiceConfig failure
292 // below, return the DWORD equivalent of -2, rather than -1.
293 return MAXDWORD - 1;
295 cfgsize = sizeof cfgbuff;
296 cfg = (LPQUERY_SERVICE_CONFIG) cfgbuff;
297 BOOL ok = QueryServiceConfig (svc, cfg, cfgsize, &needed_size);
298 if (ok)
299 return cfg->dwStartType;
300 // Zero is a valid return value for QueryServiceConfig, so if
301 // QueryServiceConfig fails, return the DWORD equivalent of -1.
302 return MAXDWORD;
305 void
306 ACE_NT_Service::capture_log_msg_attributes ()
308 ACE_Log_Msg::init_hook (this->log_msg_attributes_);
311 void
312 ACE_NT_Service::inherit_log_msg_attributes ()
314 // There's no thread descriptor involved with a NT-started
315 // thread, so the first arg is 0.
316 ACE_Log_Msg::inherit_hook (0, this->log_msg_attributes_);
320 ACE_NT_Service::start_svc (ACE_Time_Value *wait_time,
321 DWORD *svc_state,
322 DWORD argc, const ACE_TCHAR **argv)
324 SC_HANDLE svc = this->svc_sc_handle ();
325 if (svc == 0)
326 return -1;
328 if (!ACE_TEXT_StartService (svc, argc, argv))
329 return -1;
331 this->wait_for_service_state (SERVICE_RUNNING, wait_time);
332 if (svc_state != 0)
333 *svc_state = this->svc_status_.dwCurrentState;
335 return 0;
339 ACE_NT_Service::stop_svc (ACE_Time_Value *wait_time,
340 DWORD *svc_state)
342 SC_HANDLE svc = this->svc_sc_handle ();
343 if (svc == 0)
344 return -1;
346 if (!ControlService (svc,
347 SERVICE_CONTROL_STOP,
348 &this->svc_status_))
349 return -1;
351 this->wait_for_service_state (SERVICE_STOPPED,
352 wait_time);
353 if (svc_state != 0)
354 *svc_state = this->svc_status_.dwCurrentState;
356 return 0;
360 ACE_NT_Service::pause_svc (ACE_Time_Value *wait_time,
361 DWORD *svc_state)
363 SC_HANDLE svc = this->svc_sc_handle ();
364 if (svc == 0)
365 return -1;
367 if (!ControlService (svc,
368 SERVICE_CONTROL_PAUSE,
369 &this->svc_status_))
370 return -1;
372 this->wait_for_service_state (SERVICE_PAUSED,
373 wait_time);
374 if (svc_state != 0)
375 *svc_state = this->svc_status_.dwCurrentState;
377 return 0;
381 ACE_NT_Service::continue_svc (ACE_Time_Value *wait_time,
382 DWORD *svc_state)
384 SC_HANDLE svc = this->svc_sc_handle ();
385 if (svc == 0)
386 return -1;
388 if (!ControlService (svc,
389 SERVICE_CONTROL_CONTINUE,
390 &this->svc_status_))
391 return -1;
393 this->wait_for_service_state (SERVICE_RUNNING,
394 wait_time);
395 if (svc_state != 0)
396 *svc_state = this->svc_status_.dwCurrentState;
398 return 0;
401 DWORD
402 ACE_NT_Service::state (ACE_Time_Value *wait_hint)
404 DWORD curr_state;
406 if (this->state (&curr_state,
407 wait_hint) == -1)
408 return 0;
409 return curr_state;
413 ACE_NT_Service::state (DWORD *pstate,
414 ACE_Time_Value *wait_hint)
416 SC_HANDLE svc = this->svc_sc_handle ();
418 if (svc == 0)
419 return -1;
421 // Need to create a temporary copy of this variable since the
422 // QueryServiceStatus call will modify the setting depending on the
423 // current state of the Service. If the service is currently
424 // STOPPED, the value will be cleared.
425 DWORD controls_accepted = this->svc_status_.dwControlsAccepted;
427 if (QueryServiceStatus (svc,
428 &this->svc_status_) == 0)
429 return -1;
431 if (wait_hint != 0)
432 wait_hint->msec (static_cast<long> (this->svc_status_.dwWaitHint));
434 *pstate = this->svc_status_.dwCurrentState;
435 this->svc_status_.dwControlsAccepted = controls_accepted;
436 return 0;
439 // test_access
441 // Open a new handle, ignoring any handle open in svc_sc_handle_.
442 // This function's results are returned without leaving the handle
443 // open.
446 ACE_NT_Service::test_access (DWORD desired_access)
448 int status = -1; // Guilty until proven innocent
450 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
452 GENERIC_READ);
453 if (sc_mgr != 0)
455 SC_HANDLE handle = ACE_TEXT_OpenService (sc_mgr,
456 this->name (),
457 desired_access);
458 CloseServiceHandle (sc_mgr);
459 if (handle != 0)
461 status = 0;
462 CloseServiceHandle (handle);
466 return status;
469 // report_status
471 // Reports the current status. If new_status is not 0, it sets the
472 // status to the new value before reporting. NOTE - this assumes that
473 // no actual service status values have the value 0. This is true in
474 // WinNT 4. If the status is a 'pending' type, the supplied time hint
475 // is used unless it's 0, in which case the existing hint is used.
476 // The dwWaitHint is not updated by this function. The checkpoint is
477 // incremented by one after a pending report.
480 ACE_NT_Service::report_status (DWORD new_status,
481 DWORD time_hint)
483 int bump_checkpoint = 0;
484 int retval = 0;
485 DWORD save_controls = 0;
487 if (new_status != 0)
488 this->svc_status_.dwCurrentState = new_status;
489 switch (this->svc_status_.dwCurrentState)
491 case SERVICE_START_PENDING:
492 save_controls = this->svc_status_.dwControlsAccepted;
493 this->svc_status_.dwControlsAccepted = 0;
494 /* Fall through */
495 case SERVICE_STOP_PENDING:
496 case SERVICE_CONTINUE_PENDING:
497 case SERVICE_PAUSE_PENDING:
498 this->svc_status_.dwWaitHint = time_hint ? time_hint : this->start_time_;
499 bump_checkpoint = 1;
500 break;
502 default:
503 this->svc_status_.dwCheckPoint = 0;
506 retval = SetServiceStatus (this->svc_handle_,
507 &this->svc_status_) ? 0 : -1;
509 if (save_controls != 0)
510 this->svc_status_.dwControlsAccepted = save_controls;
512 if (bump_checkpoint)
513 ++this->svc_status_.dwCheckPoint;
515 return retval;
518 SC_HANDLE
519 ACE_NT_Service::svc_sc_handle ()
521 if (this->svc_sc_handle_ == 0)
523 SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
525 SC_MANAGER_ALL_ACCESS);
526 if (sc_mgr != 0)
528 this->svc_sc_handle_ = ACE_TEXT_OpenService (sc_mgr,
529 this->name (),
530 SERVICE_ALL_ACCESS);
531 if (this->svc_sc_handle_ == 0)
532 ACE_OS::set_errno_to_last_error ();
533 CloseServiceHandle (sc_mgr);
535 else
536 ACE_OS::set_errno_to_last_error ();
539 return this->svc_sc_handle_;
542 void
543 ACE_NT_Service::wait_for_service_state (DWORD desired_state,
544 ACE_Time_Value *wait_time)
546 DWORD last_state = 0;
547 DWORD last_check_point = 0;
548 int first_time = 1;
549 int service_ok;
551 ACE_Time_Value time_out = ACE_OS::gettimeofday ();
552 if (wait_time != 0)
553 time_out += *wait_time;
555 // Poll until the service reaches the desired state.
556 for (;;)
558 service_ok = 0 != QueryServiceStatus (this->svc_sc_handle_,
559 &this->svc_status_);
561 // If we cannot query the service, we are done.
562 if (!service_ok)
563 break;
565 // If the service has the desired state, we are done.
566 if (desired_state == this->svc_status_.dwCurrentState)
567 break;
569 // If we time-out, we are done
570 if (wait_time != 0 && ACE_OS::gettimeofday () > time_out )
572 errno = ETIME;
573 break;
576 if (first_time)
578 // remember the service state, the first time we wait
579 last_state = this->svc_status_.dwCurrentState;
580 last_check_point = this->svc_status_.dwCheckPoint;
581 first_time = 0;
583 else
585 // update the state change.
586 if (last_state != this->svc_status_.dwCurrentState)
588 last_state = this->svc_status_.dwCurrentState;
589 last_check_point = this->svc_status_.dwCheckPoint;
591 else
593 // The check-point should have increased
594 if (this->svc_status_.dwCheckPoint > last_check_point)
595 last_check_point = this->svc_status_.dwCheckPoint;
596 else
598 // Service control failure, we are done.
599 service_ok = 0;
600 break;
605 ::Sleep (this->svc_status_.dwWaitHint);
608 return;
611 ACE_END_VERSIONED_NAMESPACE_DECL
613 #endif /* ACE_WIN32 */