2136 lines
61 KiB
C++
2136 lines
61 KiB
C++
|
/*-----------------------------------------------------------------------------
|
|||
|
|
|||
|
Copyright (c) 1995-1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name :
|
|||
|
wamexec.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
This module executes a wam request
|
|||
|
|
|||
|
Author:
|
|||
|
David Kaplan ( DaveK ) 11-Mar-1997
|
|||
|
Lei Jin (leijin) 24-Apr-1997 (WamInfo & WamDictator)
|
|||
|
|
|||
|
Environment:
|
|||
|
User Mode - Win32
|
|||
|
|
|||
|
Project:
|
|||
|
W3 services DLL
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
|
|||
|
#include <w3p.hxx>
|
|||
|
#include "wamexec.hxx"
|
|||
|
#include "wamreq.hxx"
|
|||
|
#include <wmrgexp.h>
|
|||
|
#include <ole2.h>
|
|||
|
#include <imd.h>
|
|||
|
#include <mb.hxx>
|
|||
|
#include <issched.hxx>
|
|||
|
|
|||
|
#define dwMDDefaultTimeOut 2000
|
|||
|
|
|||
|
#define PERIODIC_RESTART_TIME_DEFAULT 0
|
|||
|
#define PERIODIC_RESTART_REQUESTS_DEFAULT 0
|
|||
|
#define SHUTDOWN_TIME_LIMIT_DEFAULT 600
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
Globals
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
WAM_DICTATOR * g_pWamDictator; // global wam dictator
|
|||
|
PFN_INTERLOCKED_COMPARE_EXCHANGE g_pfnInterlockedCompareExchange = NULL;
|
|||
|
static VOID UnloadNTApis(VOID);
|
|||
|
static VOID LoadNTApis(VOID);
|
|||
|
|
|||
|
// Default Package name, defined in wamreg.h, shared by wamreg.dll.
|
|||
|
WCHAR g_szIISInProcWAMCLSID[] = W3_INPROC_WAM_CLSID;
|
|||
|
WCHAR g_szIISOOPPoolWAMCLSID[] = W3_OOP_POOL_WAM_CLSID;
|
|||
|
WCHAR g_szIISOOPPoolPackageID[] = W3_OOP_POOL_PACKAGE_ID;
|
|||
|
// Sink function at wamreg.dll
|
|||
|
// This function will get called when user change application configuration on the fly.
|
|||
|
//
|
|||
|
HRESULT W3SVC_WamRegSink( LPCSTR szAppPath,
|
|||
|
const DWORD dwCommand,
|
|||
|
DWORD* pdwResult
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
CreateWamRequestInstance
|
|||
|
Similar to CoCreateInstance followed by QueryInterface
|
|||
|
|
|||
|
Arguments:
|
|||
|
pHttpRequest - pointer to the HTTP REQUEST object containing all information
|
|||
|
about the current request.
|
|||
|
pExec - Execution descriptor block
|
|||
|
pstrPath - Fully qualified path to Isapi DLL
|
|||
|
pWamInfo - pointer to wam-info which will process this request
|
|||
|
ppWamRequestOut -pointer to a pointer that contains the return WamRequest.
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
CreateWamRequestInstance
|
|||
|
(
|
|||
|
HTTP_REQUEST * pHttpRequest,
|
|||
|
EXEC_DESCRIPTOR * pExec,
|
|||
|
const STR * pstrPath,
|
|||
|
CWamInfo * pWamInfo,
|
|||
|
WAM_REQUEST ** ppWamRequestOut
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
WAM_REQUEST * pWamRequest =
|
|||
|
new WAM_REQUEST(
|
|||
|
pHttpRequest
|
|||
|
, pExec
|
|||
|
, pWamInfo
|
|||
|
);
|
|||
|
|
|||
|
*ppWamRequestOut = NULL;
|
|||
|
if( pWamRequest == NULL)
|
|||
|
{
|
|||
|
hr = E_OUTOFMEMORY;
|
|||
|
goto LExit;
|
|||
|
}
|
|||
|
|
|||
|
// Init the wam req
|
|||
|
hr = pWamRequest->InitWamRequest( pstrPath );
|
|||
|
|
|||
|
if ( FAILED( hr ) )
|
|||
|
{
|
|||
|
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT
|
|||
|
, "CreateWamRequestInstance - "
|
|||
|
"failed to init wam request. "
|
|||
|
"pHttpRequest(%08x) "
|
|||
|
"pWamInfo(%08x) "
|
|||
|
"pstrPath(%s) "
|
|||
|
"hr(%d) "
|
|||
|
"\n"
|
|||
|
, pHttpRequest
|
|||
|
, pWamInfo
|
|||
|
, pstrPath
|
|||
|
, hr
|
|||
|
));
|
|||
|
|
|||
|
delete pWamRequest;
|
|||
|
pWamRequest = NULL;
|
|||
|
goto LExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
pWamRequest->AddRef();
|
|||
|
|
|||
|
*ppWamRequestOut = pWamRequest;
|
|||
|
|
|||
|
LExit:
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::ProcessWamRequest
|
|||
|
This function finds a WamInfo and makes a call to the WamInfo to process a Http Server
|
|||
|
Extension Request.
|
|||
|
It uses the HTTP_REQUEST passed in as well as the EXEC_DESCRIPTOR.
|
|||
|
|
|||
|
Arguments:
|
|||
|
pHttpRequest - pointer to the HTTP REQUEST object containing all information
|
|||
|
about the current request.
|
|||
|
pExec - Execution descriptor block
|
|||
|
pstrPath - Fully qualified path to Module (DLL or ComIsapi ProgId)
|
|||
|
pfHandled - Indicates we handled this request
|
|||
|
pfFinished - Indicates no further processing is required
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::ProcessWamRequest
|
|||
|
(
|
|||
|
HTTP_REQUEST * pHttpRequest,
|
|||
|
EXEC_DESCRIPTOR * pExec,
|
|||
|
const STR * pstrPath,
|
|||
|
BOOL * pfHandled,
|
|||
|
BOOL * pfFinished
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
CWamInfo* pWamInfo = NULL;
|
|||
|
STR * pstrAppPath = NULL;
|
|||
|
CLSID clsidWam;
|
|||
|
BOOL fRet = FALSE;
|
|||
|
|
|||
|
DBG_ASSERT(pHttpRequest);
|
|||
|
DBG_ASSERT(pExec);
|
|||
|
|
|||
|
if (m_fShutdownInProgress)
|
|||
|
{
|
|||
|
DBGPRINTF((DBG_CONTEXT, "Wam Dictator: Shut down in progress, stops serving requests.\n"));
|
|||
|
|
|||
|
// Signal the child event to indicate that the request should terminate.
|
|||
|
if ( pExec->IsChild() ) {
|
|||
|
pExec->SetChildEvent();
|
|||
|
}
|
|||
|
|
|||
|
return E_FAIL;
|
|||
|
}
|
|||
|
|
|||
|
pstrAppPath = pExec->QueryAppPath();
|
|||
|
|
|||
|
if (pstrAppPath == NULL)
|
|||
|
{
|
|||
|
hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
|||
|
goto LExit;
|
|||
|
}
|
|||
|
|
|||
|
hr = GetWamInfo( pstrAppPath, pHttpRequest, &pWamInfo );
|
|||
|
if ( FAILED(hr) )
|
|||
|
{
|
|||
|
DBGPRINTF(( DBG_CONTEXT,
|
|||
|
"WAM_DICTATOR::ProcessWamRequest: GetWamInfo failed, hr = %08x\n",
|
|||
|
hr
|
|||
|
));
|
|||
|
|
|||
|
//
|
|||
|
// For Child execution just return the error and don't
|
|||
|
// disconnect, otherwise a race ensues between SSI and the disconnect
|
|||
|
// cleanup
|
|||
|
//
|
|||
|
|
|||
|
if ( pExec->IsChild() )
|
|||
|
{
|
|||
|
SetLastError( hr );
|
|||
|
goto LExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Since WAM_DICTATOR is going to send some message to the browser, we need
|
|||
|
// to set pfHandled = TRUE here.
|
|||
|
//
|
|||
|
*pfHandled = TRUE;
|
|||
|
|
|||
|
if (W3PlatformType == PtWindows95)
|
|||
|
{
|
|||
|
pHttpRequest->SetLogStatus( HT_SERVER_ERROR, hr);
|
|||
|
// Win95 platform, no event log, special message without mention EventLog.
|
|||
|
pHttpRequest->Disconnect(HT_SERVER_ERROR, IDS_WAM_FAILTOLOADONW95_ERROR, TRUE, pfFinished);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Log to Event Log first.
|
|||
|
//
|
|||
|
const CHAR *pszEventLog[2];
|
|||
|
CHAR szErrorDescription[2048];
|
|||
|
CHAR * pszErrorDescription = NULL;
|
|||
|
HANDLE hMetabase = GetModuleHandle( "METADATA.DLL" );
|
|||
|
|
|||
|
if(FormatMessage(
|
|||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|||
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|||
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|||
|
hMetabase,
|
|||
|
hr,
|
|||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|||
|
(LPTSTR) &pszErrorDescription,
|
|||
|
sizeof(szErrorDescription) - 1,
|
|||
|
NULL
|
|||
|
) && pszErrorDescription )
|
|||
|
{
|
|||
|
strcpy(szErrorDescription, pszErrorDescription);
|
|||
|
LocalFree(pszErrorDescription);
|
|||
|
|
|||
|
} else {
|
|||
|
wsprintf(szErrorDescription, "%08x", hr);
|
|||
|
}
|
|||
|
|
|||
|
pszEventLog[0] = pstrAppPath->QueryStr();
|
|||
|
pszEventLog[1] = szErrorDescription;
|
|||
|
// Event log
|
|||
|
g_pInetSvc->LogEvent(W3_EVENT_FAIL_LOADWAM,
|
|||
|
2,
|
|||
|
pszEventLog,
|
|||
|
0);
|
|||
|
|
|||
|
pHttpRequest->SetLogStatus( HT_SERVER_ERROR, hr);
|
|||
|
pHttpRequest->Disconnect(HT_SERVER_ERROR, IDS_WAM_FAILTOLOAD_ERROR, TRUE, pfFinished);
|
|||
|
}
|
|||
|
//
|
|||
|
// Since we already called Disconnect, WAM_DICTATOR should return TRUE to high level.
|
|||
|
//
|
|||
|
hr = NOERROR;
|
|||
|
goto LExit;
|
|||
|
}
|
|||
|
|
|||
|
hr = pWamInfo->ProcessWamRequest(pHttpRequest, pExec, pstrPath, pfHandled,pfFinished);
|
|||
|
|
|||
|
LExit:
|
|||
|
return hr;
|
|||
|
} // WAM_DICTATOR::ProcessWamRequest()
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------*
|
|||
|
WAM_DICTATOR::WAM_DICTATOR
|
|||
|
Constructor
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
|
|||
|
WAM_DICTATOR::WAM_DICTATOR
|
|||
|
(
|
|||
|
)
|
|||
|
:
|
|||
|
//m_pPackageCtl(NULL),
|
|||
|
m_cRef(0),
|
|||
|
m_pMetabase(NULL),
|
|||
|
m_fCleanupInProgress(FALSE),
|
|||
|
m_fShutdownInProgress(FALSE),
|
|||
|
m_hW3Svc ( (HANDLE)NULL ),
|
|||
|
m_dwScheduledId (0),
|
|||
|
m_strRootAppPath("/LM/W3SVC"),
|
|||
|
m_HashTable(LK_DFLT_MAXLOAD, LK_DFLT_INITSIZE, LK_DFLT_NUM_SUBTBLS),
|
|||
|
m_pidInetInfo(0)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::WAM_DICTATOR\n"));
|
|||
|
|
|||
|
InitializeListHead(&m_DyingListHead);
|
|||
|
|
|||
|
INITIALIZE_CRITICAL_SECTION(&m_csWamCreation);
|
|||
|
INITIALIZE_CRITICAL_SECTION(&m_csDyingList);
|
|||
|
|
|||
|
m_PTable.Init();
|
|||
|
//
|
|||
|
// Init wamreq allocation cache
|
|||
|
//
|
|||
|
|
|||
|
DBG_REQUIRE( WAM_REQUEST::InitClass() );
|
|||
|
|
|||
|
}//WAM_DICTATOR::WAM_DICTATOR
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------*
|
|||
|
WAM_DICTATOR::~WAM_DICTATOR
|
|||
|
Destructor
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
// (only interesting if out of process)
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
WAM_DICTATOR::~WAM_DICTATOR
|
|||
|
(
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::~WAM_DICTATOR\n"));
|
|||
|
|
|||
|
DBG_ASSERT(m_cRef == 0);
|
|||
|
|
|||
|
DeleteCriticalSection(&m_csWamCreation);
|
|||
|
DeleteCriticalSection(&m_csDyingList);
|
|||
|
|
|||
|
m_PTable.UnInit();
|
|||
|
//
|
|||
|
// Uninit wamreq allocation cache
|
|||
|
//
|
|||
|
WAM_REQUEST::CleanupClass();
|
|||
|
}//WAM_DICTATOR::~WAMDICTATOR
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::InitWamDictator
|
|||
|
Initializes Wam dictator
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::InitWamDictator
|
|||
|
(
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::InitWamDictator\n"));
|
|||
|
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
IMDCOM * pMetabase;
|
|||
|
|
|||
|
|
|||
|
DBG_ASSERT(g_pInetSvc->QueryMDObject());
|
|||
|
|
|||
|
m_pMetabase = new MB( (IMDCOM*) g_pInetSvc->QueryMDObject() );
|
|||
|
if (m_pMetabase == NULL)
|
|||
|
{
|
|||
|
hr = E_OUTOFMEMORY;
|
|||
|
goto LExit;
|
|||
|
}
|
|||
|
|
|||
|
// Save the handle of the W3Svc process for use when copying tokens to out of proc Wam's
|
|||
|
// NOTE: this is actually a "pseudo-handle", which is good enough. Note that pseudo-handles
|
|||
|
// do NOT need to be closed. CloseHandle is a no-op on a pseudo-handle.
|
|||
|
m_hW3Svc = GetCurrentProcess();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the process id for inetinfo. Under some circumstances com
|
|||
|
// svcs my get hosed. This causes object activation to happen in
|
|||
|
// process even if the object is registered to be launched in the
|
|||
|
// surrogate. In order to prevent inetinfo from AV'ing we want to
|
|||
|
// prevent these applications from running.
|
|||
|
//
|
|||
|
m_pidInetInfo = GetCurrentProcessId();
|
|||
|
|
|||
|
DBG_REQUIRE( m_ServerVariableMap.Initialize() );
|
|||
|
|
|||
|
// Register the Sink function at WamReg.
|
|||
|
WamReg_RegisterSinkNotify(W3SVC_WamRegSink);
|
|||
|
|
|||
|
LoadNTApis();
|
|||
|
|
|||
|
// Make the reference count to 1.
|
|||
|
Reference();
|
|||
|
LExit:
|
|||
|
return hr;
|
|||
|
}//WAM_DICTATOR::InitWamDictator
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::
|
|||
|
|
|||
|
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
LK_PREDICATE
|
|||
|
WAM_DICTATOR::DeleteInShutdown
|
|||
|
(
|
|||
|
CWamInfo* pWamInfo,
|
|||
|
void* pvState
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::DeleteInShutdown\n"));
|
|||
|
|
|||
|
DWORD dwPriorState;
|
|||
|
|
|||
|
dwPriorState = pWamInfo->ChangeToState(WIS_SHUTDOWN);
|
|||
|
|
|||
|
DBGPRINTF((DBG_CONTEXT, "Start shutdown, change state to WIS_SHUTDOWN\n"));
|
|||
|
DBG_REQUIRE(dwPriorState == WIS_RUNNING || dwPriorState == WIS_PAUSE || dwPriorState == WIS_CPUPAUSE);
|
|||
|
|
|||
|
// remove from the hash table here. and clean up later.
|
|||
|
g_pWamDictator->InsertDyingList(pWamInfo, TRUE);
|
|||
|
|
|||
|
return LKP_PERFORM;
|
|||
|
}//WAM_DICTATOR::DeleteInShutdown
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::StartShutdown
|
|||
|
Starts to shutdown Wam dictator
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::StartShutdown
|
|||
|
(
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::StartShutdown\n"));
|
|||
|
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
CWamInfo* pWamInfo;
|
|||
|
DWORD dwErr;
|
|||
|
DWORD dwPriorState;
|
|||
|
|
|||
|
InterlockedExchange((LPLONG)&m_fShutdownInProgress, (LONG)TRUE);
|
|||
|
|
|||
|
WamReg_UnRegisterSinkNotify();
|
|||
|
|
|||
|
m_HashTable.DeleteIf(DeleteInShutdown, NULL);
|
|||
|
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------*
|
|||
|
WAM_DICTATOR::UninitWamDictator
|
|||
|
Un-initializes Wam dictator
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::UninitWamDictator
|
|||
|
(
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::UninitWamDictator\n"));
|
|||
|
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
|
|||
|
// UNDONE leijin thinks we can get rid of this RemoveWorkItem code???
|
|||
|
ScheduledId_Lock();
|
|||
|
if (m_dwScheduledId != 0)
|
|||
|
{
|
|||
|
DBGPRINTF((DBG_CONTEXT, "remove work item %d\n", m_dwScheduledId));
|
|||
|
// Cancel the ScheduleWorkItem
|
|||
|
RemoveWorkItem(m_dwScheduledId);
|
|||
|
m_dwScheduledId = 0;
|
|||
|
}
|
|||
|
ScheduledId_UnLock();
|
|||
|
DBG_ASSERT(m_dwScheduledId == 0);
|
|||
|
|
|||
|
// Clean up the dying list.
|
|||
|
CleanUpDyingList();
|
|||
|
|
|||
|
// After the CleanUpDyingList() call, this should be an empty list.
|
|||
|
//
|
|||
|
DBG_ASSERT(IsListEmpty(&m_DyingListHead));
|
|||
|
|
|||
|
//
|
|||
|
// Allow all other references to WAM_DICTATOR to drain away.
|
|||
|
// This loop is especially important in shutdown during stress scenario.
|
|||
|
// Unload Application while w3svc is still running, a busy application might
|
|||
|
// still have some outstanding HTTPREQUEST unfinished which hold the reference to
|
|||
|
// this WAM_DICTATOR.
|
|||
|
//
|
|||
|
while (m_cRef != 1)
|
|||
|
{
|
|||
|
DBGPRINTF((DBG_CONTEXT, "Still have out-standing reference(%d) to this WAM_DICTATOR\n",
|
|||
|
m_cRef));
|
|||
|
Sleep(20);
|
|||
|
}
|
|||
|
|
|||
|
// Delete m_pMetabase
|
|||
|
// pMetabase should be there
|
|||
|
DBG_REQUIRE(m_pMetabase);
|
|||
|
delete m_pMetabase;
|
|||
|
m_pMetabase = NULL;
|
|||
|
|
|||
|
UnloadNTApis();
|
|||
|
|
|||
|
//
|
|||
|
// Dereference to balance the reference in InitWamDictator. after
|
|||
|
// this Dereference, ref count of WAM_DICTATOR should be 0.
|
|||
|
//
|
|||
|
Dereference();
|
|||
|
DBGPRINTF((DBG_CONTEXT, "Wam Dictator Exits.\n"));
|
|||
|
return hr;
|
|||
|
}//WAM_DICTATOR::UninitWamDictator
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::MDGetAppVariables
|
|||
|
|
|||
|
Giving a Metabase Path, find out the Application Path, WAM CLSID, and Context of WAM object of
|
|||
|
the application that apply to the metabase path.
|
|||
|
|
|||
|
Argument:
|
|||
|
szMetabasePath: [in] A metabase path.
|
|||
|
pfAllowApptoRun: [out] True, if application is enabled to run.
|
|||
|
pclsidWam: [out] a pointer to the buffer for WAM CLSID
|
|||
|
pfInProcess: [out] a pointer to DWORD for Context
|
|||
|
pfEnableTryExcept [out] a pointer to DWORD for EnableTryExcept flag
|
|||
|
|
|||
|
Return:
|
|||
|
HRESULT
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::MDGetAppVariables
|
|||
|
(
|
|||
|
LPCSTR szMetabasePath,
|
|||
|
BOOL* pfAllowAppToRun,
|
|||
|
CLSID* pclsidWam,
|
|||
|
BOOL* pfInProcess,
|
|||
|
BOOL* pfInPool,
|
|||
|
BOOL* pfEnableTryExcept,
|
|||
|
DWORD *pdwOOPCrashThreshold,
|
|||
|
BOOL* pfJobEnabled,
|
|||
|
WCHAR* wszPackageID,
|
|||
|
DWORD* pdwPeriodicRestartRequests,
|
|||
|
DWORD* pdwPeriodicRestartTime,
|
|||
|
MULTISZ*pmszPeriodicRestartSchedule,
|
|||
|
DWORD* pdwShutdownTimeLimit
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
METADATA_RECORD recMetaData;
|
|||
|
DWORD dwRequiredLen;
|
|||
|
CHAR szclsidWam[uSizeCLSIDStr];
|
|||
|
WCHAR wszclsidWam[uSizeCLSIDStr];
|
|||
|
BOOL fKeyOpen = FALSE;
|
|||
|
DWORD dwAppMode = 0;
|
|||
|
BOOL fEnableTryExcept = TRUE;
|
|||
|
DWORD dwAppState = 0;
|
|||
|
|
|||
|
DBG_ASSERT(szMetabasePath);
|
|||
|
DBG_ASSERT(pfAllowAppToRun);
|
|||
|
DBG_ASSERT(pclsidWam);
|
|||
|
DBG_ASSERT(pfInProcess);
|
|||
|
DBG_ASSERT(pfInPool);
|
|||
|
DBG_ASSERT(pfEnableTryExcept);
|
|||
|
DBG_ASSERT(pdwOOPCrashThreshold);
|
|||
|
DBG_ASSERT(pfJobEnabled);
|
|||
|
DBG_ASSERT(m_pMetabase);
|
|||
|
DBG_ASSERT(wszPackageID);
|
|||
|
DBG_ASSERT(pdwPeriodicRestartRequests);
|
|||
|
DBG_ASSERT(pdwPeriodicRestartTime);
|
|||
|
DBG_ASSERT(pmszPeriodicRestartSchedule);
|
|||
|
DBG_ASSERT(pdwShutdownTimeLimit);
|
|||
|
|
|||
|
wszPackageID[0] = L'\0';
|
|||
|
// Open Key
|
|||
|
if (!m_pMetabase->Open(szMetabasePath, METADATA_PERMISSION_READ))
|
|||
|
{
|
|||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|||
|
goto LErrExit;
|
|||
|
}
|
|||
|
fKeyOpen = TRUE;
|
|||
|
|
|||
|
// if MD_APP_STATE exists, and is defined with a value APPSTATUS_PAUSE,
|
|||
|
// then, fAllowAppToRun is set to FALSE
|
|||
|
// otherwise, fAllowAppToRun is set to TRUE
|
|||
|
if (m_pMetabase->GetDword("", MD_APP_STATE, IIS_MD_UT_WAM, (DWORD *)&dwAppState, METADATA_INHERIT))
|
|||
|
{
|
|||
|
if (dwAppState == APPSTATUS_PAUSE)
|
|||
|
{
|
|||
|
*pfAllowAppToRun = FALSE;
|
|||
|
goto LErrExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
*pfAllowAppToRun = TRUE;
|
|||
|
if (!m_pMetabase->GetDword("", MD_APP_ISOLATED, IIS_MD_UT_WAM, &dwAppMode, METADATA_INHERIT))
|
|||
|
{
|
|||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|||
|
goto LErrExit;
|
|||
|
}
|
|||
|
|
|||
|
*pfInProcess = ( dwAppMode == eAppInProc) ? TRUE : FALSE;
|
|||
|
*pfInPool = ( dwAppMode == eAppInProc || dwAppMode == eAppOOPPool ) ? TRUE : FALSE;
|
|||
|
|
|||
|
if (dwAppMode == eAppOOPIsolated)
|
|||
|
{
|
|||
|
DWORD dwRet = 0;
|
|||
|
CHAR szPackageID[uSizeCLSIDStr];
|
|||
|
|
|||
|
//
|
|||
|
// Disable job objects for IIS5.1, original code would read the MD_CPU_APP_ENABLED
|
|||
|
// property from metabase
|
|||
|
//
|
|||
|
*pfJobEnabled = FALSE;
|
|||
|
|
|||
|
dwRequiredLen= uSizeCLSIDStr;
|
|||
|
if (!m_pMetabase->GetString("", MD_APP_PACKAGE_ID, IIS_MD_UT_WAM, szPackageID, &dwRequiredLen, METADATA_INHERIT))
|
|||
|
{
|
|||
|
DBG_ASSERT(dwRequiredLen <= uSizeCLSIDStr);
|
|||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|||
|
goto LErrExit;
|
|||
|
}
|
|||
|
|
|||
|
dwRet = MultiByteToWideChar(CP_ACP, 0, szPackageID, -1, wszPackageID, uSizeCLSIDStr);
|
|||
|
DBG_ASSERT(dwRet != 0);
|
|||
|
}
|
|||
|
else if (dwAppMode == eAppOOPPool)
|
|||
|
{
|
|||
|
wcsncpy(wszPackageID, g_szIISOOPPoolPackageID, sizeof(g_szIISOOPPoolPackageID)/sizeof(WCHAR));
|
|||
|
// Always disable Job object for pool process.
|
|||
|
*pfJobEnabled = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*pfJobEnabled = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// If not present assume the default (TRUE) (For Debug builds set default to FALSE)
|
|||
|
if (m_pMetabase->GetDword("", MD_ASP_EXCEPTIONCATCHENABLE, ASP_MD_UT_APP, (DWORD *)&fEnableTryExcept, METADATA_INHERIT))
|
|||
|
{
|
|||
|
*pfEnableTryExcept = fEnableTryExcept ? TRUE : FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if DBG
|
|||
|
*pfEnableTryExcept = FALSE;
|
|||
|
#else
|
|||
|
*pfEnableTryExcept = TRUE;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
DWORD dwThreshold;
|
|||
|
|
|||
|
if (dwAppMode == eAppInProc)
|
|||
|
{
|
|||
|
hr = CLSIDFromString((LPOLESTR) g_szIISInProcWAMCLSID, (LPCLSID)pclsidWam);
|
|||
|
}
|
|||
|
else if (dwAppMode == eAppOOPPool)
|
|||
|
{
|
|||
|
hr = CLSIDFromString((LPOLESTR) g_szIISOOPPoolWAMCLSID, (LPCLSID)pclsidWam);
|
|||
|
|
|||
|
*pdwOOPCrashThreshold = 0XFFFFFFFF;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (m_pMetabase->GetDword("", MD_APP_OOP_RECOVER_LIMIT, IIS_MD_UT_WAM, (DWORD *)&dwThreshold, METADATA_INHERIT))
|
|||
|
{
|
|||
|
// It used to be OOP_CRASH_LIMIT (range 1 - 5)
|
|||
|
// Now, the name is changed to RECOVER_LIMIT with range of (0-xxx).
|
|||
|
// 0 means no recovery, same as 1 in crash limit, 1 crash, and it's over, (0 recovery).
|
|||
|
// Add 1 because internally this threshold is implemented in crash_limit concept.
|
|||
|
// 0xFFFFFFFF is unlimited.
|
|||
|
|
|||
|
// Changed to default to unlimited - Bug 240012. The idea of making this time
|
|||
|
// dependent seems much smarter than making it infinite. But no one was willing
|
|||
|
// to step up and decide what the right parameters were.
|
|||
|
if (dwThreshold != 0xFFFFFFFF)
|
|||
|
{
|
|||
|
dwThreshold++;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dwThreshold = APP_OOP_RECOVER_LIMIT_DEFAULT;
|
|||
|
}
|
|||
|
|
|||
|
*pdwOOPCrashThreshold = dwThreshold;
|
|||
|
|
|||
|
dwRequiredLen= uSizeCLSIDStr;
|
|||
|
if (!m_pMetabase->GetString("", MD_APP_WAM_CLSID, IIS_MD_UT_WAM, szclsidWam, &dwRequiredLen, METADATA_INHERIT))
|
|||
|
{
|
|||
|
DBG_ASSERT(dwRequiredLen <= uSizeCLSIDStr);
|
|||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|||
|
goto LErrExit;
|
|||
|
}
|
|||
|
|
|||
|
MultiByteToWideChar(CP_ACP, 0, szclsidWam, -1, wszclsidWam, uSizeCLSIDStr);
|
|||
|
|
|||
|
hr = CLSIDFromString((LPOLESTR)wszclsidWam, (LPCLSID)pclsidWam);
|
|||
|
}
|
|||
|
|
|||
|
if ( dwAppMode != eAppInProc )
|
|||
|
{
|
|||
|
//
|
|||
|
// Get the application recycle settings
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Disable wam recycling for IIS5.1, original code would read these properties
|
|||
|
// from metabase
|
|||
|
//
|
|||
|
*pdwPeriodicRestartTime = PERIODIC_RESTART_TIME_DEFAULT;
|
|||
|
|
|||
|
*pdwPeriodicRestartRequests = PERIODIC_RESTART_REQUESTS_DEFAULT;
|
|||
|
|
|||
|
*pdwShutdownTimeLimit = SHUTDOWN_TIME_LIMIT_DEFAULT;
|
|||
|
|
|||
|
pmszPeriodicRestartSchedule->Reset();
|
|||
|
}
|
|||
|
|
|||
|
if (FAILED(hr))
|
|||
|
goto LErrExit;
|
|||
|
|
|||
|
LErrExit:
|
|||
|
|
|||
|
if (fKeyOpen)
|
|||
|
{
|
|||
|
m_pMetabase->Close();
|
|||
|
}
|
|||
|
|
|||
|
return hr;
|
|||
|
}//WAM_DICTATOR::MDGetAppVariables
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::GetWamInfo
|
|||
|
Returns the interface pointer for a wam, NULL if wam not found
|
|||
|
|
|||
|
Arguments:
|
|||
|
pstrPath [in] wam key: path to extension dll, prog id, etc.
|
|||
|
fInProc [in] run the wam in-proc?
|
|||
|
ppIWam [out] interface pointer for the wam
|
|||
|
|
|||
|
Return Value:
|
|||
|
TRUE or FALSE
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::GetWamInfo
|
|||
|
(
|
|||
|
const STR* pstrAppPath,
|
|||
|
const HTTP_REQUEST * pHttpRequest,
|
|||
|
CWamInfo ** ppWamInfo
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
INT AppState;
|
|||
|
CWamInfo* pWamInfo = NULL;
|
|||
|
|
|||
|
DBG_ASSERT(pstrAppPath);
|
|||
|
|
|||
|
AppState = FindWamInfo(pstrAppPath, &pWamInfo);
|
|||
|
if (-1 == AppState)
|
|||
|
{
|
|||
|
//
|
|||
|
// Lock the Creation process
|
|||
|
// to avoid multiple threads try to create the same WAMInfo,
|
|||
|
// (init WAM object, very expensive in out-proc cases)
|
|||
|
//
|
|||
|
CreateWam_Lock();
|
|||
|
|
|||
|
//
|
|||
|
// Try to find it again, in case another thread already created it.
|
|||
|
// This check is better that creating another WamInfo.
|
|||
|
//
|
|||
|
AppState = FindWamInfo(pstrAppPath, &pWamInfo);
|
|||
|
if (-1 == AppState)
|
|||
|
{
|
|||
|
//
|
|||
|
// We are out of luck in our search for a valid Wam Info
|
|||
|
// Let us just create a new one and make it happen.
|
|||
|
// Note: Creation also adds the WamInfo to the hashtable
|
|||
|
//
|
|||
|
DBG_ASSERT(pHttpRequest != NULL);
|
|||
|
hr = CreateWamInfo( *pstrAppPath, &pWamInfo, pHttpRequest->QueryW3Instance());
|
|||
|
}
|
|||
|
CreateWam_UnLock();
|
|||
|
}
|
|||
|
|
|||
|
if (m_fShutdownInProgress && pWamInfo != NULL)
|
|||
|
{
|
|||
|
hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
|
|||
|
pWamInfo->Dereference();
|
|||
|
pWamInfo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
*ppWamInfo = pWamInfo;
|
|||
|
return hr;
|
|||
|
}//WAM_DICTATOR::GetWamInfo
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
|
|||
|
WAM_DICTATOR::CreateWamInfo()
|
|||
|
|
|||
|
Description:
|
|||
|
Creates a new CWamInfo object for the specified application root path.
|
|||
|
On successful creation, it adds the object to the hash table internal
|
|||
|
to the WAM_DICTATOR that contains the active WamInfos.
|
|||
|
Finally, it returns the object via the pointer supplied.
|
|||
|
|
|||
|
Arguments:
|
|||
|
strAppRootPath - string containing the metabase path for for the
|
|||
|
Application Root
|
|||
|
ppWamInfo - pointer to pointer to CWamInfo object. On successful
|
|||
|
return this contains the pointer to the new
|
|||
|
created CWamInfo
|
|||
|
|
|||
|
Returns:
|
|||
|
HRESULT
|
|||
|
NOERROR - on success
|
|||
|
and specific error codes on failure
|
|||
|
|
|||
|
Note:
|
|||
|
This function should be called with the WAM_DICTATOR WamInfo lock held.
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::CreateWamInfo
|
|||
|
(
|
|||
|
const STR & strAppRootPath,
|
|||
|
CWamInfo ** ppWamInfo,
|
|||
|
PW3_SERVER_INSTANCE pwsiInstance
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
CLSID clsidWam;
|
|||
|
BOOL fInProcess;
|
|||
|
BOOL fEnableTryExcept;
|
|||
|
CWamInfo * pWamInfo;
|
|||
|
DWORD dwThreshold; // OOP crash threshold
|
|||
|
BOOL fAllowAppToRun;
|
|||
|
BOOL fJobEnabled;
|
|||
|
BOOL fInPool;
|
|||
|
WCHAR wszPackageID[uSizeCLSIDStr];
|
|||
|
DWORD dwPeriodicRestartRequests;
|
|||
|
DWORD dwPeriodicRestartTime;
|
|||
|
DWORD dwShutdownTimeLimit;
|
|||
|
MULTISZ mzPeriodicRestartSchedule;
|
|||
|
|
|||
|
DBG_ASSERT( NULL != ppWamInfo );
|
|||
|
DBG_ASSERT( NULL == *ppWamInfo);
|
|||
|
|
|||
|
// Get the latest Application path, CLSID, and inproc/out-of-proc flag
|
|||
|
hr = MDGetAppVariables(strAppRootPath.QueryStr(),
|
|||
|
&fAllowAppToRun,
|
|||
|
&clsidWam,
|
|||
|
&fInProcess,
|
|||
|
&fInPool,
|
|||
|
&fEnableTryExcept,
|
|||
|
&dwThreshold,
|
|||
|
&fJobEnabled,
|
|||
|
wszPackageID,
|
|||
|
&dwPeriodicRestartRequests,
|
|||
|
&dwPeriodicRestartTime,
|
|||
|
&mzPeriodicRestartSchedule,
|
|||
|
&dwShutdownTimeLimit
|
|||
|
);
|
|||
|
|
|||
|
if ( SUCCEEDED( hr) && fAllowAppToRun)
|
|||
|
{
|
|||
|
//
|
|||
|
// Create a New CWamInfo
|
|||
|
//
|
|||
|
if (fInProcess)
|
|||
|
{
|
|||
|
pWamInfo = new CWamInfo( strAppRootPath,
|
|||
|
fInProcess,
|
|||
|
fInPool,
|
|||
|
fEnableTryExcept,
|
|||
|
clsidWam
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Check to see if we have scheduled restart times. If so,
|
|||
|
// set the restart time to the earlier of the configured
|
|||
|
// restart time, or the earliest scheduled time.
|
|||
|
//
|
|||
|
|
|||
|
if ( mzPeriodicRestartSchedule.QueryStringCount() )
|
|||
|
{
|
|||
|
SYSTEMTIME st;
|
|||
|
DWORD dwTimeOfDayInMinutes;
|
|||
|
DWORD dwScheduleInMinutes;
|
|||
|
LPSTR szHour;
|
|||
|
LPSTR szMinute;
|
|||
|
|
|||
|
GetLocalTime( &st );
|
|||
|
dwTimeOfDayInMinutes = st.wHour * 60 + st.wMinute;
|
|||
|
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT,
|
|||
|
"Current Time is %d.\r\n",
|
|||
|
dwTimeOfDayInMinutes
|
|||
|
));
|
|||
|
|
|||
|
szHour = mzPeriodicRestartSchedule.QueryStrA();
|
|||
|
|
|||
|
DBG_ASSERT( szHour );
|
|||
|
|
|||
|
while ( *szHour )
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT,
|
|||
|
"Considering recycle at %s.\r\n",
|
|||
|
szHour
|
|||
|
));
|
|||
|
|
|||
|
szMinute = strchr( szHour, ':' );
|
|||
|
|
|||
|
if ( !szMinute )
|
|||
|
{
|
|||
|
//
|
|||
|
// Parsing error - hour and minute not separated
|
|||
|
// by a ':'
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
szMinute++;
|
|||
|
|
|||
|
dwScheduleInMinutes = atol( szHour ) * 60;
|
|||
|
dwScheduleInMinutes += atol( szMinute );
|
|||
|
|
|||
|
if ( dwScheduleInMinutes <= dwTimeOfDayInMinutes )
|
|||
|
{
|
|||
|
dwScheduleInMinutes += 24 * 60;
|
|||
|
}
|
|||
|
|
|||
|
dwScheduleInMinutes -= dwTimeOfDayInMinutes;
|
|||
|
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT,
|
|||
|
"Scheduled time is %d minutes from now.\r\n",
|
|||
|
dwScheduleInMinutes
|
|||
|
));
|
|||
|
|
|||
|
//
|
|||
|
// If the scheduled time is earlier than the current
|
|||
|
// dwPeriodicRestartTime, then replace the existing
|
|||
|
// value.
|
|||
|
//
|
|||
|
|
|||
|
if ( dwScheduleInMinutes < dwPeriodicRestartTime ||
|
|||
|
dwPeriodicRestartTime == 0 )
|
|||
|
{
|
|||
|
dwPeriodicRestartTime = dwScheduleInMinutes;
|
|||
|
}
|
|||
|
|
|||
|
szHour += strlen( szHour ) + 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( dwPeriodicRestartRequests || dwPeriodicRestartTime )
|
|||
|
{
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT,
|
|||
|
"Recycling will occur in %d minutes. or when %d requests have been served.\r\n",
|
|||
|
dwPeriodicRestartTime,
|
|||
|
dwPeriodicRestartRequests
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
pWamInfo = new CWamInfoOutProc(strAppRootPath,
|
|||
|
fInProcess,
|
|||
|
fInPool,
|
|||
|
fEnableTryExcept,
|
|||
|
clsidWam,
|
|||
|
dwThreshold,
|
|||
|
pwsiInstance,
|
|||
|
fJobEnabled,
|
|||
|
dwPeriodicRestartRequests,
|
|||
|
dwPeriodicRestartTime,
|
|||
|
dwShutdownTimeLimit
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (pWamInfo)
|
|||
|
{
|
|||
|
//
|
|||
|
// This is WAM_DICTATOR's reference on the CWamInfo. In "normal"
|
|||
|
// operations, this will be the last reference.
|
|||
|
//
|
|||
|
|
|||
|
pWamInfo->Reference();
|
|||
|
|
|||
|
BOOL fInitialize = (fInProcess || !fJobEnabled || !pwsiInstance->AreProcsCPUStopped());
|
|||
|
if (fInitialize)
|
|||
|
{
|
|||
|
//
|
|||
|
//Init the CWamInfo. this call will CoCreateInstance WAM object.
|
|||
|
//
|
|||
|
hr = pWamInfo->Init( wszPackageID, m_pidInetInfo );
|
|||
|
if (SUCCEEDED(hr))
|
|||
|
{
|
|||
|
pWamInfo->ChangeToState(WIS_RUNNING);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pWamInfo->UnInit();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pWamInfo->ChangeToState(WIS_CPUPAUSE);
|
|||
|
}
|
|||
|
|
|||
|
if (SUCCEEDED(hr))
|
|||
|
{
|
|||
|
|
|||
|
if ( LK_SUCCESS != m_HashTable.InsertRecord(pWamInfo))
|
|||
|
{
|
|||
|
if (fInitialize)
|
|||
|
{
|
|||
|
pWamInfo->UnInit();
|
|||
|
}
|
|||
|
pWamInfo->Dereference(); // should be last reference
|
|||
|
//delete pWamInfo;
|
|||
|
hr = E_FAIL; // NYI: What is the right failure code?
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Finally we are successful with a valid WAmInfo.
|
|||
|
// Return this. Will be balanced by a DeleteRecord
|
|||
|
// in CProcessTable::RemoveWamInfoFromProcessTable
|
|||
|
//
|
|||
|
pWamInfo->Reference();
|
|||
|
*ppWamInfo = pWamInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DBG_ASSERT( NOERROR != hr);
|
|||
|
pWamInfo->Dereference();
|
|||
|
//delete pWamInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// pWamInfo == NULL
|
|||
|
hr = E_OUTOFMEMORY;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (SUCCEEDED(hr))
|
|||
|
{
|
|||
|
hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED);
|
|||
|
*ppWamInfo = NULL;
|
|||
|
}
|
|||
|
//
|
|||
|
// NYI: Isn't pstrNewPath going away?
|
|||
|
// Should I bother deleting pstrNewPath ??
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Either we should be returning an error or send back the proper pointer
|
|||
|
//
|
|||
|
DBG_ASSERT( (hr != NOERROR) || (*ppWamInfo != NULL));
|
|||
|
|
|||
|
return ( hr);
|
|||
|
} // WAM_DICTATOR::CreateWamInfo()
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::FindWamInfo
|
|||
|
Returns the interface pointer for a wam, NULL if wam not found
|
|||
|
|
|||
|
Arguments:
|
|||
|
pstrPath - [in] wam key: path to extension dll, prog id, etc.
|
|||
|
ppIWam - [out] interface pointer for the wam
|
|||
|
|
|||
|
Return Value:
|
|||
|
TRUE or FALSE
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
INT
|
|||
|
WAM_DICTATOR::FindWamInfo
|
|||
|
(
|
|||
|
const STR* pstrMetabasePath,
|
|||
|
CWamInfo** ppWamInfo
|
|||
|
)
|
|||
|
{
|
|||
|
INT AppState = -1;
|
|||
|
CWamInfo* pWamInfo = NULL;
|
|||
|
char* pszKey;
|
|||
|
|
|||
|
DBG_ASSERT(pstrMetabasePath);
|
|||
|
DBG_ASSERT(ppWamInfo);
|
|||
|
|
|||
|
pszKey = pstrMetabasePath->QueryStr();
|
|||
|
|
|||
|
HashReadLock();
|
|||
|
m_HashTable.FindKey(pszKey, &pWamInfo);
|
|||
|
HashReadUnlock();
|
|||
|
|
|||
|
if (pWamInfo)
|
|||
|
{
|
|||
|
AppState = pWamInfo->QueryState();
|
|||
|
|
|||
|
DBG_ASSERT(AppState != WIS_START || AppState != WIS_END);
|
|||
|
}
|
|||
|
|
|||
|
*ppWamInfo = pWamInfo;
|
|||
|
return AppState;
|
|||
|
}//WAM_DICTATOR::FindWamInfo
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::UnLoadWamInfo
|
|||
|
Unload an OOP Wam.
|
|||
|
|
|||
|
Arguments:
|
|||
|
pstrWamPath - [in] wam key: path to extension dll, prog id, etc.
|
|||
|
fCPUPause - [in] If true, CPU pauses the WAM. Kills the process
|
|||
|
but keeps the info around.
|
|||
|
pfAppCpuUnloaded - [out] If nonNULL, set to TRUE iff an app was killed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
HRESULT - Error code.
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::UnLoadWamInfo
|
|||
|
(
|
|||
|
STR *pstrAppPath,
|
|||
|
BOOL fCPUPause,
|
|||
|
BOOL *pfAppCpuUnloaded
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::UnLoadWamInfo\n"));
|
|||
|
|
|||
|
int iAppState;
|
|||
|
CWamInfo* pWamInfo = NULL;
|
|||
|
DWORD eWISPrevState;
|
|||
|
DWORD eWISState;
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
BOOL fInProc;
|
|||
|
BOOL fAppCpuUnloaded = FALSE;
|
|||
|
|
|||
|
CreateWam_Lock();
|
|||
|
|
|||
|
iAppState = FindWamInfo(pstrAppPath,
|
|||
|
&pWamInfo);
|
|||
|
|
|||
|
CreateWam_UnLock();
|
|||
|
|
|||
|
if (iAppState != -1)
|
|||
|
{
|
|||
|
bool fRet = FALSE;
|
|||
|
|
|||
|
CProcessEntry* pProcEntry = NULL;
|
|||
|
|
|||
|
|
|||
|
fInProc = pWamInfo->FInProcess();
|
|||
|
pProcEntry = pWamInfo->QueryProcessEntry();
|
|||
|
DBG_ASSERT(pProcEntry != NULL);
|
|||
|
|
|||
|
pProcEntry->AddRef();
|
|||
|
|
|||
|
while (pWamInfo != NULL)
|
|||
|
{
|
|||
|
eWISState = (fCPUPause == TRUE) ? WIS_CPUPAUSE : WIS_SHUTDOWN;
|
|||
|
eWISPrevState = pWamInfo->ChangeToState(WIS_RUNNING, eWISState);
|
|||
|
//
|
|||
|
// Terminate process and kill off current requests.
|
|||
|
//
|
|||
|
|
|||
|
if (WIS_RUNNING == eWISPrevState)
|
|||
|
{
|
|||
|
DBG_ASSERT(pWamInfo->HWamProcess() != NULL);
|
|||
|
|
|||
|
hr = ShutdownWamInfo(pWamInfo,
|
|||
|
HASH_TABLE_REF + FIND_KEY_REF
|
|||
|
);
|
|||
|
|
|||
|
if (!fCPUPause)
|
|||
|
{
|
|||
|
LK_RETCODE LkReturn;
|
|||
|
|
|||
|
LkReturn = m_HashTable.DeleteKey(
|
|||
|
pWamInfo->QueryApplicationPath().QueryStr());
|
|||
|
DBG_ASSERT(LK_SUCCESS == LkReturn);
|
|||
|
//
|
|||
|
// Release FIND_KEY_REF
|
|||
|
//
|
|||
|
pWamInfo->Dereference();
|
|||
|
if (LK_SUCCESS == LkReturn)
|
|||
|
{
|
|||
|
pWamInfo->Dereference();
|
|||
|
//delete pWamInfo;
|
|||
|
pWamInfo = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Application is running. Kill It
|
|||
|
//
|
|||
|
DBG_ASSERT(!pWamInfo->FInProcess());
|
|||
|
//
|
|||
|
// Get rid of the shutting down flag
|
|||
|
//
|
|||
|
pWamInfo->ClearMembers();
|
|||
|
//
|
|||
|
// Release FIND_KEY_REF
|
|||
|
//
|
|||
|
pWamInfo->Dereference();
|
|||
|
|
|||
|
fAppCpuUnloaded = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (fInProc)
|
|||
|
{
|
|||
|
pWamInfo = NULL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fRet = m_PTable.FindWamInfo(pProcEntry,&pWamInfo);
|
|||
|
DBG_ASSERT(fRet == TRUE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pProcEntry->Release();
|
|||
|
}
|
|||
|
|
|||
|
if (pfAppCpuUnloaded != NULL) {
|
|||
|
*pfAppCpuUnloaded = fAppCpuUnloaded;
|
|||
|
}
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::CPUResumeWamInfo
|
|||
|
Resumes a CPU Paused OOP Wam.
|
|||
|
|
|||
|
Arguments:
|
|||
|
pstrWamPath - [in] wam key: path to extension dll, prog id, etc.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
VOID
|
|||
|
WAM_DICTATOR::CPUResumeWamInfo
|
|||
|
(
|
|||
|
STR *pstrWamPath
|
|||
|
)
|
|||
|
{
|
|||
|
int iAppState;
|
|||
|
CWamInfo* pWICurrentApplication = NULL;
|
|||
|
DWORD eWISPrevState;
|
|||
|
CLSID clsidWam;
|
|||
|
BOOL fInProcess;
|
|||
|
BOOL fInPool;
|
|||
|
BOOL fEnableTryExcept;
|
|||
|
DWORD dwThreshold; // OOP crash threshold
|
|||
|
BOOL fAllowAppToRun;
|
|||
|
BOOL fJobEnabled;
|
|||
|
WCHAR wszPackageID[uSizeCLSIDStr];
|
|||
|
DWORD dwPeriodicRestartRequests;
|
|||
|
DWORD dwPeriodicRestartTime;
|
|||
|
DWORD dwShutdownTimeLimit;
|
|||
|
MULTISZ mzPeriodicRestartSchedule;
|
|||
|
HRESULT hr;
|
|||
|
|
|||
|
|
|||
|
CreateWam_Lock();
|
|||
|
|
|||
|
iAppState = FindWamInfo(pstrWamPath,
|
|||
|
&pWICurrentApplication);
|
|||
|
|
|||
|
CreateWam_UnLock();
|
|||
|
|
|||
|
if (iAppState != -1)
|
|||
|
{
|
|||
|
// Application may not be loaded. This method is called for every
|
|||
|
// application defined under the site being paused.
|
|||
|
|
|||
|
// Get the latest Application path, CLSID, and inproc/out-of-proc flag
|
|||
|
hr = MDGetAppVariables(pWICurrentApplication->QueryApplicationPath().QueryStr(),
|
|||
|
&fAllowAppToRun,
|
|||
|
&clsidWam,
|
|||
|
&fInProcess,
|
|||
|
&fInPool,
|
|||
|
&fEnableTryExcept,
|
|||
|
&dwThreshold,
|
|||
|
&fJobEnabled,
|
|||
|
wszPackageID,
|
|||
|
&dwPeriodicRestartRequests,
|
|||
|
&dwPeriodicRestartTime,
|
|||
|
&mzPeriodicRestartSchedule,
|
|||
|
&dwShutdownTimeLimit);
|
|||
|
|
|||
|
|
|||
|
if (iAppState == WIS_CPUPAUSE && SUCCEEDED(hr))
|
|||
|
{
|
|||
|
if (SUCCEEDED(pWICurrentApplication->Init(wszPackageID, m_pidInetInfo)))
|
|||
|
{
|
|||
|
pWICurrentApplication->ChangeToState(WIS_CPUPAUSE, WIS_RUNNING);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// FindWamInfo References this, so dereference it.
|
|||
|
//
|
|||
|
pWICurrentApplication->Dereference();
|
|||
|
}
|
|||
|
|
|||
|
}//WAM_DICTATOR::CPUResumeWamInfo
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::ShutdownWamInfo
|
|||
|
This function currently can only call a blocking method of WAM, UnInitWam.
|
|||
|
In the future, this function might support a non-blocking method provided by WAM in
|
|||
|
case of IIS shutdown.
|
|||
|
|
|||
|
Arguments:
|
|||
|
pWamInfo [in] a pointer to WamInfo
|
|||
|
cIgnoreRef [in] The reference count that need to be ignored in StartShutdown.
|
|||
|
|
|||
|
Return Value:
|
|||
|
TRUE or FALSE
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::ShutdownWamInfo
|
|||
|
(
|
|||
|
CWamInfo* pWamInfo,
|
|||
|
INT cIgnoreRefs
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::ShutdownWamInfo\n"));
|
|||
|
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
WCHAR szPackageID[uSizeCLSIDStr];
|
|||
|
|
|||
|
DBG_ASSERT(pWamInfo);
|
|||
|
|
|||
|
LPCSTR pszAppPath = (pWamInfo->QueryApplicationPath()).QueryStr();
|
|||
|
|
|||
|
|
|||
|
// Might create IPackageUtil * for later use.
|
|||
|
DBGPRINTF((DBG_CONTEXT, "Shutting down WamInfo %08p, Inproc(%d), AppRoot(%s).\n",
|
|||
|
pWamInfo,
|
|||
|
pWamInfo->FInProcess(),
|
|||
|
pszAppPath));
|
|||
|
|
|||
|
|
|||
|
hr = pWamInfo->StartShutdown(cIgnoreRefs);
|
|||
|
hr = pWamInfo->UnInit();
|
|||
|
|
|||
|
if (FAILED(hr))
|
|||
|
{
|
|||
|
const CHAR *pszEventLog[2];
|
|||
|
char szErr[20];
|
|||
|
|
|||
|
pszEventLog[0] = pszAppPath;
|
|||
|
wsprintf(szErr, "0x%08X", hr);
|
|||
|
pszEventLog[1] = szErr;
|
|||
|
|
|||
|
// Event login
|
|||
|
g_pInetSvc->LogEvent(W3_EVENT_FAIL_SHUTDOWN,
|
|||
|
2,
|
|||
|
pszEventLog,
|
|||
|
0);
|
|||
|
}
|
|||
|
|
|||
|
//delete pWamInfo;
|
|||
|
//pWamInfo = NULL;
|
|||
|
return hr;
|
|||
|
} // wAM_DICTATOR::ShutdownWamInfo
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::CPUUpdateWamInfo
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
TRUE or FALSE
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
void
|
|||
|
WAM_DICTATOR::CPUUpdateWamInfo
|
|||
|
(
|
|||
|
STR *pstrAppPath
|
|||
|
)
|
|||
|
{
|
|||
|
CWamInfo* pWamInfo = NULL;
|
|||
|
INT AppState;
|
|||
|
|
|||
|
DBG_ASSERT(pstrAppPath != NULL);
|
|||
|
|
|||
|
// Find the WamInfo.
|
|||
|
AppState = FindWamInfo(pstrAppPath, &pWamInfo);
|
|||
|
if (-1 != AppState)
|
|||
|
{
|
|||
|
BOOL fWasShutDown = FALSE;
|
|||
|
DWORD eWISPrevState;
|
|||
|
|
|||
|
if ( !pWamInfo->FInProcess() )
|
|||
|
{
|
|||
|
//
|
|||
|
// It's out of process and the job state has changed
|
|||
|
// Shut it down so new value will get picked up
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// change the state of the WamInfo
|
|||
|
//
|
|||
|
|
|||
|
eWISPrevState = pWamInfo->ChangeToState(WIS_RUNNING, WIS_SHUTDOWN);
|
|||
|
|
|||
|
//
|
|||
|
// Need to shutdown the Application
|
|||
|
// remove from the hash table here. and clean up later.
|
|||
|
if (WIS_RUNNING == eWISPrevState)
|
|||
|
{
|
|||
|
|
|||
|
ShutdownWamInfo(pWamInfo, HASH_TABLE_REF + FIND_KEY_REF);
|
|||
|
|
|||
|
// NoNeed to go through the Dying List.
|
|||
|
// But since we already have the Dying List, we might just used those functions.
|
|||
|
// this means we might release other old WamInfo in the Dying List as well.
|
|||
|
m_HashTable.DeleteKey(pstrAppPath->QueryStr());
|
|||
|
|
|||
|
fWasShutDown = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
pWamInfo->Dereference();
|
|||
|
if (fWasShutDown)
|
|||
|
{
|
|||
|
pWamInfo->Dereference();
|
|||
|
//delete pWamInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::WamRegSink
|
|||
|
Argument:
|
|||
|
szAppPath [in] Application Path.
|
|||
|
dwCommand [in] Delete, Change, Stop..etc.
|
|||
|
|
|||
|
Return:
|
|||
|
HRESULT
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::WamRegSink
|
|||
|
(
|
|||
|
LPCSTR szAppPath,
|
|||
|
const DWORD dwCommand,
|
|||
|
DWORD* pdwResult
|
|||
|
)
|
|||
|
{
|
|||
|
CWamInfo* pWamInfo = NULL;
|
|||
|
INT AppState;
|
|||
|
HRESULT hrReturn = NOERROR;
|
|||
|
|
|||
|
DBG_ASSERT(szAppPath != NULL);
|
|||
|
|
|||
|
*pdwResult = APPSTATUS_UnLoaded;
|
|||
|
|
|||
|
//
|
|||
|
// Set up the Application Path
|
|||
|
//
|
|||
|
STR* pstrAppPath = new STR(szAppPath);
|
|||
|
|
|||
|
if (NULL == pstrAppPath)
|
|||
|
{
|
|||
|
return E_OUTOFMEMORY;
|
|||
|
}
|
|||
|
|
|||
|
Reference();
|
|||
|
DBGPRINTF((DBG_CONTEXT,
|
|||
|
"Wam Dictator received a SinkNotify on MD Path %s, cmd = %d\n",
|
|||
|
szAppPath,
|
|||
|
dwCommand));
|
|||
|
|
|||
|
if (dwCommand == APPCMD_UNLOAD)
|
|||
|
{
|
|||
|
hrReturn = UnLoadWamInfo(pstrAppPath, FALSE);
|
|||
|
*pdwResult = (SUCCEEDED(hrReturn)) ? APPSTATUS_UnLoaded : APPSTATUS_Error;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Find the WamInfo.
|
|||
|
AppState = FindWamInfo(pstrAppPath, &pWamInfo);
|
|||
|
if (-1 != AppState)
|
|||
|
{
|
|||
|
LK_RETCODE LkReturn;
|
|||
|
DWORD eWISPrevState;
|
|||
|
|
|||
|
if (dwCommand == APPCMD_DELETE ||
|
|||
|
dwCommand == APPCMD_CHANGETOINPROC ||
|
|||
|
dwCommand == APPCMD_CHANGETOOUTPROC)
|
|||
|
{
|
|||
|
eWISPrevState = pWamInfo->ChangeToState(WIS_RUNNING, WIS_SHUTDOWN);
|
|||
|
|
|||
|
if (WIS_RUNNING == eWISPrevState)
|
|||
|
{
|
|||
|
LkReturn = m_HashTable.DeleteKey(pstrAppPath->QueryStr());
|
|||
|
DBG_ASSERT(LK_SUCCESS == LkReturn);
|
|||
|
|
|||
|
if (LK_SUCCESS == LkReturn)
|
|||
|
{
|
|||
|
DyingList_Lock();
|
|||
|
InsertDyingList(pWamInfo, FALSE);
|
|||
|
DyingList_UnLock();
|
|||
|
|
|||
|
ScheduledId_Lock();
|
|||
|
if (m_dwScheduledId == 0)
|
|||
|
{
|
|||
|
// DebugBreak();
|
|||
|
m_dwScheduledId = ScheduleWorkItem
|
|||
|
(
|
|||
|
WAM_DICTATOR::CleanupScheduled,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
DBGPRINTF((DBG_CONTEXT, "add schedule item %d\n", m_dwScheduledId));
|
|||
|
DBG_ASSERT(m_dwScheduledId);
|
|||
|
}
|
|||
|
ScheduledId_UnLock();
|
|||
|
|
|||
|
*pdwResult = (SUCCEEDED(hrReturn)) ? APPSTATUS_UnLoaded : APPSTATUS_Error;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Previous state is not running state. Leave it alone.
|
|||
|
//
|
|||
|
pWamInfo->Dereference();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// AppGetStatus
|
|||
|
if (WIS_RUNNING == pWamInfo->QueryState())
|
|||
|
{
|
|||
|
*pdwResult = APPSTATUS_Running;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*pdwResult = APPSTATUS_Stopped;
|
|||
|
}
|
|||
|
|
|||
|
pWamInfo->Dereference();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// This Application is not loaded.
|
|||
|
*pdwResult = APPSTATUS_NotFoundInW3SVC;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Dereference();
|
|||
|
return hrReturn;
|
|||
|
}//WAM_DICTATOR::WamRegSink
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::InsertDyingList
|
|||
|
This function insert an invalid WamInfo into DyingList. No blocking.
|
|||
|
|
|||
|
Arguments:
|
|||
|
pWamInfo [in] a pointer to WamInfo
|
|||
|
fNeedReference [in] if TRUE, then we Rereference the WamInfo. Because we get the WamInfo by
|
|||
|
Hashtable's Interator or LookUp. When we get a WamInfo from Hash Table by
|
|||
|
hash table's interator or LookUp call, the Hash table does a AddRef to
|
|||
|
WamInfo. Therefore, this parameter tells whether we need to balance that
|
|||
|
Addref or not.
|
|||
|
Return Value:
|
|||
|
NOERROR.
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::InsertDyingList
|
|||
|
(
|
|||
|
CWamInfo* pWamInfo,
|
|||
|
BOOL fNeedReference
|
|||
|
)
|
|||
|
{
|
|||
|
DBG_ASSERT(pWamInfo);
|
|||
|
|
|||
|
DyingList_Lock();
|
|||
|
//
|
|||
|
// Note: Delete Dereference WamInfo. This Dereference() within Delete() balances
|
|||
|
// the AddRef() in Insert() call to the hash table.
|
|||
|
//
|
|||
|
InsertHeadList(&m_DyingListHead, &pWamInfo->ListEntry);
|
|||
|
//
|
|||
|
// Unfortunately, I can not move this Dereference() out of Critical Section.
|
|||
|
// If I move the Dereference() after the Critical Section, then, since the WamInfo is
|
|||
|
// already on the DyingList. therefore, any other thread happens to do the CleanUpDyingList()
|
|||
|
// call before this Dereference() will have an unbalanced reference to the CWamInfo.
|
|||
|
//
|
|||
|
if (fNeedReference)
|
|||
|
{
|
|||
|
pWamInfo->Reference();
|
|||
|
}
|
|||
|
|
|||
|
DyingList_UnLock();
|
|||
|
|
|||
|
return NOERROR;
|
|||
|
}//WAM_DICTATOR::InsertDyingList
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::CleanUpDyingList
|
|||
|
This function clean up any remaining WamInfo on the dying list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
HRESULT
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
HRESULT
|
|||
|
WAM_DICTATOR::CleanUpDyingList
|
|||
|
(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::CleanUpDyingList\n"));
|
|||
|
|
|||
|
CWamInfo * pWamInfo = NULL;
|
|||
|
HRESULT hr = NOERROR;
|
|||
|
PLIST_ENTRY pleTemp;
|
|||
|
BOOL fNeedCleanupAction = FALSE;
|
|||
|
|
|||
|
DyingList_Lock();
|
|||
|
if (!IsListEmpty(&m_DyingListHead))
|
|||
|
{
|
|||
|
if (!m_fCleanupInProgress)
|
|||
|
{
|
|||
|
m_fCleanupInProgress = TRUE;
|
|||
|
fNeedCleanupAction = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DBGPRINTF((DBG_CONTEXT, "Clean up dying list. (ScheduledId:%d).\n",
|
|||
|
m_dwScheduledId));
|
|||
|
|
|||
|
DyingList_UnLock();
|
|||
|
|
|||
|
if (!fNeedCleanupAction)
|
|||
|
{
|
|||
|
goto Egress;
|
|||
|
}
|
|||
|
|
|||
|
// From now on, only one thread is working on the killing dying list.
|
|||
|
while (!IsListEmpty(&m_DyingListHead))
|
|||
|
{
|
|||
|
DyingList_Lock();
|
|||
|
pleTemp = RemoveHeadList(&m_DyingListHead);
|
|||
|
DyingList_UnLock();
|
|||
|
|
|||
|
DBG_ASSERT(pleTemp);
|
|||
|
pWamInfo = CONTAINING_RECORD(
|
|||
|
pleTemp,
|
|||
|
CWamInfo,
|
|||
|
ListEntry);
|
|||
|
|
|||
|
ShutdownWamInfo(pWamInfo, DYING_LIST_REF);
|
|||
|
|
|||
|
pWamInfo->Dereference();
|
|||
|
|
|||
|
pWamInfo->Dereference();
|
|||
|
//delete pWamInfo;
|
|||
|
pWamInfo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
InterlockedExchange((LPLONG)&m_fCleanupInProgress, (LONG)FALSE);
|
|||
|
|
|||
|
DBGPRINTF((DBG_CONTEXT, "CleanupDyingList done, ScheduledId(%d)\n", m_dwScheduledId));
|
|||
|
|
|||
|
Egress:
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::CleanUpDyingList done\n"));
|
|||
|
|
|||
|
m_dwScheduledId = 0;
|
|||
|
return hr;
|
|||
|
}//WAM_DICTATOR::CleanUpDyingList
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::StopAplicationsByInstance
|
|||
|
|
|||
|
Unload out-proc applications for a particular w3 server instance.
|
|||
|
|
|||
|
Arguments:
|
|||
|
W3_SERVER_INSTANCE *pInstance
|
|||
|
pointer to W3 Server Instance object. Used to query the server instance
|
|||
|
metabase path.
|
|||
|
|
|||
|
Return Value:
|
|||
|
none
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
VOID
|
|||
|
WAM_DICTATOR::StopApplicationsByInstance
|
|||
|
(
|
|||
|
VOID *pContext //W3_SERVER_INSTANCE *pInstance
|
|||
|
)
|
|||
|
{
|
|||
|
IF_DEBUG( WAM_ISA_CALLS )
|
|||
|
DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::StopApplicationsByInstance\n"));
|
|||
|
|
|||
|
BUFFER bufDataPaths;
|
|||
|
MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
|
|||
|
LPSTR pszCurrentPath;
|
|||
|
STR strPath;
|
|||
|
W3_SERVER_INSTANCE *pInstance;
|
|||
|
|
|||
|
DBG_ASSERT(pContext != NULL);
|
|||
|
DBG_ASSERT(g_pWamDictator != NULL);
|
|||
|
|
|||
|
pInstance = (W3_SERVER_INSTANCE *)pContext;
|
|||
|
|
|||
|
g_pWamDictator->Reference();
|
|||
|
if (!g_pWamDictator->FIsShutdown())
|
|||
|
{
|
|||
|
if ( mb.Open( pInstance->QueryMDPath(),
|
|||
|
METADATA_PERMISSION_READ ) )
|
|||
|
{
|
|||
|
//
|
|||
|
// First find the OOP Applications
|
|||
|
//
|
|||
|
if (mb.GetDataPaths(NULL,
|
|||
|
MD_APP_WAM_CLSID,
|
|||
|
STRING_METADATA,
|
|||
|
&bufDataPaths))
|
|||
|
{
|
|||
|
//
|
|||
|
// For each OOP Application
|
|||
|
//
|
|||
|
mb.Close();
|
|||
|
for (pszCurrentPath = (LPSTR)bufDataPaths.QueryPtr();
|
|||
|
*pszCurrentPath != '\0';
|
|||
|
pszCurrentPath += (strlen(pszCurrentPath) + 1))
|
|||
|
{
|
|||
|
strPath.Copy(pInstance->QueryMDPath());
|
|||
|
strPath.Append(pszCurrentPath);
|
|||
|
strPath.SetLen(strlen(strPath.QueryStr()) - 1);
|
|||
|
g_pWamDictator->UnLoadWamInfo(&strPath, FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mb.Close();
|
|||
|
}
|
|||
|
}
|
|||
|
} // WamDictator is in shutdown
|
|||
|
g_pWamDictator->Dereference();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
WAM_DICTATOR::DeleteWamInfoFromHashTable
|
|||
|
(
|
|||
|
CWamInfo * pWamInfo
|
|||
|
)
|
|||
|
{
|
|||
|
LK_RETCODE lkReturn;
|
|||
|
const CHAR * szKey;
|
|||
|
|
|||
|
DBG_ASSERT( pWamInfo );
|
|||
|
|
|||
|
szKey = m_HashTable.ExtractKey( pWamInfo );
|
|||
|
|
|||
|
if ( !szKey )
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
lkReturn = m_HashTable.DeleteKey( szKey );
|
|||
|
|
|||
|
return ( lkReturn == LK_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
WAM_DICTATOR::DumpWamDictatorInfo
|
|||
|
|
|||
|
Description:
|
|||
|
This function dumps the stats on all WAMs for diagnostics
|
|||
|
|
|||
|
Arguments:
|
|||
|
pchBuffer - pointer to buffer that will contain the html results
|
|||
|
lpcchBuffer - pointer to DWORD containing the size of buffer on entry
|
|||
|
On return this contains the # of bytes written out to buffer
|
|||
|
|
|||
|
Return:
|
|||
|
TRUE for success and FALSE for failure
|
|||
|
Look at GetLastError() for the error code.
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
BOOL
|
|||
|
WAM_DICTATOR::DumpWamDictatorInfo
|
|||
|
(
|
|||
|
OUT CHAR * pchBuffer,
|
|||
|
IN OUT LPDWORD lpcchBuffer
|
|||
|
)
|
|||
|
{
|
|||
|
LIST_ENTRY * pEntry;
|
|||
|
DWORD iCount, cch;
|
|||
|
BOOL fRet = TRUE;
|
|||
|
|
|||
|
if ( lpcchBuffer == NULL )
|
|||
|
{
|
|||
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|||
|
return ( FALSE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( 200 < *lpcchBuffer )
|
|||
|
{
|
|||
|
// Print the header blob
|
|||
|
cch = wsprintf( pchBuffer,
|
|||
|
" Wam Director Table (%x)<br>"
|
|||
|
"<TABLE BORDER> <TR> "
|
|||
|
"<TH> Wam Instance </TH> "
|
|||
|
"<TH> Total Reqs </TH> "
|
|||
|
"<TH> Current Reqs </TH> "
|
|||
|
"<TH> Max Reqs </TH> "
|
|||
|
" </TR>"
|
|||
|
,
|
|||
|
this
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cch = 200;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For now there is only ONE WAM. Later use a loop to iterate thru WAMs
|
|||
|
//
|
|||
|
|
|||
|
iCount = 0;
|
|||
|
|
|||
|
CWamInfoHash::CIterator iter;
|
|||
|
LK_RETCODE lkReturn = m_HashTable.InitializeIterator(&iter);
|
|||
|
WAM_STATISTICS_INFO wsi;
|
|||
|
CWamInfo* pWamInfo;
|
|||
|
DWORD dwErr;
|
|||
|
HRESULT hr;
|
|||
|
|
|||
|
ZeroMemory( (PVOID ) &wsi, sizeof(wsi));
|
|||
|
|
|||
|
while (LK_SUCCESS == lkReturn)
|
|||
|
{
|
|||
|
CWamInfoHash::Record *pRec = iter.Record();
|
|||
|
|
|||
|
pWamInfo = (CWamInfo *)pRec;
|
|||
|
|
|||
|
hr = pWamInfo->GetStatistics( 0, &wsi);
|
|||
|
|
|||
|
if (SUCCEEDED(hr))
|
|||
|
{
|
|||
|
DBGPRINTF(( DBG_CONTEXT, " Wam(%08x)::GetStatistics( %08x) => %08x\n",
|
|||
|
pWamInfo->QueryIWam(), &wsi, hr));
|
|||
|
|
|||
|
if ( (cch + 150 ) < *lpcchBuffer)
|
|||
|
{
|
|||
|
cch += wsprintf( pchBuffer + cch,
|
|||
|
" <TR> <TD> [%d] %s </TD> "
|
|||
|
" <TD> %4d </TD>"
|
|||
|
" <TD> %4d </TD>"
|
|||
|
" <TD> %4d </TD>"
|
|||
|
" </TR>"
|
|||
|
,
|
|||
|
iCount,
|
|||
|
pWamInfo->QueryKey(),
|
|||
|
wsi.WamStats0.TotalWamRequests,
|
|||
|
wsi.WamStats0.CurrentWamRequests,
|
|||
|
wsi.WamStats0.MaxWamRequests
|
|||
|
);
|
|||
|
iCount++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cch += 150;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
lkReturn = m_HashTable.IncrementIterator(&iter);
|
|||
|
} // while()
|
|||
|
|
|||
|
DBG_ASSERT(lkReturn == LK_NO_MORE_ELEMENTS);
|
|||
|
|
|||
|
lkReturn = m_HashTable.CloseIterator(&iter);
|
|||
|
|
|||
|
DBG_REQUIRE(lkReturn == LK_SUCCESS);
|
|||
|
|
|||
|
//
|
|||
|
// dump the final summary
|
|||
|
//
|
|||
|
if ( (cch + 100 ) < *lpcchBuffer)
|
|||
|
{
|
|||
|
cch += wsprintf( pchBuffer + cch,
|
|||
|
" </TABLE>"
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cch += 100;
|
|||
|
}
|
|||
|
|
|||
|
if ( *lpcchBuffer < cch )
|
|||
|
{
|
|||
|
SetLastError( ERROR_INSUFFICIENT_BUFFER);
|
|||
|
fRet = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
*lpcchBuffer = cch;
|
|||
|
|
|||
|
return (fRet);
|
|||
|
} // WAM_DICTATOR::DumpWamDictatorInfo()
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
Description:
|
|||
|
Thunk so that we can dll export DumpWamDictatorInfo.
|
|||
|
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
extern "C"
|
|||
|
BOOL WamDictatorDumpInfo
|
|||
|
(
|
|||
|
OUT CHAR * pch,
|
|||
|
IN OUT LPDWORD lpcchBuff
|
|||
|
)
|
|||
|
{
|
|||
|
return ( g_pWamDictator->DumpWamDictatorInfo( pch, lpcchBuff));
|
|||
|
} // WamDictatorDumpInfo()
|
|||
|
|
|||
|
HRESULT W3SVC_WamRegSink
|
|||
|
(
|
|||
|
LPCSTR szAppPath,
|
|||
|
const DWORD dwCommand,
|
|||
|
DWORD* pdwResult
|
|||
|
)
|
|||
|
{
|
|||
|
return (g_pWamDictator->WamRegSink(szAppPath, dwCommand, pdwResult));
|
|||
|
} // W3SVCDictatorDumpInfo()
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
Thunks for Fake NT APIs
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
CRITICAL_SECTION g_csNonNTAPIs;
|
|||
|
LONG FakeInterlockedCompareExchange(
|
|||
|
LONG *Destination,
|
|||
|
LONG Exchange,
|
|||
|
LONG Comperand
|
|||
|
);
|
|||
|
|
|||
|
LONG
|
|||
|
FakeInterlockedCompareExchange(
|
|||
|
LONG *Destination,
|
|||
|
LONG Exchange,
|
|||
|
LONG Comperand
|
|||
|
)
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
Description:
|
|||
|
This function fakes the interlocked compare exchange operation for non NT
|
|||
|
platforms
|
|||
|
See WAMLoadNTApis() for details
|
|||
|
|
|||
|
Returns:
|
|||
|
returns the old value at Destination
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
{
|
|||
|
LONG oldValue;
|
|||
|
|
|||
|
EnterCriticalSection( &g_csNonNTAPIs);
|
|||
|
|
|||
|
oldValue = *Destination;
|
|||
|
|
|||
|
if ( oldValue == Comperand ) {
|
|||
|
*Destination = Exchange;
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection( &g_csNonNTAPIs);
|
|||
|
|
|||
|
return( oldValue);
|
|||
|
} // FakeInterlockedCompareExchange()
|
|||
|
|
|||
|
|
|||
|
static VOID
|
|||
|
LoadNTApis(VOID)
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
Description:
|
|||
|
This function loads the entry point for functions from
|
|||
|
Kernel32.dll. If the entry point is missing, the function
|
|||
|
pointer will point to a fake routine which does nothing. Otherwise,
|
|||
|
it will point to the real function.
|
|||
|
|
|||
|
It dynamically loads the kernel32.dll to find the entry ponit and then
|
|||
|
unloads it after getting the address. For the resulting function
|
|||
|
pointer to work correctly one has to ensure that the kernel32.dll is
|
|||
|
linked with the dll/exe which links to this file.
|
|||
|
-----------------------------------------------------------------------------*/
|
|||
|
{
|
|||
|
// Initialize the critical section for non NT API support, in case if we need this
|
|||
|
INITIALIZE_CRITICAL_SECTION( &g_csNonNTAPIs);
|
|||
|
|
|||
|
if ( g_pfnInterlockedCompareExchange == NULL )
|
|||
|
{
|
|||
|
HINSTANCE tmpInstance;
|
|||
|
//
|
|||
|
// load kernel32 and get NT specific entry points
|
|||
|
//
|
|||
|
|
|||
|
tmpInstance = LoadLibrary("kernel32.dll");
|
|||
|
if ( tmpInstance != NULL )
|
|||
|
{
|
|||
|
|
|||
|
// For some reason the original function is _InterlockedCompareExchange!
|
|||
|
g_pfnInterlockedCompareExchange = (PFN_INTERLOCKED_COMPARE_EXCHANGE )
|
|||
|
GetProcAddress( tmpInstance, "InterlockedCompareExchange");
|
|||
|
|
|||
|
if ( g_pfnInterlockedCompareExchange == NULL )
|
|||
|
{
|
|||
|
// the function is not available
|
|||
|
// Just thunk it.
|
|||
|
g_pfnInterlockedCompareExchange = FakeInterlockedCompareExchange;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We can free this because we are statically linked to it
|
|||
|
//
|
|||
|
|
|||
|
FreeLibrary(tmpInstance);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
} // WAMLoadNTApis()
|
|||
|
|
|||
|
static void
|
|||
|
UnloadNTApis(VOID)
|
|||
|
{
|
|||
|
DeleteCriticalSection( &g_csNonNTAPIs);
|
|||
|
|
|||
|
return;
|
|||
|
} // WAMUnloadNTApis()
|
|||
|
|
|||
|
VOID
|
|||
|
RecycleCallback(
|
|||
|
VOID * pvContext
|
|||
|
)
|
|||
|
{
|
|||
|
CWamInfo * pWamInfo = (CWamInfo*)pvContext;
|
|||
|
|
|||
|
DBG_ASSERT( pWamInfo );
|
|||
|
|
|||
|
pWamInfo->m_fRecycled = TRUE;
|
|||
|
|
|||
|
if (!g_pWamDictator->m_PTable.RecycleWamInfo( pWamInfo ) )
|
|||
|
{
|
|||
|
DBGPRINTF((
|
|||
|
DBG_CONTEXT,
|
|||
|
"[RecycleCallback] failed to recycle CWamInfo 0x%08x.\r\n"
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
pWamInfo->Dereference();
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/************************ End of File ***********************/
|
|||
|
|