mirror of https://github.com/tongzx/nt5src
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.
3520 lines
84 KiB
3520 lines
84 KiB
/*++
|
|
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
iissvc.cxx
|
|
|
|
Abstract:
|
|
|
|
Defines the IIS_SERVICE class
|
|
|
|
FILE HISTORY:
|
|
MuraliK 15-Nov-1994 Created.
|
|
CezaryM 11-May-2000 Added events:
|
|
started/stopped/paused/resumed
|
|
--*/
|
|
|
|
|
|
|
|
#include "tcpdllp.hxx"
|
|
|
|
#include <rpc.h>
|
|
#include <tsunami.hxx>
|
|
#include <iistypes.hxx>
|
|
#include <iisendp.hxx>
|
|
#include "inetreg.h"
|
|
#include "tcpcons.h"
|
|
#include "apiutil.h"
|
|
#include <imd.h>
|
|
#include <ole2.h>
|
|
#include <inetsvcs.h>
|
|
#include <issched.hxx>
|
|
#include <pwsdata.hxx>
|
|
|
|
#include "reftrce2.h"
|
|
|
|
/************************************************************
|
|
* Symbolic Constants
|
|
************************************************************/
|
|
|
|
|
|
//
|
|
// What we assume to be the last winsock error
|
|
//
|
|
#define WSA_MAX_ERROR (WSABASEERR + 3000)
|
|
|
|
//
|
|
// For socket errors, we return the numeric socket error
|
|
//
|
|
|
|
#define SOCK_ERROR_STR_W L"Socket error %d"
|
|
#define SOCK_ERROR_STR_A "Socket error %d"
|
|
|
|
#define LM_PREFIX "/" IIS_MD_LOCAL_MACHINE_PATH "/"
|
|
#define LM_PREFIX_CCH sizeof(LM_PREFIX) - sizeof(CHAR)
|
|
|
|
|
|
//
|
|
// The time indicating how long it will take for IIS to start up a service
|
|
// <-- Service controller will wait for this duration before telling user
|
|
// that there is some problem.
|
|
// For PDC 1996, a hacked value of 90 seconds used.
|
|
// The new value of 30 seconds is plugged in on 7/7/97
|
|
//
|
|
|
|
# define IIS_SERVICE_START_WAIT_HINT_SECONDS (30) // 30 seconds
|
|
# define IIS_SERVICE_START_WAIT_HINT (IIS_SERVICE_START_WAIT_HINT_SECONDS * 1000) // 30 seconds
|
|
# define IIS_SERVICE_START_WAIT_HINT_EXTENDED (IIS_SERVICE_START_WAIT_HINT * 4) // 2 minutes
|
|
|
|
# define IIS_SERVICE_START_INDICATOR_INTERVAL (IIS_SERVICE_START_WAIT_HINT_EXTENDED / 2) // 1 minute
|
|
# define IIS_SERVICE_START_INDICATOR_INTERVAL_SECONDS (IIS_SERVICE_START_INDICATOR_INTERVAL / 1000)
|
|
|
|
# define MAX_NUMBER_OF_START_HINT_REPETITIONS 200 // 50 minutes
|
|
|
|
//
|
|
// MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL
|
|
// - specifies the time interval in milli-seconds for the interval
|
|
// to notify the service controller that a service is shutting down.
|
|
//
|
|
# define MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL \
|
|
(SERVICE_STOP_WAIT_HINT/2)
|
|
|
|
|
|
|
|
#ifdef _KNFOCOMM
|
|
//
|
|
// List of "known" services that use knfocomm -
|
|
// This is needed to break deadlocks between infocomm & knfocomm..
|
|
//
|
|
|
|
static char* rgKnfoServices[] = {
|
|
TEXT("pop3svc"),
|
|
TEXT("imap4svc")
|
|
};
|
|
|
|
static DWORD gNumKnfoServices = 3;
|
|
#endif // _KNFOCOMM
|
|
|
|
|
|
//
|
|
// Deferred metabase change notify
|
|
//
|
|
|
|
VOID
|
|
WINAPI
|
|
DeferredMDChange(
|
|
PVOID pv
|
|
);
|
|
|
|
BOOL
|
|
I_StopInstanceEndpoint( PVOID pvContext1,
|
|
PVOID pvContext2,
|
|
IIS_SERVER_INSTANCE * pInstance );
|
|
|
|
VOID
|
|
WINAPI
|
|
ServiceShutdownIndicator( VOID * pSvcContext);
|
|
|
|
|
|
//
|
|
// Critical section used for locking the list of IIS_SERVICE objects
|
|
// during insertion and deletion
|
|
//
|
|
|
|
CRITICAL_SECTION IIS_SERVICE::sm_csLock;
|
|
LIST_ENTRY IIS_SERVICE::sm_ServiceInfoListHead;
|
|
BOOL IIS_SERVICE::sm_fInitialized = FALSE;
|
|
PISRPC IIS_SERVICE::sm_isrpc = NULL;
|
|
IUnknown * IIS_SERVICE::sm_MDObject = NULL;
|
|
IUnknown * IIS_SERVICE::sm_MDNseObject = NULL;
|
|
|
|
#if SERVICE_REF_TRACKING
|
|
//
|
|
// Ref count trace log size
|
|
//
|
|
#define C_SERVICE_REFTRACES 400
|
|
#define C_LOCAL_SERVICE_REFTRACES 40
|
|
#endif // SERVICE_REF_TRACKING
|
|
|
|
//
|
|
PTRACE_LOG IIS_SERVICE::sm_pDbgRefTraceLog = NULL;
|
|
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
DWORD
|
|
InitMetadataDCom(
|
|
PVOID Context,
|
|
PVOID NseContext
|
|
);
|
|
|
|
//
|
|
// LOCAL Functions
|
|
//
|
|
|
|
extern MIME_MAP * g_pMimeMap;
|
|
#define MAX_ADDRESSES_SUPPORTED 20
|
|
#define SIZEOF_IP_SEC_LIST( IPList ) (sizeof(INET_INFO_IP_SEC_LIST) + \
|
|
(IPList)->cEntries * \
|
|
sizeof(INET_INFO_IP_SEC_ENTRY))
|
|
|
|
BOOL g_fIgnoreSC = FALSE;
|
|
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
//
|
|
// These 2 functions cannot be inline as they reference sm_csLock
|
|
// which is a static non-exported member of IIS_SERVICE
|
|
// Having them inline causes build to break when compiled with /Od
|
|
//
|
|
|
|
VOID
|
|
IIS_SERVICE::AcquireGlobalLock( )
|
|
{
|
|
EnterCriticalSection(&sm_csLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::ReleaseGlobalLock( )
|
|
{
|
|
LeaveCriticalSection(&sm_csLock);
|
|
}
|
|
|
|
|
|
IIS_SERVICE::IIS_SERVICE(
|
|
IN LPCSTR pszServiceName,
|
|
IN LPCSTR pszModuleName,
|
|
IN LPCSTR pszRegParamKey,
|
|
IN DWORD dwServiceId,
|
|
IN ULONGLONG SvcLocId,
|
|
IN BOOL MultipleInstanceSupport,
|
|
IN DWORD cbAcceptExRecvBuffer,
|
|
IN ATQ_CONNECT_CALLBACK pfnConnect,
|
|
IN ATQ_COMPLETION pfnConnectEx,
|
|
IN ATQ_COMPLETION pfnIoCompletion
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Contructor for IIS_SERVICE class.
|
|
This constructs a new service info object for the service specified.
|
|
|
|
Arguments:
|
|
|
|
pszServiceName - name of the service to be created.
|
|
|
|
pszModuleName - name of the module for loading string resources.
|
|
|
|
pszRegParamKey - fully qualified name of the registry key that
|
|
contains the common service data for this server
|
|
|
|
dwServiceId - DWORD containing the bitflag id for service.
|
|
|
|
SvcLocId - Service locator id
|
|
|
|
MultipleInstanceSupport - Does this service support multiple instances
|
|
|
|
cbAcceptExRecvBuffer, pfnConnect, pfnConnectEx, pfnIoCompletion
|
|
- parameters for ATQ Endpoint
|
|
|
|
On success it initializes all the members of the object,
|
|
inserts itself to the global list of service info objects and
|
|
returns with success.
|
|
|
|
Note:
|
|
The caller of this function should check the validity by
|
|
invoking the member function IsValid() after constructing
|
|
this object.
|
|
|
|
--*/
|
|
:
|
|
m_state ( BlockStateInvalid), // state is invalid at start
|
|
m_pMimeMap ( g_pMimeMap),
|
|
m_dwServiceId ( dwServiceId),
|
|
m_strServiceName ( pszServiceName),
|
|
m_strServiceComment ( ),
|
|
m_strModuleName ( pszModuleName),
|
|
m_strParametersKey ( pszRegParamKey),
|
|
m_SvcLocId ( SvcLocId ),
|
|
m_EventLog ( pszServiceName ),
|
|
m_fSocketsInitialized ( FALSE ),
|
|
m_fIpcStarted ( FALSE ),
|
|
m_fSvcLocationDone ( FALSE ),
|
|
m_nInstance ( 0),
|
|
m_nStartedInstances ( 0),
|
|
m_maxInstanceId ( 1),
|
|
m_dwDownlevelInstance ( 1),
|
|
m_reference ( 1),
|
|
m_pDbgRefTraceLog ( NULL),
|
|
m_hShutdownEvent ( NULL),
|
|
m_fMultiInstance ( MultipleInstanceSupport ),
|
|
m_fEnableSvcLocation ( INETA_DEF_ENABLE_SVC_LOCATION ),
|
|
m_fIsDBCS ( FALSE ),
|
|
//
|
|
// Initialize ATQ callbacks
|
|
//
|
|
m_pfnConnect ( pfnConnect),
|
|
m_pfnConnectEx ( pfnConnectEx),
|
|
m_pfnIoCompletion ( pfnIoCompletion),
|
|
m_cbRecvBuffer ( cbAcceptExRecvBuffer),
|
|
m_dwShutdownScheduleId( 0),
|
|
m_nShutdownIndicatorCalls (0)
|
|
{
|
|
MB mb( (IMDCOM*) QueryMDObject() );
|
|
DWORD errInit = NO_ERROR;
|
|
|
|
//
|
|
// Initialize endpoint list
|
|
//
|
|
|
|
InitializeListHead( &m_EndpointListHead );
|
|
InitializeListHead( &m_InstanceListHead );
|
|
|
|
INITIALIZE_CRITICAL_SECTION( &m_lock );
|
|
|
|
#if SERVICE_REF_TRACKING
|
|
m_pDbgRefTraceLog = CreateRefTraceLog(C_LOCAL_SERVICE_REFTRACES, 0);
|
|
#endif // SERVICE_REF_TRACKING
|
|
|
|
//
|
|
// Initialize the service metapath
|
|
//
|
|
|
|
strcpy( m_achServiceMetaPath, "/" IIS_MD_LOCAL_MACHINE_PATH "/" );
|
|
strcat( m_achServiceMetaPath, QueryServiceName() );
|
|
strcat( m_achServiceMetaPath, "/" );
|
|
|
|
DBG_ASSERT( strlen(m_achServiceMetaPath) < sizeof(m_achServiceMetaPath) );
|
|
|
|
//
|
|
// Read the downlevel instance
|
|
//
|
|
|
|
if ( mb.Open( QueryMDPath() ) )
|
|
{
|
|
mb.GetDword( "",
|
|
MD_DOWNLEVEL_ADMIN_INSTANCE,
|
|
IIS_MD_UT_SERVER,
|
|
0xffffffff, // default value
|
|
&m_dwDownlevelInstance
|
|
);
|
|
mb.Close( );
|
|
}
|
|
else
|
|
{
|
|
errInit = GetLastError();
|
|
}
|
|
|
|
//
|
|
// Is this a DBCS locale?
|
|
//
|
|
|
|
WORD wPrimaryLangID = PRIMARYLANGID( GetSystemDefaultLangID() );
|
|
|
|
m_fIsDBCS = ((wPrimaryLangID == LANG_JAPANESE) ||
|
|
(wPrimaryLangID == LANG_CHINESE) ||
|
|
(wPrimaryLangID == LANG_KOREAN) );
|
|
|
|
|
|
if ( !m_EventLog.Success() ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" Eventlog not initialized\n"));
|
|
|
|
if ( GetLastError() != ERROR_ACCESS_DENIED )
|
|
{
|
|
DBG_ASSERT( m_state != BlockStateActive);
|
|
errInit = GetLastError();
|
|
|
|
//
|
|
// Skip anything else that might fail since we don't have an
|
|
// event log object
|
|
//
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we failed to open the service path in the metabase above, bail
|
|
// out of the initialization
|
|
//
|
|
|
|
if ( errInit )
|
|
{
|
|
const CHAR * apsz[1];
|
|
apsz[0] = QueryMDPath();
|
|
|
|
LogEvent( INET_SVC_INVALID_MB_PATH,
|
|
1,
|
|
(const CHAR **) apsz,
|
|
errInit );
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Get module name
|
|
//
|
|
|
|
m_hModule = GetModuleHandle( pszModuleName);
|
|
if ( m_hModule == NULL ) {
|
|
|
|
CHAR * apsz[1];
|
|
|
|
errInit = GetLastError();
|
|
|
|
apsz[0] = (PCHAR)pszModuleName;
|
|
m_EventLog.LogEvent( INET_SVC_GET_MODULE_FAILED,
|
|
1,
|
|
(const CHAR**)apsz,
|
|
errInit );
|
|
DBG_ASSERT( m_state != BlockStateActive);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Init others
|
|
//
|
|
|
|
if ( MultipleInstanceSupport ) {
|
|
m_strServiceComment.Copy(DEF_MULTI_SERVER_COMMENT_A);
|
|
}
|
|
|
|
//
|
|
// Create pending shutdown event
|
|
//
|
|
|
|
m_hPendingShutdownEvent = CreateEvent( NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL );
|
|
if ( m_hPendingShutdownEvent == NULL )
|
|
{
|
|
errInit = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
//
|
|
// Add ourself to the list - Note we must always get on this list as the
|
|
// destructor assumes this.
|
|
//
|
|
|
|
AcquireGlobalLock( );
|
|
|
|
InsertHeadList( & sm_ServiceInfoListHead, &m_ServiceListEntry );
|
|
|
|
if ( errInit == NO_ERROR )
|
|
{
|
|
//
|
|
// put service information into metabase
|
|
//
|
|
|
|
AdvertiseServiceInformationInMB( );
|
|
|
|
//
|
|
// we're on. now set the state to be active!
|
|
//
|
|
|
|
m_state = BlockStateActive;
|
|
}
|
|
|
|
ReleaseGlobalLock( );
|
|
|
|
//
|
|
// Initialize the service status structure.
|
|
//
|
|
|
|
m_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
m_svcStatus.dwCurrentState = SERVICE_STOPPED;
|
|
m_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
|
|
| SERVICE_ACCEPT_PAUSE_CONTINUE
|
|
| SERVICE_ACCEPT_SHUTDOWN;
|
|
m_svcStatus.dwWin32ExitCode = errInit;
|
|
m_svcStatus.dwServiceSpecificExitCode = errInit;
|
|
m_svcStatus.dwCheckPoint = 0;
|
|
m_svcStatus.dwWaitHint = 0;
|
|
|
|
return;
|
|
|
|
} // IIS_SERVICE::IIS_SERVICE()
|
|
|
|
|
|
|
|
IIS_SERVICE::~IIS_SERVICE( VOID)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Cleanup the TsvcInfo object. If the service is not already
|
|
terminated, it terminates the service before cleanup.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
IF_DEBUG(INSTANCE) {
|
|
DBGPRINTF((DBG_CONTEXT,"~IIS_SERVICE: nRef %d nInstances %d\n",
|
|
m_reference, m_nInstance));
|
|
}
|
|
|
|
DBG_ASSERT( m_reference == 0 );
|
|
DBG_ASSERT( IsListEmpty(&m_InstanceListHead) );
|
|
DBG_ASSERT( IsListEmpty(&m_EndpointListHead) );
|
|
DBG_ASSERT( m_dwShutdownScheduleId == 0);
|
|
|
|
if ( m_hShutdownEvent != NULL ) {
|
|
DBG_REQUIRE(CloseHandle(m_hShutdownEvent));
|
|
m_hShutdownEvent = NULL;
|
|
}
|
|
|
|
if ( m_hPendingShutdownEvent != NULL )
|
|
{
|
|
DBG_REQUIRE( CloseHandle( m_hPendingShutdownEvent ) );
|
|
m_hPendingShutdownEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// remove from global list
|
|
//
|
|
|
|
AcquireGlobalLock( );
|
|
RemoveEntryList( &m_ServiceListEntry );
|
|
ReleaseGlobalLock( );
|
|
|
|
#if SERVICE_REF_TRACKING
|
|
DestroyRefTraceLog( m_pDbgRefTraceLog );
|
|
#endif // SERVICE_REF_TRACKING
|
|
|
|
DeleteCriticalSection( &m_lock );
|
|
} // IIS_SERVICE::~IIS_SERVICE()
|
|
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::StartServiceOperation(
|
|
IN PFN_SERVICE_CTRL_HANDLER pfnCtrlHandler,
|
|
IN PFN_SERVICE_SPECIFIC_INITIALIZE pfnInitialize,
|
|
IN PFN_SERVICE_SPECIFIC_CLEANUP pfnCleanup
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Starts the operation of service instantiated in the given
|
|
Service Info Object.
|
|
|
|
|
|
Arguments:
|
|
|
|
pfnCtrlHandler
|
|
pointer to a callback function for handling dispatch of
|
|
service controller requests. A separate function is required
|
|
since Service Controller call back function does not send
|
|
context information.
|
|
|
|
pfnInitialize
|
|
pointer to a callback function implemented by the service DLL;
|
|
the callback is responsible for all ServiceSpecific initializations
|
|
|
|
pfnCleanup
|
|
pointer to a callback function implemented by the service DLL;
|
|
the callback is responsible for all ServiceSpecific Cleanups
|
|
|
|
Returns:
|
|
|
|
NO_ERROR on success and Win32 error code if any failure.
|
|
--*/
|
|
{
|
|
|
|
DWORD err;
|
|
DWORD cbBuffer;
|
|
BOOL fInitCalled = FALSE;
|
|
|
|
DBG_ASSERT((pfnInitialize != NULL) && (pfnCleanup != NULL));
|
|
|
|
if ( !IsActive()) {
|
|
|
|
//
|
|
// Not successfully initialized.
|
|
//
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Service not ready. Failing StartServiceOperation\n"));
|
|
return ( ERROR_NOT_READY );
|
|
}
|
|
|
|
//
|
|
// Create shutdown event.
|
|
//
|
|
|
|
DBG_ASSERT(m_hShutdownEvent == NULL);
|
|
m_hShutdownEvent = CreateEvent( NULL, // lpsaSecurity
|
|
TRUE, // fManualReset
|
|
FALSE, // fInitialState
|
|
TsIsWindows95() ?
|
|
PWS_SHUTDOWN_EVENT : NULL
|
|
);
|
|
|
|
if( m_hShutdownEvent == NULL ) {
|
|
|
|
err = GetLastError();
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"InitializeService(): Cannot create shutdown event,"
|
|
" error %lu\n", err ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !g_fIgnoreSC ) {
|
|
m_hsvcStatus = RegisterServiceCtrlHandler(
|
|
QueryServiceName(),
|
|
pfnCtrlHandler
|
|
);
|
|
|
|
//
|
|
// Register the Control Handler routine.
|
|
//
|
|
|
|
if( m_hsvcStatus == NULL_SERVICE_STATUS_HANDLE ) {
|
|
|
|
err = GetLastError();
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"cannot connect to register ctrl handler, error %lu\n",
|
|
err )
|
|
);
|
|
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Indicate to the service that we are starting up,
|
|
// Update the service status.
|
|
//
|
|
|
|
err = UpdateServiceStatus( SERVICE_START_PENDING,
|
|
NO_ERROR,
|
|
1,
|
|
IIS_SERVICE_START_WAIT_HINT
|
|
);
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"StartServiceOperation(): cannot update service status,"
|
|
" error %lu\n",
|
|
err )
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the various service specific components.
|
|
//
|
|
|
|
m_dwNextSCMUpdateTime = GetCurrentTimeInSeconds() + IIS_SERVICE_START_WAIT_HINT_SECONDS / 2;
|
|
m_dwStartUpIndicatorCalls = 0;
|
|
m_dwClientStartActivityIndicator = 1;
|
|
|
|
if ( pfnInitialize != NULL ) {
|
|
|
|
err = ( *pfnInitialize)( this);
|
|
fInitCalled = TRUE;
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Initialization of service failed with %d\n",
|
|
err));
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are done with all initializatios, Update the service status.
|
|
//
|
|
|
|
|
|
err = UpdateServiceStatus( SERVICE_RUNNING,
|
|
NO_ERROR,
|
|
0,
|
|
0 );
|
|
|
|
if( err != NO_ERROR ) {
|
|
DBGPRINTF( ( DBG_CONTEXT, "cannot update service status, error %lu\n",
|
|
err )
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Wait for the shutdown event.
|
|
//
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"IIS_SERVICE(%08p) %s - Waiting for ShutDown Event ...\n",
|
|
this, QueryServiceName()
|
|
));
|
|
|
|
#if 0
|
|
err = WaitForSingleObject( m_hShutdownEvent,
|
|
INFINITE );
|
|
|
|
if ( err != WAIT_OBJECT_0) {
|
|
|
|
//
|
|
// Error. Unable to wait for single object.
|
|
//
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Wait for single object failed with Error %lu\n",
|
|
err )
|
|
);
|
|
}
|
|
#else
|
|
|
|
while ( TRUE ) {
|
|
|
|
MSG msg;
|
|
|
|
//
|
|
// Need to do MsgWait instead of WaitForSingleObject
|
|
// to process windows msgs. We now have a window
|
|
// because of COM.
|
|
//
|
|
|
|
err = MsgWaitForMultipleObjects( 1,
|
|
&m_hShutdownEvent,
|
|
FALSE,
|
|
INFINITE,
|
|
QS_ALLINPUT );
|
|
|
|
if ( err == WAIT_OBJECT_0 ) {
|
|
break;
|
|
}
|
|
|
|
while ( PeekMessage( &msg,
|
|
NULL,
|
|
0,
|
|
0,
|
|
PM_REMOVE ))
|
|
{
|
|
DispatchMessage( &msg );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
err = NO_ERROR;
|
|
|
|
//
|
|
// Stop time. Tell the Service Controller that we're stopping,
|
|
// then terminate the various service components.
|
|
//
|
|
|
|
UpdateServiceStatus( SERVICE_STOP_PENDING,
|
|
0,
|
|
1,
|
|
SERVICE_STOP_WAIT_HINT );
|
|
|
|
Cleanup:
|
|
|
|
if ( fInitCalled && (pfnCleanup != NULL) ) {
|
|
|
|
//
|
|
// 1. Register a scheduled work item for periodic update to the
|
|
// Service Controller while shutdown is happening in this thread
|
|
// (Reason: Shutdown takes far longer time
|
|
// than SERVICE_STOP_WAIT_HINT)
|
|
//
|
|
|
|
m_nShutdownIndicatorCalls = 0;
|
|
|
|
DBG_ASSERT( m_dwShutdownScheduleId == 0);
|
|
m_dwShutdownScheduleId =
|
|
ScheduleWorkItem( ServiceShutdownIndicator,
|
|
this,
|
|
MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL,
|
|
TRUE ); // Periodic
|
|
|
|
if ( m_dwShutdownScheduleId == 0) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"ScheduleShutdown for Service(%s) failed."
|
|
" Error = %d\n",
|
|
QueryServiceName(),
|
|
GetLastError()
|
|
));
|
|
}
|
|
|
|
//
|
|
// 2. Stop all endpoints for the service
|
|
//
|
|
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"IIS_SERVICE(%08p) Stopping all endpoints for %s\n",
|
|
this, QueryServiceName()
|
|
));
|
|
}
|
|
|
|
DBG_REQUIRE( EnumServiceInstances( NULL, NULL,
|
|
I_StopInstanceEndpoint)
|
|
);
|
|
|
|
//
|
|
// 3. Cleanup partially initialized modules
|
|
//
|
|
|
|
DWORD err1 = ( *pfnCleanup)( this);
|
|
|
|
|
|
// calls MB.Save so that next MB.Save will be fast
|
|
// and will not cause delay during shutdown
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,"[IIS_SERVICE::StartServiceOperation] Pre-Saving Metabase\n" ));
|
|
|
|
MB mb( (IMDCOM*) IIS_SERVICE::QueryMDObject() );
|
|
mb.Save();
|
|
}
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"Cleanup done\n"));
|
|
|
|
if ( err1 != NO_ERROR )
|
|
{
|
|
if ( err1 != ERROR_IO_PENDING )
|
|
{
|
|
//
|
|
// Compound errors possible
|
|
//
|
|
|
|
if ( err != NO_ERROR) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Error %d occured during cleanup of service %s\n",
|
|
err1, QueryServiceName()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( err1 == ERROR_IO_PENDING )
|
|
{
|
|
//
|
|
// Shutdown is not complete yet. Wait for it to complete
|
|
//
|
|
|
|
WaitForSingleObject( m_hPendingShutdownEvent, INFINITE );
|
|
}
|
|
|
|
//
|
|
// 4. If present, remove the scheduled work item
|
|
//
|
|
if ( m_dwShutdownScheduleId != 0) {
|
|
RemoveWorkItem( m_dwShutdownScheduleId);
|
|
m_dwShutdownScheduleId = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we managed to actually connect to the Service Controller,
|
|
// then tell it that we're stopped.
|
|
//
|
|
|
|
if( m_hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) {
|
|
UpdateServiceStatus( SERVICE_STOPPED,
|
|
err,
|
|
0,
|
|
0 );
|
|
}
|
|
|
|
return ( err);
|
|
|
|
} // IIS_SERVICE::StartServiceOperation()
|
|
|
|
VOID
|
|
IIS_SERVICE::IndicateShutdownComplete(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Used by services which return ERROR_IO_PENDING in their TerminateService
|
|
routines. In this case, they should use this method to indicate
|
|
that shutdown is complete.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( m_hPendingShutdownEvent )
|
|
{
|
|
SetEvent( m_hPendingShutdownEvent );
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::UpdateServiceStatus(
|
|
IN DWORD dwState,
|
|
IN DWORD dwWin32ExitCode,
|
|
IN DWORD dwCheckPoint,
|
|
IN DWORD dwWaitHint
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Updates the local copy status of service controller status
|
|
and reports it to the service controller.
|
|
|
|
Arguments:
|
|
|
|
dwState - New service state.
|
|
|
|
dwWin32ExitCode - Service exit code.
|
|
|
|
dwCheckPoint - Check point for lengthy state transitions.
|
|
|
|
dwWaitHint - Wait hint for lengthy state transitions.
|
|
|
|
Returns:
|
|
|
|
NO_ERROR on success and returns Win32 error if failure.
|
|
On success the status is reported to service controller.
|
|
|
|
--*/
|
|
{
|
|
m_svcStatus.dwCurrentState = dwState;
|
|
m_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
|
|
m_svcStatus.dwCheckPoint = dwCheckPoint;
|
|
m_svcStatus.dwWaitHint = dwWaitHint;
|
|
|
|
return ReportServiceStatus();
|
|
|
|
} // IIS_SERVICE::UpdateServiceStatus()
|
|
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::ReportServiceStatus( VOID)
|
|
/*++
|
|
Description:
|
|
|
|
Wraps the call to SetServiceStatus() function.
|
|
Prints the service status data if need be
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
NO_ERROR if successful. other Win32 error code on failure.
|
|
If successfull the new status has been reported to the service
|
|
controller.
|
|
--*/
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
|
|
IF_DEBUG( DLL_SERVICE_INFO) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwServiceType = %08lX\n",
|
|
m_svcStatus.dwServiceType ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwCurrentState = %08lX\n",
|
|
m_svcStatus.dwCurrentState ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwControlsAccepted = %08lX\n",
|
|
m_svcStatus.dwControlsAccepted ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwWin32ExitCode = %08lX\n",
|
|
m_svcStatus.dwWin32ExitCode ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwServiceSpecificExitCode = %08lX\n",
|
|
m_svcStatus.dwServiceSpecificExitCode ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwCheckPoint = %08lX\n",
|
|
m_svcStatus.dwCheckPoint ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "dwWaitHint = %08lX\n",
|
|
m_svcStatus.dwWaitHint ));
|
|
}
|
|
|
|
if ( !g_fIgnoreSC ) {
|
|
|
|
IF_DEBUG(DLL_SERVICE_INFO) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" Setting Service Status for %s to %d\n",
|
|
QueryServiceName(), m_svcStatus.dwCurrentState)
|
|
);
|
|
}
|
|
|
|
if( !SetServiceStatus( m_hsvcStatus, &m_svcStatus ) ) {
|
|
|
|
err = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
|
|
err = NO_ERROR;
|
|
}
|
|
|
|
return err;
|
|
} // IIS_SERVICE::ReportServiceStatus()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::ServiceCtrlHandler (
|
|
IN DWORD dwOpCode
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function received control requests from the service controller.
|
|
It runs in the context of service controller's dispatcher thread and
|
|
performs the requested function.
|
|
( Note: Avoid time consuming operations in this function.)
|
|
|
|
Arguments:
|
|
|
|
dwOpCode
|
|
indicates the requested operation. This should be
|
|
one of the SERVICE_CONTROL_* manifests.
|
|
|
|
|
|
Returns:
|
|
None. If successful, then the state of the service might be changed.
|
|
|
|
Note:
|
|
if an operation ( especially SERVICE_CONTROL_STOP) is very lengthy,
|
|
then this routine should report a STOP_PENDING status and create
|
|
a worker thread to do the dirty work. The worker thread would then
|
|
perform the necessary work and for reporting timely wait hints and
|
|
final SERVICE_STOPPED status.
|
|
|
|
History:
|
|
KeithMo 07-March-1993 Created
|
|
MuraliK 15-Nov-1994 Generalized it for all services.
|
|
--*/
|
|
{
|
|
//
|
|
// Interpret the opcode and let the worker functions update the state.
|
|
// Also let the workers to update service state as appropriate
|
|
//
|
|
|
|
switch( dwOpCode ) {
|
|
|
|
case SERVICE_CONTROL_INTERROGATE :
|
|
InterrogateService();
|
|
break;
|
|
|
|
case SERVICE_CONTROL_STOP :
|
|
case SERVICE_CONTROL_SHUTDOWN :
|
|
StopService();
|
|
break;
|
|
|
|
case SERVICE_CONTROL_PAUSE :
|
|
PauseService();
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE :
|
|
ContinueService();
|
|
break;
|
|
|
|
|
|
default :
|
|
DBGPRINTF(( DBG_CONTEXT, "Unrecognized Service Opcode %lu\n",
|
|
dwOpCode ));
|
|
break;
|
|
}
|
|
|
|
return;
|
|
} // IIS_SERVICE::ServiceCtrlHandler()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::InterrogateService( VOID )
|
|
/*++
|
|
Description:
|
|
|
|
This function interrogates with the service status.
|
|
Actually, nothing needs to be done here; the
|
|
status is always updated after a service control.
|
|
We have this function here to provide useful
|
|
debug info.
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
MuraliK 15-Nov-1994 Ported to Tcpsvcs.dll
|
|
--*/
|
|
{
|
|
IF_DEBUG( DLL_SERVICE_INFO) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "Interrogating service status for %s\n",
|
|
QueryServiceName())
|
|
);
|
|
}
|
|
|
|
ReportServiceStatus();
|
|
|
|
return;
|
|
|
|
} // IIS_SERVICE::InterrogateService()
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::PauseService( VOID )
|
|
/*++
|
|
Description:
|
|
|
|
This function pauses the service. When the service is paused,
|
|
no new user sessions are to be accepted, but existing connections
|
|
are not effected.
|
|
|
|
This function must update the SERVICE_STATUS::dwCurrentState
|
|
field before returning.
|
|
|
|
Returns:
|
|
|
|
None. If successful the service is paused.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
IIS_SERVER_INSTANCE *instance;
|
|
DWORD status;
|
|
|
|
IF_DEBUG( DLL_SERVICE_INFO) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "pausing service %s\n",
|
|
QueryServiceName())
|
|
);
|
|
}
|
|
|
|
//
|
|
// Scan all installed instances. For each instance, save its current
|
|
// state (so we can retrieve it in ContinueService()) and, if the
|
|
// current state is "started", then pause the instance.
|
|
//
|
|
|
|
AcquireServiceLock( TRUE );
|
|
|
|
for( entry = m_InstanceListHead.Flink ;
|
|
entry != &m_InstanceListHead ;
|
|
entry = entry->Flink ) {
|
|
|
|
instance = CONTAINING_RECORD(
|
|
entry,
|
|
IIS_SERVER_INSTANCE,
|
|
m_InstanceListEntry
|
|
);
|
|
|
|
instance->SaveServerState();
|
|
|
|
if( instance->QueryServerState() == MD_SERVER_STATE_STARTED ) {
|
|
status = instance->PauseInstance();
|
|
DBG_ASSERT( status == NO_ERROR );
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseServiceLock( TRUE );
|
|
|
|
//
|
|
// Set the *service* state to paused.
|
|
//
|
|
|
|
m_svcStatus.dwCurrentState = SERVICE_PAUSED;
|
|
ReportServiceStatus();
|
|
|
|
return;
|
|
} // IIS_SERVICE::PauseService()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::ContinueService( VOID )
|
|
/*++
|
|
|
|
Description:
|
|
This function restarts ( continues) a paused service. This
|
|
will return the service to the running state.
|
|
|
|
This function must update the m_svcStatus.dwCurrentState
|
|
field to running mode before returning.
|
|
|
|
Returns:
|
|
None. If successful then the service is running.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
IIS_SERVER_INSTANCE *instance;
|
|
DWORD status;
|
|
|
|
IF_DEBUG( DLL_SERVICE_INFO) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "continuing service %s\n",
|
|
QueryServiceName())
|
|
);
|
|
}
|
|
|
|
//
|
|
// Scan all installed instances. For each instance, if its current
|
|
// state is "paused" and its saved state is "running", then we know
|
|
// the instanced was paused in PauseService(), so continue it.
|
|
//
|
|
|
|
AcquireServiceLock( TRUE );
|
|
|
|
for( entry = m_InstanceListHead.Flink ;
|
|
entry != &m_InstanceListHead ;
|
|
entry = entry->Flink ) {
|
|
|
|
instance = CONTAINING_RECORD(
|
|
entry,
|
|
IIS_SERVER_INSTANCE,
|
|
m_InstanceListEntry
|
|
);
|
|
|
|
if( instance->QueryServerState() == MD_SERVER_STATE_PAUSED &&
|
|
instance->QuerySavedState() == MD_SERVER_STATE_STARTED ) {
|
|
status = instance->ContinueInstance();
|
|
DBG_ASSERT( status == NO_ERROR );
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseServiceLock( TRUE );
|
|
|
|
//
|
|
// Set the *service* state to running.
|
|
//
|
|
|
|
m_svcStatus.dwCurrentState = SERVICE_RUNNING;
|
|
ReportServiceStatus();
|
|
|
|
return;
|
|
} // IIS_SERVICE::ContinueService()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::StopService( VOID )
|
|
/*++
|
|
Description:
|
|
|
|
This function performs the shutdown on a service.
|
|
This is called during system shutdown.
|
|
|
|
This function is time constrained. The service controller gives a
|
|
maximum of 20 seconds for shutdown for all active services.
|
|
Only timely operations should be performed in this function.
|
|
|
|
What we really do in IIS is, this thread sets the Shutdown Event
|
|
inside the IIS_SERVICE structure. The shutdown event will wake
|
|
the intial thread that started this service (see
|
|
IIS_SERVICE::StartServiceOperation()) => some other thread does
|
|
the bulk of cleanup operations.
|
|
|
|
Returns:
|
|
|
|
None. If successful, the service is shutdown.
|
|
--*/
|
|
{
|
|
IF_DEBUG( DLL_SERVICE_INFO) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "shutting down service %s\n",
|
|
QueryServiceName())
|
|
);
|
|
}
|
|
|
|
m_svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
m_svcStatus.dwCheckPoint = 0;
|
|
|
|
//
|
|
// Update state before setting the event to wake up the waiting thread
|
|
//
|
|
|
|
ReportServiceStatus();
|
|
|
|
DBG_REQUIRE( SetEvent( m_hShutdownEvent ));
|
|
|
|
return;
|
|
} // IIS_SERVICE::StopService()
|
|
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::InitializeSockets( VOID )
|
|
/*++
|
|
|
|
Initializes Socket access.
|
|
It is responsible for connecting to WinSock.
|
|
|
|
Returns:
|
|
|
|
NO_ERROR on success
|
|
Otherwise returns a Win32 error code.
|
|
|
|
Limitations:
|
|
This is for a single thread and not mult-thread safe.
|
|
This function should be called after initializing globals.
|
|
|
|
--*/
|
|
{
|
|
|
|
#ifndef ATQ_FORGOT_TO_CALL_WSASTARTUP
|
|
return ( NO_ERROR);
|
|
#else
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
WSADATA wsaData;
|
|
INT serr;
|
|
|
|
//
|
|
// Connect to WinSock
|
|
//
|
|
|
|
serr = WSAStartup( MAKEWORD( 2, 0), & wsaData);
|
|
|
|
if( serr != 0 ) {
|
|
|
|
SetServiceSpecificExitCode( ( DWORD) serr);
|
|
dwError = ( ERROR_SERVICE_SPECIFIC_ERROR);
|
|
m_EventLog.LogEvent( INET_SVC_WINSOCK_INIT_FAILED,
|
|
0,
|
|
(const CHAR **) NULL,
|
|
serr);
|
|
}
|
|
|
|
m_fSocketsInitialized = ( dwError == NO_ERROR);
|
|
|
|
return ( dwError);
|
|
#endif // ATQ_FORGOT_TO_CALL_WSASTARTUP
|
|
} // IIS_SERVICE::InitializeSockets()
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::CleanupSockets( VOID)
|
|
/*++
|
|
|
|
Cleansup the static information of sockets
|
|
|
|
Returns:
|
|
|
|
0 if no errors,
|
|
non-zero error code for any socket errors
|
|
|
|
Limitations:
|
|
This is for a single thread and not mult-thread safe.
|
|
This function should be called after initializing globals.
|
|
|
|
Note:
|
|
This function should be called after shutting down all
|
|
active socket connections.
|
|
|
|
--*/
|
|
{
|
|
#ifndef ATQ_FORGOT_TO_CALL_WSASTARTUP
|
|
return ( NO_ERROR);
|
|
#else
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
if ( m_fSocketsInitialized ) {
|
|
|
|
INT serr = WSACleanup();
|
|
|
|
if ( serr != 0) {
|
|
|
|
SetServiceSpecificExitCode( ( DWORD) serr);
|
|
dwError = ( ERROR_SERVICE_SPECIFIC_ERROR);
|
|
}
|
|
}
|
|
|
|
m_fSocketsInitialized = FALSE;
|
|
|
|
return (dwError);
|
|
#endif // ATQ_FORGOT_TO_CALL_WSASTARTUP
|
|
|
|
} // IIS_SERVICE::CleanupSockets()
|
|
|
|
|
|
|
|
# if 0
|
|
|
|
VOID
|
|
IIS_SERVICE::Print( VOID) const
|
|
{
|
|
IIS_SERVICE::Print();
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Printing IIS_SERVICE object ( %08p) \n"
|
|
" State = %u. SocketsInitFlag = %u\n"
|
|
" ServiceStatusHandle = %08p. ShutDownEvent = %08p\n"
|
|
" MimeMap = %08p\n"
|
|
/* " InitFunction = %08x. CleanupFunction = %08x.\n" */
|
|
,
|
|
this,
|
|
m_state, m_fSocketsInitialized,
|
|
m_hsvcStatus, m_hShutdownEvent,
|
|
m_pMimeMap
|
|
));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" Printing IIS_SERVICE object (%08p)\n"
|
|
" IpcStarted = %u\n"
|
|
" EnableSvcLoc = %u; SvcLocationDone = %u\n"
|
|
" Service Id = %u. Service Name = %s\n"
|
|
" Module handle = %08p. ModuleName = %s\n"
|
|
" Reg Parameters Key = %s\n"
|
|
,
|
|
this,
|
|
m_fIpcStarted,
|
|
m_fEnableSvcLocation, m_fSvcLocationDone,
|
|
m_dwServiceId, m_strServiceName.QueryStr(),
|
|
m_hModule, m_strModuleName.QueryStr(),
|
|
m_strParametersKey.QueryStr()
|
|
));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" Eventlog = %08p\n",
|
|
&m_EventLog
|
|
));
|
|
|
|
return;
|
|
} // IIS_SERVICE::Print()
|
|
|
|
#endif // DBG
|
|
|
|
|
|
|
|
// Former inline functions that make of class static variables
|
|
|
|
BOOL
|
|
IIS_SERVICE::CheckAndReference( )
|
|
{
|
|
AcquireServiceLock( );
|
|
if ( m_state == BlockStateActive ) {
|
|
InterlockedIncrement( &m_reference );
|
|
ReleaseServiceLock( );
|
|
LONG lEntry = SHARED_LOG_REF_COUNT();
|
|
LOCAL_LOG_REF_COUNT();
|
|
IF_DEBUG( INSTANCE )
|
|
DBGPRINTF((DBG_CONTEXT,"IIS_SERVICE ref count %ld (%ld)\n",
|
|
m_reference, lEntry));
|
|
return(TRUE);
|
|
}
|
|
ReleaseServiceLock( );
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::Dereference( )
|
|
{
|
|
LONG lEntry = SHARED_EARLY_LOG_REF_COUNT();
|
|
LOCAL_EARLY_LOG_REF_COUNT();
|
|
|
|
LONG Reference = InterlockedDecrement( &m_reference );
|
|
if ( 0 == Reference) {
|
|
IF_DEBUG( INSTANCE )
|
|
DBGPRINTF((DBG_CONTEXT,"deleting IIS_SERVICE %p (%ld)\n",
|
|
this, lEntry));
|
|
delete this;
|
|
} else {
|
|
IF_DEBUG( INSTANCE )
|
|
DBGPRINTF((DBG_CONTEXT,"IIS_SERVICE deref count %ld (%ld)\n",
|
|
Reference, lEntry));
|
|
}
|
|
}
|
|
|
|
|
|
PISRPC
|
|
IIS_SERVICE::QueryInetInfoRpc( VOID )
|
|
{
|
|
return sm_isrpc;
|
|
}
|
|
|
|
|
|
//
|
|
// Static Functions belonging to IIS_SERVICE class
|
|
//
|
|
|
|
BOOL
|
|
IIS_SERVICE::InitializeServiceInfo( VOID)
|
|
/*++
|
|
Description:
|
|
|
|
This function initializes all necessary local data for IIS_SERVICE class
|
|
|
|
Only the first initialization call does the initialization.
|
|
Others return without any effect.
|
|
|
|
Should be called from the entry function for DLL.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if any failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
if ( !IIS_SERVICE::sm_fInitialized) {
|
|
|
|
//
|
|
// The static data was Not Already initialized
|
|
//
|
|
|
|
#if SERVICE_REF_TRACKING
|
|
sm_pDbgRefTraceLog = CreateRefTraceLog(C_SERVICE_REFTRACES, 0);
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF((DBG_CONTEXT,"IIS_SERVICE RefTraceLog=%p\n",
|
|
sm_pDbgRefTraceLog));
|
|
}
|
|
#endif // SERVICE_REF_TRACKING
|
|
|
|
INITIALIZE_CRITICAL_SECTION( & IIS_SERVICE::sm_csLock);
|
|
InitializeListHead( & IIS_SERVICE::sm_ServiceInfoListHead);
|
|
IIS_SERVICE::sm_fInitialized = TRUE;
|
|
|
|
IIS_SERVER_INSTANCE::Initialize();
|
|
|
|
dwError = ISRPC::Initialize();
|
|
|
|
if ( dwError != NO_ERROR) {
|
|
SetLastError( dwError);
|
|
}
|
|
}
|
|
|
|
return ( dwError == NO_ERROR);
|
|
} // IIS_SERVICE::InitializeServiceInfo()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::CleanupServiceInfo(
|
|
VOID
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Cleanup the data stored and services running.
|
|
This function should be called only after freeing all the
|
|
services running using this DLL.
|
|
This function is called typically when the DLL is unloaded.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS rpcerr;
|
|
|
|
DBG_REQUIRE( ISRPC::Cleanup() == NO_ERROR);
|
|
|
|
//
|
|
// Should we walk down the list of all services and stop them?
|
|
// Are should we expect the caller to have done that? NYI
|
|
//
|
|
|
|
DBG_ASSERT( IsListEmpty(&sm_ServiceInfoListHead) );
|
|
|
|
IIS_SERVER_INSTANCE::Cleanup();
|
|
|
|
//
|
|
// The DLL is going away so make sure all of the threads get terminated
|
|
// here
|
|
//
|
|
|
|
DeleteCriticalSection( & sm_csLock);
|
|
|
|
#if SERVICE_REF_TRACKING
|
|
if (sm_pDbgRefTraceLog != NULL)
|
|
{
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"IIS_SERVICE: Closing RefTraceLog=%p\n",
|
|
sm_pDbgRefTraceLog));
|
|
}
|
|
DestroyRefTraceLog( sm_pDbgRefTraceLog );
|
|
}
|
|
sm_pDbgRefTraceLog = NULL;
|
|
#endif // SERVICE_REF_TRACKING
|
|
|
|
IIS_SERVICE::sm_fInitialized = FALSE;
|
|
|
|
} // IIS_SERVICE::CleanupServiceInfo()
|
|
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::InitializeServiceRpc(
|
|
IN LPCSTR pszServiceName,
|
|
IN RPC_IF_HANDLE hRpcInterface
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Initializes the rpc endpoint for the infocomm service.
|
|
|
|
Arguments:
|
|
pszServiceName - pointer to null-terminated string containing the name
|
|
of the service.
|
|
|
|
hRpcInterface - Handle for RPC interface.
|
|
|
|
Returns:
|
|
Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
PISRPC pIsrpc = NULL;
|
|
|
|
if(TsIsWindows95()) {
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"IPC Win95 : RPC servicing disabled \n"
|
|
));
|
|
|
|
dwError = NO_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
DBG_ASSERT( pszServiceName != NULL);
|
|
DBG_ASSERT( sm_isrpc == NULL );
|
|
|
|
pIsrpc = new ISRPC( pszServiceName);
|
|
|
|
if ( pIsrpc == NULL) {
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// bind over Named pipe only.
|
|
// If needed to bind over TCP, bind with bit flag ISRPC_OVER_TCPIP on.
|
|
//
|
|
|
|
dwError = pIsrpc->AddProtocol( ISRPC_OVER_TCPIP
|
|
| ISRPC_OVER_NP | ISRPC_OVER_LPC
|
|
);
|
|
|
|
if( (dwError == RPC_S_DUPLICATE_ENDPOINT) ||
|
|
(dwError == RPC_S_OK)
|
|
) {
|
|
|
|
dwError = pIsrpc->RegisterInterface(hRpcInterface);
|
|
}
|
|
|
|
if ( dwError != RPC_S_OK ) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Start the RPC listen thread
|
|
//
|
|
|
|
dwError = pIsrpc->StartServer( );
|
|
|
|
exit:
|
|
|
|
if ( dwError != NO_ERROR ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Cannot start RPC Server for %s, error %lu\n",
|
|
pszServiceName, dwError ));
|
|
|
|
delete pIsrpc;
|
|
SetLastError(dwError);
|
|
return(FALSE);
|
|
}
|
|
|
|
sm_isrpc = pIsrpc;
|
|
return(TRUE);
|
|
|
|
} // IIS_SERVICE::InitializeServiceRpc
|
|
|
|
|
|
|
|
|
|
IIS_SERVICE::CleanupServiceRpc(
|
|
VOID
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Cleanup the data stored and services running.
|
|
This function should be called only after freeing all the
|
|
services running using this DLL.
|
|
This function is called typically when the DLL is unloaded.
|
|
|
|
Arguments:
|
|
pszServiceName - pointer to null-terminated string containing the name
|
|
of the service.
|
|
|
|
hRpcInterface - Handle for RPC interface.
|
|
|
|
|
|
Returns:
|
|
Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
if ( sm_isrpc == NULL ) {
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"no isrpc object to cleanup. Returning success\n"));
|
|
return(TRUE);
|
|
}
|
|
|
|
(VOID) sm_isrpc->StopServer( );
|
|
dwError = sm_isrpc->CleanupData();
|
|
|
|
if( dwError != RPC_S_OK ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"ISRPC(%08p) Cleanup returns %lu\n", sm_isrpc, dwError ));
|
|
DBG_ASSERT( !"RpcServerUnregisterIf failure" );
|
|
SetLastError( dwError);
|
|
}
|
|
|
|
delete sm_isrpc;
|
|
sm_isrpc = NULL;
|
|
|
|
return TRUE;
|
|
} // CleanupServiceRpc
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::InitializeMetabaseComObject(
|
|
VOID
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function initializes the metabase object
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if any failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
HANDLE hThreadHandle = NULL;
|
|
DWORD dwTemp;
|
|
BOOL fRet = FALSE;
|
|
|
|
IF_DEBUG(METABASE) {
|
|
DBGPRINTF((DBG_CONTEXT,"Initializing metabase object\n"));
|
|
}
|
|
|
|
fRet = InitMetadataDCom( (PVOID)&IIS_SERVICE::sm_MDObject,
|
|
(PVOID)&IIS_SERVICE::sm_MDNseObject );
|
|
|
|
if ( fRet )
|
|
{
|
|
fRet = InitializeMetabaseSink( sm_MDObject );
|
|
}
|
|
|
|
return(fRet);
|
|
|
|
} // IIS_SERVICE::InitializeMetabaseComObject
|
|
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::CleanupMetabaseComObject(
|
|
VOID
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function initializes the metabase object
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if any failure.
|
|
|
|
--*/
|
|
{
|
|
IF_DEBUG(METABASE) {
|
|
DBGPRINTF((DBG_CONTEXT,"Cleaning up metabase object %p\n",
|
|
IIS_SERVICE::sm_MDObject));
|
|
}
|
|
|
|
TerminateMetabaseSink();
|
|
|
|
if ( IIS_SERVICE::sm_MDObject != NULL ) {
|
|
((IMDCOM*)IIS_SERVICE::sm_MDObject)->ComMDTerminate(TRUE);
|
|
IIS_SERVICE::sm_MDObject = NULL;
|
|
}
|
|
if ( IIS_SERVICE::sm_MDNseObject != NULL ) {
|
|
((IMDCOM*)IIS_SERVICE::sm_MDNseObject)->ComMDTerminate(TRUE);
|
|
((IMDCOM*)IIS_SERVICE::sm_MDNseObject)->Release();
|
|
IIS_SERVICE::sm_MDNseObject = NULL;
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} // IIS_SERVICE::CleanupMetabaseComObject
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::MDChangeNotify(
|
|
DWORD dwMDNumElements,
|
|
MD_CHANGE_OBJECT pcoChangeList[]
|
|
)
|
|
/*++
|
|
|
|
This method handles the metabase change notification for the running
|
|
services. Note that since we're not allowed to reenter the metabase from
|
|
this notification, we do not directly notify the running services here.
|
|
Rather, we capture the state of this notification and queue the request
|
|
to a worker thread.
|
|
|
|
Arguments:
|
|
|
|
hMDHandle - Metabase handle generating the change notification
|
|
dwMDNumElements - Number of change elements in pcoChangeList
|
|
pcoChangeList - Array of paths and ids that have changed
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD totalLength;
|
|
DWORD i;
|
|
LPDWORD nextIdArray;
|
|
LPBYTE prevString;
|
|
PMD_CHANGE_OBJECT mdScan;
|
|
PMD_CHANGE_OBJECT nextObject;
|
|
PDEFERRED_MD_CHANGE pdeferredChange;
|
|
|
|
#if DO_NOTIFICATION_DEFERRED
|
|
//
|
|
// First off, we need to calculate the size of the buffer required
|
|
// to capture the change data. We'll start off with the known
|
|
// fixed-size data.
|
|
//
|
|
|
|
totalLength = sizeof(DEFERRED_MD_CHANGE) +
|
|
( sizeof(MD_CHANGE_OBJECT) * dwMDNumElements );
|
|
|
|
//
|
|
// Now, we'll scan the change list and accumulate the lengths
|
|
// of the metadata paths and the ID arrays.
|
|
//
|
|
|
|
for( i = dwMDNumElements, mdScan = pcoChangeList ;
|
|
i > 0 ;
|
|
i--, mdScan++ ) {
|
|
|
|
totalLength += (DWORD)strlen( (CHAR *)mdScan->pszMDPath ) + 1;
|
|
totalLength += mdScan->dwMDNumDataIDs * sizeof(DWORD);
|
|
|
|
}
|
|
|
|
//
|
|
// Now we can actually allocate the work item.
|
|
//
|
|
|
|
pdeferredChange = (PDEFERRED_MD_CHANGE) TCP_ALLOC( totalLength );
|
|
|
|
if( pdeferredChange == NULL ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"MDChangeNotify: Cannot allocate work item (%lu)\n",
|
|
totalLength
|
|
));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Capture the change information.
|
|
//
|
|
|
|
nextObject = (PMD_CHANGE_OBJECT)( pdeferredChange + 1 );
|
|
prevString = (LPBYTE)pdeferredChange + totalLength;
|
|
nextIdArray = (LPDWORD)( (LPBYTE)nextObject +
|
|
( sizeof(MD_CHANGE_OBJECT) * dwMDNumElements ) );
|
|
|
|
for( i = 0, mdScan = pcoChangeList ;
|
|
i < dwMDNumElements ;
|
|
i++, mdScan++, nextObject++ ) {
|
|
|
|
DWORD cchPath;
|
|
|
|
//
|
|
// Initialize the object.
|
|
//
|
|
|
|
cchPath = (DWORD)strlen( (CHAR *)mdScan->pszMDPath ) + 1;
|
|
prevString -= cchPath;
|
|
|
|
nextObject->pszMDPath = prevString;
|
|
memcpy(
|
|
nextObject->pszMDPath,
|
|
mdScan->pszMDPath,
|
|
cchPath
|
|
);
|
|
|
|
nextObject->dwMDChangeType = mdScan->dwMDChangeType;
|
|
nextObject->dwMDNumDataIDs = mdScan->dwMDNumDataIDs;
|
|
|
|
nextObject->pdwMDDataIDs = nextIdArray;
|
|
memcpy(
|
|
nextObject->pdwMDDataIDs,
|
|
mdScan->pdwMDDataIDs,
|
|
nextObject->dwMDNumDataIDs * sizeof(DWORD)
|
|
);
|
|
|
|
nextIdArray += nextObject->dwMDNumDataIDs;
|
|
|
|
}
|
|
|
|
//
|
|
// Ensure we didn't mess up the buffer.
|
|
//
|
|
|
|
DBG_ASSERT( (LPBYTE)nextIdArray == prevString );
|
|
|
|
//
|
|
// Now, just enqueue the request.
|
|
//
|
|
|
|
pdeferredChange->dwMDNumElements = dwMDNumElements;
|
|
|
|
if( !ScheduleWorkItem( DeferredMDChange,
|
|
pdeferredChange,
|
|
0 ) ) {
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"MDChangeNotify: cannot queue work item\n"
|
|
));
|
|
|
|
TCP_FREE( pdeferredChange );
|
|
}
|
|
|
|
}
|
|
#else
|
|
IIS_SERVICE::DeferredMDChangeNotify( dwMDNumElements,
|
|
pcoChangeList );
|
|
|
|
IIS_SERVICE::DeferredGlobalConfig( dwMDNumElements,
|
|
pcoChangeList );
|
|
#endif
|
|
|
|
} // IIS_SERVICE::MDChangeNotify
|
|
|
|
#if DO_NOTIFICATION_DEFERRED
|
|
VOID
|
|
WINAPI
|
|
DeferredMDChange(
|
|
PVOID pv
|
|
)
|
|
{
|
|
PDEFERRED_MD_CHANGE pdmc = (PDEFERRED_MD_CHANGE) pv;
|
|
|
|
IF_DEBUG(METABASE) {
|
|
DBGPRINTF((DBG_CONTEXT,"DeferredMDChange(%p)\n", pdmc));
|
|
}
|
|
|
|
IIS_SERVICE::DeferredMDChangeNotify( pdmc->dwMDNumElements,
|
|
(PMD_CHANGE_OBJECT)(pdmc + 1) );
|
|
|
|
IIS_SERVICE::DeferredGlobalConfig( pdmc->dwMDNumElements,
|
|
(PMD_CHANGE_OBJECT)(pdmc + 1 ) );
|
|
|
|
TCP_FREE( pdmc );
|
|
}
|
|
#endif
|
|
|
|
VOID
|
|
IIS_SERVICE::DeferredGlobalConfig(
|
|
DWORD dwMDNumElements,
|
|
MD_CHANGE_OBJECT pcoChangeList[]
|
|
)
|
|
/*++
|
|
|
|
Update configuration of options that are above the service name in the
|
|
metabase (global to all services). For example, global Bandwidth
|
|
Throttling
|
|
|
|
Arguments:
|
|
|
|
dwMDNumElements - Number of change elements in pcoChangeList
|
|
pcoChangeList - Array of paths and ids that have changed
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
BOOL fUpdateGlobalConfig = FALSE;
|
|
|
|
for ( i = 0; i < dwMDNumElements; i++ )
|
|
{
|
|
if ( !_stricmp( (CHAR*) pcoChangeList[i].pszMDPath, LM_PREFIX ) )
|
|
{
|
|
fUpdateGlobalConfig = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fUpdateGlobalConfig )
|
|
{
|
|
DWORD dwVal;
|
|
|
|
AcquireGlobalLock();
|
|
|
|
MB mb( (IMDCOM*) IIS_SERVICE::QueryMDObject() );
|
|
|
|
if (!mb.Open("/lm", METADATA_PERMISSION_READ) ||
|
|
!mb.GetDword("", MD_MAX_BANDWIDTH, IIS_MD_UT_SERVER, &dwVal))
|
|
{
|
|
dwVal = INETA_DEF_BANDWIDTH_LEVEL;
|
|
}
|
|
|
|
AtqSetInfo( AtqBandwidthThrottle, (ULONG_PTR)dwVal);
|
|
|
|
if ( mb.GetDword("", MD_MAX_BANDWIDTH_BLOCKED, IIS_MD_UT_SERVER, &dwVal))
|
|
{
|
|
AtqSetInfo( AtqBandwidthThrottleMaxBlocked, (ULONG_PTR)dwVal );
|
|
}
|
|
|
|
ReleaseGlobalLock();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::DeferredMDChangeNotify(
|
|
DWORD dwMDNumElements,
|
|
MD_CHANGE_OBJECT pcoChangeList[]
|
|
)
|
|
/*++
|
|
|
|
This method handles the metabase change notification for the running services
|
|
and notifies the appropriate service. This is a static method, invoked by
|
|
the deferred worker thread.
|
|
|
|
Arguments:
|
|
|
|
dwMDNumElements - Number of change elements in pcoChangeList
|
|
pcoChangeList - Array of paths and ids that have changed
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
|
|
#ifdef _KNFOCOMM
|
|
//
|
|
// Knfocomm sink will only process MB notifications whose path
|
|
// corresponds to services that use knfocomm...
|
|
//
|
|
const CHAR * pszKnfoSvcName;
|
|
DWORD cchKnfoSvcName;
|
|
DWORD i,j;
|
|
BOOL fMatch = FALSE;
|
|
|
|
for( j=0; j < gNumKnfoServices; j++ )
|
|
{
|
|
pszKnfoSvcName = rgKnfoServices[j];
|
|
cchKnfoSvcName = strlen( pszKnfoSvcName );
|
|
|
|
for ( i = 0; i < dwMDNumElements; i++ )
|
|
{
|
|
if ( !_strnicmp( (CHAR *) pcoChangeList[i].pszMDPath + LM_PREFIX_CCH,
|
|
pszKnfoSvcName,
|
|
cchKnfoSvcName ))
|
|
{
|
|
// MB change list contains a path that matches one of the known
|
|
// knfocomm services.
|
|
fMatch = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !fMatch ) {
|
|
// Knfocomm has nothing to do with this notification...
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Walk the list of services and change notifications looking for a match
|
|
//
|
|
|
|
AcquireGlobalLock();
|
|
|
|
for ( pEntry = sm_ServiceInfoListHead.Flink;
|
|
pEntry != &sm_ServiceInfoListHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
const CHAR * pszSvcName;
|
|
DWORD cchSvcName;
|
|
DWORD i;
|
|
IIS_SERVICE * pService = CONTAINING_RECORD( pEntry,
|
|
IIS_SERVICE,
|
|
m_ServiceListEntry );
|
|
|
|
pszSvcName = pService->QueryServiceName();
|
|
cchSvcName = strlen( pszSvcName );
|
|
|
|
for ( i = 0; i < dwMDNumElements; i++ )
|
|
{
|
|
if ( !_strnicmp( (CHAR *) pcoChangeList[i].pszMDPath + LM_PREFIX_CCH,
|
|
pszSvcName,
|
|
cchSvcName ))
|
|
{
|
|
if( pService->CheckAndReference() ) {
|
|
pService->MDChangeNotify( &pcoChangeList[i] );
|
|
pService->Dereference();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseGlobalLock();
|
|
|
|
} // IIS_SERVICE::DeferredMDChangeNotify
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::MDChangeNotify(
|
|
MD_CHANGE_OBJECT * pco
|
|
)
|
|
/*++
|
|
|
|
This method handles the metabase change notification for this server instance
|
|
|
|
Arguments:
|
|
|
|
pco - path and id that has changed
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
LPSTR serviceName;
|
|
DWORD serviceNameLength;
|
|
DWORD instanceId;
|
|
LPSTR instanceIdString;
|
|
LPSTR stringEnd;
|
|
BOOL parentChange;
|
|
BOOL didAddOrDelete;
|
|
DWORD i;
|
|
|
|
//
|
|
// Find the instance ID in the path.
|
|
//
|
|
|
|
serviceName = (LPSTR)QueryServiceName();
|
|
serviceNameLength = (DWORD)strlen( serviceName );
|
|
|
|
DBG_ASSERT( !_strnicmp(
|
|
(CHAR *)pco->pszMDPath,
|
|
LM_PREFIX,
|
|
LM_PREFIX_CCH
|
|
) );
|
|
|
|
DBG_ASSERT( !_strnicmp(
|
|
(CHAR *)pco->pszMDPath + LM_PREFIX_CCH,
|
|
serviceName,
|
|
(size_t)serviceNameLength
|
|
) );
|
|
|
|
instanceIdString = (LPSTR)pco->pszMDPath + LM_PREFIX_CCH + serviceNameLength;
|
|
|
|
//
|
|
// Lock the service before we start mucking with things too much.
|
|
//
|
|
|
|
parentChange = TRUE;
|
|
didAddOrDelete = FALSE;
|
|
|
|
AcquireServiceLock();
|
|
|
|
if( instanceIdString[0] == '/' &&
|
|
instanceIdString[1] != '\0' ) {
|
|
|
|
parentChange = FALSE;
|
|
instanceId = strtoul( instanceIdString + 1, &stringEnd, 10 );
|
|
|
|
//
|
|
// If this is an "instance add" or "instance delete", then Do The
|
|
// Right Thing. Note that strtoul() will set stringEnd to point to
|
|
// the character that "stopped" the conversion. This will point to
|
|
// the string terminator ('\0') if the converted ulong is at the end
|
|
// of the string. In our case, this would indicate the string is of
|
|
// the form:
|
|
//
|
|
// /LM/{service_name}/{instance_id}/
|
|
//
|
|
// Note there are no path components beyond the instance ID. This is
|
|
// our indication that an instance is getting created/deleted.
|
|
//
|
|
|
|
if( ( pco->dwMDChangeType & MD_CHANGE_TYPE_ADD_OBJECT ) &&
|
|
stringEnd[0] == '/' &&
|
|
stringEnd[1] == '\0' ) {
|
|
|
|
didAddOrDelete = TRUE;
|
|
|
|
if( !AddInstanceInfo( instanceId ) ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"MDChangeNotify: cannot add instance %lu, error %lu\n",
|
|
instanceId,
|
|
GetLastError()
|
|
));
|
|
|
|
}
|
|
|
|
} else
|
|
if( ( pco->dwMDChangeType & MD_CHANGE_TYPE_DELETE_OBJECT ) &&
|
|
stringEnd[0] == '/' &&
|
|
stringEnd[1] == '\0' ) {
|
|
|
|
didAddOrDelete = TRUE;
|
|
|
|
if( !DeleteInstanceInfo( instanceId ) ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"MDChangeNotify: cannot delete instance %lu, error %lu\n",
|
|
instanceId,
|
|
GetLastError()
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( !didAddOrDelete ) {
|
|
|
|
//
|
|
// Walk the list of instances and change notifications looking
|
|
// for a match on the metabase path or a path that is above the
|
|
// instance (to make sure any inherited changes are picked up).
|
|
//
|
|
|
|
DWORD pathLength = strlen( (CHAR *)pco->pszMDPath );
|
|
|
|
for ( pEntry = m_InstanceListHead.Flink;
|
|
pEntry != &m_InstanceListHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
IIS_SERVER_INSTANCE * pInstance = CONTAINING_RECORD( pEntry,
|
|
IIS_SERVER_INSTANCE,
|
|
m_InstanceListEntry );
|
|
|
|
if ( parentChange ||
|
|
( pInstance->QueryMDPathLen() <= pathLength &&
|
|
!_strnicmp( (CHAR *) pco->pszMDPath,
|
|
pInstance->QueryMDPath(),
|
|
pInstance->QueryMDPathLen() ) &&
|
|
( pco->pszMDPath[pInstance->QueryMDPathLen()] == '\0' ||
|
|
pco->pszMDPath[pInstance->QueryMDPathLen()] == '/' ) ) )
|
|
{
|
|
pInstance->MDChangeNotify( pco );
|
|
|
|
if ( !parentChange )
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Watch for the downlevel instance changing
|
|
//
|
|
|
|
for ( i = 0; i < pco->dwMDNumDataIDs; i++ )
|
|
{
|
|
switch ( pco->pdwMDDataIDs[i] )
|
|
{
|
|
case MD_DOWNLEVEL_ADMIN_INSTANCE:
|
|
{
|
|
MB mb( (IMDCOM*) QueryMDObject() );
|
|
IIS_SERVER_INSTANCE * pInst;
|
|
|
|
if ( mb.Open( QueryMDPath() ) )
|
|
{
|
|
if ( !mb.GetDword( "",
|
|
MD_DOWNLEVEL_ADMIN_INSTANCE,
|
|
IIS_MD_UT_SERVER,
|
|
&m_dwDownlevelInstance ))
|
|
{
|
|
m_dwDownlevelInstance = 0xffffffff;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mirror the new vroots to the registry
|
|
//
|
|
|
|
if ( pInst = FindIISInstance( m_dwDownlevelInstance ))
|
|
{
|
|
pInst->MDMirrorVirtualRoots();
|
|
}
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
ReleaseServiceLock();
|
|
|
|
} // IIS_SERVICE::MDChangeNotify
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::LoadStr(
|
|
OUT STR & str,
|
|
IN DWORD dwResId,
|
|
IN BOOL fForceEnglish ) const
|
|
/*++
|
|
|
|
This function loads the string, whose resource id is ( dwResId), into
|
|
the string str passed.
|
|
|
|
Arguments:
|
|
str reference to string object into which the string specified
|
|
by resource id is loaded
|
|
dwResId DWORD containing the resource id for string to be loaded.
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is any failure.
|
|
--*/
|
|
{
|
|
BOOL fReturn = FALSE;
|
|
|
|
if ( (dwResId >= WSABASEERR) && (dwResId < WSA_MAX_ERROR) ) {
|
|
|
|
if (( fReturn = str.Resize((sizeof(SOCK_ERROR_STR_A) + 11) *
|
|
sizeof( WCHAR )))) {
|
|
|
|
wsprintfA( str.QueryStr(), SOCK_ERROR_STR_A, dwResId );
|
|
|
|
} // if ( Resize()
|
|
|
|
} else {
|
|
|
|
//
|
|
// Try to load the string from current module or system table
|
|
// depending upon if the Id < STR_RES_ID_BASE.
|
|
// System table contains strings for id's < STR_RES_ID_BASE.
|
|
//
|
|
|
|
if ( dwResId < STR_RES_ID_BASE) {
|
|
|
|
// Use English strings for System table
|
|
fReturn = str.LoadString( dwResId, (LPCTSTR ) NULL,
|
|
( m_fIsDBCS && fForceEnglish ) ? 0x409 : 0);
|
|
|
|
} else {
|
|
|
|
fReturn = str.LoadString( dwResId, m_hModule );
|
|
}
|
|
}
|
|
|
|
if ( !fReturn ) {
|
|
DBGPRINTF((DBG_CONTEXT,"Error %d in load string[%d]\n",
|
|
GetLastError(), dwResId ));
|
|
}
|
|
|
|
return ( fReturn);
|
|
|
|
} // IIS_SERVICE::LoadStr()
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::InitializeDiscovery(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Register this server and service with service discoverer.
|
|
It will discover us using these information for administering us.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
Win32 Error Code;
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
PISRPC pIsrpc;
|
|
|
|
//
|
|
// Only enable on server as we don't have remove admin on
|
|
// the PWS. -jra !!! of course, we could change our minds again.
|
|
//
|
|
|
|
if ( g_hSvcLocDll == NULL ) {
|
|
m_fEnableSvcLocation = FALSE;
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
INET_BINDINGS TotalBindings = { 0, NULL};
|
|
HKEY hkey = NULL;
|
|
|
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
QueryRegParamKey(),
|
|
0,
|
|
KEY_READ,
|
|
&hkey );
|
|
|
|
if ( dwError != NO_ERROR )
|
|
{
|
|
IF_DEBUG( ERROR) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"IIS_SERVICE::InitializeDiscovery() "
|
|
" RegOpenKeyEx returned error %d\n",
|
|
dwError ));
|
|
}
|
|
|
|
return (dwError);
|
|
}
|
|
|
|
m_fEnableSvcLocation = !!ReadRegistryDword( hkey,
|
|
INETA_ENABLE_SVC_LOCATION,
|
|
INETA_DEF_ENABLE_SVC_LOCATION);
|
|
|
|
if ( hkey != NULL) {
|
|
|
|
RegCloseKey( hkey);
|
|
}
|
|
|
|
if ( !m_fEnableSvcLocation ) {
|
|
|
|
//
|
|
// Service Location is not enabled (by admin presumably).
|
|
// So Let us not register ourselves now.
|
|
//
|
|
return ( NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// Form the global binding information
|
|
//
|
|
|
|
pIsrpc = QueryInetInfoRpc( );
|
|
dwError = pIsrpc->EnumBindingStrings( &TotalBindings);
|
|
|
|
if ( dwError == NO_ERROR) {
|
|
|
|
dwError = pfnInetRegisterSvc(
|
|
m_SvcLocId,
|
|
INetServiceRunning,
|
|
m_strServiceComment.QueryStr(),
|
|
&TotalBindings
|
|
);
|
|
|
|
IF_DEBUG( DLL_RPC) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"INetRegisterService( %u), Running, returns %u\n",
|
|
QueryServiceId(),
|
|
dwError));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Log the error then ignore it as it only affects service discovery
|
|
//
|
|
|
|
if ( dwError != NO_ERROR ) {
|
|
|
|
m_EventLog.LogEvent( INET_SVC_SERVICE_REG_FAILED,
|
|
0,
|
|
(const CHAR **) NULL,
|
|
dwError );
|
|
|
|
dwError = NO_ERROR; // Ignore the error .....
|
|
} else {
|
|
|
|
m_fSvcLocationDone = TRUE;
|
|
}
|
|
|
|
pIsrpc->FreeBindingStrings( &TotalBindings);
|
|
|
|
return( dwError);
|
|
|
|
} // IIS_SERVICE::InitializeDiscovery()
|
|
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::TerminateDiscovery(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
//
|
|
// Deregister the service from the Discovery Service. This will
|
|
// prevent admins from picking up our machine for administration.
|
|
//
|
|
|
|
if ( m_fEnableSvcLocation && m_fSvcLocationDone) {
|
|
|
|
dwError = pfnInetDeregisterSvc(m_SvcLocId);
|
|
|
|
DBG_ASSERT( dwError == NO_ERROR);
|
|
m_fSvcLocationDone = FALSE;
|
|
}
|
|
|
|
return( dwError);
|
|
|
|
} // IIS_SERVICE::TerminateDiscovery()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::DestroyAllServerInstances(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Destroys all server instanes of this service.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
|
|
PLIST_ENTRY listEntry;
|
|
PIIS_SERVER_INSTANCE pInstance;
|
|
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF(( DBG_CONTEXT, "DestroyAllServerInstances called\n"));
|
|
}
|
|
|
|
//
|
|
// Loop and delete all instances
|
|
//
|
|
|
|
AcquireServiceLock( );
|
|
|
|
while ( !IsListEmpty(&m_InstanceListHead) ) {
|
|
|
|
listEntry = RemoveHeadList( &m_InstanceListHead );
|
|
m_nInstance--;
|
|
ReleaseServiceLock( );
|
|
|
|
pInstance = CONTAINING_RECORD(
|
|
listEntry,
|
|
IIS_SERVER_INSTANCE,
|
|
m_InstanceListEntry
|
|
);
|
|
|
|
//
|
|
// Close and dereference the instance.
|
|
//
|
|
|
|
pInstance->CloseInstance();
|
|
pInstance->Dereference();
|
|
|
|
AcquireServiceLock( );
|
|
|
|
}
|
|
|
|
ReleaseServiceLock( );
|
|
|
|
} // IIS_SERVICE::DestroyAllServerInstances
|
|
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::EnumServiceInstances(
|
|
PVOID pvContext,
|
|
PVOID pvContext2,
|
|
PFN_INSTANCE_ENUM pfnEnum
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Enumerates all instances on this service
|
|
|
|
Arguments:
|
|
|
|
pvContext - Context to pass back to the caller
|
|
pvContext2 - 2nd context to pass back to the caller
|
|
pfnEnum - Callback to make for each instance
|
|
|
|
Returns:
|
|
|
|
TRUE if no errors were returned, FALSE if a callback returned
|
|
an error
|
|
--*/
|
|
{
|
|
|
|
PLIST_ENTRY listEntry;
|
|
PIIS_SERVER_INSTANCE pInstance;
|
|
BOOL fRet = TRUE;
|
|
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF(( DBG_CONTEXT, "EnumServiceInstances called\n"));
|
|
}
|
|
|
|
//
|
|
// Loop and delete all instances
|
|
//
|
|
|
|
AcquireServiceLock( TRUE );
|
|
|
|
for ( listEntry = m_InstanceListHead.Flink;
|
|
listEntry != &m_InstanceListHead;
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
pInstance = CONTAINING_RECORD(
|
|
listEntry,
|
|
IIS_SERVER_INSTANCE,
|
|
m_InstanceListEntry
|
|
);
|
|
|
|
if ( !(fRet = pfnEnum( pvContext,
|
|
pvContext2,
|
|
pInstance )))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseServiceLock( TRUE );
|
|
return fRet;
|
|
|
|
} // IIS_SERVICE::EnumServerInstances
|
|
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::CloseService(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
This function cleans up the service object.
|
|
|
|
Returns:
|
|
None.
|
|
--*/
|
|
{
|
|
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"IIS_SERVICE:Destroy service object %p ref %d\n",this,m_reference));
|
|
}
|
|
|
|
DBG_ASSERT(m_state != BlockStateActive);
|
|
|
|
DestroyAllServerInstances( );
|
|
|
|
//
|
|
// We can't return from this function before the refcount hits zero, because
|
|
// after we return TerminateGlobals will destroy structures that the other
|
|
// threads might need while cleaning up. To prevent this we busy wait here
|
|
// until the reference count reaches 1.
|
|
//
|
|
|
|
#if DBG
|
|
int cRetries = 0;
|
|
const int nDelay = 1000;
|
|
#else
|
|
const int nDelay = 200;
|
|
#endif
|
|
|
|
while ( m_reference > 1 )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"IIS_SERVICE:Destroy service object %p ref %d\n",this,m_reference ));
|
|
|
|
#if DBG
|
|
++cRetries;
|
|
#endif
|
|
|
|
Sleep(nDelay);
|
|
}
|
|
|
|
Dereference( );
|
|
|
|
} // IIS_SERVICE::CloseService
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::AddServerInstance(
|
|
IN PIIS_SERVER_INSTANCE pInstance
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
References a new instane of this service
|
|
|
|
Arguments:
|
|
|
|
pInstance - instance to link.
|
|
|
|
Returns:
|
|
|
|
NO_ERROR on success and Win32 error code if any failure.
|
|
--*/
|
|
{
|
|
DWORD err;
|
|
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF((DBG_CONTEXT,"AddServerInstance called with %p\n",
|
|
pInstance ));
|
|
}
|
|
|
|
//
|
|
// Insert instance into service list
|
|
//
|
|
|
|
AcquireServiceLock( );
|
|
|
|
if ( !IsActive() ) {
|
|
err = ERROR_NOT_READY;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// reference the instance since we now have a link to it.
|
|
//
|
|
|
|
pInstance->Reference();
|
|
|
|
InsertTailList( &m_InstanceListHead, &pInstance->m_InstanceListEntry );
|
|
m_nInstance++;
|
|
ReleaseServiceLock( );
|
|
return(TRUE);
|
|
|
|
error_exit:
|
|
ReleaseServiceLock( );
|
|
SetLastError(err);
|
|
return(FALSE);
|
|
|
|
} // AddServerInstance
|
|
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::RemoveServerInstance(
|
|
IN PIIS_SERVER_INSTANCE pInstance
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
References a new instane of this service
|
|
|
|
Arguments:
|
|
|
|
pInstance - instance to link.
|
|
|
|
Returns:
|
|
|
|
NO_ERROR on success and Win32 error code if any failure.
|
|
--*/
|
|
{
|
|
|
|
IF_DEBUG( INSTANCE ) {
|
|
DBGPRINTF((DBG_CONTEXT,"RemoveServerInstance called with %p\n",
|
|
pInstance ));
|
|
}
|
|
|
|
//
|
|
// Remove instance from service list
|
|
//
|
|
|
|
AcquireServiceLock( );
|
|
RemoveEntryList( &pInstance->m_InstanceListEntry );
|
|
m_nInstance--;
|
|
ReleaseServiceLock( );
|
|
|
|
pInstance->Dereference( );
|
|
|
|
return(TRUE);
|
|
|
|
} // RemoveServerInstance
|
|
|
|
|
|
IIS_ENDPOINT *
|
|
IIS_SERVICE::FindAndReferenceEndpoint(
|
|
IN USHORT Port,
|
|
IN DWORD IpAddress,
|
|
IN BOOL CreateIfNotFound,
|
|
IN BOOL IsSecure,
|
|
IN BOOL fDisableSocketPooling
|
|
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Searches the service's endpoint list looking for one bound to
|
|
the specified port.
|
|
|
|
Arguments:
|
|
|
|
Port - The port to search for.
|
|
|
|
IpAddress - The IP Address to search for.
|
|
|
|
CreateIfNotFound - If TRUE, and the port cannot be found in
|
|
the endpoint list, then a new endpoint is created and
|
|
attached to the list.
|
|
|
|
IsSecure - TRUE for secure ports. Only used when a new
|
|
endpoint is created.
|
|
|
|
fDisableSocketPooling - Used only if CreateIfNotFound is TRUE.
|
|
If TRUE, create an endpoint qualified by both Port & IP.
|
|
Else create an endpoint qualified only by Port.
|
|
|
|
Returns:
|
|
|
|
IIS_ENDPOINT * - Pointer to the endpoint if successful, NULL
|
|
otherwise. If !NULL, then the endpoint is referenced and it
|
|
is the caller's responsibility to dereference the endpoint
|
|
at a later time.
|
|
|
|
--*/
|
|
{
|
|
|
|
PLIST_ENTRY listEntry;
|
|
PIIS_ENDPOINT endpoint = NULL;
|
|
DWORD searchIpAddress = IpAddress;
|
|
|
|
//
|
|
// Walk the list looking for a matching port. Note that the endpoints
|
|
// are stored in ascending port order.
|
|
//
|
|
// Initially we search for an endpoint that is qualified by both IpAddress
|
|
// and Port.
|
|
|
|
AcquireServiceLock();
|
|
|
|
SearchEndpointList:
|
|
|
|
for( listEntry = m_EndpointListHead.Flink ;
|
|
listEntry != &m_EndpointListHead ;
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
endpoint = CONTAINING_RECORD(
|
|
listEntry,
|
|
IIS_ENDPOINT,
|
|
m_EndpointListEntry
|
|
);
|
|
|
|
if( endpoint->m_Port > Port ) {
|
|
break;
|
|
}
|
|
|
|
if( endpoint->m_Port == Port &&
|
|
endpoint->m_IpAddress == searchIpAddress
|
|
)
|
|
{
|
|
endpoint->Reference();
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The search failed. If this was a search qualified by IpAddress,
|
|
// we need to re-search using INADDR_ANY as the IP Address
|
|
//
|
|
|
|
if (INADDR_ANY != searchIpAddress)
|
|
{
|
|
searchIpAddress = INADDR_ANY;
|
|
goto SearchEndpointList;
|
|
}
|
|
|
|
//
|
|
// The port is not in the list. Create a new endpoint if required.
|
|
//
|
|
|
|
if( CreateIfNotFound ) {
|
|
|
|
//
|
|
// CODEWORK: It may be necessary in the future to move this
|
|
// endpoint creation to a virtual method so that classes derived
|
|
// from IIS_SERVICE can create specific types of endpoints.
|
|
//
|
|
|
|
endpoint = new IIS_ENDPOINT(
|
|
this,
|
|
Port,
|
|
fDisableSocketPooling ? IpAddress : INADDR_ANY,
|
|
IsSecure
|
|
);
|
|
|
|
if( endpoint != NULL ) {
|
|
|
|
//
|
|
// Insert it into the list.
|
|
//
|
|
|
|
listEntry = listEntry->Blink;
|
|
InsertHeadList(
|
|
listEntry,
|
|
&endpoint->m_EndpointListEntry
|
|
);
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we made it this far, then we could not find the endpoint and
|
|
// either could not create a new one OR were not asked to create one.
|
|
//
|
|
|
|
endpoint = NULL;
|
|
|
|
done:
|
|
|
|
ReleaseServiceLock();
|
|
return endpoint;
|
|
|
|
} // IIS_SERVICE::FindAndReferenceEndpoint
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::AddInstanceInfoHelper(
|
|
IN IIS_SERVER_INSTANCE * pInstance
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Helper routine called by the service-specific AddInstanceInfo()
|
|
virtual routine. This helper just commonizes some startup code
|
|
that all services need to do.
|
|
|
|
Arguments:
|
|
|
|
pInstance - The instance to associate with the service.
|
|
|
|
Returns:
|
|
|
|
BOOL - TRUE if successful, FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
If this routine returns FALSE, then the instance object passed in
|
|
is properly destroyed and extended error information is available
|
|
via GetLastError().
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD status;
|
|
|
|
if( pInstance == NULL ) {
|
|
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"AddInstanceInfoHelper: cannot create new instance, error %lu\n",
|
|
status
|
|
));
|
|
|
|
//
|
|
// The memory allocation failed, so we've nothing to delete.
|
|
//
|
|
|
|
} else
|
|
if( pInstance->QueryServerState() == MD_SERVER_STATE_INVALID ) {
|
|
|
|
//
|
|
// Unfortunately, I don't think we can depend on "last error"
|
|
// getting set correctly on a constructor failure, so we'll
|
|
// just kludge up an error code.
|
|
//
|
|
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"AddInstanceInfoHelper: constructor failed, error %lu\n",
|
|
status
|
|
));
|
|
|
|
//
|
|
// The constructor failed. The instance may or may not be on
|
|
// the service's instance list. If the base constructor failed,
|
|
// then the instance is NOT on the list. If the derived constructor
|
|
// failed, then the instance IS on the list.
|
|
//
|
|
// CleanupAfterConstructorFailure() will Do The Right Thing
|
|
// to properly destroy the partially constructed instance.
|
|
//
|
|
|
|
pInstance->CleanupAfterConstructorFailure();
|
|
|
|
} else
|
|
if( pInstance->IsAutoStart() && !AssociateInstance( pInstance ) ) {
|
|
|
|
status = GetLastError();
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"AddInstanceInfoHelper: cannot associate instance, error %lu\n",
|
|
status
|
|
));
|
|
|
|
//
|
|
// The constructor succeeded, but the instance could not be
|
|
// associated with the service. The reference count should be
|
|
// exactly one. We can't just delete the object as the destructor
|
|
// will assert because the reference count is non-zero, so we'll
|
|
// call RemoveServerInstance(), which will remove the instance from
|
|
// the service's list and then dereference the instance.
|
|
//
|
|
|
|
RemoveServerInstance( pInstance );
|
|
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
|
|
SetLastError( status );
|
|
return FALSE;
|
|
|
|
} // IIS_SERVICE::AddInstanceInfoHelper
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::IsService()
|
|
{
|
|
return !g_fIgnoreSC;
|
|
}
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::AdvertiseServiceInformationInMB(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
MB mb( (IMDCOM*) QueryMDObject() );
|
|
CHAR szServiceKey[MAX_PATH+1];
|
|
DWORD capabilities = 0;
|
|
DWORD version = 0;
|
|
DWORD productType = 0;
|
|
HKEY hkey;
|
|
|
|
strcpy( szServiceKey, IIS_MD_LOCAL_MACHINE_PATH "/" );
|
|
strcat( szServiceKey, QueryServiceName() );
|
|
|
|
if ( !mb.Open( szServiceKey,
|
|
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[AddCapabilityFlag] Cannot open path %s, error %lu\n",
|
|
szServiceKey, GetLastError() ));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// set version
|
|
//
|
|
|
|
if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
|
|
MD_SERVER_VERSION_MAJOR,
|
|
IIS_MD_UT_SERVER,
|
|
IIS_VERSION_MAJOR,
|
|
0 ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Error %d setting major version %x\n",
|
|
GetLastError(), IIS_VERSION_MAJOR));
|
|
}
|
|
|
|
if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
|
|
MD_SERVER_VERSION_MINOR,
|
|
IIS_MD_UT_SERVER,
|
|
IIS_VERSION_MINOR,
|
|
0 ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Error %d setting minor version %x\n",
|
|
GetLastError(), IIS_VERSION_MINOR));
|
|
}
|
|
|
|
//
|
|
// set platform type
|
|
//
|
|
|
|
switch (IISGetPlatformType()) {
|
|
|
|
case PtNtServer:
|
|
productType = INET_INFO_PRODUCT_NTSERVER;
|
|
capabilities = IIS_CAP1_NTS;
|
|
break;
|
|
case PtNtWorkstation:
|
|
productType = INET_INFO_PRODUCT_NTWKSTA;
|
|
capabilities = IIS_CAP1_NTW;
|
|
break;
|
|
case PtWindows95:
|
|
productType = INET_INFO_PRODUCT_WINDOWS95;
|
|
capabilities = IIS_CAP1_W95;
|
|
break;
|
|
default:
|
|
productType = INET_INFO_PRODUCT_UNKNOWN;
|
|
capabilities = IIS_CAP1_W95;
|
|
}
|
|
|
|
if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
|
|
MD_SERVER_PLATFORM,
|
|
IIS_MD_UT_SERVER,
|
|
productType,
|
|
0 ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Error %d setting platform type %x\n",
|
|
GetLastError(), productType));
|
|
|
|
}
|
|
|
|
//
|
|
// Check to see if FrontPage is installed
|
|
//
|
|
|
|
if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
REG_FP_PATH,
|
|
0,
|
|
KEY_READ,
|
|
&hkey ))
|
|
{
|
|
capabilities |= IIS_CAP1_FP_INSTALLED;
|
|
|
|
DBG_REQUIRE( !RegCloseKey( hkey ));
|
|
}
|
|
|
|
//
|
|
// We also need to determine the IIS_CAP1_DIGEST_SUPPORT and IIS_CAP1_NT_CERTMAP_SUPPORT
|
|
// bits but we don't do it here because Net Api calls take forever resulting in Service Control
|
|
// Manager timeouts. Hence we do that check in the InitializeService method (only for W3C).
|
|
//
|
|
|
|
//
|
|
// Set the capabilities flag
|
|
//
|
|
|
|
if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
|
|
MD_SERVER_CAPABILITIES,
|
|
IIS_MD_UT_SERVER,
|
|
capabilities,
|
|
0 ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Error %d setting capabilities flag %x\n",
|
|
GetLastError(), capabilities));
|
|
|
|
}
|
|
|
|
mb.Close();
|
|
return;
|
|
|
|
} // IIS_SERVICE::AdvertiseServiceInformationInMB
|
|
|
|
|
|
|
|
IUnknown *
|
|
IIS_SERVICE::QueryMDObject(
|
|
VOID
|
|
)
|
|
{
|
|
return IIS_SERVICE::sm_MDObject;
|
|
|
|
} // IIS_SERVICE::QueryMDObject
|
|
|
|
|
|
IUnknown *
|
|
IIS_SERVICE::QueryMDNseObject(
|
|
VOID
|
|
)
|
|
{
|
|
return IIS_SERVICE::sm_MDNseObject;
|
|
|
|
} // IIS_SERVICE::QueryMDObject
|
|
|
|
|
|
DWORD
|
|
InitMetadataDCom(
|
|
PVOID Context,
|
|
PVOID NseContext
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
A dummy thread, used only to create the Metadata DCOM object in
|
|
the right fashion.
|
|
|
|
Arguments:
|
|
Context - Pointer to the global md object pointer
|
|
NseContext - Pointer to the global md NSE object pointer
|
|
|
|
|
|
Returns:
|
|
TRUE if we initialized DCOM properly, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hRes;
|
|
IMDCOM* pcCom;
|
|
IMDCOM* pcNseCom;
|
|
BOOL fRet = FALSE;
|
|
IMDCOM ** pMetaObject = (IMDCOM**)Context;
|
|
IMDCOM ** pNseMetaObject = (IMDCOM**)NseContext;
|
|
|
|
hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
|
|
if (FAILED(hRes) && (hRes != E_INVALIDARG) ) {
|
|
DBGPRINTF((DBG_CONTEXT,"CoInitializeEx failed %x\n", hRes));
|
|
DBG_ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
hRes = CoCreateInstance(
|
|
#ifndef _KETADATA
|
|
GETMDCLSID(!g_fIgnoreSC),
|
|
#else
|
|
GETMDPCLSID(!g_fIgnoreSC),
|
|
#endif
|
|
NULL,
|
|
CLSCTX_SERVER,
|
|
IID_IMDCOM,
|
|
(void**) &pcCom
|
|
);
|
|
|
|
if (!FAILED(hRes)) {
|
|
|
|
hRes = CoCreateInstance(CLSID_NSEPMCOM, NULL, CLSCTX_INPROC_SERVER, IID_NSECOM, (void**) &pcNseCom);
|
|
|
|
if (FAILED(hRes)) {
|
|
|
|
// non-fatal error
|
|
DBGPRINTF((DBG_CONTEXT,"QueryInterface for NSE failed with %x\n", hRes));
|
|
pcNseCom = NULL;
|
|
}
|
|
|
|
hRes = pcCom->ComMDInitialize();
|
|
|
|
if (FAILED(hRes)) {
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"MDInitialize failed with %x\n", hRes));
|
|
pcCom->Release();
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if ( pcNseCom ) {
|
|
hRes = pcNseCom->ComMDInitialize();
|
|
|
|
if (FAILED(hRes)) {
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"NSEMDInitialize failed with %x\n", hRes));
|
|
pcNseCom->Release();
|
|
pcCom->Release();
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
*pMetaObject = pcCom;
|
|
*pNseMetaObject = pcNseCom;
|
|
IF_DEBUG(METABASE) {
|
|
DBGPRINTF((DBG_CONTEXT,"CoCreateInstance returns %x\n", pcCom));
|
|
}
|
|
fRet = TRUE;
|
|
goto exit;
|
|
|
|
} else {
|
|
DBGPRINTF((DBG_CONTEXT,"CoCreateInstance failed with %x\n", hRes));
|
|
}
|
|
|
|
exit:
|
|
|
|
CoUninitialize( );
|
|
return(fRet);
|
|
|
|
} // InitMetadataDCom
|
|
|
|
|
|
BOOL
|
|
IIS_SERVICE::RecordInstanceStart( VOID )
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Records that an instance is starting.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
BOOL - TRUE if it's OK to start the instance, FALSE if it
|
|
must not be started.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG result;
|
|
|
|
result = InterlockedIncrement( &m_nStartedInstances );
|
|
|
|
if( !TsIsNtServer() && result > 1 ) {
|
|
InterlockedDecrement( &m_nStartedInstances );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // IIS_SERVICE::RecordInstanceStart
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::RecordInstanceStop( VOID )
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Records that an instance is stopping.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG result;
|
|
|
|
result = InterlockedDecrement( &m_nStartedInstances );
|
|
DBG_ASSERT( result >= 0 );
|
|
|
|
} // IIS_SERVICE::RecordInstanceStop
|
|
|
|
|
|
BOOL
|
|
I_StopInstanceEndpoint( PVOID pvContext1,
|
|
PVOID pvContext2,
|
|
IIS_SERVER_INSTANCE * pInstance )
|
|
{
|
|
IF_DEBUG( INSTANCE) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"I_StopInstanceEndpoint( %p, %p, %08p)\n",
|
|
pvContext1, pvContext2, pInstance));
|
|
}
|
|
|
|
DBG_ASSERT( NULL == pvContext1);
|
|
DBG_ASSERT( NULL == pvContext2);
|
|
|
|
return ( pInstance->StopEndpoints());
|
|
} // I_StopInstanceEndpoint()
|
|
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
ServiceShutdownIndicator( VOID * pSvcContext)
|
|
{
|
|
IIS_SERVICE * pIisService = (IIS_SERVICE * ) pSvcContext;
|
|
|
|
IF_DEBUG( INSTANCE) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"ServiceShutdownIndicator(%p)\n", pSvcContext));
|
|
}
|
|
|
|
if ( pSvcContext == NULL) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" ServiceShutdownIndicator() called with NULL service\n"));
|
|
}
|
|
|
|
// Do Shutdown processing work ...
|
|
DBG_ASSERT( pIisService->QueryShutdownScheduleId() != 0);
|
|
pIisService->ShutdownScheduleCallback();
|
|
|
|
return;
|
|
|
|
} // ServiceShutdownIndicator()
|
|
|
|
|
|
DWORD
|
|
IIS_SERVICE::ShutdownScheduleCallback(VOID)
|
|
/*++
|
|
Description:
|
|
This function is the periodic callback from scheduler for IIS_SERVICE to
|
|
tell the Service Control Manager that we are shutting down
|
|
and need more time.
|
|
|
|
Problem: IIS_SERVICE shutdown operation takes long time. The time is
|
|
highly dependent on the number of components to be shutdown, number
|
|
of IO operations to be cancelled and cleaned up, etc.
|
|
Service Control Manager(SCM) in NT allows the Service shutdown to
|
|
happen within a specified time limit - usually less than 20 seconds.
|
|
If the shutdown did not happen within this window, NT SCM will report
|
|
shutdown failure.
|
|
|
|
This function will indicate to SCM that we will need more time to shutdown.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
NO_ERROR on success. DWORD on error.
|
|
|
|
--*/
|
|
{
|
|
|
|
m_nShutdownIndicatorCalls++;
|
|
|
|
# define NUM_SHUTDOWN_INDICATOR_CALLS_FOR_ONE_MINUTE \
|
|
( (60 * 1000) / MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL)
|
|
|
|
if ( (m_nShutdownIndicatorCalls %
|
|
NUM_SHUTDOWN_INDICATOR_CALLS_FOR_ONE_MINUTE)
|
|
== 0) {
|
|
|
|
char rgchShutdown[256];
|
|
|
|
//
|
|
// Generate a message telling that shutdown is in progress
|
|
//
|
|
|
|
wsprintf( rgchShutdown,
|
|
"[%d]Service (%s) shutting down for %d minutes ... \n",
|
|
GetCurrentThreadId(),
|
|
QueryServiceName(),
|
|
(m_nShutdownIndicatorCalls /
|
|
NUM_SHUTDOWN_INDICATOR_CALLS_FOR_ONE_MINUTE
|
|
)
|
|
);
|
|
|
|
OutputDebugString( rgchShutdown);
|
|
}
|
|
|
|
DBG_ASSERT( m_dwShutdownScheduleId);
|
|
|
|
//
|
|
// Indicate to the SCM that we are in shutdown
|
|
//
|
|
|
|
DBG_REQUIRE( NO_ERROR ==
|
|
DelayCurrentServiceCtrlOperation( SERVICE_STOP_WAIT_HINT)
|
|
);
|
|
|
|
return (NO_ERROR);
|
|
} // IIS_SERVICE::ShutdownScheduleCallback()
|
|
|
|
|
|
|
|
VOID
|
|
IIS_SERVICE::StartUpIndicateClientActivity(VOID)
|
|
{
|
|
DWORD dwCurrentTime;
|
|
|
|
if ( m_svcStatus.dwCurrentState == SERVICE_START_PENDING)
|
|
{
|
|
m_dwClientStartActivityIndicator++;
|
|
|
|
dwCurrentTime = GetCurrentTimeInSeconds();
|
|
if (dwCurrentTime > m_dwNextSCMUpdateTime)
|
|
{
|
|
m_dwNextSCMUpdateTime = dwCurrentTime + IIS_SERVICE_START_INDICATOR_INTERVAL_SECONDS;
|
|
m_dwStartUpIndicatorCalls++;
|
|
if (m_dwStartUpIndicatorCalls < MAX_NUMBER_OF_START_HINT_REPETITIONS)
|
|
{
|
|
UpdateServiceStatus( SERVICE_START_PENDING,
|
|
NO_ERROR,
|
|
m_dwClientStartActivityIndicator,
|
|
IIS_SERVICE_START_WAIT_HINT_EXTENDED + IIS_SERVICE_START_INDICATOR_INTERVAL);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT," StartUpIndicateClientActivity max startup extension periods exceeded\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|