1042 lines
25 KiB
C++
1042 lines
25 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1995-1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name :
|
|||
|
wamobj.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
This module implements the WAM (web application manager) object
|
|||
|
|
|||
|
Author:
|
|||
|
David Kaplan ( DaveK ) 26-Feb-1997
|
|||
|
|
|||
|
Environment:
|
|||
|
User Mode - Win32
|
|||
|
|
|||
|
Project:
|
|||
|
Wam DLL
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
/************************************************************
|
|||
|
* Include Headers
|
|||
|
************************************************************/
|
|||
|
#include <isapip.hxx>
|
|||
|
#include "setable.hxx"
|
|||
|
|
|||
|
# include "isapi.hxx"
|
|||
|
# include "WamW3.hxx"
|
|||
|
|
|||
|
#include "wamobj.hxx"
|
|||
|
#include "iwr.h"
|
|||
|
#include "iwr_i.c"
|
|||
|
# include "timer.h"
|
|||
|
|
|||
|
#include <irtlmisc.h>
|
|||
|
|
|||
|
#include <ooptoken.h>
|
|||
|
|
|||
|
// UNDONE where do these belong?
|
|||
|
extern PSE_TABLE g_psextensions;
|
|||
|
|
|||
|
|
|||
|
class CComContextHelper
|
|||
|
/*++
|
|||
|
|
|||
|
Class description:
|
|||
|
|
|||
|
Stack based helper class to enable calls to CoInitialize inside
|
|||
|
ISAPI.
|
|||
|
|
|||
|
Replaces member functions PrepareCom/UnprepareCom in WAM_EXEC_INFO.
|
|||
|
These had to be replaced because WAM_EXEC_INFO will persist beyond
|
|||
|
the initial ISAPI call in the async case and keeping the call context
|
|||
|
in member data would leak and overrelease under certain conditions.
|
|||
|
|
|||
|
|
|||
|
Public Interface:
|
|||
|
|
|||
|
PrepareCom : For OOP get the call context and use our
|
|||
|
private interface to enable coinit calls.
|
|||
|
UnprepareCom : Release the call context.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
public:
|
|||
|
|
|||
|
CComContextHelper( BOOL fInProcess )
|
|||
|
: m_fInProcess( fInProcess ),
|
|||
|
m_pComContext( NULL ),
|
|||
|
m_pComInitsCookie( NULL )
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
~CComContextHelper( void )
|
|||
|
{
|
|||
|
UnprepareCom();
|
|||
|
}
|
|||
|
|
|||
|
HRESULT PrepareCom()
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Prepare com before call into ISAPI. For OOP get the call context
|
|||
|
and use our private interface to enable coinit calls.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
|
|||
|
// Never call twice.
|
|||
|
DBG_ASSERT( NULL == m_pComInitsCookie );
|
|||
|
DBG_ASSERT( NULL == m_pComContext );
|
|||
|
|
|||
|
if( !m_fInProcess )
|
|||
|
{
|
|||
|
// Save COM Call Context in MTS case
|
|||
|
hr = CoGetCallContext( IID_IComDispatchInfo, (void **)&m_pComContext );
|
|||
|
if( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
hr = m_pComContext->EnableComInits( &m_pComInitsCookie );
|
|||
|
}
|
|||
|
}
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
void UnprepareCom()
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Restores com state after call into ISAPI. Release call context.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
// Restore COM Call Context
|
|||
|
if( m_pComContext )
|
|||
|
{
|
|||
|
DBG_ASSERT( !m_fInProcess );
|
|||
|
DBG_ASSERT( m_pComInitsCookie );
|
|||
|
|
|||
|
m_pComContext->DisableComInits( m_pComInitsCookie );
|
|||
|
m_pComContext->Release();
|
|||
|
|
|||
|
m_pComContext = NULL;
|
|||
|
m_pComInitsCookie = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private:
|
|||
|
//NO-OP
|
|||
|
CComContextHelper() {}
|
|||
|
CComContextHelper( const CComContextHelper & ref ) {}
|
|||
|
|
|||
|
private:
|
|||
|
|
|||
|
BOOL m_fInProcess;
|
|||
|
IComDispatchInfo * m_pComContext;
|
|||
|
void * m_pComInitsCookie;
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/*---------------------------------------------------------------------*
|
|||
|
WAM::InitWam
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Initializes this WAM.
|
|||
|
|
|||
|
Arguments:
|
|||
|
See below
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
*/
|
|||
|
STDMETHODIMP
|
|||
|
WAM::InitWam
|
|||
|
(
|
|||
|
BOOL fInProcess, // are we in-proc or out-of-proc?
|
|||
|
BOOL fInPool, // !Isolated
|
|||
|
BOOL fEnableTryExcept, // catch exceptions in ISAPI calls?
|
|||
|
int pt, // PLATFORM_TYPE - are we running on Win95?
|
|||
|
DWORD *pPID // Process Id of the process the wam was created in
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
|
|||
|
|
|||
|
IF_DEBUG( INIT_CLEAN ) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT,
|
|||
|
"\n ********** WAM(%08x)::InitWam() *****\n",
|
|||
|
this));
|
|||
|
}
|
|||
|
|
|||
|
INITIALIZE_CRITICAL_SECTION( &m_csWamExecInfoList );
|
|||
|
InitializeListHead( &m_WamExecInfoListHead );
|
|||
|
|
|||
|
m_fInProcess = fInProcess;
|
|||
|
m_fInPool = fInPool;
|
|||
|
|
|||
|
DBG_ASSERT( pt != PtInvalid );
|
|||
|
m_pt = (PLATFORM_TYPE) pt;
|
|||
|
|
|||
|
// Get the process id of the current process so we can return it to w3svc
|
|||
|
*pPID = GetCurrentProcessId();
|
|||
|
|
|||
|
// Acquire a reference to the SE_TABLE object
|
|||
|
DBG_REQUIRE( g_psextensions->AddRefWam() > 0);
|
|||
|
|
|||
|
IF_DEBUG( INIT_CLEAN) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT, "\n************Leaving WAM::InitWam ...\n" ));
|
|||
|
}
|
|||
|
|
|||
|
DBG_ASSERT( SUCCEEDED(hr));
|
|||
|
|
|||
|
//
|
|||
|
// UNDONE: See the DoGlobalInitializations() for details.
|
|||
|
//
|
|||
|
hr = DoGlobalInitializations( fInProcess, fEnableTryExcept);
|
|||
|
|
|||
|
return (hr);
|
|||
|
} // WAM::InitWam()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*---------------------------------------------------------------------*
|
|||
|
WAM::StartShutdown
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Phase 1 of shutdown process.
|
|||
|
Set the shutdown flag on the WAM object
|
|||
|
Loop for small duration checking if all WAM_EXEC_INFO's have drained.
|
|||
|
At the end of loop initiate the first phase of TerminateExtension()
|
|||
|
|
|||
|
NOTE: TerminateExtension() - currently have been tested only for the
|
|||
|
MUST_UNLOAD option => there is no two-phase operation there.
|
|||
|
So, we will rely on calling it once only.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
*/
|
|||
|
STDMETHODIMP
|
|||
|
WAM::StartShutdown(
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
|
|||
|
IF_DEBUG( WAM ) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT,
|
|||
|
"WAM(%08x)::StartShutdown() %d Active Requests\n",
|
|||
|
this, QueryWamStats().QueryCurrentWamRequests() ));
|
|||
|
}
|
|||
|
|
|||
|
// Set the Shutting down flag to true now
|
|||
|
DBG_REQUIRE( FALSE ==
|
|||
|
InterlockedExchange((LPLONG)&m_fShuttingDown, (LONG)TRUE)
|
|||
|
);
|
|||
|
|
|||
|
for ( i = 0;
|
|||
|
( (i < 10) &&
|
|||
|
(QueryWamStats().QueryCurrentWamRequests())
|
|||
|
);
|
|||
|
i++ )
|
|||
|
{
|
|||
|
# ifndef SHUTOFF_AFTER_BETA
|
|||
|
DBGPRINTF( (
|
|||
|
DBG_CONTEXT,
|
|||
|
"[%d] WAM(%08x) has %d requests waiting for cleanup\n",
|
|||
|
GetCurrentThreadId(),
|
|||
|
this, QueryWamStats().QueryCurrentWamRequests()
|
|||
|
) );
|
|||
|
# endif // SHUTOFF_AFTER_BETA
|
|||
|
|
|||
|
Sleep( 200 ); // sleep for a while before restarting the check again
|
|||
|
} // for
|
|||
|
|
|||
|
// Release the reference to the SE_TABLE object
|
|||
|
g_psextensions->ReleaseRefWam();
|
|||
|
|
|||
|
return NOERROR;
|
|||
|
} // WAM::StartShutdown()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*---------------------------------------------------------------------*
|
|||
|
WAM::UninitWam
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Phase 2 of shutdown process.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
NOERROR on success
|
|||
|
E_FAIL if there are any pending items to be deleted still
|
|||
|
|
|||
|
*/
|
|||
|
STDMETHODIMP
|
|||
|
WAM::UninitWam()
|
|||
|
{
|
|||
|
//
|
|||
|
// If there are any pending requests being processed, wait for them
|
|||
|
// to be drained off from this WAM
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG( WAM ) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT,
|
|||
|
"WAM(%08x)::UninitWam() %d Active Requests\n",
|
|||
|
this, QueryWamStats().QueryCurrentWamRequests() ));
|
|||
|
}
|
|||
|
|
|||
|
if ( QueryWamStats().QueryCurrentWamRequests() != 0 )
|
|||
|
{
|
|||
|
IF_DEBUG( ERROR) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT,
|
|||
|
"WAM(%08x)::UninitWam() Error - "
|
|||
|
" Failed with active requests! (%d active)\n",
|
|||
|
this,
|
|||
|
QueryWamStats().QueryCurrentWamRequests() ));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate and dump information on all requests that are hanging
|
|||
|
// and the associated ISAPI DLLs
|
|||
|
//
|
|||
|
# ifndef SHUTOFF_AFTER_BETA
|
|||
|
while ( QueryWamStats().QueryCurrentWamRequests() > 0) {
|
|||
|
|
|||
|
DBGPRINTF(
|
|||
|
( DBG_CONTEXT,
|
|||
|
"\n\n[Thd %d]WAM(%08x) has %d requests waiting ... \n",
|
|||
|
GetCurrentThreadId(),
|
|||
|
this, QueryWamStats().QueryCurrentWamRequests()
|
|||
|
) );
|
|||
|
|
|||
|
g_psextensions->PrintRequestCounts();
|
|||
|
|
|||
|
// sleep for a while before restarting the check again
|
|||
|
Sleep( 1000 );
|
|||
|
} // while
|
|||
|
|
|||
|
# endif // SHUTOFF_AFTER_BETA
|
|||
|
|
|||
|
|
|||
|
// return failure, since we failed to shutdown gracefully!
|
|||
|
// NYI: Should I ignore the fact that some long-hanging connections
|
|||
|
// are okay?
|
|||
|
// Shouldn't I be forcing the exit now?
|
|||
|
DBG_ASSERT( QueryWamStats().QueryCurrentWamRequests() == 0);
|
|||
|
// return ( E_FAIL);
|
|||
|
}
|
|||
|
|
|||
|
DeleteCriticalSection( &m_csWamExecInfoList );
|
|||
|
|
|||
|
return NOERROR;
|
|||
|
} // WAM::UninitWam()
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------*
|
|||
|
WAM::ProcessAsyncIO
|
|||
|
Completes an async i/o process for a given wam request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
*/
|
|||
|
STDMETHODIMP
|
|||
|
WAM::ProcessAsyncIO
|
|||
|
(
|
|||
|
#ifdef _WIN64
|
|||
|
UINT64 pWamExecInfoIn, // WAM_EXEC_INFO *
|
|||
|
#else
|
|||
|
DWORD_PTR pWamExecInfoIn, // WAM_EXEC_INFO *
|
|||
|
#endif
|
|||
|
DWORD dwStatus,
|
|||
|
DWORD cbWritten
|
|||
|
)
|
|||
|
{
|
|||
|
return ProcessAsyncIOImpl( pWamExecInfoIn,
|
|||
|
dwStatus,
|
|||
|
cbWritten
|
|||
|
);
|
|||
|
|
|||
|
} // WAM::ProcessAsyncIO
|
|||
|
|
|||
|
STDMETHODIMP
|
|||
|
WAM::ProcessAsyncReadOop
|
|||
|
(
|
|||
|
#ifdef _WIN64
|
|||
|
UINT64 pWamExecInfoIn,
|
|||
|
#else
|
|||
|
DWORD_PTR pWamExecInfoIn,
|
|||
|
#endif
|
|||
|
DWORD dwStatus,
|
|||
|
DWORD cbRead,
|
|||
|
unsigned char * lpDataRead
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Handle callback for out of process AsyncRead
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pWamExecInfoIn - The smuggled pointer to the WAM_EXEC_INFO
|
|||
|
dwStatus - IO status
|
|||
|
cbRead - Number of bytes read
|
|||
|
lpDataRead - Marshalled data read
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
The initial design for AsyncRead was inadequate when the isapi is
|
|||
|
running out of process. The problem was that the data buffer was
|
|||
|
marshalled over to inetinfo and back during the AsyncRead call.
|
|||
|
Since the completion will happen on another thread the ISAPI could
|
|||
|
get the completion before the data was completely marshalled back
|
|||
|
or the address in inetinfo could be invalidated before the read was
|
|||
|
complete. The solution is to add a separate path for oop async reads
|
|||
|
and marshall the data on the io completion and copy it into the
|
|||
|
client's buffer then.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DBG_ASSERT( lpDataRead != NULL );
|
|||
|
DBG_ASSERT( !m_fInProcess );
|
|||
|
|
|||
|
return ProcessAsyncIOImpl( pWamExecInfoIn,
|
|||
|
dwStatus,
|
|||
|
cbRead,
|
|||
|
lpDataRead
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
HRESULT
|
|||
|
WAM::ProcessAsyncIOImpl
|
|||
|
(
|
|||
|
#ifdef _WIN64
|
|||
|
UINT64 pWamExecInfoIn,
|
|||
|
#else
|
|||
|
DWORD_PTR pWamExecInfoIn,
|
|||
|
#endif
|
|||
|
DWORD dwStatus,
|
|||
|
DWORD cb,
|
|||
|
LPBYTE lpDataRead // = NULL
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hrRet;
|
|||
|
CComContextHelper callContext( m_fInProcess );
|
|||
|
|
|||
|
WAM_EXEC_INFO * pWamExecInfo =
|
|||
|
reinterpret_cast<WAM_EXEC_INFO *>(pWamExecInfoIn);
|
|||
|
|
|||
|
//
|
|||
|
// NOTE we assert because we believe the pointer can never be null
|
|||
|
// AND we fail gracefully if it is null because there is a long
|
|||
|
// code path across many threads between setting the pointer
|
|||
|
// and here, and you never know ...
|
|||
|
//
|
|||
|
|
|||
|
DBG_ASSERT ( pWamExecInfo != NULL );
|
|||
|
|
|||
|
if ( pWamExecInfo == NULL ) {
|
|||
|
|
|||
|
return HRESULT_FROM_WIN32( E_POINTER );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Note: The AddRef/Release calls may not be necessary anymore.
|
|||
|
//
|
|||
|
// Make sure that UnprepareCom has valid WamInfo
|
|||
|
//
|
|||
|
|
|||
|
pWamExecInfo->AddRef();
|
|||
|
|
|||
|
callContext.PrepareCom();
|
|||
|
|
|||
|
if( lpDataRead == NULL )
|
|||
|
{
|
|||
|
// Doing an out of process async read
|
|||
|
|
|||
|
hrRet = HresultFromBool(
|
|||
|
pWamExecInfo->ProcessAsyncIO(
|
|||
|
dwStatus,
|
|||
|
cb
|
|||
|
) );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// All other async io completions
|
|||
|
|
|||
|
hrRet = HresultFromBool(
|
|||
|
pWamExecInfo->ProcessAsyncReadOop(
|
|||
|
dwStatus,
|
|||
|
cb,
|
|||
|
lpDataRead
|
|||
|
) );
|
|||
|
}
|
|||
|
|
|||
|
callContext.UnprepareCom();
|
|||
|
|
|||
|
//
|
|||
|
// Balance AddRef() above
|
|||
|
//
|
|||
|
|
|||
|
pWamExecInfo->Release();
|
|||
|
|
|||
|
return hrRet;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------*
|
|||
|
WAM::ProcessRequest
|
|||
|
Processes a WAM request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
pIWamRequest - pointer to IWamRequest interface
|
|||
|
cbWrcStrings - Count of bytes for wamreq core strings
|
|||
|
pfHandled - Indicates we handled this request
|
|||
|
pfFinished - Indicates no further processing is required
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
*/
|
|||
|
STDMETHODIMP
|
|||
|
WAM::ProcessRequest
|
|||
|
(
|
|||
|
IWamRequest * pIWamRequest,
|
|||
|
DWORD cbWrcStrings,
|
|||
|
OOP_CORE_STATE * pOopCoreState,
|
|||
|
BOOL * pfHandled
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hrRet = NOERROR; // this function's return value
|
|||
|
HRESULT hr;
|
|||
|
int iretInvokeExt; // return value from InvokeExtension
|
|||
|
BOOL fFreeContext = TRUE;// do we need to free up wamex-info?
|
|||
|
WAM_EXEC_INFO * pWamExecInfo = NULL;
|
|||
|
BOOL fImpersonated = FALSE;
|
|||
|
BYTE * pbEntityBody = NULL;
|
|||
|
DWORD cbEntityBody = 0;
|
|||
|
CComContextHelper callContext( m_fInProcess );
|
|||
|
|
|||
|
|
|||
|
DBG_ASSERT( pIWamRequest );
|
|||
|
IF_DEBUG( WAM) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT,
|
|||
|
"WAM(%08x)::ProcessRequest(%08x, %08x, ... )\n",
|
|||
|
this, pIWamRequest ));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( m_fShuttingDown ) {
|
|||
|
|
|||
|
IF_DEBUG( WAM) {
|
|||
|
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT
|
|||
|
, "WAM(%08x) shutting down. "
|
|||
|
"Request(%08x) will be aborted.\n"
|
|||
|
, this
|
|||
|
, pIWamRequest
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
// UNDONE something besides E_FAIL?
|
|||
|
return E_FAIL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// create, init the wamexec-info and add it to list
|
|||
|
pWamExecInfo = new WAM_EXEC_INFO( this );
|
|||
|
if( NULL == pWamExecInfo )
|
|||
|
{
|
|||
|
hrRet = E_OUTOFMEMORY;
|
|||
|
goto LError;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update the statistics counters
|
|||
|
//
|
|||
|
|
|||
|
m_WamStats.IncrWamRequests();
|
|||
|
|
|||
|
|
|||
|
if ( FAILED( hrRet = pWamExecInfo->InitWamExecInfo( pIWamRequest,
|
|||
|
cbWrcStrings,
|
|||
|
pOopCoreState
|
|||
|
) ) )
|
|||
|
{
|
|||
|
goto LError;
|
|||
|
}
|
|||
|
|
|||
|
if( !m_fInProcess )
|
|||
|
{
|
|||
|
//
|
|||
|
// This test shouldn't really be necessary. In the case where com plus
|
|||
|
// activates us in process even if we are marked to run in the
|
|||
|
// surrogate, we will fail the app creation.
|
|||
|
//
|
|||
|
DBG_ASSERT( CWamOopTokenInfo::HasInstance() );
|
|||
|
if( CWamOopTokenInfo::HasInstance() )
|
|||
|
{
|
|||
|
hrRet = CWamOopTokenInfo::QueryInstance()->ModifyTokenForOop
|
|||
|
(
|
|||
|
WRC_GET_FIX.m_hUserToken
|
|||
|
);
|
|||
|
|
|||
|
if( FAILED(hrRet) )
|
|||
|
{
|
|||
|
goto LError;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this->InsertIntoList( &pWamExecInfo->_ListEntry);
|
|||
|
|
|||
|
|
|||
|
DBG_WAMREQ_REFCOUNTS(( "WAM::ProcessRequest right after wrc construction ...", pWamExecInfo ));
|
|||
|
|
|||
|
if ( !FWin95() )
|
|||
|
{
|
|||
|
if ( !ImpersonateLoggedOnUser( WRC_GET_FIX.m_hUserToken ) )
|
|||
|
{
|
|||
|
IF_DEBUG( ERROR ) {
|
|||
|
DBGPRINTF((DBG_CONTEXT,
|
|||
|
"WAM(%08x) ImpersonateLoggedOnUser(%08x)"
|
|||
|
"failed[err %d]\n",
|
|||
|
this, WRC_GET_FIX.m_hUserToken, GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
hrRet = HresultFromGetLastError();
|
|||
|
goto LError;
|
|||
|
}
|
|||
|
fImpersonated = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
pWamExecInfo->_psExtension = NULL;
|
|||
|
|
|||
|
if ( ! g_psextensions->GetExtension( WRC_GET_SZ( WRC_I_ISADLLPATH ),
|
|||
|
WRC_GET_FIX.m_hUserToken,
|
|||
|
WRC_GET_FIX.m_fAnonymous,
|
|||
|
WRC_GET_FIX.m_fCacheISAPIApps,
|
|||
|
&(pWamExecInfo->_psExtension ) ) )
|
|||
|
{
|
|||
|
|
|||
|
hrRet = HresultFromGetLastError( );
|
|||
|
goto LError;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Add a reference to ensure that wamexec is valid until we hit the
|
|||
|
// cleanup code. Don't put any goto LError after this or the wamexec
|
|||
|
// will leak.
|
|||
|
pWamExecInfo->AddRef();
|
|||
|
|
|||
|
// Invoke the server extension
|
|||
|
callContext.PrepareCom();
|
|||
|
iretInvokeExt = InvokeExtension( pWamExecInfo->_psExtension,
|
|||
|
WRC_GET_SZ( WRC_I_ISADLLPATH ),
|
|||
|
pWamExecInfo );
|
|||
|
callContext.UnprepareCom();
|
|||
|
|
|||
|
if ( fImpersonated ) {
|
|||
|
::RevertToSelf( );
|
|||
|
fImpersonated = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
pWamExecInfo->_dwFlags &= ~SE_PRIV_FLAG_IN_CALLBACK;
|
|||
|
|
|||
|
switch ( iretInvokeExt )
|
|||
|
{
|
|||
|
|
|||
|
case HSE_STATUS_PENDING: {
|
|||
|
|
|||
|
IF_DEBUG( WAM_EXEC ) {
|
|||
|
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT
|
|||
|
, "WAM(%08x)::ProcessRequest case HSE_STATUS_PENDING\n"
|
|||
|
, this
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG( WAM_REFCOUNTS ) {
|
|||
|
|
|||
|
DBG_WAMREQ_REFCOUNTS((
|
|||
|
"WAM::ProcessRequest case HSE_STATUS_PENDING ",
|
|||
|
pWamExecInfo
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Figure out whether this mainline thread or callback thread
|
|||
|
// hit its cleanup code first
|
|||
|
//
|
|||
|
// This protects us against isapis that disobey the async rules.
|
|||
|
// The isapi should be in one of two modes:
|
|||
|
//
|
|||
|
// 1. It return HSE_STATUS_PENDING in the mainline thread and
|
|||
|
// always calls HSE_DONE_WITH_SESSION.
|
|||
|
//
|
|||
|
// 2. It returns any other status code from the mainline and
|
|||
|
// NEVER calls HSE_DONE_WITH_SESSION.
|
|||
|
//
|
|||
|
// Unfortunately isapi writers frequently do bad things to good
|
|||
|
// servers.
|
|||
|
//
|
|||
|
// NOTE return value of INTERLOCKED_COMPARE_EXCHANGE
|
|||
|
// is initial value of the destination
|
|||
|
//
|
|||
|
|
|||
|
LONG FirstThread = INTERLOCKED_COMPARE_EXCHANGE(
|
|||
|
(LONG *) &pWamExecInfo->_FirstThread
|
|||
|
, (LONG) FT_MAINLINE
|
|||
|
, (LONG) FT_NULL
|
|||
|
);
|
|||
|
|
|||
|
if( FirstThread == (LONG) FT_CALLBACK ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we made it here, then we need to do cleanup, meaning:
|
|||
|
// - SSF HSE_REQ_DONE_WITH_SESSION callback has already run
|
|||
|
// - we set fFreeContext TRUE to trigger cleanup below
|
|||
|
//
|
|||
|
|
|||
|
fFreeContext = TRUE;
|
|||
|
|
|||
|
IF_DEBUG( WAM_EXEC ) {
|
|||
|
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT
|
|||
|
, "\tSession done.\n"
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DBG_ASSERT( FirstThread == (LONG) FT_NULL );
|
|||
|
|
|||
|
//
|
|||
|
// If we made it here, then we can't cleanup yet, meaning:
|
|||
|
// - SSF HSE_REQ_DONE_WITH_SESSION callback will cleanup
|
|||
|
// when it runs
|
|||
|
// - we set fFreeContext FALSE to avoid cleanup below
|
|||
|
//
|
|||
|
fFreeContext = FALSE;
|
|||
|
|
|||
|
IF_DEBUG( WAM_EXEC ) {
|
|||
|
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT
|
|||
|
, "\tSession NOT done.\n"
|
|||
|
));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If fFreeContext is FALSE at this point, we may not use
|
|||
|
// pWamExecInfo from this point on - callback thread can
|
|||
|
// invalidate it at any time.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
} // case HSE_STATUS_PENDING
|
|||
|
|
|||
|
|
|||
|
case HSE_STATUS_ERROR:
|
|||
|
case HSE_STATUS_SUCCESS:
|
|||
|
|
|||
|
//
|
|||
|
// in error and success cases we no-op
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
|
|||
|
|
|||
|
//
|
|||
|
// remember that ISA asked us to set keep-conn
|
|||
|
//
|
|||
|
|
|||
|
pWamExecInfo->_dwIsaKeepConn = KEEPCONN_TRUE;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
break;
|
|||
|
} // switch()
|
|||
|
|
|||
|
// Release the ref held by this call
|
|||
|
pWamExecInfo->Release();
|
|||
|
|
|||
|
if ( fFreeContext ) {
|
|||
|
|
|||
|
DBG_ASSERT( pWamExecInfo != NULL);
|
|||
|
|
|||
|
DBG_WAMREQ_REFCOUNTS(( "WAM::ProcessRequest fFreeContext ...",
|
|||
|
pWamExecInfo));
|
|||
|
|
|||
|
|
|||
|
|
|||
|
pWamExecInfo->CleanupAndRelease( TRUE );
|
|||
|
|
|||
|
pWamExecInfo = NULL;
|
|||
|
|
|||
|
} // if ( fFreeContext);
|
|||
|
|
|||
|
*pfHandled = TRUE;
|
|||
|
DBG_ASSERT( hrRet == NOERROR );
|
|||
|
|
|||
|
|
|||
|
LExit:
|
|||
|
|
|||
|
if ( fImpersonated ) {
|
|||
|
|
|||
|
::RevertToSelf( );
|
|||
|
fImpersonated = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return hrRet;
|
|||
|
|
|||
|
|
|||
|
LError:
|
|||
|
|
|||
|
//
|
|||
|
// release pWamExecInfo on failure case
|
|||
|
// NOTE we separate this from normal exit code because wamexecinfo
|
|||
|
// must hang around in many non-error cases (status-pending, async i/o)
|
|||
|
//
|
|||
|
|
|||
|
if ( pWamExecInfo != NULL) {
|
|||
|
|
|||
|
pWamExecInfo->CleanupAndRelease( FALSE );
|
|||
|
|
|||
|
pWamExecInfo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
goto LExit;
|
|||
|
|
|||
|
} // WAM::ProcessRequest()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
STDMETHODIMP
|
|||
|
WAM::GetStatistics(
|
|||
|
/*[in]*/ DWORD dwLevel,
|
|||
|
/*[out, switch_is(Level)]*/
|
|||
|
LPWAM_STATISTICS_INFO pWamStatsInfo
|
|||
|
)
|
|||
|
/*++
|
|||
|
Description:
|
|||
|
Obtains the statistics for the given instance of WAM for specified level.
|
|||
|
|
|||
|
Arguments:
|
|||
|
dwLevel - specifies the information level. (Currently level 0 is supported)
|
|||
|
pWamStatsInfo - pointer to the WAM STATISTICS INFO object that will
|
|||
|
receive the statistics.
|
|||
|
|
|||
|
Returns:
|
|||
|
HRESULT - NOERROR on success and E_FAIL on failure.
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
|
|||
|
DBG_ASSERT( pWamStatsInfo != NULL);
|
|||
|
|
|||
|
IF_DEBUG( API_ENTRY) {
|
|||
|
DBGPRINTF(( DBG_CONTEXT, "WAM(%08x)::GetStatistics(%d, %08x)\n",
|
|||
|
this, dwLevel, pWamStatsInfo));
|
|||
|
}
|
|||
|
|
|||
|
switch ( dwLevel) {
|
|||
|
case 0: {
|
|||
|
// copy values out of the statistics structure
|
|||
|
m_WamStats.CopyToStatsBuffer( &pWamStatsInfo->WamStats0);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
DBG_ASSERT( FALSE);
|
|||
|
hr = E_FAIL;
|
|||
|
break;
|
|||
|
|
|||
|
} // switch()
|
|||
|
|
|||
|
return (hr);
|
|||
|
|
|||
|
} // WAM::GetStatistics()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
WAM::InvokeExtension
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Invokes a server extension.
|
|||
|
NOTE without this cover function, we get a compile error
|
|||
|
error C2712: Cannot use __try in functions that require object unwinding
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
psExt - pointer to server extension
|
|||
|
szISADllPath - Fully qualified path to Module (DLL name)
|
|||
|
pWamExecInfo - ptr to wamexec info
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
DWORD - HSE_STATUS_ code
|
|||
|
|
|||
|
--*/
|
|||
|
DWORD
|
|||
|
WAM::InvokeExtension
|
|||
|
(
|
|||
|
IN PHSE psExt,
|
|||
|
const char * szISADllPath,
|
|||
|
WAM_EXEC_INFO * pWamExecInfo
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
DWORD ret;
|
|||
|
|
|||
|
//
|
|||
|
// Protect the call to the server extension so we don't hose the
|
|||
|
// server
|
|||
|
//
|
|||
|
|
|||
|
__try
|
|||
|
{
|
|||
|
ret = psExt->ExecuteRequest( pWamExecInfo );
|
|||
|
}
|
|||
|
__except ( g_fEnableTryExcept ?
|
|||
|
WAMExceptionFilter( GetExceptionInformation(),
|
|||
|
WAM_EVENT_EXTENSION_EXCEPTION,
|
|||
|
pWamExecInfo ) :
|
|||
|
EXCEPTION_CONTINUE_SEARCH )
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// exception caused us to to leave HSE_APPDLL::ExecuteRequest
|
|||
|
// with unbalanced AddRef()
|
|||
|
//
|
|||
|
|
|||
|
pWamExecInfo->Release();
|
|||
|
|
|||
|
ret = HSE_STATUS_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
} // WAM::InvokeExtension
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
WAM::HseReleaseExtension
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Releases a server extension.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
psExt - pointer to server extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
VOID
|
|||
|
WAM::HseReleaseExtension
|
|||
|
(
|
|||
|
IN PHSE psExt
|
|||
|
)
|
|||
|
{
|
|||
|
g_psextensions->ReleaseExtension( psExt);
|
|||
|
} // HseReleaseExtension()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/************************************************************
|
|||
|
* Member Functions of WAM_STATISTICS
|
|||
|
************************************************************/
|
|||
|
|
|||
|
|
|||
|
WAM_STATISTICS::WAM_STATISTICS( VOID)
|
|||
|
/*++
|
|||
|
Initializes statistics information for server.
|
|||
|
--*/
|
|||
|
{
|
|||
|
INITIALIZE_CRITICAL_SECTION( & m_csStatsLock);
|
|||
|
ClearStatistics();
|
|||
|
|
|||
|
} // WAM_STATISTICS::WAM_STATISTICS();
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
WAM_STATISTICS::ClearStatistics( VOID)
|
|||
|
/*++
|
|||
|
|
|||
|
Clears the counters used for statistics information
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LockStatistics();
|
|||
|
|
|||
|
memset( &m_WamStats, 0, sizeof(WAM_STATISTICS_0) );
|
|||
|
m_WamStats.TimeOfLastClear = GetCurrentTimeInSeconds();
|
|||
|
|
|||
|
UnlockStatistics();
|
|||
|
|
|||
|
} // WAM_STATISTICS::ClearStatistics()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
WAM_STATISTICS::CopyToStatsBuffer( PWAM_STATISTICS_0 pStat0)
|
|||
|
/*++
|
|||
|
Description:
|
|||
|
copies the statistics data from the server statistcs structure
|
|||
|
to the WAM_STATISTICS_0 structure for RPC access.
|
|||
|
|
|||
|
Arugments:
|
|||
|
pStat0 pointer to WAM_STATISTICS_0 object which contains the
|
|||
|
data on successful return
|
|||
|
|
|||
|
Returns:
|
|||
|
Win32 error codes. NO_ERROR on success.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
DBG_ASSERT( pStat0 != NULL);
|
|||
|
|
|||
|
LockStatistics();
|
|||
|
|
|||
|
CopyMemory( pStat0, &m_WamStats, sizeof(WAM_STATISTICS_0) );
|
|||
|
|
|||
|
UnlockStatistics();
|
|||
|
|
|||
|
return ( NO_ERROR);
|
|||
|
|
|||
|
} // WAM_STATISTICS::CopyToStatsBuffer()
|
|||
|
|
|||
|
/************************ End of File ***********************/
|