771 lines
16 KiB
C++
771 lines
16 KiB
C++
/*++
|
||
|
||
Copyright (c) 1995-1996 Microsoft Corporation
|
||
|
||
Module Name :
|
||
setable.cxx
|
||
|
||
Abstract:
|
||
This module declares the SE_TABLE object which consists
|
||
of all the extensions for W3 service
|
||
|
||
Author:
|
||
|
||
Murali R. Krishnan ( MuraliK ) 18-July-1996
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Project:
|
||
|
||
W3 Services DLL
|
||
|
||
--*/
|
||
|
||
|
||
/************************************************************
|
||
* Include Headers
|
||
************************************************************/
|
||
|
||
# include <isapip.hxx>
|
||
|
||
# include "isapidll.hxx"
|
||
# include "setable.hxx"
|
||
|
||
# include <issched.hxx>
|
||
|
||
/************************************************************
|
||
* Global Data
|
||
************************************************************/
|
||
|
||
PSE_TABLE g_psextensions; // all extensions
|
||
static BOOL g_fExtInitialized = FALSE;
|
||
|
||
|
||
|
||
|
||
/**************************************************
|
||
* Member functions of SESD_LIST
|
||
**************************************************/
|
||
|
||
|
||
SESD_LIST::SESD_LIST(VOID)
|
||
: m_idWorkItem( 0)
|
||
{
|
||
INITIALIZE_CRITICAL_SECTION( &m_csLock);
|
||
InitializeListHead( &m_Head);
|
||
|
||
} // SESD_LIST::SESD_LIST()
|
||
|
||
|
||
SESD_LIST::~SESD_LIST(VOID)
|
||
{
|
||
// Should be all done
|
||
DBG_ASSERT( 0 == m_idWorkItem);
|
||
DBG_ASSERT( IsListEmpty( &m_Head));
|
||
|
||
DeleteCriticalSection( &m_csLock);
|
||
|
||
} // SESD_LIST::~SESD_LIST()
|
||
|
||
|
||
|
||
VOID
|
||
SESD_LIST::ScheduleExtensionForDeletion(
|
||
IN PHSE psExt
|
||
)
|
||
{
|
||
Lock();
|
||
|
||
// add to the list
|
||
InsertHeadList( &m_Head, &psExt->m_ListEntry);
|
||
|
||
if ( 0 == m_idWorkItem) {
|
||
// schedule the work item if not scheduled one already
|
||
m_idWorkItem = ScheduleWorkItem( SchedulerCallback, this, 0);
|
||
DBG_ASSERT( 0 != m_idWorkItem);
|
||
}
|
||
|
||
Unlock();
|
||
|
||
return;
|
||
} // SESD_LIST::ScheduleExtensionForDeletion()
|
||
|
||
|
||
|
||
VOID
|
||
SESD_LIST::WaitTillFinished(VOID)
|
||
{
|
||
Lock();
|
||
|
||
// wait until nothing is scheduled
|
||
|
||
while ( 0 != m_idWorkItem) {
|
||
|
||
// let it drain itself while in the unlocked state
|
||
Unlock();
|
||
Sleep( 200 );
|
||
Lock();
|
||
}
|
||
|
||
Unlock();
|
||
|
||
return;
|
||
} // SESD_LIST::WaitTillFinished()
|
||
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
SESD_LIST::SchedulerCallback(
|
||
void *pvContext
|
||
)
|
||
{
|
||
DBG_ASSERT( NULL != pvContext);
|
||
|
||
((SESD_LIST *)pvContext)->DeleteScheduledExtensions();
|
||
|
||
return;
|
||
} // SESD_LIST::SchedulerCallback()
|
||
|
||
|
||
VOID
|
||
SESD_LIST::DeleteScheduledExtensions(VOID)
|
||
{
|
||
PHSE psExt;
|
||
|
||
Lock();
|
||
|
||
// If it is scheduled there better be a reason why
|
||
DBG_ASSERT( 0 != m_idWorkItem);
|
||
|
||
while ( !IsListEmpty( &m_Head)) {
|
||
|
||
// extract extension from the list and delete it
|
||
|
||
psExt = CONTAINING_RECORD( m_Head.Flink,
|
||
HSE_BASE,
|
||
m_ListEntry );
|
||
|
||
RemoveEntryList( &psExt->m_ListEntry );
|
||
InitializeListHead( &psExt->m_ListEntry);
|
||
|
||
delete psExt;
|
||
|
||
} // while
|
||
|
||
|
||
// reset to 0 to indicate that we are done
|
||
m_idWorkItem = 0;
|
||
|
||
Unlock();
|
||
|
||
return;
|
||
} // SESD_LIST::DeleteScheduledExtensions()
|
||
|
||
|
||
|
||
/**************************************************
|
||
* Member functions of SE_TABLE
|
||
**************************************************/
|
||
|
||
|
||
SE_TABLE::SE_TABLE(VOID)
|
||
: m_cRefWams ( 0)
|
||
{
|
||
IF_DEBUG( INIT_CLEAN) {
|
||
DBGPRINTF(( DBG_CONTEXT, "Constructing SE_TABLE(%08x)\n", this));
|
||
}
|
||
|
||
INITIALIZE_CRITICAL_SECTION( &m_csLock);
|
||
InitializeListHead( &m_ExtensionHead);
|
||
|
||
} // SE_TABLE::SE_TABLE()
|
||
|
||
|
||
SE_TABLE::~SE_TABLE(VOID)
|
||
{
|
||
IF_DEBUG( INIT_CLEAN) {
|
||
DBGPRINTF(( DBG_CONTEXT, "Deleteing SE_TABLE(%08x) cWamRef=%d\n",
|
||
this, m_cRefWams));
|
||
}
|
||
|
||
// unload all the extensions if not already done
|
||
DBG_REQUIRE( UnloadExtensions());
|
||
|
||
// No WAM should be holding ref to the SE_TABLE
|
||
DBG_ASSERT( 0 == m_cRefWams);
|
||
|
||
DeleteCriticalSection( &m_csLock);
|
||
DBG_ASSERT( IsListEmpty( &m_ExtensionHead));
|
||
|
||
} // SE_TABLE::~SE_TABLE()
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SE_TABLE::Print(VOID)
|
||
{
|
||
PLIST_ENTRY pEntry;
|
||
int i = 0;
|
||
|
||
// NYI: I need to write code to print out data here.
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
" SE_TABLE(%08x) ListHead=%08x [%08x:%08x] cRefWams=%d\n",
|
||
this, &m_ExtensionHead,
|
||
m_ExtensionHead.Flink, m_ExtensionHead.Blink,
|
||
m_cRefWams
|
||
));
|
||
|
||
Lock();
|
||
|
||
for ( pEntry = m_ExtensionHead.Flink;
|
||
(pEntry != &m_ExtensionHead);
|
||
pEntry = pEntry->Flink, i++ )
|
||
{
|
||
HSE_BASE *
|
||
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
" Dll(%d): HSE_BASE = %08x [%08x:%08x]; Module=%s\n",
|
||
i, pExtension,
|
||
pExtension->m_ListEntry.Flink,
|
||
pExtension->m_ListEntry.Blink,
|
||
pExtension->QueryModuleName()));
|
||
} // for all entries in table
|
||
|
||
Unlock();
|
||
|
||
return;
|
||
} // SE_TABLE::Print()
|
||
|
||
|
||
BOOL
|
||
SE_TABLE::GetExtension(
|
||
IN const CHAR * pchModuleName,
|
||
IN HANDLE hImpersonation,
|
||
IN BOOL fCacheImpersonation,
|
||
IN BOOL fCache,
|
||
OUT PHSE * ppsExt
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Retrieves an extension's DLL entry point
|
||
|
||
The appropriate user should be impersonated before calling this function
|
||
|
||
Arguments:
|
||
|
||
pchModuleName - Extension DLL module name
|
||
hImpersonation - Impersonation token of user making this call
|
||
fCacheImpersonation - if TRUE, the hImpersonation can be cached
|
||
fCache - TRUE if this item should be cached, FALSE otherwise
|
||
ppsExt - pointer to the Extensions handler object
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful, FALSE on error
|
||
|
||
--*/
|
||
{
|
||
LIST_ENTRY * pEntry;
|
||
HSE_BASE * pExtension = NULL;
|
||
BOOL fRet = FALSE;
|
||
|
||
IF_DEBUG( BGI ) {
|
||
|
||
DBGPRINTF(( DBG_CONTEXT, "[GetEntryPoint] Looking for module %s\n",
|
||
pchModuleName ));
|
||
|
||
}
|
||
|
||
//
|
||
// NYI: Optimize the locked section here - reduce time spent inside
|
||
// the locked block.
|
||
// Check cache to see if DLL is already loaded
|
||
//
|
||
|
||
DWORD cchModuleName = strlen( pchModuleName);
|
||
|
||
Lock();
|
||
|
||
for ( pEntry = m_ExtensionHead.Flink;
|
||
pEntry != &m_ExtensionHead;
|
||
pEntry = pEntry->Flink )
|
||
{
|
||
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
||
|
||
if ( pExtension->IsMatch( pchModuleName, cchModuleName)) {
|
||
//
|
||
// Already Loaded, return the extension object after access check
|
||
//
|
||
|
||
fRet = TRUE;
|
||
break;
|
||
} // a match
|
||
} // for all entries in table
|
||
|
||
IF_DEBUG( BGI ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[GetEntryPoint] Lookup module %s => fRet =%d\n",
|
||
pchModuleName, fRet ));
|
||
}
|
||
|
||
if ( !fRet) {
|
||
|
||
//
|
||
// The module is not loaded. Load the module now.
|
||
// The module name refers to an ISAPI application.
|
||
// Try ISAPI app loader now...
|
||
pExtension = HSE_APPDLL::LoadModule( pchModuleName,
|
||
hImpersonation,
|
||
fCache);
|
||
|
||
fRet = (pExtension != NULL);
|
||
if (fRet) {
|
||
if ( !fCache) {
|
||
|
||
// Temporarily the ref count will drop to 0, but the code below
|
||
// will bump this up to 1.
|
||
|
||
pExtension->Dereference();
|
||
} else {
|
||
// add this extension to the list of cached extensions
|
||
InsertIntoListWithLock( pExtension);
|
||
}
|
||
} else {
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"LoadModule failed with %x\n",GetLastError()));
|
||
}
|
||
}
|
||
|
||
if ( fRet ) {
|
||
|
||
if ( pExtension->AccessCheck( hImpersonation, fCacheImpersonation)) {
|
||
|
||
DBG_ASSERT( pExtension != NULL);
|
||
|
||
if ( ppsExt != NULL) {
|
||
// ref the object before giving ptr away
|
||
pExtension->Reference();
|
||
*ppsExt = pExtension;
|
||
}
|
||
} else {
|
||
fRet = FALSE;
|
||
}
|
||
}
|
||
|
||
Unlock();
|
||
|
||
return fRet;
|
||
} // SE_TABLE::GetExtension()
|
||
|
||
|
||
|
||
|
||
BOOL
|
||
SE_TABLE::RefreshAcl(
|
||
IN const CHAR * pchDLL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function reloads the ACL on an ISAPI Application .dll after that
|
||
.dll has already been loaded.
|
||
|
||
Arguments:
|
||
|
||
pchDLL - pointer to the ISAPI DLL for which ACL has to be refreshed.
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE on failure
|
||
|
||
--*/
|
||
{
|
||
LIST_ENTRY * pEntry;
|
||
PHSE pExtension;
|
||
BOOL fRet = FALSE;
|
||
BOOL fFound = FALSE;
|
||
|
||
IF_DEBUG( BGI )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[RefreshISAPIAcl] Rereading ACL for %s\n",
|
||
pchDLL ));
|
||
|
||
}
|
||
|
||
//
|
||
// Check cache to see if the DLL is loaded
|
||
//
|
||
DWORD cchDll = strlen( pchDLL);
|
||
|
||
Lock();
|
||
|
||
for ( pEntry = m_ExtensionHead.Flink;
|
||
pEntry != &m_ExtensionHead;
|
||
pEntry = pEntry->Flink )
|
||
{
|
||
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
||
|
||
if ( pExtension->IsMatch( pchDLL, cchDll) )
|
||
{
|
||
//
|
||
// Force an access check on the next request with the new ACL
|
||
//
|
||
|
||
fRet = pExtension->LoadAcl();
|
||
fFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
Unlock();
|
||
|
||
if ( !fFound) { SetLastError( ERROR_FILE_NOT_FOUND ); }
|
||
|
||
return (fRet);
|
||
} // SE_TABLE::RefreshAcl()
|
||
|
||
|
||
|
||
BOOL
|
||
SE_TABLE::RefreshAcl(
|
||
IN DWORD dwId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function reloads the ACL on an ISAPI Application .dll after that
|
||
.dll has already been loaded.
|
||
|
||
Arguments:
|
||
|
||
pchDLL - pointer to the ISAPI DLL for which ACL has to be refreshed.
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE on failure
|
||
|
||
--*/
|
||
{
|
||
LIST_ENTRY * pEntry;
|
||
PHSE pExtension;
|
||
BOOL fRet = FALSE;
|
||
BOOL fFound = FALSE;
|
||
|
||
IF_DEBUG( BGI )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[RefreshISAPIAcl] Rereading ACL for %d\n",
|
||
dwId ));
|
||
|
||
}
|
||
|
||
//
|
||
// Check cache to see if the DLL is loaded
|
||
//
|
||
|
||
Lock();
|
||
|
||
for ( pEntry = m_ExtensionHead.Flink;
|
||
pEntry != &m_ExtensionHead;
|
||
pEntry = pEntry->Flink )
|
||
{
|
||
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
||
|
||
if ( pExtension->GetDirMonitorId() == dwId )
|
||
{
|
||
//
|
||
// Force an access check on the next request with the new ACL
|
||
//
|
||
|
||
fRet = pExtension->LoadAcl();
|
||
fFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
Unlock();
|
||
|
||
if ( !fFound) { SetLastError( ERROR_FILE_NOT_FOUND ); }
|
||
|
||
return (fRet);
|
||
} // SE_TABLE::RefreshAcl()
|
||
|
||
|
||
|
||
BOOL
|
||
SE_TABLE::FlushAccessToken(
|
||
IN HANDLE hAccTok
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reset last successfull user it same as hAccTok
|
||
|
||
Arguments:
|
||
|
||
hAccTok - access token to remove from cache
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE on failure
|
||
|
||
--*/
|
||
{
|
||
LIST_ENTRY * pEntry;
|
||
HSE_APPDLL * pExtension;
|
||
BOOL fRet = FALSE;
|
||
BOOL fFound = FALSE;
|
||
|
||
IF_DEBUG( BGI )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[FlushAccessToken] removing handle %x from cache\n",
|
||
hAccTok ));
|
||
|
||
}
|
||
|
||
//
|
||
// Check cache for access token
|
||
//
|
||
|
||
Lock();
|
||
|
||
for ( pEntry = m_ExtensionHead.Flink;
|
||
pEntry != &m_ExtensionHead;
|
||
pEntry = pEntry->Flink )
|
||
{
|
||
pExtension = CONTAINING_RECORD( pEntry, HSE_APPDLL, m_ListEntry );
|
||
|
||
if ( pExtension->QueryLastSuccessfulUser() == hAccTok )
|
||
{
|
||
//
|
||
// Force an access check on the next request for this token
|
||
//
|
||
|
||
pExtension->SetLastSuccessfulUser( NULL );
|
||
}
|
||
}
|
||
|
||
Unlock();
|
||
|
||
return TRUE;
|
||
} // SE_TABLE::RefreshAcl()
|
||
|
||
|
||
|
||
BOOL
|
||
SE_TABLE::UnloadExtensions(VOID)
|
||
{
|
||
PHSE psExt;
|
||
|
||
DBG_ASSERT( m_cRefWams == 0);
|
||
|
||
Lock();
|
||
|
||
while ( !IsListEmpty( &m_ExtensionHead )) {
|
||
|
||
psExt = CONTAINING_RECORD( m_ExtensionHead.Flink,
|
||
HSE_BASE,
|
||
m_ListEntry );
|
||
|
||
//
|
||
// Move from the current list to the UnloadList
|
||
//
|
||
|
||
RemoveEntryList( &psExt->m_ListEntry );
|
||
InitializeListHead( &psExt->m_ListEntry); // Cleanup
|
||
|
||
IF_DEBUG( INIT_CLEAN) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"Deref of HSE_APPDLL(%08x) = %s. Ref=%d\n",
|
||
psExt, psExt->QueryModuleName(), psExt->RefCount()));
|
||
}
|
||
|
||
//
|
||
// Cause an Unload of the ISAPI DLL
|
||
// Ugly cast should be cleaned up post-beta2
|
||
//
|
||
|
||
DBG_REQUIRE( ((HSE_APPDLL * )psExt)->Unload() == NO_ERROR);
|
||
|
||
// Decrement the life reference count of the ISAPI DLL structure
|
||
if ( !psExt->Dereference()) {
|
||
|
||
delete psExt;
|
||
}
|
||
} // while
|
||
|
||
Unlock();
|
||
|
||
// Drain the list of non-cached extensions scheduled for deletion
|
||
m_sesdExtensions.WaitTillFinished();
|
||
|
||
return (TRUE);
|
||
} // SE_TABLE::UnloadExtensions()
|
||
|
||
|
||
VOID
|
||
SE_TABLE::PrintRequestCounts(VOID)
|
||
/*++
|
||
Description:
|
||
Prints a summary of all the ISAPI Dlls loaded and the # requests
|
||
pending in each of them primarily for debugging purposes.
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Return:
|
||
None
|
||
--*/
|
||
{
|
||
PHSE psExt;
|
||
PLIST_ENTRY pEntry;
|
||
|
||
Lock();
|
||
|
||
if( m_ExtensionHead.Flink != &m_ExtensionHead ) {
|
||
DBGPRINTF( (DBG_CONTEXT, "SE_TABLE::Printing refs for all loaded ISAPI\n") );
|
||
|
||
for ( pEntry = m_ExtensionHead.Flink;
|
||
pEntry != &m_ExtensionHead;
|
||
pEntry = pEntry->Flink )
|
||
{
|
||
|
||
psExt = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
||
|
||
RemoveEntryList( &psExt->m_ListEntry );
|
||
|
||
DBGPRINTF( (DBG_CONTEXT,
|
||
" \'%s\') has RefCount = %d\n",
|
||
psExt->QueryModuleName(),
|
||
psExt->RefCount()
|
||
) );
|
||
} // for
|
||
DBGPRINTF( (DBG_CONTEXT, "-------------------------------\n") );
|
||
} // if
|
||
|
||
Unlock();
|
||
|
||
return;
|
||
} // SE_TABLE::PrintRequestCounts()
|
||
|
||
|
||
|
||
VOID
|
||
SE_TABLE::ReleaseExtension( IN PHSE psExt)
|
||
{
|
||
// decrement ref count and remove from table if needed
|
||
if ( !psExt->Dereference()) {
|
||
|
||
if ( psExt->IsCached())
|
||
{
|
||
RemoveFromList( psExt);
|
||
delete psExt;
|
||
}
|
||
else {
|
||
|
||
// non cached ISAPIs should be deleted (TerminateExtension
|
||
// call should be called) from a different thread because
|
||
// this thread could be an ISAPI thread (in case of pended
|
||
// ISAPI this could be the DONE_WITH_SESSION thread)
|
||
|
||
m_sesdExtensions.ScheduleExtensionForDeletion( psExt);
|
||
}
|
||
}
|
||
|
||
return;
|
||
} // SE_TABLE::ReleaseExtension()
|
||
|
||
|
||
|
||
APIERR
|
||
InitializeHseExtensions(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes the extension list cache
|
||
|
||
|
||
Return Value:
|
||
|
||
0 on success, win32 error on failure
|
||
|
||
--*/
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT, " InitExtensions() entered \n"));
|
||
|
||
DWORD err = NO_ERROR;
|
||
|
||
// UNDONE needs to move into wam.dll DllInit
|
||
if ( err == NO_ERROR ) {
|
||
|
||
// initialize other objects
|
||
g_psextensions = new SE_TABLE();
|
||
|
||
if ( (g_psextensions == NULL) ) {
|
||
|
||
err = (ERROR_NOT_ENOUGH_MEMORY);
|
||
if ( g_psextensions != NULL ) {
|
||
delete (g_psextensions);
|
||
g_psextensions = NULL;
|
||
}
|
||
|
||
} else {
|
||
g_fExtInitialized = TRUE;
|
||
}
|
||
}
|
||
|
||
DBGPRINTF(( DBG_CONTEXT, " Leaving InitExtensions().\n" ));
|
||
|
||
return ( err);
|
||
|
||
} // InitializeHseExtensions()
|
||
|
||
|
||
|
||
VOID
|
||
CleanupHseExtensions(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Walks list and unloads each extension. No clients should be in an
|
||
extension at this point
|
||
|
||
--*/
|
||
{
|
||
DWORD i;
|
||
|
||
DBGPRINTF(( DBG_CONTEXT, " TerminateExtensions() called \n"
|
||
));
|
||
|
||
|
||
if ( !g_fExtInitialized )
|
||
return;
|
||
|
||
DBG_ASSERT( g_psextensions != NULL);
|
||
|
||
delete g_psextensions;
|
||
|
||
return;
|
||
} // CleanupHseExtensions()
|
||
|
||
|
||
/************************ End of File ***********************/
|