summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Huston <shuston@riverace.com>1998-10-06 20:21:36 +0000
committerSteve Huston <shuston@riverace.com>1998-10-06 20:21:36 +0000
commit6f4be9ceac3b0595721a50462112d1d871953b39 (patch)
treee0583e7df0b626ce702258f77b0456564b93014a
parent6c9f26c72230fb1cc0d355c588f4cff16d9249f4 (diff)
downloadATCD-6f4be9ceac3b0595721a50462112d1d871953b39.tar.gz
New for NT only - adapt an NT Service to an ACE_Task.
-rw-r--r--ace/NT_Service.cpp148
-rw-r--r--ace/NT_Service.h179
-rw-r--r--ace/NT_Service.i27
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;
+}