|
|
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name :
instance.cxx
Abstract:
Defines the functions for TCP services Info class. This module is intended to capture the common scheduler code for the tcp services ( especially internet services) which involves the Service Controller dispatch functions. Also this class provides an interface for common dll of servers.
Author:
Murali R. Krishnan ( MuraliK ) 15-Nov-1994
Project:
Internet Servers Common DLL
--*/
#include "tcpdllp.hxx"
#include <rpc.h>
#include <tsunami.hxx>
#include <iistypes.hxx>
#include <iisbind.hxx>
#include "inetreg.h"
#include "tcpcons.h"
#include "apiutil.h"
#include <imd.h>
#include <mb.hxx>
#include "reftrce2.h"
/************************************************************
* Symbolic Constants ************************************************************/
//
// LOCAL Functions
//
static ULONGLONG InetServiceIdForService( IN DWORD serviceId);
#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))
#if SERVICE_REF_TRACKING
//
// Ref count trace log size
//
#define C_INSTANCE_REFTRACES 4000
#define C_LOCAL_INSTANCE_REFTRACES 40
#endif // SERVICE_REF_TRACKING
//
PTRACE_LOG IIS_SERVER_INSTANCE::sm_pDbgRefTraceLog = NULL;
IIS_SERVER_INSTANCE::IIS_SERVER_INSTANCE( IN PIIS_SERVICE pService, IN DWORD dwInstanceId, IN USHORT sPort, IN LPCSTR lpszRegParamKey, IN LPWSTR lpwszAnonPasswordSecretName, IN LPWSTR lpwszVirtualRootsSecretName, IN BOOL fMigrateVroots ) /*++
Desrcription:
Contructor for IIS_SERVER_INSTANCE class. This constructs a new service info object for the service specified.
Arguments:
pService - pointer to the service object.
dwInstanceId - Instance number of this instance.
sPort - Default port number
lpszRegParamKey fully qualified name of the registry key that contains the common service data for this server
lpszAnonPasswordSecretName The name of the LSA secret the anonymous password is stored under
lpszVirtualRootsSecretName The name of the LSA secret the virtual root passwords are stored under
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_tslock ( ), m_fZapRegKey ( FALSE), m_fDoServerNameCheck ( FALSE), m_reference ( 0), m_pDbgRefTraceLog ( NULL), m_sDefaultPort ( sPort ), m_dwServerState ( MD_SERVER_STATE_STOPPED), m_dwSavedState ( MD_SERVER_STATE_STOPPED), m_Service ( pService), m_instanceId ( dwInstanceId), m_strParametersKey ( lpszRegParamKey), m_cReadLocks ( 0), m_lpwszAnonPasswordSecretName( lpwszAnonPasswordSecretName ), m_lpwszRootPasswordSecretName( lpwszVirtualRootsSecretName ), m_strMDPath ( ), m_strSiteName ( ), m_strMDVirtualRootPath( ), m_dwMaxConnections ( INETA_DEF_MAX_CONNECTIONS), m_dwMaxEndpointConnections( INETA_DEF_MAX_ENDPOINT_CONNECTIONS ), m_dwCurrentConnections( 0), m_dwConnectionTimeout ( INETA_DEF_CONNECTION_TIMEOUT), m_dwServerSize ( INETA_DEF_SERVER_SIZE), m_nAcceptExOutstanding( INETA_DEF_ACCEPTEX_OUTSTANDING), m_AcceptExTimeout ( INETA_DEF_ACCEPTEX_TIMEOUT), m_dwLevelsToScan ( INETA_DEF_LEVELS_TO_SCAN ), m_fAddedToServerInstanceList( FALSE ), m_pBandwidthInfo ( NULL ) {
BOOL fReferenced = FALSE;
DBG_ASSERT( lpszRegParamKey != NULL );
IF_DEBUG(INSTANCE) { DBGPRINTF( ( DBG_CONTEXT,"Creating iis instance %p [%u]. \n", this, dwInstanceId)); }
//
// Limit PWS connections
//
if ( !TsIsNtServer() ) { m_dwMaxConnections = INETA_DEF_MAX_CONNECTIONS_PWS; }
//
// initialize locks
//
INITIALIZE_CRITICAL_SECTION(&m_csLock);
//
// initialize binding support
//
InitializeListHead( &m_NormalBindingListHead ); InitializeListHead( &m_SecureBindingListHead );
#if SERVICE_REF_TRACKING
m_pDbgRefTraceLog = CreateRefTraceLog(C_LOCAL_INSTANCE_REFTRACES, 0); #endif // SERVICE_REF_TRACKING
//
// reference the service
//
if ( !pService->CheckAndReference( )) { goto error_exit; } //
// remember if we referenced the service
//
fReferenced = TRUE;
m_Service = pService;
//
// Set the metabase path
//
if ( QueryInstanceId() == INET_INSTANCE_ROOT ) {
DBG_ASSERT( FALSE );
} else {
CHAR szTemp[64];
wsprintf(szTemp,"/%s/%s/%d", IIS_MD_LOCAL_MACHINE_PATH, pService->QueryServiceName(), QueryInstanceId());
m_strMDPath.Copy(szTemp);
wsprintf(szTemp,"/%s/%s/%d/%s/", IIS_MD_LOCAL_MACHINE_PATH, pService->QueryServiceName(), QueryInstanceId(), IIS_MD_INSTANCE_ROOT );
m_strMDVirtualRootPath.Copy(szTemp);
/* This doesn't do anything.
if ( fMigrateVroots ) { MoveVrootFromRegToMD(); }
*/ }
//
// Initialize the bare minimum parameters needed to start.
//
if ( !RegReadCommonParams( FALSE, FALSE)) { goto error_exit; }
//
// Set a reasonable initial state.
//
SetServerState( MD_SERVER_STATE_STOPPED, NO_ERROR );
//
// link this to the service
//
if ( dwInstanceId != INET_INSTANCE_ROOT ) { if ( !pService->AddServerInstance( this ) ) { DBG_ASSERT(m_reference == 0); goto error_exit; } DBG_ASSERT(m_reference == 1); }
m_fAddedToServerInstanceList = TRUE; return;
error_exit:
if ( fReferenced ) { m_Service->Dereference(); }
m_dwServerState = MD_SERVER_STATE_INVALID; DBG_ASSERT(m_reference == 0); return;
} // IIS_SERVER_INSTANCE::IIS_SERVER_INSTANCE()
IIS_SERVER_INSTANCE::~IIS_SERVER_INSTANCE( VOID) /*++
Description:
Cleanup the instance object. If the service is not already terminated, it terminates the service before cleanup.
Arguments: None
Returns: None
--*/ { DBG_ASSERT(m_dwServerState != MD_SERVER_STATE_STARTED); DBG_ASSERT(m_reference == 0);
//
// If we failed to create this instance or it's getting deleted, remove
// the configuration tree
//
if ( m_fZapRegKey ) { DBGPRINTF((DBG_CONTEXT,"Zapping reg key for %p\n",this)); ZapRegistryKey( NULL, QueryRegParamKey() ); ZapInstanceMBTree( ); }
//
// endpoints should have been dereferenced
//
DBG_ASSERT(IsListEmpty( &m_NormalBindingListHead )); DBG_ASSERT(IsListEmpty( &m_SecureBindingListHead ));
//
// dereference the service
//
if ( m_fAddedToServerInstanceList && m_Service != NULL ) { m_Service->Dereference( ); }
//
// destroy bandwidth throttling descriptor
//
if ( m_pBandwidthInfo != NULL ) { AtqFreeBandwidthInfo( m_pBandwidthInfo ); m_pBandwidthInfo = NULL; }
#if SERVICE_REF_TRACKING
DestroyRefTraceLog( m_pDbgRefTraceLog ); #endif // SERVICE_REF_TRACKING
DeleteCriticalSection(&m_csLock);
} // IIS_SERVER_INSTANCE::~IIS_SERVER_INSTANCE()
//
// Static Functions belonging to IIS_SERVICE class
//
BOOL IIS_SERVER_INSTANCE::Initialize( VOID) /*++
Description:
This function initializes all necessary local data for IIS_SERVER_INSTANCE 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.
--*/ { #if SERVICE_REF_TRACKING
if (sm_pDbgRefTraceLog == NULL) { sm_pDbgRefTraceLog = CreateRefTraceLog(C_INSTANCE_REFTRACES, 0); IF_DEBUG( INSTANCE ) { DBGPRINTF((DBG_CONTEXT,"IIS_SERVER_INSTANCE RefTraceLog=%p\n", sm_pDbgRefTraceLog)); } } #endif // SERVICE_REF_TRACKING
return TRUE; }
VOID IIS_SERVER_INSTANCE::Cleanup( VOID ) /*++
Description:
Cleanup the data stored. 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
--*/ { #if SERVICE_REF_TRACKING
if (sm_pDbgRefTraceLog != NULL) { IF_DEBUG( INSTANCE ) { DBGPRINTF((DBG_CONTEXT, "IIS_SERVER_INSTANCE: Closing RefTraceLog=%p\n", sm_pDbgRefTraceLog)); } DestroyRefTraceLog( sm_pDbgRefTraceLog ); } sm_pDbgRefTraceLog = NULL; #endif // SERVICE_REF_TRACKING
}
# if 0
VOID IIS_SERVER_INSTANCE::Print( VOID) const { IIS_SERVER_INSTANCE::Print();
DBGPRINTF( ( DBG_CONTEXT, " Printing IIS_SERVER_INSTANCE object ( %08p) \n" " State = %u.\n" , this, m_dwServerState ));
DBGPRINTF(( DBG_CONTEXT, " Server Admin Params: \n" " Log Anon = %u. Log NonAnon = %u.\n" , m_fLogAnonymous, m_fLogNonAnonymous ));
DBGPRINTF(( DBG_CONTEXT, " Printing IIS_SERVER_INSTANCE object (%08p)\n" " Readers # = %u.\n" " Reg Parameters Key = %s\n" " MaxConn = %d. ConnTimeout = %u secs.\n" , this, m_cReadLocks, m_strParametersKey.QueryStr(), m_dwMaxConnections, m_dwConnectionTimeout )); return; } // IIS_SERVER_INSTANCE::Print()
#endif // DBG
VOID IIS_SERVER_INSTANCE::Reference( ) { InterlockedIncrement( &m_reference ); LONG lEntry = SHARED_LOG_REF_COUNT(); LOCAL_LOG_REF_COUNT(); IF_DEBUG( INSTANCE ) DBGPRINTF((DBG_CONTEXT, "IIS_SERVER_INSTANCE ref count %ld\n (%ld)", m_reference, lEntry)); }
VOID IIS_SERVER_INSTANCE::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_SERVER_INSTANCE %p (%ld)\n", this, lEntry)); delete this; } else { IF_DEBUG( INSTANCE ) DBGPRINTF((DBG_CONTEXT, "IIS_SERVER_INSTANCE deref count %ld (%ld)\n", Reference, lEntry)); } }
VOID IIS_SERVER_INSTANCE::ZapInstanceMBTree( VOID ) {
MB mb( (IMDCOM*) m_Service->QueryMDObject() );
//
// Do the metabase
//
IF_DEBUG(METABASE) { DBGPRINTF((DBG_CONTEXT,"Deleting metabase node %s\n", QueryMDPath())); }
if ( !mb.Open( "/", METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )) { IF_DEBUG(METABASE) { DBGPRINTF((DBG_CONTEXT,"Open MD instance root %s returns %d\n", "/", GetLastError() )); } return; }
//
// Delete the instance tree
//
if ( !mb.DeleteObject( QueryMDPath() )) { IF_DEBUG(METABASE) { DBGPRINTF((DBG_CONTEXT, "Deleting instance node %s returns %d\n", QueryMDPath(), GetLastError())); } }
return;
} // IIS_SERVER_INSTANCE::ZapInstanceMBTree
DWORD IIS_SERVER_INSTANCE::BindInstance( VOID ) /*++
Routine Description:
Binds an instance to all configured endpoints (normal & secure).
Arguments:
None.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
DWORD err;
//
// Update the "normal" (i.e. non-secure) bindings.
//
err = UpdateNormalBindings();
if( err != NO_ERROR ) {
IF_DEBUG(INSTANCE) { DBGPRINTF((DBG_CONTEXT, "UpdateNormalBindings() failed, %lu\n", err)); }
return err;
}
//
// Update the secure bindings.
//
err = UpdateSecureBindings();
if( err != NO_ERROR ) {
IF_DEBUG(INSTANCE) { DBGPRINTF((DBG_CONTEXT, "UpdateSecureBindings() failed, %lu\n", err)); }
//
// The main port(s) are OK, but the SSL port(s) failed,
// so start anyway.
//
err = NO_ERROR;
}
//
// Success!
//
DBG_ASSERT( err == NO_ERROR ); return NO_ERROR;
} // IIS_SERVER_INSTANCE::BindInstance
DWORD IIS_SERVER_INSTANCE::UnbindInstance( VOID ) /*++
Routine Description:
Removes all bindings from an instance.
Arguments:
None.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
LockThisForWrite(); DBG_REQUIRE( RemoveNormalBindings() == NO_ERROR ); DBG_REQUIRE( RemoveSecureBindings() == NO_ERROR ); UnlockThis();
return NO_ERROR;
} // IIS_SERVER_INSTANCE::UnbindInstance
DWORD IIS_SERVER_INSTANCE::UnbindHelper( IN PLIST_ENTRY BindingListHead ) /*++
Routine Description:
Helper routine for UnbindInstance().
Arguments:
BindingListHead - The binding list to remove.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
PLIST_ENTRY listEntry; PIIS_SERVER_BINDING binding;
//
// Walk the list of bindings and destroy them.
//
while( !IsListEmpty( BindingListHead ) ) {
listEntry = RemoveHeadList( BindingListHead );
binding = CONTAINING_RECORD( listEntry, IIS_SERVER_BINDING, m_BindingListEntry );
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "unbinding %p from %p, binding %p (%lx:%d:%s)\n", binding->QueryEndpoint(), this, binding, binding->QueryIpAddress(), binding->QueryEndpoint()->QueryPort(), binding->QueryHostName() )); }
binding->QueryEndpoint()->RemoveInstance( this, binding->QueryIpAddress(), binding->QueryHostName() );
binding->QueryEndpoint()->Dereference(); delete binding;
}
//
// Success!
//
return NO_ERROR;
} // IIS_SERVER_INSTANCE::UnbindHelper
DWORD IIS_SERVER_INSTANCE::UpdateBindingsHelper( IN BOOL IsSecure ) /*++
Routine Description:
Helper routine for UpdateNormalBindings() and UpdateSecureBindings().
Arguments:
IsSecure - TRUE if we're to update the secure bindings, FALSE for the normal bindings.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
MB mb( (IMDCOM*)m_Service->QueryMDObject() ); MULTISZ msz; DWORD status = NO_ERROR; const CHAR * scan; DWORD ipAddress; USHORT ipPort; const CHAR * hostName; PIIS_SERVER_BINDING binding; LIST_ENTRY createdBindings; PLIST_ENTRY listEntry; PLIST_ENTRY targetBindingListHead; USHORT targetDefaultPort; DWORD targetMetadataId; DWORD numBindings = 0; const CHAR * apszSubStrings[2]; CHAR instanceIdString[sizeof("4294967295")]; DWORD fDisableSocketPooling;
//
// Setup locals.
//
InitializeListHead( &createdBindings );
if( IsSecure ) { targetBindingListHead = &m_SecureBindingListHead; targetDefaultPort = 0; targetMetadataId = MD_SECURE_BINDINGS; } else { targetBindingListHead = &m_NormalBindingListHead; targetDefaultPort = m_sDefaultPort; targetMetadataId = MD_SERVER_BINDINGS; }
//
// Open the metabase and get the current binding list.
//
if( mb.Open( QueryMDPath() ) ) {
if( !mb.GetMultisz( "", targetMetadataId, IIS_MD_UT_SERVER, &msz ) ) {
status = GetLastError();
}
//
// Get socket pooling flag.
//
mb.GetDword( "", MD_DISABLE_SOCKET_POOLING, IIS_MD_UT_SERVER, FALSE, &fDisableSocketPooling ); //
// Close the metabase before continuing, as anyone that needs
// to update the service status will need write access.
//
mb.Close();
} else {
status = GetLastError();
}
//
// Lock the instance.
//
LockThisForWrite();
if ( status == MD_ERROR_DATA_NOT_FOUND ) { //
// if the bindings just don't exist (as happens on service creation)
// don't log an error.
//
goto fatal_nolog; } else if( status != NO_ERROR ) { goto fatal; }
//
// Scan the multisz and look for instances we'll need to create.
//
for( scan = msz.First() ; scan != NULL ; scan = msz.Next( scan ) ) {
//
// Parse the descriptor (in "ip_address:port:host_name" form)
// into its component parts.
//
status = IIS_SERVER_BINDING::ParseDescriptor( scan, &ipAddress, &ipPort, &hostName );
if( status == NO_ERROR ) {
if( IsSecure ) {
//
// Secure bindings cannot key off the hostname, as
// the hostname is encrypted in the header.
//
if( *hostName != '\0' ) {
DBGPRINTF(( DBG_CONTEXT, "Secure bindings cannot have host name! %s\n", scan ));
wsprintfA( instanceIdString, "%lu", QueryInstanceId() );
apszSubStrings[0] = (const CHAR *)instanceIdString; apszSubStrings[1] = (const CHAR *)scan;
m_Service->LogEvent( INET_SVC_INVALID_SECURE_BINDING, 2, apszSubStrings );
//
// Press on regardless, but ignore the hostname.
//
hostName = "";
}
}
//
// See if the descriptor is in our current binding list.
//
if( IsInCurrentBindingList( targetBindingListHead, ipAddress, ipPort, hostName ) ) {
//
// It is, so remember that we have a binding.
//
numBindings++;
} else {
//
// It's not, so we need to create a new binding.
//
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "Adding %lx:%d:%s\n", ipAddress, ipPort, hostName )); }
DBG_CODE( binding = NULL );
status = CreateNewBinding( ipAddress, ipPort, hostName, IsSecure, fDisableSocketPooling, &binding );
if( status == NO_ERROR ) {
//
// Add the new binding to the local list of
// newly created bindings.
//
DBG_ASSERT( binding != NULL );
InsertTailList( &createdBindings, &binding->m_BindingListEntry );
numBindings++;
} else {
//
// Could not create the new binding.
//
// Press on regardless.
//
}
}
} else {
//
// Could not parse the descriptor.
//
DBGPRINTF(( DBG_CONTEXT, "UpdateNormalBindings: could not parse %s, error %lu\n", scan, status ));
wsprintfA( instanceIdString, "%lu", QueryInstanceId() );
apszSubStrings[0] = (const CHAR *)instanceIdString; apszSubStrings[1] = (const CHAR *)scan;
m_Service->LogEvent( INET_SVC_INVALID_BINDING, 2, apszSubStrings );
//
// Press on regardless.
//
}
}
if( status != NO_ERROR ) {
if( numBindings == 0 ) {
//
// All bindings failed, so fail the request.
//
goto fatal;
}
//
// At least one binding succeeded, so succeed the request.
//
status = NO_ERROR;
}
//
// Scan the existing bindings and look for those that need to
// be deleted.
//
listEntry = targetBindingListHead->Flink;
while( listEntry != targetBindingListHead ) {
binding = CONTAINING_RECORD( listEntry, IIS_SERVER_BINDING, m_BindingListEntry );
listEntry = listEntry->Flink;
if( !IsBindingInMultiSz( binding, msz ) ) {
//
// Got one. Remove it from the instance list, dereference
// the corresponding endpoint, then delete the binding.
//
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "zapping %p from %p, binding %p (%lx:%d:%s)\n", binding->QueryEndpoint(), this, binding, binding->QueryIpAddress(), binding->QueryEndpoint()->QueryPort(), binding->QueryHostName() )); }
binding->QueryEndpoint()->RemoveInstance( this, binding->QueryIpAddress(), binding->QueryHostName() );
RemoveEntryList( &binding->m_BindingListEntry );
binding->QueryEndpoint()->Dereference(); delete binding;
}
}
//
// Move the newly created bindings over to the current binding
// list.
//
targetBindingListHead->Blink->Flink = createdBindings.Flink; createdBindings.Flink->Blink = targetBindingListHead->Blink; createdBindings.Blink->Flink = targetBindingListHead; targetBindingListHead->Blink = createdBindings.Blink;
UnlockThis();
DBG_ASSERT( status == NO_ERROR ); return NO_ERROR;
fatal:
//
// An unrecoverable binding error occured. Log an event.
//
DBG_ASSERT( status != NO_ERROR );
wsprintfA( instanceIdString, "%lu", QueryInstanceId() );
apszSubStrings[0] = (const CHAR *)instanceIdString;
//
// OK. Let's avoid a change in the UI by mapping
// ERROR_INVALID_PARAMETER to ERROR_DUP_NAME so that the eventlog
// message is more useful.
//
m_Service->LogEvent( INET_SVC_FATAL_BINDING_ERROR, 1, apszSubStrings, status == ERROR_INVALID_PARAMETER ? ERROR_DUP_NAME : status );
fatal_nolog:
//
// Loop through the local list of newly created bindings and delete them.
//
while( !IsListEmpty( &createdBindings ) ) {
listEntry = RemoveHeadList( &createdBindings );
binding = CONTAINING_RECORD( listEntry, IIS_SERVER_BINDING, m_BindingListEntry );
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "zapping %p from %p, binding %p (%lx:%d:%s) (ERROR)\n", binding->QueryEndpoint(), this, binding, binding->QueryIpAddress(), binding->QueryEndpoint()->QueryPort(), binding->QueryHostName() )); }
binding->QueryEndpoint()->RemoveInstance( this, binding->QueryIpAddress(), binding->QueryHostName() );
binding->QueryEndpoint()->Dereference(); delete binding;
}
UnlockThis(); return status;
} // IIS_SERVER_INSTANCE::UpdateBindingsHelper
DWORD IIS_SERVER_INSTANCE::CreateNewBinding( IN DWORD IpAddress, IN USHORT IpPort, IN const CHAR * HostName, IN BOOL IsSecure, IN BOOL fDisableSocketPooling, OUT IIS_SERVER_BINDING ** NewBinding ) /*++
Routine Description:
Creates a new binding object for the specified ip address, port, and host name, and creates/references the appropriate endpoint object.
Arguments:
IpAddress - The binding IP address. May be INADDR_ANY.
IpPort - The binding IP port. Required.
HostName - The binding host name. May be empty ("").
IsSecure - TRUE for secure endpoints. Only used if a new endpoint is created.
fDisableSocketPooling - TRUE to create unique endpoints based on both port & IP. Only used if a new endpoint is created.
NewBinding - Receives a pointer to the new binding object if successful.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
PIIS_ENDPOINT endpoint; PIIS_SERVER_BINDING binding; DWORD status;
//
// Sanity check.
//
DBG_ASSERT( IpPort != 0 ); DBG_ASSERT( HostName != NULL ); DBG_ASSERT( NewBinding != NULL );
//
// Setup locals so we know how to cleanup on exit.
//
endpoint = NULL; binding = NULL;
//
// Try to find an endpoint for the specified port.
//
endpoint = m_Service->FindAndReferenceEndpoint( IpPort, IpAddress, TRUE, // CreateIfNotFound
IsSecure, fDisableSocketPooling );
if( endpoint != NULL ) {
//
// Create a new binding.
//
binding = new IIS_SERVER_BINDING( IpAddress, IpPort, HostName, endpoint );
if( binding != NULL ) {
if( endpoint->AddInstance( this, IpAddress, HostName ) ) {
endpoint->Reference(); *NewBinding = binding; status = NO_ERROR;
} else {
//
// Could not associate the instance with the endpoint.
//
status = GetLastError(); ASSERT( status != NO_ERROR );
//
// if we didn't get an error code back for some reason
// we choose this one since resource shortages are
// the most likely failure case.
//
if ( NO_ERROR == status ) { status = ERROR_NOT_ENOUGH_MEMORY; }
}
} else {
//
// Could not create new binding object.
//
status = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
//
// Could not find & reference endpoint.
//
status = ERROR_NOT_ENOUGH_MEMORY;
}
//
// Remove the reference added in FindAndReferenceEndpoint().
//
if( endpoint != NULL ) {
endpoint->Dereference();
}
//
// Cleanup if necessary.
//
if( status != NO_ERROR ) {
if( binding != NULL ) {
delete binding;
}
}
return status;
} // IIS_SERVER_INSTANCE::CreateNewBinding
BOOL IIS_SERVER_INSTANCE::IsInCurrentBindingList( IN PLIST_ENTRY BindingListHead, IN DWORD IpAddress OPTIONAL, IN USHORT IpPort, IN const CHAR * HostName OPTIONAL ) /*++
Routine Description:
Scans the current binding list looking for the specified IP address, port, and host name.
Arguments:
BindingListHead - The binding list to scan.
IpAddress - The IP address to search for. May be INADDR_ANY.
IpPort - The IP port to search for. Required.
HostName - The host name to search for. May be empty ("").
Return Value:
BOOL - TRUE if the binding was found, FALSE otherwise.
--*/ {
PLIST_ENTRY listEntry; PIIS_SERVER_BINDING binding;
//
// Sanity check.
//
DBG_ASSERT( IpPort != 0 ); DBG_ASSERT( HostName != NULL );
//
// Scan the bindings.
//
for( listEntry = BindingListHead->Flink ; listEntry != BindingListHead ; listEntry = listEntry->Flink ) {
binding = CONTAINING_RECORD( listEntry, IIS_SERVER_BINDING, m_BindingListEntry );
if( binding->Compare( IpAddress, IpPort, HostName ) ) {
return TRUE;
}
}
return FALSE;
} // IIS_SERVER_INSTANCE::IsInCurrentBindingList
BOOL IIS_SERVER_INSTANCE::IsBindingInMultiSz( IN PIIS_SERVER_BINDING Binding, IN const MULTISZ &msz ) /*++
Routine Description:
Scans the specified MULTISZ object to see if it contains a descriptor matching the specified binding object.
Arguments:
Binding - The binding to search for.
msz - The MULTISZ to search.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
const CHAR * scan; DWORD status; BOOL result;
//
// Sanity check.
//
DBG_ASSERT( Binding != NULL );
//
// Scan the MULTISZ.
//
for( scan = msz.First() ; scan != NULL ; scan = msz.Next( scan ) ) {
status = Binding->Compare( scan, &result );
if( status == NO_ERROR && result ) {
return TRUE;
}
}
return FALSE;
} // IIS_SERVER_INSTANCE::IsBindingInMultiSz
DWORD IIS_SERVER_INSTANCE::PerformClusterModeChange( VOID ) /*++
Routine Description:
Reads the server cluster mode from the metabase and performs any necessary changes.
Arguments:
None.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ { MB mb( (IMDCOM *)m_Service->QueryMDObject() ); DWORD status; DWORD currentState; DWORD serviceState; BOOL fPreviousClusterEnabled;
//
// Setup locals.
//
status = NO_ERROR; fPreviousClusterEnabled = m_fClusterEnabled; serviceState = m_Service->QueryCurrentServiceState(); currentState = QueryServerState();
//
// Open the metabase and query the cluster enabled flag
//
if( mb.Open( QueryMDPath(), METADATA_PERMISSION_READ ) ) { if( !mb.GetDword( "", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, (LPDWORD)&m_fClusterEnabled ) ) { m_fClusterEnabled = FALSE;
status = GetLastError();
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "PerformClusterModeChange: cannot read server command, error %lu\n", status )); }
}
//
// Close it so that code needed to update the metabase when
// changing state can indeed open the metabase.
//
mb.Close(); } else { status = GetLastError();
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "PerformClusterModeChange: cannot open metabase for READ, error %lu\n", status )); }
}
//
// If cluster mode transition from non-cluster to cluster
// then must make sure that instance is stopped.
// instance will be started as required by cluster manager.
//
if ( status == NO_ERROR && m_fClusterEnabled && !fPreviousClusterEnabled ) { if( ( serviceState == SERVICE_RUNNING || serviceState == SERVICE_PAUSED ) && ( currentState == MD_SERVER_STATE_STARTED || currentState == MD_SERVER_STATE_PAUSED ) ) {
LockThisForWrite();
status = StopInstance();
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "PerformClusterModeChange: cannot stop instance, error %lu\n", status ));
}
UnlockThis(); }
//
// Restore the state to the previous value if the state change failed.
//
if( status != NO_ERROR ) { SetServerState( currentState, status ); } }
return status; }
DWORD IIS_SERVER_INSTANCE::PerformStateChange( VOID ) /*++
Routine Description:
Reads the server instance state from the metabase and performs any necessary state changes.
Arguments:
None.
Return Value:
DWORD - Completion status, 0 if successful, !0 otherwise.
--*/ {
MB mb( (IMDCOM *)m_Service->QueryMDObject() ); DWORD status; DWORD command; DWORD currentState; DWORD serviceState;
//
// Setup locals.
//
status = NO_ERROR; serviceState = m_Service->QueryCurrentServiceState(); currentState = QueryServerState();
//
// Open the metabase and query the state change command.
//
if( mb.Open( QueryMDPath(), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) {
if( !mb.GetDword( "", IsClusterEnabled() ? MD_CLUSTER_SERVER_COMMAND : MD_SERVER_COMMAND, IIS_MD_UT_SERVER, &command ) ) {
status = GetLastError();
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot read server command, error %lu\n", status )); }
}
//
// Update the instance AutoStart value so the instance state is
// persisted across service restarts
//
if( status == NO_ERROR ) {
switch( command ) { case MD_SERVER_COMMAND_START :
mb.SetDword( "", MD_SERVER_AUTOSTART, IIS_MD_UT_SERVER, TRUE ); break;
case MD_SERVER_COMMAND_STOP :
mb.SetDword( "", MD_SERVER_AUTOSTART, IIS_MD_UT_SERVER, FALSE ); break;
default: break; } }
//
// Close it so that code needed to update the metabase when
// changing state can indeed open the metabase.
//
mb.Close();
} else {
status = GetLastError();
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot open metabase for READ, error %lu\n", status )); }
}
//
// Lock the instance.
//
LockThisForWrite();
//
// Interpret the command. Note that the StartInstance(), StopInstance(),
// PauseInstance(), and ContinueInstance() methods will set the instance
// state if they complete successfully, but it is this routine's
// responsibility to reset the state to the original value if the
// methods fail.
//
if( status == NO_ERROR ) {
switch( command ) {
case MD_SERVER_COMMAND_START :
//
// Start the instance.
//
// If it's stopped, then start it. If it's in any other state,
// this is an invalid state transition.
//
// Note that the *service* must be running before an instance
// can be started.
//
if( serviceState == SERVICE_RUNNING && currentState == MD_SERVER_STATE_STOPPED ) {
status = DoStartInstance();
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot start instance, error %lu\n", status ));
}
} else {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: invalid command %lu for state %lu\n", command, currentState ));
status = ERROR_INVALID_SERVICE_CONTROL;
} break;
case MD_SERVER_COMMAND_STOP :
//
// Stop the instance.
//
// If it's running or paused, then start it. If it's in any
// other state, this is an invalid state transition.
//
// Note that the *service* must be either running or paused
// before an instance can be paused.
//
if( ( serviceState == SERVICE_RUNNING || serviceState == SERVICE_PAUSED ) && ( currentState == MD_SERVER_STATE_STARTED || currentState == MD_SERVER_STATE_PAUSED ) ) {
status = StopInstance();
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot stop instance, error %lu\n", status ));
}
} else {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: invalid command %lu for state %lu\n", command, currentState ));
status = ERROR_INVALID_SERVICE_CONTROL;
} break;
case MD_SERVER_COMMAND_PAUSE :
//
// Pause the instance.
//
// If it's running, then pause it. If it's in any other state,
// this is an invalid state transition.
//
// Note that the *service* must be running before an instance
// can be paused.
//
if( serviceState == SERVICE_RUNNING && currentState == MD_SERVER_STATE_STARTED ) {
status = PauseInstance();
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot pause instance, error %lu\n", status ));
}
} else {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: invalid command %lu for state %lu\n", command, currentState ));
status = ERROR_INVALID_SERVICE_CONTROL;
} break;
case MD_SERVER_COMMAND_CONTINUE :
//
// Continue the instance.
//
// If it's paused, then continue it. If it's in any other
// state, this is an invalid state transition.
//
// Note that the *service* must be running before an instance
// can be continued.
//
if( serviceState == SERVICE_RUNNING && currentState == MD_SERVER_STATE_PAUSED ) {
status = ContinueInstance();
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot continue instance, error %lu\n", status ));
}
} else {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: invalid command %lu for state %lu\n", command, currentState ));
status = ERROR_INVALID_SERVICE_CONTROL;
} break;
default : DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: invalid command %lu\n", command ));
status = ERROR_INVALID_SERVICE_CONTROL; break;
}
} else {
DBGPRINTF(( DBG_CONTEXT, "PerformStateChange: cannot read metabase, error %lu\n", status ));
}
//
// Unlock the instance before trying to reopen the metabase.
//
UnlockThis();
//
// Restore the state to the previous value if the state change failed.
//
if( status != NO_ERROR ) { SetServerState( currentState, status ); }
return status;
} // IIS_SERVER_INSTANCE::PerformStateChange
VOID IIS_SERVER_INSTANCE::SetServerState( IN DWORD NewState, IN DWORD Win32Error ) /*++
Routine Description:
Sets the new server state, storing it locally and also storing the new state in the metabase.
Arguments:
NewState - The new server state.
Win32Error - New Win32 error value.
Return Value:
None.
--*/ {
DWORD status = NO_ERROR; MB mb( (IMDCOM *)m_Service->QueryMDObject() );
//
// Open the metabase and save the new state. Note that we map
// MD_SERVER_STATE_INVALID to MD_SERVER_STATE_STOPPED in the metabase.
// Client applications would probably be confused by the _INVALID state.
//
if( mb.Open( QueryMDPath(), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) {
if( !mb.SetDword( "", MD_WIN32_ERROR, IIS_MD_UT_SERVER, Win32Error, 0 ) || !mb.SetDword( "", MD_SERVER_STATE, IIS_MD_UT_SERVER, NewState == MD_SERVER_STATE_INVALID ? MD_SERVER_STATE_STOPPED : NewState, 0 ) ) {
status = GetLastError();
}
} else {
status = GetLastError();
}
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "SetServerState: cannot write metabase (%lu), error %lu\n", NewState, status ));
}
//
// Save it in the object also.
//
m_dwServerState = NewState;
} // IIS_SERVER_INSTANCE::SetServerState
BOOL IIS_SERVER_INSTANCE::StopEndpoints( VOID) /*++
Routine Description:
Walks down the list of Endpoints held by the Server instance and calls IIS_ENDPOINT::StopEndpoint for the endpoints.
Arguments:
None
Return Value:
TRUE if stop is successful, FALSE otherwise --*/ { BOOL fReturn = TRUE;
//
// Inside the locked section walk the normal & secure bindings
// to stop all relevant endpoints
//
LockThisForWrite();
if (!StopEndpointsHelper( &m_NormalBindingListHead)) { fReturn = FALSE; }
if (!StopEndpointsHelper( &m_SecureBindingListHead)) { fReturn = FALSE; }
UnlockThis();
return ( fReturn);
} // IIS_SERVER_INSTANCE::StopEndpoints()
BOOL IIS_SERVER_INSTANCE::StopEndpointsHelper( PLIST_ENTRY pBindingListHead) /*++
Routine Description:
Helper routine for StopEndpoints(). This function should be called with the Endpoints lock held
Arguments:
pBindingListHead - pointer to the binding list for endpoints to be stopped
Return Value:
BOOL - TRUE on success and FALSE on failure
--*/ { BOOL fReturn = TRUE; PLIST_ENTRY plBindingScan; PIIS_SERVER_BINDING binding;
//
// Walk the list of bindings and destroy them.
//
for( plBindingScan = pBindingListHead->Flink; plBindingScan != pBindingListHead; plBindingScan = plBindingScan->Flink ) {
binding = CONTAINING_RECORD( plBindingScan, IIS_SERVER_BINDING, m_BindingListEntry );
IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "stop ATQ EP of %p from instance %p, " " binding %p (%lx:%d:%s)\n", binding->QueryEndpoint(), this, binding, binding->QueryIpAddress(), binding->QueryEndpoint()->QueryPort(), binding->QueryHostName() )); }
if ( !binding->QueryEndpoint()->StopEndpoint()) { fReturn = FALSE; } } // for
//
// Success!
//
return ( fReturn);
} // IIS_SERVER_INSTANCE::StopEndpointsHelper()
BOOL IIS_SERVER_INSTANCE::CloseInstance( VOID ) /*++
Routine Description:
Shuts down instance
Arguments:
None
Return Value:
TRUE if Shutdown successful, FALSE otherwise
--*/ {
IF_DEBUG(INSTANCE) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::Close called for %p\n", this )); }
(VOID)m_Service->DisassociateInstance( this ); return TRUE;
} // IIS_SERVER_INSTANCE::CloseInstance
DWORD IIS_SERVER_INSTANCE::StartInstance() /*++
Routine Description:
Sets instance to RUNNING
Arguments:
None.
Return Value:
DWORD - 0 if successful, !0 otherwise.
--*/ {
DWORD status;
IF_DEBUG(INSTANCE) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::StartInstance called for %p. Current state %d\n", this, QueryServerState() )); }
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_STOPPED );
//
// Set the transient state.
//
SetServerState( MD_SERVER_STATE_STARTING, NO_ERROR );
//
// Set cache parameters
//
m_tsCache.SetParameters( m_Service->QueryServiceId(), QueryInstanceId(), this );
if (( QueryInstanceId() != INET_INSTANCE_ROOT ) && IsDownLevelInstance() ) { MoveMDVroots2Registry(); // no longer supporting migrating VRoots back from the registry
//PdcHackVRReg2MD( );
}
//
// Read all common parameters and initialize VDirs
//
if ( !RegReadCommonParams( TRUE, TRUE) ) { return ERROR_NOT_ENOUGH_MEMORY; }
//
// Start logging
//
m_Logging.ActivateLogging( m_Service->QueryServiceName(), QueryInstanceId(), m_strMDPath.QueryStr(), m_Service->QueryMDObject() );
//
// Verify the service can handle another instance.
//
if( !m_Service->RecordInstanceStart() ) {
m_Logging.ShutdownLogging(); QueryVrootTable()->RemoveVirtualRoots();
return ERROR_NOT_SUPPORTED; }
//
// Bind the instance.
//
status = BindInstance();
if ( status != NO_ERROR ) {
m_Logging.ShutdownLogging(); QueryVrootTable()->RemoveVirtualRoots();
//
// Tell the service that we failed to start the instance.
//
m_Service->RecordInstanceStop();
}
return status;
} // IIS_SERVER_INSTANCE::StartInstance
DWORD IIS_SERVER_INSTANCE::StopInstance() /*++
Routine Description:
Sets instance to STOPPED
Arguments:
None.
Return Value:
DWORD - 0 if successful, !0 otherwise.
--*/ {
DWORD status;
IF_DEBUG(INSTANCE) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::StopInstance called for %p. Current state %d\n", this, QueryServerState() )); }
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_STARTED || QueryServerState() == MD_SERVER_STATE_PAUSED );
Reference();
//
// Set the transient state.
//
SetServerState( MD_SERVER_STATE_STOPPING, NO_ERROR );
m_Service->StopInstanceProcs( this );
//
// Note that we call DisconnectUsersByInstance() before *and* after
// unbinding the instance. This is to prevent a potential race condition
// that can occur if another thread is already in IIS_ENDPOINT::
// FindAndReferenceInstance(), has found the instance, checked its state,
// and found it to be MD_SERVER_STATE_STARTED. The call to UnbindInstance()
// will lock any affected endpoints, ensuring that there are no other
// threads in the midst of a FindAndReferenceInstance(). The second
// (seemingly redundant) call to DisconnectUsersByInstance() will catch
// any threads that "snuck in" under these conditions.
//
status = m_Service->DisconnectUsersByInstance( this );
if( status == NO_ERROR ) { status = UnbindInstance(); }
if( status == NO_ERROR ) { status = m_Service->DisconnectUsersByInstance( this ); }
if( status == NO_ERROR ) { SetServerState( MD_SERVER_STATE_STOPPED, NO_ERROR ); m_dwSavedState = MD_SERVER_STATE_STOPPED; m_Service->RecordInstanceStop(); }
//
// logging cleanup
//
DBG_REQUIRE( m_Logging.ShutdownLogging()); DBG_REQUIRE( QueryVrootTable()->RemoveVirtualRoots());
Dereference(); return status;
} // IIS_SERVER_INSTANCE::StopInstance
DWORD IIS_SERVER_INSTANCE::PauseInstance() /*++
Routine Description:
Sets instance to PAUSE
Arguments:
None.
Return Value:
DWORD - 0 if successful, !0 otherwise.
--*/ {
IF_DEBUG(INSTANCE) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::Pause called for %p. Current state %d\n", this, QueryServerState() )); }
//
// Just set the paused state (no need for a transient state).
// Setting the instance to paused will prevent new incoming
// connections on the instance.
//
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_STARTED ); SetServerState( MD_SERVER_STATE_PAUSED, NO_ERROR );
//
// Success!
//
return NO_ERROR;
} // IIS_SERVER_INSTANCE::PauseInstance
DWORD IIS_SERVER_INSTANCE::ContinueInstance() /*++
Routine Description:
Sets instance to STARTED.
Arguments:
None.
Return Value:
DWORD - 0 if successful, !0 otherwise.
--*/ {
IF_DEBUG(INSTANCE) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::Continue called for %p. Current state %d\n", this, QueryServerState() )); }
//
// Just set the stated state (no need for a transient state).
// Setting the instance to started will allow new incoming
// connections on the instance.
//
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_PAUSED ); SetServerState( MD_SERVER_STATE_STARTED, NO_ERROR );
//
// Success!
//
return NO_ERROR;
} // IIS_SERVER_INSTANCE::ContinueInstance
VOID IIS_SERVER_INSTANCE::SetWin32Error( DWORD err ) {
MB mb( (IMDCOM *)m_Service->QueryMDObject() ); DWORD status = NO_ERROR;
//
// Open the metabase and save the error code.
//
if( mb.Open( QueryMDPath(), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) {
if( !mb.SetDword( "", MD_WIN32_ERROR, IIS_MD_UT_SERVER, err, 0 ) ) {
status = GetLastError();
}
} else {
status = GetLastError();
}
if( status != NO_ERROR ) {
DBGPRINTF(( DBG_CONTEXT, "SetWin32Error: cannot save error %lu (%lx), error %lx\n", err, err, status ));
}
} // IIS_SERVER_INSTANCE::SetWin32Error
BOOL IIS_SERVER_INSTANCE::SetBandwidthThrottle( IN MB * pMB ) /*++
Routine Description:
Set the bandwidth throttle threshold for this instance
Arguments:
pMB - pointer to metabase handle
Return Value:
TRUE if successful, FALSE otherwise
--*/ { DWORD dwBandwidth;
if ( !TsIsNtServer() ) { return TRUE; }
DBG_ASSERT( pMB != NULL );
if ( !pMB->GetDword( "", MD_MAX_BANDWIDTH, IIS_MD_UT_SERVER, &dwBandwidth, 0 ) ) { VOID * pTemp;
pTemp = InterlockedExchangePointer( (PVOID *) &m_pBandwidthInfo, NULL );
if ( pTemp ) { DBG_REQUIRE( AtqFreeBandwidthInfo( pTemp ) ); } } else { if ( m_pBandwidthInfo == NULL ) { VOID * pTemp = AtqCreateBandwidthInfo(); if ( pTemp != NULL ) { AtqBandwidthSetInfo( pTemp, ATQ_BW_BANDWIDTH_LEVEL, dwBandwidth );
AtqBandwidthSetInfo( pTemp, ATQ_BW_DESCRIPTION, (ULONG_PTR) m_strMDPath.QueryStr() );
InterlockedExchangePointer( (PVOID *) &m_pBandwidthInfo, (PVOID) pTemp ); } } else { AtqBandwidthSetInfo( m_pBandwidthInfo, ATQ_BW_BANDWIDTH_LEVEL, (ULONG_PTR)dwBandwidth ); } }
return TRUE; }
BOOL IIS_SERVER_INSTANCE::SetBandwidthThrottleMaxBlocked( IN MB * pMB ) { DWORD dwMaxBlocked = INFINITE;
if ( !TsIsNtServer() ) { return TRUE; }
DBG_ASSERT( pMB != NULL );
if ( pMB->GetDword( "", MD_MAX_BANDWIDTH_BLOCKED, IIS_MD_UT_SERVER, &dwMaxBlocked, 0 ) ) { if ( m_pBandwidthInfo ) { AtqBandwidthSetInfo( m_pBandwidthInfo, ATQ_BW_MAX_BLOCKED, dwMaxBlocked ); } } return TRUE; }
DWORD IIS_SERVER_INSTANCE::DoStartInstance( VOID ) /*++
Routine Description:
Start an instance. This call encompasses the IIS_SERVER_INSTANCE and inherited invocations of StartInstance
Arguments:
None.
Return Value:
DWORD - 0 if successful, !0 otherwise.
--*/ { DWORD dwError;
dwError = StartInstance(); if ( dwError == NO_ERROR ) { if( m_Service->QueryCurrentServiceState() == SERVICE_PAUSED ) { SetServerState( MD_SERVER_STATE_PAUSED, NO_ERROR ); } else { SetServerState( MD_SERVER_STATE_STARTED, NO_ERROR ); } }
return dwError; }
|