You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
765 lines
19 KiB
765 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name :
|
|
scmmanager.cxx
|
|
|
|
Abstract:
|
|
Manage SCM
|
|
|
|
Author:
|
|
Bilal Alam (balam) 6-June-2000
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
W3SSL.DLL
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
SCM_MANAGER::~SCM_MANAGER( VOID )
|
|
{
|
|
}
|
|
|
|
HRESULT
|
|
SCM_MANAGER::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize SCM_MANAGER
|
|
|
|
Arguments:
|
|
|
|
pszServiceName - Name of service
|
|
pfnShutdown - Function to call on service shutdown
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwError = NO_ERROR;
|
|
BOOL fRet = FALSE;
|
|
|
|
//
|
|
// initialize critical section
|
|
//
|
|
|
|
fRet = InitializeCriticalSectionAndSpinCount(
|
|
&_csSCMLock,
|
|
0x80000000 /* precreate event */ |
|
|
IIS_DEFAULT_CS_SPIN_COUNT );
|
|
if ( !fRet )
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error calling InitializeCriticalSectionAndSpinCount(). hr = %x\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
_fInitcsSCMLock = TRUE;
|
|
|
|
//
|
|
// set default value
|
|
//
|
|
|
|
_dwStartupWaitHint = HTTPFILTER_SERVICE_STARTUP_WAIT_HINT;
|
|
_dwStopWaitHint = HTTPFILTER_SERVICE_STOP_WAIT_HINT;
|
|
|
|
//
|
|
// read wait hint from registry
|
|
//
|
|
|
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
|
|
0,
|
|
KEY_READ,
|
|
&hKey );
|
|
if ( dwError == NO_ERROR )
|
|
{
|
|
DWORD dwValue = 0;
|
|
DWORD dwType = 0;
|
|
DWORD cbData = 0;
|
|
DBG_ASSERT( hKey != NULL );
|
|
|
|
|
|
cbData = sizeof( dwValue );
|
|
dwError = RegQueryValueEx( hKey,
|
|
REGISTRY_VALUE_HTTPFILTER_STARTUP_WAIT_HINT,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwValue,
|
|
&cbData );
|
|
if ( dwError == NO_ERROR && dwType == REG_DWORD )
|
|
{
|
|
_dwStartupWaitHint = dwValue;
|
|
}
|
|
|
|
cbData = sizeof( dwValue );
|
|
dwError = RegQueryValueEx( hKey,
|
|
REGISTRY_VALUE_HTTPFILTER_STOP_WAIT_HINT,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwValue,
|
|
&cbData );
|
|
if ( dwError == NO_ERROR && dwType == REG_DWORD )
|
|
{
|
|
_dwStopWaitHint = dwValue;
|
|
}
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
//
|
|
// Open SCM
|
|
//
|
|
|
|
_hService = RegisterServiceCtrlHandlerEx( _szServiceName,
|
|
GlobalServiceControlHandler,
|
|
this );
|
|
if ( _hService == NULL )
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error calling RegisterServiceCtrlHandlerEx(). hr = %x\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// create the event object. The control handler function signals
|
|
// this event when it receives the "stop" control code.
|
|
//
|
|
_hServiceStopEvent = CreateEvent( NULL, // no security attributes
|
|
FALSE, // manual reset event
|
|
FALSE, // not-signalled
|
|
NULL ); // no name
|
|
|
|
|
|
if ( _hServiceStopEvent == NULL )
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in CreateEvent(). hr = %x\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// create the event object. The control handler function signals
|
|
// this event when it receives the "pause" control code.
|
|
//
|
|
_hServicePauseEvent = CreateEvent( NULL, // no security attributes
|
|
FALSE, // manual reset event
|
|
FALSE, // not-signalled
|
|
NULL ); // no name
|
|
|
|
|
|
if ( _hServicePauseEvent == NULL )
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in CreateEvent(). hr = %x\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// create the event object. The control handler function signals
|
|
// this event when it receives the "continue" control code.
|
|
//
|
|
_hServiceContinueEvent = CreateEvent( NULL, // no security attributes
|
|
FALSE, // manual reset event
|
|
FALSE, // not-signalled
|
|
NULL ); // no name
|
|
|
|
|
|
if ( _hServiceContinueEvent == NULL )
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in CreateEvent(). hr = %x\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
SCM_MANAGER::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
if ( _hServiceStopEvent != NULL )
|
|
{
|
|
CloseHandle( _hServiceStopEvent );
|
|
_hServiceStopEvent = NULL;
|
|
}
|
|
if ( _hServicePauseEvent != NULL )
|
|
{
|
|
CloseHandle( _hServicePauseEvent );
|
|
_hServicePauseEvent = NULL;
|
|
}
|
|
|
|
if ( _hServiceContinueEvent != NULL )
|
|
{
|
|
CloseHandle( _hServiceContinueEvent );
|
|
_hServiceContinueEvent = NULL;
|
|
}
|
|
|
|
if ( _fInitcsSCMLock )
|
|
{
|
|
DeleteCriticalSection( &_csSCMLock );
|
|
_fInitcsSCMLock = FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
SCM_MANAGER::IndicatePending(
|
|
DWORD dwPendingState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicate (peridically) that we starting/stopping
|
|
|
|
Arguments:
|
|
|
|
dwPendingState -
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
DWORD dwWaitHint;
|
|
|
|
if ( dwPendingState != SERVICE_START_PENDING &&
|
|
dwPendingState != SERVICE_STOP_PENDING &&
|
|
dwPendingState != SERVICE_CONTINUE_PENDING &&
|
|
dwPendingState != SERVICE_PAUSE_PENDING )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( dwPendingState == SERVICE_START_PENDING ||
|
|
dwPendingState == SERVICE_CONTINUE_PENDING )
|
|
{
|
|
dwWaitHint = _dwStartupWaitHint;
|
|
}
|
|
else
|
|
{
|
|
dwWaitHint = _dwStopWaitHint;
|
|
}
|
|
|
|
EnterCriticalSection( &_csSCMLock );
|
|
if( _serviceStatus.dwCurrentState == dwPendingState )
|
|
{
|
|
//
|
|
// if last reported status is the same as the one to report now
|
|
// then increment the checkpoint
|
|
//
|
|
_serviceStatus.dwCheckPoint ++;
|
|
}
|
|
else
|
|
{
|
|
_serviceStatus.dwCheckPoint = 1;
|
|
}
|
|
|
|
_serviceStatus.dwCurrentState = dwPendingState;
|
|
_serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
_serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
_serviceStatus.dwWaitHint = dwWaitHint;
|
|
|
|
SetServiceStatus( _hService,
|
|
&_serviceStatus );
|
|
|
|
LeaveCriticalSection( &_csSCMLock );
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT
|
|
SCM_MANAGER::IndicateComplete(
|
|
DWORD dwState,
|
|
HRESULT hrErrorToReport
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicate the service has started/stopped
|
|
This means stopping the periodic ping (if any)
|
|
|
|
Arguments:
|
|
dwState - new service state
|
|
dwWin32Error - Win32 Error to report to SCM with completion
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
//
|
|
// If error happened at the begining of the initialization
|
|
// it is possible that Service status handle was not established yet.
|
|
// In that case use the default reporting function
|
|
//
|
|
|
|
if ( !_fInitcsSCMLock || _hService == NULL )
|
|
{
|
|
SCM_MANAGER::ReportFatalServiceStartupError(
|
|
HTTPFILTER_SERVICE_NAME_W,
|
|
( FAILED( hrErrorToReport ) ?
|
|
WIN32_FROM_HRESULT( hrErrorToReport ) :
|
|
NO_ERROR ) );
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
if ( dwState != SERVICE_RUNNING &&
|
|
dwState != SERVICE_STOPPED &&
|
|
dwState != SERVICE_PAUSED )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
EnterCriticalSection( &_csSCMLock );
|
|
|
|
_serviceStatus.dwCurrentState = dwState;
|
|
_serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
_serviceStatus.dwWin32ExitCode = ( FAILED( hrErrorToReport ) ?
|
|
WIN32_FROM_HRESULT( hrErrorToReport ) :
|
|
NO_ERROR );
|
|
_serviceStatus.dwCheckPoint = 0;
|
|
_serviceStatus.dwWaitHint = 0;
|
|
|
|
if ( _serviceStatus.dwCurrentState == SERVICE_RUNNING )
|
|
{
|
|
_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
|
SERVICE_ACCEPT_SHUTDOWN;
|
|
}
|
|
|
|
DBG_ASSERT( _hService != NULL );
|
|
|
|
if ( SetServiceStatus( _hService,
|
|
&_serviceStatus ) )
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Address = %x\n",
|
|
&_serviceStatus ));
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
LeaveCriticalSection( &_csSCMLock );
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
DWORD
|
|
SCM_MANAGER::ControlHandler(
|
|
DWORD dwControlCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle SCM command
|
|
|
|
Arguments:
|
|
|
|
dwControlCode - SCM command
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
switch( dwControlCode )
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
if ( _serviceStatus.dwCurrentState == SERVICE_STOPPED )
|
|
{
|
|
UpdateServiceStatus();
|
|
return NO_ERROR;
|
|
}
|
|
IndicatePending( SERVICE_STOP_PENDING );
|
|
//
|
|
// Initiate shutdown
|
|
//
|
|
DBG_ASSERT( _hServiceStopEvent );
|
|
SetEvent( _hServiceStopEvent );
|
|
|
|
break;
|
|
case SERVICE_CONTROL_PAUSE:
|
|
if ( _serviceStatus.dwCurrentState == SERVICE_PAUSED )
|
|
{
|
|
UpdateServiceStatus();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
IndicatePending( SERVICE_PAUSE_PENDING );
|
|
|
|
//
|
|
// Initiate pause
|
|
//
|
|
DBG_ASSERT( _hServicePauseEvent );
|
|
SetEvent( _hServicePauseEvent );
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
if ( _serviceStatus.dwCurrentState == SERVICE_RUNNING )
|
|
{
|
|
UpdateServiceStatus();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
IndicatePending( SERVICE_CONTINUE_PENDING );
|
|
//
|
|
// Initiate continue
|
|
//
|
|
DBG_ASSERT( _hServiceContinueEvent );
|
|
SetEvent( _hServiceContinueEvent );
|
|
break;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
UpdateServiceStatus();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SCM_MANAGER::RunService(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Executes the service (initialize, startup, stopping, reporting to SCM
|
|
|
|
SERVICE implementation class that inherits from SCM_MANAGER
|
|
must implement OnStart() and OnStop()
|
|
|
|
Arguments:
|
|
|
|
VOID
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr= E_FAIL;
|
|
DWORD dwRet = 0;
|
|
HANDLE events[2];
|
|
|
|
hr = Initialize();
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in SCM_MANAGER::Initialize(). hr = %x\n",
|
|
hr ));
|
|
goto Finished;
|
|
}
|
|
|
|
hr = IndicatePending( SERVICE_START_PENDING );
|
|
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in SCM_MANAGER::IndicatePending(). hr = %x\n",
|
|
hr ));
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// loop for SCM CONTINUE request
|
|
//
|
|
for(;;)
|
|
{
|
|
hr = OnServiceStart();
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in OnStart(). hr = %x\n",
|
|
hr ));
|
|
goto Finished;
|
|
}
|
|
|
|
hr = IndicateComplete( SERVICE_RUNNING );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in SCM_MANAGER::IndicateComplete(). hr = %x\n",
|
|
hr ));
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Wait till service stop is requested
|
|
//
|
|
events[0] = _hServiceStopEvent;
|
|
events[1] = _hServicePauseEvent;
|
|
dwRet = WaitForMultipleObjects( 2,
|
|
events,
|
|
FALSE,
|
|
INFINITE );
|
|
|
|
if ( dwRet == WAIT_OBJECT_0 )
|
|
{
|
|
// transition from RUNNING to STOPPED
|
|
hr = OnServiceStop();
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in OnStop(). hr = %x\n",
|
|
hr ));
|
|
goto Finished;
|
|
}
|
|
|
|
hr = S_OK;
|
|
goto Finished;
|
|
}
|
|
else if ( dwRet == WAIT_OBJECT_0 + 1 )
|
|
{
|
|
// transition from RUNNING to PAUSED
|
|
// this is the request to pause
|
|
// we implement pause being similar to stop
|
|
//
|
|
hr = OnServiceStop();
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in OnStop() when trying to pause. hr = %x\n",
|
|
hr ));
|
|
goto Finished;
|
|
}
|
|
hr = IndicateComplete( SERVICE_PAUSED,
|
|
hr );
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
goto Finished;
|
|
}
|
|
|
|
|
|
//
|
|
// Wait till service stop is requested
|
|
//
|
|
events[0] = _hServiceStopEvent;
|
|
events[1] = _hServiceContinueEvent;
|
|
dwRet = WaitForMultipleObjects( 2,
|
|
events,
|
|
FALSE,
|
|
INFINITE );
|
|
|
|
if ( dwRet == WAIT_OBJECT_0 )
|
|
{
|
|
// transition from PAUSED to STOPPED
|
|
// we are in stopped state already
|
|
//
|
|
goto Finished;
|
|
}
|
|
else if ( dwRet == WAIT_OBJECT_0 + 1 )
|
|
{
|
|
// transition from PAUSED to CONTINUED
|
|
// while (TRUE) loop will handle the continue
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
goto Finished;
|
|
}
|
|
}
|
|
Finished:
|
|
|
|
//
|
|
// Error means that we must report SCM that service is stopping
|
|
// SCM will be notified of error that occured
|
|
// Note: even though IndicateComplete received HRESULT for error
|
|
// SCM accepts only Win32 errors and it truncates the upper word
|
|
// of HRESULT errors sent to it. Hence IndicateComplete
|
|
// calls WIN32_FROM_HRESULT to convert hr, but that may mean
|
|
// loss of error
|
|
//
|
|
hr = IndicateComplete( SERVICE_STOPPED,
|
|
hr );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in IndicateComplete(). hr = %x\n",
|
|
hr ));
|
|
}
|
|
|
|
Terminate();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
DWORD
|
|
WINAPI
|
|
SCM_MANAGER::GlobalServiceControlHandler(
|
|
DWORD dwControl,
|
|
DWORD /*dwEventType*/,
|
|
LPVOID /*pEventData*/,
|
|
LPVOID pServiceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SCM callback passed to RegisterServiceCtrlHandlerEx
|
|
|
|
Arguments:
|
|
|
|
for details see LPHANDLER_FUNCTION_EX in MSDN
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( pServiceContext == NULL )
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
SCM_MANAGER * pManager = reinterpret_cast<SCM_MANAGER*>(pServiceContext);
|
|
|
|
DBG_ASSERT( pManager != NULL );
|
|
|
|
return pManager->ControlHandler( dwControl );
|
|
|
|
}
|
|
|
|
VOID
|
|
SCM_MANAGER::UpdateServiceStatus(
|
|
BOOL fUpdateCheckpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resend the last serviceStatus
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DBG_ASSERT( _hService != NULL );
|
|
|
|
EnterCriticalSection( &_csSCMLock );
|
|
if( fUpdateCheckpoint )
|
|
{
|
|
_serviceStatus.dwCheckPoint ++;
|
|
}
|
|
SetServiceStatus( _hService,
|
|
&_serviceStatus );
|
|
LeaveCriticalSection( &_csSCMLock );
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
SCM_MANAGER::ReportFatalServiceStartupError(
|
|
WCHAR * pszServiceName,
|
|
DWORD dwError )
|
|
|
|
{
|
|
SERVICE_STATUS serviceStatus;
|
|
ZeroMemory( &serviceStatus, sizeof( serviceStatus ) );
|
|
|
|
SERVICE_STATUS_HANDLE hService =
|
|
RegisterServiceCtrlHandlerEx( pszServiceName,
|
|
GlobalServiceControlHandler,
|
|
NULL /* indicates that nothing should be
|
|
executed on callback*/ );
|
|
if ( hService == NULL )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to get Service Ctrl Handler Win32 = %d\n",
|
|
GetLastError() ));
|
|
return;
|
|
}
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
serviceStatus.dwWin32ExitCode = dwError;
|
|
serviceStatus.dwCheckPoint = 0;
|
|
serviceStatus.dwWaitHint = 0;
|
|
|
|
if ( ! SetServiceStatus( hService,
|
|
&serviceStatus ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to set service status Win32 = %d\n",
|
|
GetLastError() ));
|
|
}
|
|
|
|
//
|
|
// per MSDN the Service Status handle doesn't need to be closed
|
|
//
|
|
|
|
hService = NULL;
|
|
}
|
|
|