summaryrefslogtreecommitdiff
path: root/ACE/ace/NT_Service.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/NT_Service.cpp')
-rw-r--r--ACE/ace/NT_Service.cpp618
1 files changed, 618 insertions, 0 deletions
diff --git a/ACE/ace/NT_Service.cpp b/ACE/ace/NT_Service.cpp
new file mode 100644
index 00000000000..0a1e249cb4f
--- /dev/null
+++ b/ACE/ace/NT_Service.cpp
@@ -0,0 +1,618 @@
+// $Id$
+
+#include "ace/config-all.h"
+#if defined (ACE_WIN32) && \
+ !defined (ACE_HAS_PHARLAP) && !defined (ACE_HAS_WINCE)
+
+#include "ace/NT_Service.h"
+
+#if !defined (__ACE_INLINE__)
+#include "ace/NT_Service.inl"
+#endif /* __ACE_INLINE__ */
+
+#include "ace/Log_Msg.h"
+#include "ace/Service_Object.h"
+#include "ace/OS_NS_errno.h"
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+ACE_ALLOC_HOOK_DEFINE(ACE_NT_Service)
+
+// ACE_NT_Service destructor.
+
+ACE_NT_Service::~ACE_NT_Service (void)
+{
+ if (this->svc_sc_handle_ != 0)
+ {
+ CloseServiceHandle (this->svc_sc_handle_);
+ this->svc_sc_handle_ = 0;
+ }
+ delete [] this->desc_;
+ delete [] this->name_;
+ delete [] this->host_;
+}
+
+// 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)
+{
+ ACE_UNUSED_ARG (args);
+ this->report_status (SERVICE_START_PENDING, 0);
+
+ int svc_return = this->svc ();
+ if (svc_return == 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;
+ }
+ }
+
+ return svc_return;
+
+}
+
+int
+ACE_NT_Service::fini (void)
+{
+ return this->report_status (SERVICE_STOPPED, 0);
+}
+
+
+void
+ACE_NT_Service::handle_control (DWORD control_code)
+{
+ switch (control_code)
+ {
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ this->stop_requested (control_code);
+ break;
+
+ case SERVICE_CONTROL_PAUSE:
+ this->pause_requested (control_code);
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ this->continue_requested (control_code);
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ this->interrogate_requested (control_code);
+ break;
+ }
+}
+
+void
+ACE_NT_Service::stop_requested (DWORD)
+{
+ this->report_status (SERVICE_STOP_PENDING);
+ /* how to cancel? */
+}
+
+void
+ACE_NT_Service::pause_requested (DWORD)
+{
+ this->report_status (SERVICE_PAUSE_PENDING);
+ this->suspend ();
+ this->report_status (SERVICE_PAUSED);
+}
+
+void
+ACE_NT_Service::continue_requested (DWORD)
+{
+ this->report_status (SERVICE_CONTINUE_PENDING);
+ this->resume ();
+ this->report_status (SERVICE_RUNNING);
+}
+
+void
+ACE_NT_Service::interrogate_requested (DWORD)
+{
+ this->report_status (0);
+}
+
+void
+ACE_NT_Service::name (const ACE_TCHAR *name, const ACE_TCHAR *desc)
+{
+ delete [] this->desc_;
+ delete [] this->name_;
+
+ if (desc == 0)
+ desc = name;
+
+ this->name_ = ACE::strnew (name);
+ this->desc_ = ACE::strnew (desc);
+}
+
+void
+ACE_NT_Service::host (const ACE_TCHAR *host)
+{
+ delete [] this->host_;
+
+ if (this->svc_sc_handle_ != 0)
+ {
+ CloseServiceHandle (this->svc_sc_handle_);
+ this->svc_sc_handle_ = 0;
+ }
+
+ if (host == 0)
+ {
+ this->host_ = 0;
+ }
+ else
+ {
+ this->host_ = ACE::strnew (host);
+ }
+}
+
+int
+ACE_NT_Service::insert (DWORD start_type,
+ DWORD error_control,
+ const ACE_TCHAR *exe_path,
+ const ACE_TCHAR *group_name,
+ LPDWORD tag_id,
+ const ACE_TCHAR *dependencies,
+ const ACE_TCHAR *account_name,
+ const ACE_TCHAR *password)
+{
+ ACE_TCHAR this_exe[MAXPATHLEN + 2];
+
+ // Insure ACE_OS::last_error finds GetLastError unless we set errno.
+ errno = 0;
+
+ if (exe_path == 0)
+ {
+ if (ACE_TEXT_GetModuleFileName (0, this_exe + 1, MAXPATHLEN) == 0)
+ return -1;
+ // Make sure that this_exe is quoted
+ this_exe[0] = ACE_LIB_TEXT ('\"');
+ ACE_OS::strcat (this_exe, ACE_LIB_TEXT ("\""));
+ exe_path = this_exe;
+ }
+
+ SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
+ 0,
+ SC_MANAGER_ALL_ACCESS);
+ if (sc_mgr == 0)
+ return -1;
+
+ SC_HANDLE sh = ACE_TEXT_CreateService (sc_mgr,
+ this->name (),
+ this->desc (),
+ SERVICE_ALL_ACCESS,
+ this->svc_status_.dwServiceType,
+ start_type,
+ error_control,
+ exe_path,
+ group_name,
+ tag_id,
+ dependencies,
+ account_name,
+ password);
+ // If there was an error, stash GetLastError before CloseServiceHandle
+ // smashes it. ACE_OS::last_error will find the saved error value.
+ if (sh == 0)
+ ACE_OS::set_errno_to_last_error ();
+
+ CloseServiceHandle (sc_mgr);
+
+ if (sh == 0)
+ return -1;
+
+ if (this->svc_sc_handle_ != 0)
+ CloseServiceHandle (this->svc_sc_handle_);
+ this->svc_sc_handle_ = sh;
+
+ return 0;
+
+}
+
+int
+ACE_NT_Service::remove (void)
+{
+ if (this->svc_sc_handle () == 0)
+ return -1;
+
+ if (DeleteService (this->svc_sc_handle()) == 0
+ && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE)
+ return -1;
+
+ return 0;
+}
+
+// Sets the startup type for the service. Returns -1 on error, 0 on
+// success.
+int
+ACE_NT_Service::startup (DWORD startup)
+{
+ SC_HANDLE svc = this->svc_sc_handle ();
+ if (svc == 0)
+ return -1;
+
+ BOOL ok =
+ ChangeServiceConfig (svc,
+ (DWORD) SERVICE_NO_CHANGE,// No change to service type
+ startup, // New startup type
+ (DWORD) SERVICE_NO_CHANGE,// No change to error ctrl
+ 0, // No change to pathname
+ 0, // No change to load group
+ 0, // No change to tag
+ 0, // No change to dependencies
+ 0, 0, // No change to acct/passwd
+ 0); // No change to name
+
+ return ok ? 0 : -1;
+}
+
+// Returns the current startup type.
+
+DWORD
+ACE_NT_Service::startup (void)
+{
+ // The query buffer will hold strings as well as the defined struct.
+ // The string pointers in the struct point to other areas in the
+ // passed memory area, so it has to be large enough to hold the
+ // struct plus all the strings.
+ char cfgbuff[1024];
+ LPQUERY_SERVICE_CONFIG cfg;
+ DWORD cfgsize, needed_size;
+
+ SC_HANDLE svc = this->svc_sc_handle ();
+ if (svc == 0)
+ {
+ // To distinguish this error from the QueryServiceConfig failure
+ // below, return the DWORD equivalent of -2, rather than -1.
+ return MAXDWORD - 1;
+ }
+ cfgsize = sizeof cfgbuff;
+ cfg = (LPQUERY_SERVICE_CONFIG) cfgbuff;
+ BOOL ok = QueryServiceConfig (svc, cfg, cfgsize, &needed_size);
+ if (ok)
+ return cfg->dwStartType;
+ // Zero is a valid return value for QueryServiceConfig, so if
+ // QueryServiceConfig fails, return the DWORD equivalent of -1.
+ return MAXDWORD;
+
+}
+
+
+void
+ACE_NT_Service::capture_log_msg_attributes (void)
+{
+ ACE_Log_Msg::init_hook (this->log_msg_attributes_);
+}
+
+void
+ACE_NT_Service::inherit_log_msg_attributes (void)
+{
+ // There's no thread descriptor involved with a NT-started
+ // thread, so the first arg is 0.
+ ACE_Log_Msg::inherit_hook (0, this->log_msg_attributes_);
+}
+
+
+int
+ACE_NT_Service::start_svc (ACE_Time_Value *wait_time,
+ DWORD *svc_state,
+ DWORD argc, const ACE_TCHAR **argv)
+{
+ SC_HANDLE svc = this->svc_sc_handle ();
+ if (svc == 0)
+ return -1;
+
+ if (!ACE_TEXT_StartService (svc, argc, argv))
+ return -1;
+
+ this->wait_for_service_state (SERVICE_RUNNING, wait_time);
+ if (svc_state != 0)
+ *svc_state = this->svc_status_.dwCurrentState;
+
+ return 0;
+}
+
+int
+ACE_NT_Service::stop_svc (ACE_Time_Value *wait_time,
+ DWORD *svc_state)
+{
+ SC_HANDLE svc = this->svc_sc_handle ();
+ if (svc == 0)
+ return -1;
+
+ if (!ControlService (svc,
+ SERVICE_CONTROL_STOP,
+ &this->svc_status_))
+ return -1;
+
+ this->wait_for_service_state (SERVICE_STOPPED,
+ wait_time);
+ if (svc_state != 0)
+ *svc_state = this->svc_status_.dwCurrentState;
+
+ return 0;
+}
+
+int
+ACE_NT_Service::pause_svc (ACE_Time_Value *wait_time,
+ DWORD *svc_state)
+{
+ SC_HANDLE svc = this->svc_sc_handle ();
+ if (svc == 0)
+ return -1;
+
+ if (!ControlService (svc,
+ SERVICE_CONTROL_PAUSE,
+ &this->svc_status_))
+ return -1;
+
+ this->wait_for_service_state (SERVICE_PAUSED,
+ wait_time);
+ if (svc_state != 0)
+ *svc_state = this->svc_status_.dwCurrentState;
+
+ return 0;
+}
+
+int
+ACE_NT_Service::continue_svc (ACE_Time_Value *wait_time,
+ DWORD *svc_state)
+{
+ SC_HANDLE svc = this->svc_sc_handle ();
+ if (svc == 0)
+ return -1;
+
+ if (!ControlService (svc,
+ SERVICE_CONTROL_CONTINUE,
+ &this->svc_status_))
+ return -1;
+
+ this->wait_for_service_state (SERVICE_RUNNING,
+ wait_time);
+ if (svc_state != 0)
+ *svc_state = this->svc_status_.dwCurrentState;
+
+ return 0;
+}
+
+DWORD
+ACE_NT_Service::state (ACE_Time_Value *wait_hint)
+{
+ DWORD curr_state;
+
+ if (this->state (&curr_state,
+ wait_hint) == -1)
+ return 0;
+ return curr_state;
+}
+
+int
+ACE_NT_Service::state (DWORD *pstate,
+ ACE_Time_Value *wait_hint)
+{
+ SC_HANDLE svc = this->svc_sc_handle ();
+
+ if (svc == 0)
+ return -1;
+
+ // Need to create a temporary copy of this variable since the
+ // QueryServiceStatus call will modify the setting depending on the
+ // current state of the Service. If the service is currently
+ // STOPPED, the value will be cleared.
+ DWORD controls_accepted = this->svc_status_.dwControlsAccepted;
+
+ if (QueryServiceStatus (svc,
+ &this->svc_status_) == 0)
+ return -1;
+
+ if (wait_hint != 0)
+ wait_hint->msec (this->svc_status_.dwWaitHint);
+
+ *pstate = this->svc_status_.dwCurrentState;
+ this->svc_status_.dwControlsAccepted = controls_accepted;
+ return 0;
+}
+
+// test_access
+//
+// Open a new handle, ignoring any handle open in svc_sc_handle_.
+// This function's results are returned without leaving the handle
+// open.
+
+int
+ACE_NT_Service::test_access (DWORD desired_access)
+{
+ int status = -1; // Guilty until proven innocent
+
+ SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
+ 0,
+ GENERIC_READ);
+ if (sc_mgr != 0)
+ {
+ SC_HANDLE handle = ACE_TEXT_OpenService (sc_mgr,
+ this->name (),
+ desired_access);
+ CloseServiceHandle (sc_mgr);
+ if (handle != 0)
+ {
+ status = 0;
+ CloseServiceHandle (handle);
+ }
+ }
+
+ return status;
+}
+
+// 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;
+ int 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;
+}
+
+SC_HANDLE
+ACE_NT_Service::svc_sc_handle (void)
+{
+ if (this->svc_sc_handle_ == 0)
+ {
+ SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
+ 0,
+ SC_MANAGER_ALL_ACCESS);
+ if (sc_mgr != 0)
+ {
+ this->svc_sc_handle_ = ACE_TEXT_OpenService (sc_mgr,
+ this->name (),
+ SERVICE_ALL_ACCESS);
+ if (this->svc_sc_handle_ == 0)
+ ACE_OS::set_errno_to_last_error ();
+ CloseServiceHandle (sc_mgr);
+ }
+ else
+ ACE_OS::set_errno_to_last_error ();
+ }
+
+ return this->svc_sc_handle_;
+}
+
+void
+ACE_NT_Service::wait_for_service_state (DWORD desired_state,
+ ACE_Time_Value *wait_time)
+{
+ DWORD last_state = 0;
+ DWORD last_check_point = 0;
+ int first_time = 1;
+ int service_ok;
+
+ ACE_Time_Value time_out = ACE_OS::gettimeofday ();
+ if (wait_time != 0)
+ time_out += *wait_time;
+
+ // Poll until the service reaches the desired state.
+ for (;;)
+ {
+ service_ok = 0 != QueryServiceStatus (this->svc_sc_handle_,
+ &this->svc_status_);
+
+ // If we cannot query the service, we are done.
+ if (!service_ok)
+ break;
+
+ // If the service has the desired state, we are done.
+ if (desired_state == this->svc_status_.dwCurrentState)
+ break;
+
+ // If we time-out, we are done
+ if (wait_time != 0 && ACE_OS::gettimeofday () > time_out )
+ {
+ errno = ETIME;
+ break;
+ }
+
+ if (first_time)
+ {
+ // remember the service state, the first time we wait
+ last_state = this->svc_status_.dwCurrentState;
+ last_check_point = this->svc_status_.dwCheckPoint;
+ first_time = 0;
+ }
+ else
+ {
+ // update the state change.
+ if (last_state != this->svc_status_.dwCurrentState)
+ {
+ last_state = this->svc_status_.dwCurrentState;
+ last_check_point = this->svc_status_.dwCheckPoint;
+ }
+ else
+ {
+ // The check-point should have increased
+ if (this->svc_status_.dwCheckPoint > last_check_point)
+ last_check_point = this->svc_status_.dwCheckPoint;
+ else
+ {
+ // Service control failure, we are done.
+ service_ok = 0;
+ break;
+ }
+ }
+ }
+
+ ::Sleep (this->svc_status_.dwWaitHint);
+ }
+
+ return;
+}
+
+ACE_END_VERSIONED_NAMESPACE_DECL
+
+#endif /* ACE_WIN32 && !ACE_HAS_PHARLAP */