mirror of
https://github.com/mangosfour/server.git
synced 2025-12-12 19:37:03 +00:00
618 lines
16 KiB
C++
618 lines
16 KiB
C++
// $Id: NT_Service.cpp 81862 2008-06-09 10:41:41Z sma $
|
|
|
|
#include "ace/config-all.h"
|
|
#if defined (ACE_WIN32) && !defined (ACE_LACKS_WIN32_SERVICES)
|
|
|
|
#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,
|
|
DWORD desired_access)
|
|
{
|
|
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_TEXT ('\"');
|
|
ACE_OS::strcat (this_exe, ACE_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 (),
|
|
desired_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 (static_cast<long> (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_LACKS_WIN32_SERVICES */
|