2026 lines
45 KiB
C++
2026 lines
45 KiB
C++
/*++
|
||
|
||
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 <iiscnfg.h>
|
||
#include <imd.h>
|
||
#include <mb.hxx>
|
||
|
||
/************************************************************
|
||
* 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))
|
||
|
||
|
||
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_SecureBinding ( NULL ),
|
||
m_fZapRegKey ( FALSE),
|
||
m_fDoServerNameCheck ( FALSE),
|
||
m_reference ( 0),
|
||
m_sSecurePort ( 0),
|
||
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_dwAnonAcctDescLen ( 0),
|
||
m_cReadLocks ( 0),
|
||
m_strMDPath ( ),
|
||
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_Logging( pService->QueryServiceName(), dwInstanceId ),
|
||
m_dwLevelsToScan ( INETA_DEF_LEVELS_TO_SCAN ),
|
||
m_fAddedToServerInstanceList( FALSE )
|
||
{
|
||
|
||
DBG_ASSERT( lpszRegParamKey != NULL );
|
||
|
||
IF_DEBUG(INSTANCE) {
|
||
DBGPRINTF( ( DBG_CONTEXT,"Creating iis instance %x[%u]. \n",
|
||
this, dwInstanceId));
|
||
}
|
||
|
||
//
|
||
// Limit PWS connections
|
||
//
|
||
|
||
if ( !TsIsNtServer() ) {
|
||
m_dwMaxConnections = INETA_DEF_MAX_CONNECTIONS_PWS;
|
||
}
|
||
|
||
//
|
||
// initialize locks
|
||
//
|
||
|
||
InitializeCriticalSection(&m_csLock);
|
||
|
||
//
|
||
// initialize binding support
|
||
//
|
||
|
||
InitializeListHead( &m_BindingListHead );
|
||
|
||
//
|
||
// reference the service
|
||
//
|
||
|
||
if ( !pService->CheckAndReference( )) {
|
||
goto error_exit;
|
||
}
|
||
|
||
m_Service = pService;
|
||
|
||
//
|
||
// Set cache parameters
|
||
//
|
||
|
||
m_tsCache.SetParameters(
|
||
pService->QueryServiceId(),
|
||
dwInstanceId,
|
||
this );
|
||
|
||
//
|
||
// Set the metadatabase path
|
||
//
|
||
|
||
if ( QueryInstanceId() == INET_INSTANCE_ROOT ) {
|
||
|
||
DBG_ASSERT( FALSE );
|
||
|
||
} else {
|
||
|
||
CHAR szTemp[32];
|
||
|
||
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);
|
||
|
||
if ( fMigrateVroots ) {
|
||
MoveVrootFromRegToMD();
|
||
}
|
||
|
||
if ( QueryInstanceId() == 1 ) {
|
||
if ( !MoveMDVroots2Registry() ) {
|
||
PdcHackVRReg2MD( );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Read common parameters.
|
||
//
|
||
|
||
if ( !RegReadCommonParams() ) {
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Start logging
|
||
//
|
||
|
||
m_Logging.InitializeInstance( m_strMDPath.QueryStr(), m_Service->QueryMDObject() );
|
||
m_Logging.CreateLog();
|
||
m_Logging.Active();
|
||
|
||
//
|
||
// 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:
|
||
|
||
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);
|
||
|
||
//
|
||
// start cleanup
|
||
//
|
||
|
||
DBG_REQUIRE( m_Logging.TerminateLogging());
|
||
|
||
//
|
||
// 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 %x\n",this));
|
||
ZapRegistryKey( NULL, QueryRegParamKey() );
|
||
ZapInstanceMBTree( );
|
||
}
|
||
|
||
//
|
||
// endpoints should have been dereferenced
|
||
//
|
||
|
||
DBG_ASSERT(m_SecureBinding == NULL);
|
||
DBG_ASSERT(IsListEmpty( &m_BindingListHead ));
|
||
|
||
//
|
||
// dereference the service
|
||
//
|
||
|
||
if ( m_fAddedToServerInstanceList && m_Service != NULL ) {
|
||
m_Service->Dereference( );
|
||
}
|
||
|
||
DeleteCriticalSection(&m_csLock);
|
||
|
||
} // IIS_SERVER_INSTANCE::~IIS_SERVER_INSTANCE()
|
||
|
||
|
||
|
||
|
||
# if DBG
|
||
|
||
VOID
|
||
IIS_SERVER_INSTANCE::Print( VOID) const
|
||
{
|
||
IIS_SERVER_INSTANCE::Print();
|
||
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
" Printing IIS_SERVER_INSTANCE object ( %08x) \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 (%08x)\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::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()));
|
||
}
|
||
} else {
|
||
|
||
mb.Save();
|
||
}
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
|
||
PLIST_ENTRY listEntry;
|
||
PIIS_SERVER_BINDING binding;
|
||
|
||
LockThisForWrite();
|
||
|
||
//
|
||
// Walk the list of normal bindings and destroy them.
|
||
//
|
||
|
||
while( !IsListEmpty( &m_BindingListHead ) ) {
|
||
|
||
listEntry = RemoveHeadList( &m_BindingListHead );
|
||
|
||
binding = CONTAINING_RECORD(
|
||
listEntry,
|
||
IIS_SERVER_BINDING,
|
||
m_BindingListEntry
|
||
);
|
||
|
||
IF_DEBUG( INSTANCE ) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"unbinding %lx from %lx, binding %lx (%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;
|
||
|
||
}
|
||
|
||
//
|
||
// Remove from secure endpoint list if necessary.
|
||
//
|
||
|
||
if( m_SecureBinding != NULL ) {
|
||
|
||
m_SecureBinding->QueryEndpoint()->RemoveInstance(
|
||
this,
|
||
m_SecureBinding->QueryIpAddress(),
|
||
m_SecureBinding->QueryHostName()
|
||
);
|
||
|
||
m_SecureBinding->QueryEndpoint()->Dereference();
|
||
delete m_SecureBinding;
|
||
m_SecureBinding = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// Success!
|
||
//
|
||
|
||
UnlockThis();
|
||
return NO_ERROR;
|
||
|
||
} // IIS_SERVER_INSTANCE::UnbindInstance
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::UpdateNormalBindings(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads the normal binding list from the metabase and incorporates any
|
||
changes into the current binding configuration.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
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;
|
||
|
||
//
|
||
// Setup locals.
|
||
//
|
||
|
||
InitializeListHead( &createdBindings );
|
||
|
||
//
|
||
// Open the metabase and get the current binding list.
|
||
//
|
||
|
||
if( mb.Open( QueryMDPath() ) ) {
|
||
|
||
if( !mb.GetMultisz(
|
||
"",
|
||
MD_SERVER_BINDINGS,
|
||
IIS_MD_UT_SERVER,
|
||
&msz
|
||
) ) {
|
||
|
||
status = GetLastError();
|
||
|
||
if( status == MD_ERROR_DATA_NOT_FOUND ) {
|
||
|
||
CHAR defaultBinding[sizeof(":65535:\0")];
|
||
INT length;
|
||
|
||
//
|
||
// The bindings are not in the registry, so create
|
||
// a default wildcard binding based on the default
|
||
// port.
|
||
//
|
||
|
||
length = wsprintf(
|
||
defaultBinding,
|
||
":%u:",
|
||
m_sDefaultPort
|
||
);
|
||
|
||
length++; // account for terminator
|
||
defaultBinding[length++] = '\0'; // add another terminator
|
||
|
||
if( msz.Copy( defaultBinding, (DWORD)length ) ) {
|
||
|
||
status = NO_ERROR;
|
||
|
||
} else {
|
||
|
||
status = GetLastError();
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// 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();
|
||
|
||
//
|
||
// Scan the multisz and look for instances we'll need to create.
|
||
//
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
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 ) {
|
||
|
||
//
|
||
// See if the descriptor is in our current binding list.
|
||
//
|
||
|
||
if( !IsInCurrentBindingList(
|
||
ipAddress,
|
||
ipPort,
|
||
hostName
|
||
) ) {
|
||
|
||
//
|
||
// 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,
|
||
FALSE, // IsSecure
|
||
&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
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Could not create the new binding.
|
||
//
|
||
|
||
goto fatal;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Could not parse the descriptor. We should probably
|
||
// write something to the event log here.
|
||
//
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"UpdateNormalBindings: could not parse %s, error %lu\n",
|
||
scan,
|
||
status
|
||
));
|
||
|
||
//
|
||
// Press on regardless.
|
||
//
|
||
|
||
status = NO_ERROR;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Scan the existing bindings and look for those that need to
|
||
// be deleted.
|
||
//
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
listEntry = m_BindingListHead.Flink;
|
||
|
||
while( listEntry != &m_BindingListHead ) {
|
||
|
||
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 %lx from %lx, binding %lx (%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;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
//
|
||
// Move the newly created bindings over to the current binding
|
||
// list.
|
||
//
|
||
|
||
m_BindingListHead.Blink->Flink = createdBindings.Flink;
|
||
createdBindings.Flink->Blink = m_BindingListHead.Blink;
|
||
createdBindings.Blink->Flink = &m_BindingListHead;
|
||
m_BindingListHead.Blink = createdBindings.Blink;
|
||
|
||
}
|
||
|
||
UnlockThis();
|
||
return status;
|
||
|
||
fatal:
|
||
|
||
//
|
||
// An unrecoverable error occurred, so loop through the local list
|
||
// of newly created bindings and delete them.
|
||
//
|
||
|
||
DBG_ASSERT( status != NO_ERROR );
|
||
|
||
while( !IsListEmpty( &createdBindings ) ) {
|
||
|
||
listEntry = RemoveHeadList(
|
||
&createdBindings
|
||
);
|
||
|
||
binding = CONTAINING_RECORD(
|
||
listEntry,
|
||
IIS_SERVER_BINDING,
|
||
m_BindingListEntry
|
||
);
|
||
|
||
IF_DEBUG( INSTANCE ) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"zapping %lx from %lx, binding %lx (%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::UpdateNormalBindings
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::UpdateSecureBindings(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads the secure binding list from the metabase and incorporates any
|
||
changes into the current binding configuration.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
DWORD - Completion status, 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
MB mb( (IMDCOM*)m_Service->QueryMDObject() );
|
||
DWORD securePort = 0;
|
||
DWORD status = NO_ERROR;
|
||
PIIS_SERVER_BINDING binding;
|
||
|
||
//
|
||
// Open the metabase and get the secure port.
|
||
//
|
||
|
||
if( mb.Open( QueryMDPath() ) ) {
|
||
|
||
if( !mb.GetDword(
|
||
"",
|
||
MD_SECURE_PORT,
|
||
IIS_MD_UT_SERVER,
|
||
&securePort
|
||
) ) {
|
||
securePort = 0;
|
||
}
|
||
|
||
//
|
||
// 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();
|
||
|
||
//
|
||
// See if the secure port has changed.
|
||
//
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
if( ( m_SecureBinding == NULL && securePort != 0 ) ||
|
||
( m_SecureBinding != NULL &&
|
||
!m_SecureBinding->Compare(
|
||
DEF_SECURE_IP_ADDRESS,
|
||
(USHORT)securePort,
|
||
DEF_SECURE_HOST_NAME
|
||
) ) ) {
|
||
|
||
//
|
||
// Special case for new port == 0, meaning that the
|
||
// we're disabling secure access to the instance.
|
||
//
|
||
|
||
if( securePort == 0 ) {
|
||
binding = NULL;
|
||
goto RemoveBinding;
|
||
}
|
||
|
||
//
|
||
// Mismatch. Create a new binding.
|
||
//
|
||
|
||
status = CreateNewBinding(
|
||
DEF_SECURE_IP_ADDRESS,
|
||
(USHORT)securePort,
|
||
DEF_SECURE_HOST_NAME,
|
||
TRUE, // IsSecure
|
||
&binding
|
||
);
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
RemoveBinding:
|
||
|
||
if( m_SecureBinding != NULL ) {
|
||
|
||
m_SecureBinding->QueryEndpoint()->RemoveInstance(
|
||
this,
|
||
m_SecureBinding->QueryIpAddress(),
|
||
m_SecureBinding->QueryHostName()
|
||
);
|
||
|
||
m_SecureBinding->QueryEndpoint()->Dereference();
|
||
delete m_SecureBinding;
|
||
|
||
}
|
||
|
||
m_SecureBinding = binding;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
UnlockThis();
|
||
return status;
|
||
|
||
} // IIS_SERVER_INSTANCE::UpdateSecureBindings
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::CreateNewBinding(
|
||
IN DWORD IpAddress,
|
||
IN USHORT IpPort,
|
||
IN const CHAR * HostName,
|
||
IN BOOL IsSecure,
|
||
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.
|
||
|
||
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,
|
||
TRUE, // CreateIfNotFound
|
||
IsSecure
|
||
);
|
||
|
||
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();
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Could not create new binding object.
|
||
//
|
||
|
||
status = GetLastError();
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Could not find & reference endpoint.
|
||
//
|
||
|
||
status = GetLastError();
|
||
|
||
}
|
||
|
||
//
|
||
// 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 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:
|
||
|
||
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 = m_BindingListHead.Flink ;
|
||
listEntry != &m_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::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 requestedState;
|
||
DWORD currentState;
|
||
DWORD serviceState;
|
||
BOOL needToUpdateState;
|
||
|
||
//
|
||
// Setup locals.
|
||
//
|
||
|
||
status = NO_ERROR;
|
||
needToUpdateState = TRUE;
|
||
serviceState = m_Service->QueryCurrentServiceState();
|
||
currentState = QueryServerState();
|
||
|
||
//
|
||
// Open the metabase and query the new state.
|
||
//
|
||
|
||
if( mb.Open(
|
||
QueryMDPath(),
|
||
METADATA_PERMISSION_READ ) ) {
|
||
|
||
if( !mb.GetDword(
|
||
"",
|
||
MD_SERVER_STATE,
|
||
IIS_MD_UT_SERVER,
|
||
&requestedState
|
||
) ) {
|
||
|
||
status = GetLastError();
|
||
|
||
IF_DEBUG( INSTANCE ) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: cannot read server state, 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,
|
||
"PerformStateChange: cannot open metabase for READ, error %lu\n",
|
||
status
|
||
));
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Lock the instance.
|
||
//
|
||
|
||
LockThisForWrite();
|
||
|
||
//
|
||
// Interpret the state change.
|
||
//
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
switch( requestedState ) {
|
||
|
||
case MD_SERVER_STATE_STARTING :
|
||
|
||
//
|
||
// Starting the service. If it's already running, there's
|
||
// nothing to do. 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( currentState == MD_SERVER_STATE_STARTED ) {
|
||
|
||
break;
|
||
|
||
} else
|
||
if( serviceState == SERVICE_RUNNING &&
|
||
currentState == MD_SERVER_STATE_STOPPED ) {
|
||
|
||
status = StartInstance( ¤tState );
|
||
|
||
if( status != NO_ERROR ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: cannot start instance, error %lu\n",
|
||
status
|
||
));
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: invalid state transition: %lu to %lu\n",
|
||
currentState,
|
||
requestedState
|
||
));
|
||
|
||
status = ERROR_INVALID_SERVICE_CONTROL;
|
||
|
||
}
|
||
break;
|
||
|
||
case MD_SERVER_STATE_STOPPING :
|
||
|
||
//
|
||
// Stopping the service. If it's already stopped, there's
|
||
// nothing to do. 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( currentState == MD_SERVER_STATE_STOPPED ) {
|
||
|
||
break;
|
||
|
||
} else
|
||
if( ( serviceState == SERVICE_RUNNING ||
|
||
serviceState == SERVICE_PAUSED ) &&
|
||
( currentState == MD_SERVER_STATE_STARTED ||
|
||
currentState == MD_SERVER_STATE_PAUSED ) ) {
|
||
|
||
status = StopInstance( ¤tState );
|
||
|
||
if( status != NO_ERROR ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: cannot stop instance, error %lu\n",
|
||
status
|
||
));
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: invalid state transition: %lu to %lu\n",
|
||
currentState,
|
||
requestedState
|
||
));
|
||
|
||
status = ERROR_INVALID_SERVICE_CONTROL;
|
||
|
||
}
|
||
break;
|
||
|
||
case MD_SERVER_STATE_PAUSING :
|
||
|
||
//
|
||
// Pausing the service. If it's already paused, there's
|
||
// nothing to do. 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( currentState == MD_SERVER_STATE_PAUSED ) {
|
||
|
||
break;
|
||
|
||
} else
|
||
if( serviceState == SERVICE_RUNNING &&
|
||
currentState == MD_SERVER_STATE_STARTED ) {
|
||
|
||
status = PauseInstance( ¤tState );
|
||
|
||
if( status != NO_ERROR ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: cannot pause instance, error %lu\n",
|
||
status
|
||
));
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: invalid state transition: %lu to %lu\n",
|
||
currentState,
|
||
requestedState
|
||
));
|
||
|
||
status = ERROR_INVALID_SERVICE_CONTROL;
|
||
|
||
}
|
||
break;
|
||
|
||
case MD_SERVER_STATE_CONTINUING :
|
||
|
||
//
|
||
// Continuing the service. If it's already running, there's
|
||
// nothing to do. 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( currentState == MD_SERVER_STATE_STARTED ) {
|
||
|
||
break;
|
||
|
||
} else
|
||
if( serviceState == SERVICE_RUNNING &&
|
||
currentState == MD_SERVER_STATE_PAUSED ) {
|
||
|
||
status = ContinueInstance( ¤tState );
|
||
|
||
if( status != NO_ERROR ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: cannot continue instance, error %lu\n",
|
||
status
|
||
));
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: invalid state transition: %lu to %lu\n",
|
||
currentState,
|
||
requestedState
|
||
));
|
||
|
||
status = ERROR_INVALID_SERVICE_CONTROL;
|
||
|
||
}
|
||
break;
|
||
|
||
case MD_SERVER_STATE_STARTED :
|
||
case MD_SERVER_STATE_STOPPED :
|
||
case MD_SERVER_STATE_PAUSED :
|
||
|
||
if( currentState == requestedState ) {
|
||
|
||
//
|
||
// This is a false notification; ignore it.
|
||
//
|
||
|
||
needToUpdateState = FALSE;
|
||
|
||
} else {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: invalid state transition: %lu to %lu\n",
|
||
currentState,
|
||
requestedState
|
||
));
|
||
|
||
status = ERROR_INVALID_SERVICE_CONTROL;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
default :
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"PerformStateChange: invalid state %lu\n",
|
||
requestedState
|
||
));
|
||
|
||
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();
|
||
|
||
if( needToUpdateState ) {
|
||
|
||
//
|
||
// Update the server state and completion status.
|
||
//
|
||
|
||
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
|
||
) ||
|
||
!mb.SetDword(
|
||
"",
|
||
MD_SERVER_STATE,
|
||
IIS_MD_UT_SERVER,
|
||
NewState == MD_SERVER_STATE_INVALID
|
||
? MD_SERVER_STATE_STOPPED
|
||
: NewState
|
||
) ) {
|
||
|
||
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::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 %x\n",
|
||
this
|
||
));
|
||
}
|
||
|
||
(VOID)m_Service->DisassociateInstance( this );
|
||
return TRUE;
|
||
|
||
} // IIS_SERVER_INSTANCE::CloseInstance
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::StartInstance(
|
||
LPDWORD NewState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets instance to RUNNING
|
||
|
||
Arguments:
|
||
|
||
NewState - Receives the new state.
|
||
|
||
Return Value:
|
||
|
||
DWORD - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD status;
|
||
|
||
IF_DEBUG(INSTANCE) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"IIS_SERVER_INSTANCE::StartInstance called for %x. Current state %d\n",
|
||
this,
|
||
QueryServerState()
|
||
));
|
||
}
|
||
|
||
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_STOPPED );
|
||
|
||
//
|
||
// Bind the instance.
|
||
//
|
||
|
||
status = BindInstance();
|
||
|
||
if( status == NO_ERROR ) {
|
||
|
||
//
|
||
// Update the server state.
|
||
//
|
||
|
||
*NewState = MD_SERVER_STATE_STARTED;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
} // IIS_SERVER_INSTANCE::StartInstance
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::StopInstance(
|
||
LPDWORD NewState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets instance to STOPPED
|
||
|
||
Arguments:
|
||
|
||
NewState - Receives the new state.
|
||
|
||
Return Value:
|
||
|
||
DWORD - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
|
||
IF_DEBUG(INSTANCE) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"IIS_SERVER_INSTANCE::StopInstance called for %x. Current state %d\n",
|
||
this,
|
||
QueryServerState()
|
||
));
|
||
}
|
||
|
||
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_STARTED ||
|
||
QueryServerState() == MD_SERVER_STATE_PAUSED );
|
||
|
||
//
|
||
// Set the state to STOPPED *before* unbinding to prevent a
|
||
// race condition with incoming connections.
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
m_dwServerState = MD_SERVER_STATE_STOPPED;
|
||
|
||
status = m_Service->DisconnectUsersByInstance( this );
|
||
|
||
if( status == NO_ERROR ) {
|
||
status = UnbindInstance();
|
||
}
|
||
|
||
if( status == NO_ERROR ) {
|
||
status = m_Service->DisconnectUsersByInstance( this );
|
||
}
|
||
|
||
if( status == NO_ERROR ) {
|
||
*NewState = MD_SERVER_STATE_STOPPED;
|
||
m_dwSavedState = MD_SERVER_STATE_STOPPED;
|
||
}
|
||
|
||
return status;
|
||
|
||
} // IIS_SERVER_INSTANCE::StopInstance
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::PauseInstance(
|
||
LPDWORD NewState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets instance to PAUSE
|
||
|
||
Arguments:
|
||
|
||
NewState - Receives the new state.
|
||
|
||
Return Value:
|
||
|
||
DWORD - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
IF_DEBUG(INSTANCE) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"IIS_SERVER_INSTANCE::Pause called for %x. Current state %d\n",
|
||
this,
|
||
QueryServerState()
|
||
));
|
||
}
|
||
|
||
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_STARTED );
|
||
*NewState = MD_SERVER_STATE_PAUSED;
|
||
return NO_ERROR;
|
||
|
||
} // IIS_SERVER_INSTANCE::PauseInstance
|
||
|
||
|
||
DWORD
|
||
IIS_SERVER_INSTANCE::ContinueInstance(
|
||
LPDWORD NewState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets instance to STARTED.
|
||
|
||
Arguments:
|
||
|
||
NewState - Receives the new state.
|
||
|
||
Return Value:
|
||
|
||
DWORD - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
IF_DEBUG(INSTANCE) {
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"IIS_SERVER_INSTANCE::Continue called for %x. Current state %d\n",
|
||
this,
|
||
QueryServerState()
|
||
));
|
||
}
|
||
|
||
DBG_ASSERT( QueryServerState() == MD_SERVER_STATE_PAUSED );
|
||
*NewState = MD_SERVER_STATE_STARTED;
|
||
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
|
||
) ) {
|
||
|
||
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
|
||
|