/*++ 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 # include "isapidll.hxx" # include "setable.hxx" # include /************************************************************ * 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 ***********************/