diff options
-rw-r--r-- | ace/NT_Service.cpp | 148 | ||||
-rw-r--r-- | ace/NT_Service.h | 179 | ||||
-rw-r--r-- | ace/NT_Service.i | 27 |
3 files changed, 354 insertions, 0 deletions
diff --git a/ace/NT_Service.cpp b/ace/NT_Service.cpp new file mode 100644 index 00000000000..9229ed2e219 --- /dev/null +++ b/ace/NT_Service.cpp @@ -0,0 +1,148 @@ +// $Id$ + +// NT_Service.cpp + +#include "ace/inc_user_config.h" +#if defined (ACE_HAS_WINNT4) && ACE_HAS_WINNT4 != 0 + +#define ACE_BUILD_DLL +#include "ace/NT_Service.h" +#include "ace/Service_Object.h" + +#if !defined (__ACE_INLINE__) +#include "ace/NT_Service.i" +#endif /* __ACE_INLINE__ */ + +ACE_ALLOC_HOOK_DEFINE(ACE_NT_Service) + +// This default implementation of ACE_NT_Service::open sets the service's +// status to START_PENDING with the estimated time until STARTED set to the +// value given when this object was constructed. Then the svc function is +// called, which implements the guts of the service. Note that this function +// is running in a thread created by the OS, not by ACE_Thread_Manager. +// The thread manager does not know anything about this thread. The service +// can, however, use ACE_Thread_Manager to start more threads if desired. +// When the svc function returns, the service status is set to STOPPED, and +// exit codes set based on errno/GetLastError if the svc function returns -1. +// +// The svc function is expected to set the service status to SERVICE_RUNNING +// after it initializes. +// +// The handle_control function will be called for each time there is a request +// for the service. It is up to that function and svc to cooperate to both +// respond appropriately to the request (by at least updating the service's +// status) and to fulfill the request. + +int +ACE_NT_Service::open (void *args) +{ + +int svc_return; + + report_status(SERVICE_START_PENDING, 0); + + if ((svc_return = this->svc()) == 0) { + this->svc_status_.dwWin32ExitCode = NO_ERROR; + this->svc_status_.dwServiceSpecificExitCode = 0; + } + else { + if (errno == 0) { + this->svc_status_.dwWin32ExitCode = GetLastError(); + } + else { + this->svc_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + this->svc_status_.dwServiceSpecificExitCode = errno; + } + } + + report_status(SERVICE_STOPPED, 0); + + return svc_return; + +} + +void +ACE_NT_Service::handle_control (DWORD control_code) +{ + + switch(control_code) { + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + report_status(SERVICE_STOP_PENDING); + /* how to cancel? */ + break; + + case SERVICE_CONTROL_PAUSE: + report_status(SERVICE_PAUSE_PENDING); + this->suspend(); + report_status(SERVICE_PAUSED); + break; + + case SERVICE_CONTROL_CONTINUE: + report_status(SERVICE_CONTINUE_PENDING); + this->resume(); + report_status(SERVICE_RUNNING); + break; + + case SERVICE_CONTROL_INTERROGATE: + report_status(0); + break; + } + + return; + +} + + + +// report_status +// +// Reports the current status. If new_status is not 0, it sets the +// status to the new value before reporting. NOTE - this assumes that +// no actual service status values have the value 0. This is true in +// WinNT 4. +// If the status is a 'pending' type, the supplied time hint is used +// unless it's 0, in which case the existing hint is used. The dwWaitHint +// is not updated by this function. The checkpoint is incremented +// by one after a pending report. +// +int +ACE_NT_Service::report_status (DWORD new_status, DWORD time_hint) +{ + +int bump_checkpoint = 0, + retval = 0; +DWORD save_controls = 0; + + + if (new_status != 0) + this->svc_status_.dwCurrentState = new_status; + switch(this->svc_status_.dwCurrentState) { + case SERVICE_START_PENDING: + save_controls = this->svc_status_.dwControlsAccepted; + this->svc_status_.dwControlsAccepted = 0; + /* Fall through */ + case SERVICE_STOP_PENDING: + case SERVICE_CONTINUE_PENDING: + case SERVICE_PAUSE_PENDING: + this->svc_status_.dwWaitHint = time_hint ? time_hint : this->start_time_; + bump_checkpoint = 1; + break; + + default: + this->svc_status_.dwCheckPoint = 0; + } + + retval = SetServiceStatus(this->svc_handle_, &this->svc_status_) ? 0 : -1; + + if (save_controls != 0) + this->svc_status_.dwControlsAccepted = save_controls; + + if (bump_checkpoint) + ++this->svc_status_.dwCheckPoint; + + return retval; + +} + +#endif /* ACE_HAS_WINNT4 */ diff --git a/ace/NT_Service.h b/ace/NT_Service.h new file mode 100644 index 00000000000..ef1e1562b77 --- /dev/null +++ b/ace/NT_Service.h @@ -0,0 +1,179 @@ +/* -*- C++ -*- */ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// ace +// +// = FILENAME +// NT_Service.h +// +// = AUTHOR +// Steve Huston +// +// ============================================================================ + +#if !defined (ACE_NT_SERVICE_H) +#define ACE_NT_SERVICE_H + +#include "ace/inc_user_config.h" + +#if defined (ACE_HAS_WINNT4) && ACE_HAS_WINNT4 != 0 + +#include "ace/Service_Object.h" +#include "ace/Synch.h" +#include "ace/Task.h" + +// NT Services can be implemented using the framework defined by the +// ACE_NT_Service class, and the macros defined in this file. Some quick +// refresher notes on NT Services: +// - The main program defines an array of entries describing the services +// offered. The ACE_NT_SERVICE_ENTRY macro can help with this. +// - For each service, a separate ServiceMain and Handler function need to +// be defined. These are taken care of by the ACE_NT_SERVICE_DEFINE +// macro. +// - When the main program/thread calls StartServiceCtrlDispatcher, NT +// creates a thread for each service, and runs the ServiceMain function +// for the service in that new thread. When that thread exits, the service +// is gone. +// +// To use this facility, you could derive a class from ACE_Service_Object (if +// you want to start via ACE's service configurator), or use any other class +// to run when the image starts (assuming that NT runs the image). You must +// set up an NT SERVICE_TABLE_ENTRY array to define your service(s). You +// can use the ACE_NT_SERVICE_... macros defined below for this. +// A SERVICE_TABLE might look like this: +// ACE_NT_SERVICE_REFERENCE(Svc1); // If service is in another file +// SERVICE_TABLE_ENTRY myServices[] = { +// ACE_NT_SERVICE_ENTRY ("MyNeatService", Svc1), +// { 0, 0 } }; +// +// In the file where your service(s) are implemented, use the +// ACE_NT_SERVICE_DEFINE macro to set up the following: +// 1. A pointer to the service's implementation object (must be derived +// from ACE_NT_Service). +// 2. The service's Handler function (forwards all requests to the +// ACE_NT_Service-derived object's handle_control function). +// 3. The service's ServiceMain function. Creates a new instance of the +// ACE_NT_Service-derived class SVCCLASS, unless one has been created +// already. +// +// If you are using all the default constructor values, you +// can let the generated ServiceMain function create the object, else +// you need to create it by hand before calling StartServiceCtrlDispatcher. +// Set the pointer so ServiceMain won't create another one. +// Another reason you may want to do the object creation yourself is if you +// want to also implement suspend and resume functions (the ones inherited +// from ACE_Service_Object) to do something intelligent to the services which +// are running, like call their handle_control functions to request suspend +// and resume actions, similar to what NT would do if a Services control panel +// applet would do if the user clicks on Suspend. + + +// ACE_NT_SERVICE_START_TIMEOUT is an estimate of the number of milliseconds +// your service will take to start. Default is 5 seconds; you can pass +// a different value (or set one) when you create the ACE_NT_Service object +// for your service. +#if !defined ACE_NT_SERVICE_START_TIMEOUT +#define ACE_NT_SERVICE_START_TIMEOUT 5000 +#endif /* ACE_NT_SERVICE_TIMEOUT */ + +class ACE_Export ACE_NT_Service : public ACE_Task<ACE_MT_SYNCH> +{ + // = TITLE + // Provide the base class which defines the interface for controlling + // an NT service. + // + // = DESCRIPTION +public: + // = Initialization and termination methods. + ACE_NT_Service (DWORD start_timeout = ACE_NT_SERVICE_START_TIMEOUT, + DWORD service_type = SERVICE_WIN32_OWN_PROCESS, + DWORD controls_mask = SERVICE_ACCEPT_STOP); + + virtual ~ACE_NT_Service (void); + + virtual int open (void *args = 0); + // Hook called to open the service. By default, will set the status to + // START_PENDING, svc(), wait(), then set the status to STOPPED. + + virtual int svc (void) = 0; + // The actual service implementation. This function must be implemented + // by subclasses. It is expected that this function will set the status + // to RUNNING. + + virtual void handle_control (DWORD control_code); + // This function is called in response to a request from the Service + // Dispatcher. It must interact with the svc() function to effect the + // requested control operation. The default implementation handles + // all requests as follows: + // SERVICE_CONTROL_STOP: set stop pending, set cancel flag + // SERVICE_CONTROL_PAUSE: set pause pending, suspend(), set paused + // SERVICE_CONTROL_CONTINUE: set continue pending, resume(), set running + // SERVICE_CONTROL_INTERROGATE: reports current status + // SERVICE_CONTROL_SHUTDOWN: same as SERVICE_CONTROL_STOP. + + void svc_handle (const SERVICE_STATUS_HANDLE new_svc_handle); + // Set the svc_handle_ member. + + ACE_ALLOC_HOOK_DECLARE; + // Declare the dynamic allocation hooks. + +protected: + int report_status (DWORD new_status, DWORD time_hint = 0); + +protected: + DWORD start_time_; // Estimate of init time needed + SERVICE_STATUS_HANDLE svc_handle_; // Service handle - doesn't need close. + SERVICE_STATUS svc_status_; +}; + + +// These macros help to get things set up correctly at compile time and +// to take most of the grudge work out of creating the proper functions +// and doing the registrations. +// +// ACE_NT_SERVICE_DEFINE - defines the 'ServiceMain' function which NT will +// call in its own thread when the service control +// dispatcher starts. + +#define ACE_NT_SERVICE_DEFINE(SVCNAME, SVCCLASS, SVCDESC) \ + ACE_NT_Service * _ace_nt_svc_obj_##SVCNAME = 0; \ + VOID WINAPI ace_nt_svc_handler_##SVCNAME (DWORD fdwControl) { \ + _ace_nt_svc_obj_##SVCNAME->handle_control(fdwControl); \ + } \ + VOID WINAPI ace_nt_svc_main_##SVCNAME (DWORD dwArgc, LPTSTR *lpszArgv) { \ + int delete_svc_obj = 0; \ + if (_ace_nt_svc_obj_##SVCNAME == 0) { \ + ACE_NEW (_ace_nt_svc_obj_##SVCNAME, SVCCLASS); \ + if (_ace_nt_svc_obj_##SVCNAME == 0) \ + return; \ + delete_svc_obj = 1; \ + } \ + _ace_nt_svc_obj_##SVCNAME->init(dwArgc, lpszArgv); \ + _ace_nt_svc_obj_##SVCNAME->svc_handle( \ + RegisterServiceCtrlHandler(SVCDESC, \ + &ace_nt_svc_handler_##SVCNAME)); \ + _ace_nt_svc_obj_##SVCNAME->open(); \ + _ace_nt_svc_obj_##SVCNAME->wait(); \ + _ace_nt_svc_obj_##SVCNAME->fini(); \ + if (delete_svc_obj) { \ + delete _ace_nt_svc_obj_##SVCNAME; \ + _ace_nt_svc_obj_##SVCNAME = 0; \ + } \ + return; \ + } + +#define ACE_NT_SERVICE_REFERENCE(SVCNAME) \ +extern VOID WINAPI ace_nt_svc_main_##SVCNAME (DWORD dwArgc, LPTSTR *lpszArgv); + +#define ACE_NT_SERVICE_ENTRY(SVCDESC, SVCNAME) \ + { SVCDESC, &ace_nt_svc_main_##SVCNAME } + +#if defined (__ACE_INLINE__) +#include "ace/NT_Service.i" +#endif /* __ACE_INLINE__ */ + +#endif /* ACE_HAS_WINNT4 */ +#endif /* ACE_SERVICE_OBJECT_H */ diff --git a/ace/NT_Service.i b/ace/NT_Service.i new file mode 100644 index 00000000000..18b40ed0aa3 --- /dev/null +++ b/ace/NT_Service.i @@ -0,0 +1,27 @@ +/* -*- C++ -*- */ +// $Id$ + +ACE_NT_Service::ACE_NT_Service (DWORD start_timeout, + DWORD service_type, + DWORD controls_mask) : + start_time_(start_timeout), + svc_handle_(0) +{ + svc_status_.dwServiceType = service_type; + svc_status_.dwCurrentState = 0; + svc_status_.dwControlsAccepted = controls_mask; + svc_status_.dwWin32ExitCode = NO_ERROR; + svc_status_.dwServiceSpecificExitCode = 0; + svc_status_.dwCheckPoint = 0; +} + +ACE_NT_Service::~ACE_NT_Service (void) +{ +} + +void +ACE_NT_Service::svc_handle(const SERVICE_STATUS_HANDLE new_svc_handle) +{ + this->svc_handle_ = new_svc_handle; + return; +} |