windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/wam/wamreg/wamadm.cpp
2020-09-26 16:20:57 +08:00

2827 lines
72 KiB
C++

/*===================================================================
Microsoft IIS
Microsoft Confidential.
Copyright 1997 Microsoft Corporation. All Rights Reserved.
Component: WAMREG
File: WamAdm.cpp
Implementation of WamAdm object, including ClassFactory, IWamAdm,
IMSAdminReplication
Owner: LeiJin
Note:
WamAdm implementation
===================================================================*/
#include "common.h"
#include "iiscnfg.h"
#include "iwamreg.h"
#include "WamAdm.h"
#include "auxfunc.h"
#include "wmrgexp.h"
#include "dbgutil.h"
#include "mtxrepli.c"
#include "mtxrepl.h"
#ifdef _IIS_6_0
#include "string.hxx"
#include "multisz.hxx"
#include "w3ctrlps.h"
#include "iiscnfgp.h"
#endif // _IIS_6_0
#define ReleaseInterface(p) if (p) { p->Release(); p = NULL; }
const LPCWSTR APPPOOLPATH = L"/LM/W3SVC/AppPools/";
#ifndef DBGERROR
#define DBGERROR(args) ((void)0) /* Do Nothing */
#endif
#ifndef DBGWARN
#define DBGWARN(args) ((void)0) /* Do Nothing */
#endif
/////////////////////////////////////////////////////////////////////////////
// CWamAdmin
/*===================================================================
CWamAdmin
Constructor
Parameter:
NONE.
Return:
===================================================================*/
CWamAdmin::CWamAdmin()
: m_cRef(1)
{
InterlockedIncrement((long *)&g_dwRefCount);
}
/*===================================================================
~CWamAdmin
Constructor
Parameter:
NONE.
Return:
===================================================================*/
CWamAdmin::~CWamAdmin()
{
InterlockedDecrement((long *)&g_dwRefCount);
}
/*===================================================================
CWamAdmin::QueryInterface
QueryInterface, CWamAdmin supports 2 interfaces, one is IID_IWamAdmin,
the other is IID_IMSAdminReplication.
Parameter:
riid
ppv pointer to Interface pointer
Return: HRESULT
===================================================================*/
STDMETHODIMP CWamAdmin::QueryInterface(REFIID riid, void ** ppv)
{
if (riid == IID_IUnknown || riid == IID_IWamAdmin)
{
*ppv = static_cast<IWamAdmin*>(this);
}
else if (riid == IID_IWamAdmin2)
{
*ppv = static_cast<IWamAdmin2*>(this);
}
else if (riid == IID_IMSAdminReplication)
{
*ppv = static_cast<IMSAdminReplication*>(this);
}
#ifdef _IIS_6_0
else if (riid == IID_IIISApplicationAdmin)
{
*ppv = static_cast<IIISApplicationAdmin*>(this);
}
#endif //_IIS_6_0
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return NOERROR;
}
/*===================================================================
CWamAdmin::AddRef
Parameter:
NONE
Return: HRESULT
===================================================================*/
STDMETHODIMP_(ULONG) CWamAdmin::AddRef( )
{
return InterlockedIncrement(&m_cRef);
}
/*===================================================================
CWamAdmin::Release
Parameter:
NONE
Return: HRESULT
===================================================================*/
STDMETHODIMP_(ULONG) CWamAdmin::Release( )
{
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
return 0;
}
return m_cRef;
}
/*===================================================================
CWamAdmin::AppCreate
Create an application on szMDPath. The fInProc indicates whether the
result application is in-proc or out-proc. If There is already an application
existed on szMDPath, AppCreate will remove the old application if fInProc does not
match with existing application. Otherwise, it is no-op.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
fInProc TRUE if wants to have an InProc application,
FALSE if wants to have an outproc application.
Return: HRESULT
===================================================================*/
STDMETHODIMP CWamAdmin::AppCreate(LPCWSTR szMDPath, BOOL fInProc)
{
DWORD dwAppMode = (fInProc) ?
eAppRunInProc : eAppRunOutProcIsolated;
return AppCreate2(szMDPath, dwAppMode);
}
/*===================================================================
CWamAdmin::AppDelete
Delete an application on a Metabase Path. If there is no application existed
before, it is no-op.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
fRecursive TRUE if wants to delete applications from all sub nodes of szMDPath,
FALSE otherwise.
Return: HRESULT
===================================================================*/
STDMETHODIMP CWamAdmin::AppDelete(LPCWSTR szMDPath, BOOL fRecursive)
{
return PrivateDeleteApplication(szMDPath,
fRecursive,
FALSE, // Recoverable?
FALSE); // RemoveAppPool?
}
HRESULT
CWamAdmin::PrivateDeleteApplication
(
LPCWSTR szMDPath,
BOOL fRecursive,
BOOL fRecoverable,
BOOL fRemoveAppPool
)
{
HRESULT hr = NOERROR;
DWORD dwAppMode;
WamRegMetabaseConfig MDConfig;
LPWSTR pwszFormattedPath = NULL;
if (szMDPath == NULL)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
//
// Refer to function comment of FormatMetabasePath.
//
hr = FormatMetabasePath(szMDPath, &pwszFormattedPath);
if (FAILED(hr))
{
return hr;
}
if (!g_WamRegGlobal.FAppPathAllowConfig(pwszFormattedPath))
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
// Acquire a Lock
g_WamRegGlobal.AcquireAdmWriteLock();
if (!fRecursive)
{
hr = g_WamRegGlobal.DeleteApp(pwszFormattedPath, fRecoverable, fRemoveAppPool);
if (hr == MD_ERROR_DATA_NOT_FOUND)
{
hr = NOERROR;
}
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to Delete on path %S, hr = %08x\n",
szMDPath,
hr));
}
}
else
{
HRESULT hrT = NOERROR;
DWORD dwSizePrefix;
WCHAR* pbBufferTemp = NULL;
DWORD dwBufferSizeTemp = 0;
dwSizePrefix = wcslen(pwszFormattedPath);
hr = MDConfig.MDGetPropPaths(pwszFormattedPath, MD_APP_ISOLATED, &pbBufferTemp, &dwBufferSizeTemp);
if (SUCCEEDED(hr) && pbBufferTemp)
{
WCHAR* pszString = NULL;
WCHAR* pszMetabasePath = NULL;
for (pszString = (LPWSTR)pbBufferTemp;
*pszString != (WCHAR)'\0' && SUCCEEDED(hr);
pszString += (wcslen(pszString) + 1))
{
hr = g_WamRegGlobal.ConstructFullPath(pwszFormattedPath,
dwSizePrefix,
pszString,
&pszMetabasePath
);
if (SUCCEEDED(hr))
{
if (!g_WamRegGlobal.FIsW3SVCRoot(pszMetabasePath))
{
hr = g_WamRegGlobal.DeleteApp(pszMetabasePath, fRecoverable, fRemoveAppPool);
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to Delete on path %S, hr = %08x\n",
pszString,
hr));
break;
}
}
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
else
{
DBGPRINTF((DBG_CONTEXT, "Failed to DeleteRecoverable, hr = %08x\n",
pszString,
hr));
}
}
delete [] pbBufferTemp;
pbBufferTemp = NULL;
}
else
{
DBGPRINTF((DBG_CONTEXT, "Delete: GetPropPaths failed hr = %08x\n", hr));
}
}
// Release a Lock
g_WamRegGlobal.ReleaseAdmWriteLock();
if (pwszFormattedPath != szMDPath)
{
delete [] pwszFormattedPath;
pwszFormattedPath = NULL;
}
return hr;
}
/*===================================================================
CWamAdmin::AppUnLoad
UnLoad an application on a Metabase Path. If there is no application running
it returns NOERROR.
For non-administrators we prevent them from unloading applications
in the pool. If the recursive flag is set, we will silently
ignore failures due to insufficient access.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
fRecursive TRUE if wants to unload applications from all sub nodes of szMDPath,
FALSE otherwise.
Return: HRESULT
===================================================================*/
STDMETHODIMP CWamAdmin::AppUnLoad(LPCWSTR szMDPath, BOOL fRecursive)
{
HRESULT hr = NOERROR;
DWORD dwCallBack = 0;
WamRegMetabaseConfig MDConfig;
DWORD dwAppIsolated = 0;
BOOL bIsAdmin = TRUE;
if (szMDPath == NULL || *szMDPath == L'\0')
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
bIsAdmin = MDConfig.HasAdminAccess();
// Acquire a Lock
g_WamRegGlobal.AcquireAdmWriteLock();
if (fRecursive)
{
DWORD dwSizePrefix = wcslen(szMDPath);;
WCHAR* pbBufferTemp = NULL;
DWORD dwBufferSizeTemp = 0;
hr = MDConfig.MDGetPropPaths( szMDPath,
MD_APP_ISOLATED,
&pbBufferTemp,
&dwBufferSizeTemp);
if (SUCCEEDED(hr))
{
WCHAR* pszString = NULL;
WCHAR* pszMetabasePath = NULL;
BOOL bDoUnload;
for( pszString = (LPWSTR)pbBufferTemp;
*pszString != (WCHAR)'\0' && SUCCEEDED(hr);
pszString += (wcslen(pszString) + 1))
{
bDoUnload = TRUE;
hr = g_WamRegGlobal.ConstructFullPath(szMDPath,
dwSizePrefix,
pszString,
&pszMetabasePath
);
if( SUCCEEDED(hr) && !bIsAdmin )
{
hr = MDConfig.MDGetDWORD( pszMetabasePath,
MD_APP_ISOLATED,
&dwAppIsolated );
DBG_ASSERT( SUCCEEDED(hr) );
if( SUCCEEDED(hr) && eAppRunOutProcInDefaultPool == dwAppIsolated )
{
// Do not unload
bDoUnload = FALSE;
DBGPRINTF((DBG_CONTEXT,
"Insufficient Access to unload Application %S, hr = %08x\n",
pszMetabasePath,
hr));
}
}
if( SUCCEEDED(hr) && bDoUnload )
{
hr = g_WamRegGlobal.W3ServiceUtil( pszMetabasePath,
APPCMD_UNLOAD,
&dwCallBack);
}
if( pszMetabasePath )
{
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
} // for each application
}
if (pbBufferTemp != NULL)
{
delete [] pbBufferTemp;
pbBufferTemp = NULL;
}
}
else
{
if( !bIsAdmin )
{
// Non recursive
hr = MDConfig.MDGetDWORD( szMDPath,
MD_APP_ISOLATED,
&dwAppIsolated );
if( SUCCEEDED(hr) && eAppRunOutProcInDefaultPool == dwAppIsolated )
{
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
DBGPRINTF((DBG_CONTEXT,
"Insufficient Access to unload Application %S, hr = %08x\n",
szMDPath,
hr));
}
}
if( SUCCEEDED(hr) )
{
hr = g_WamRegGlobal.W3ServiceUtil(szMDPath, APPCMD_UNLOAD, &dwCallBack);
}
}
// Release a Lock
g_WamRegGlobal.ReleaseAdmWriteLock();
return hr;
}
/*===================================================================
CWamAdmin::AppGetStatus
GetStatus an application on a Metabase Path. If there is an application on the
metabase path, and the application is currently running, the dwStatus is set to
APPSTATUS_RUNNING, if the application is not running, the dwStatus is set to
APPSTATUS_STOPPED, if there is no application defined on the metabase path, the
dwStatus is set to APPSTATUS_NOTDEFINED.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
pdwAppStatus pointer DWORD buffer contains status result.
Return: HRESULT
NOERROR if succeeded.
===================================================================*/
STDMETHODIMP CWamAdmin::AppGetStatus(LPCWSTR szMDPath, DWORD* pdwAppStatus)
{
HRESULT hr = NOERROR;
HRESULT hrT;
DWORD dwCallBack = 0;
WamRegMetabaseConfig MDConfig;
if (szMDPath == NULL)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
// Acquire a Lock
g_WamRegGlobal.AcquireAdmWriteLock();
hrT = g_WamRegGlobal.W3ServiceUtil(szMDPath, APPCMD_GETSTATUS, &dwCallBack);
if (dwCallBack == APPSTATUS_Running)
{
*pdwAppStatus = APPSTATUS_RUNNING;
}
else if (dwCallBack == APPSTATUS_Stopped)
{
*pdwAppStatus = APPSTATUS_STOPPED;
}
else
{
DWORD dwAppMode;
hr = MDConfig.MDGetDWORD(szMDPath, MD_APP_ISOLATED, &dwAppMode);
if (hr == MD_ERROR_DATA_NOT_FOUND)
{
*pdwAppStatus = APPSTATUS_NOTDEFINED;
hr = NOERROR;
}
else if (hr == NOERROR)
{
*pdwAppStatus = APPSTATUS_STOPPED;
hr = NOERROR;
}
}
// Release a Lock
g_WamRegGlobal.ReleaseAdmWriteLock();
return hr;
}
/*===================================================================
CWamAdmin::AppDeleteRecoverable
Delete an application on a Metabase Path. If there is no application existed
before, it is no-op. It leaves AppIsolated untouched, because, this value is
needed in Recover operation.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
fRecursive TRUE if wants to deleteRecoverable applications from all sub nodes of szMDPath,
FALSE otherwise.
Return: HRESULT
===================================================================*/
STDMETHODIMP CWamAdmin::AppDeleteRecoverable(LPCWSTR szMDPath, BOOL fRecursive)
{
return PrivateDeleteApplication(szMDPath,
fRecursive,
TRUE, // Recoverable?
FALSE); // RemoveAppPool?
}
/*===================================================================
CWamAdmin::AppRecover
Recover an application on a Metabase Path. Based on the AppIsolated value
on the metabase path, this function recreates an application.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
fRecursive TRUE if wants to Recover applications from all sub nodes of szMDPath,
FALSE otherwise.
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdmin::AppRecover(LPCWSTR szMDPath, BOOL fRecursive)
{
HRESULT hr = NOERROR;
WamRegMetabaseConfig MDConfig;
LPWSTR pwszFormattedPath = NULL;
if (szMDPath == NULL)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
// Refer to function comment.
hr = FormatMetabasePath(szMDPath, &pwszFormattedPath);
if (FAILED(hr))
{
return hr;
}
//
// Grab the Lock
//
g_WamRegGlobal.AcquireAdmWriteLock();
if (fRecursive)
{
DWORD dwSizePrefix;
WCHAR* pbBufferTemp = 0;
DWORD dwBufferSizeTemp;
dwSizePrefix = wcslen(pwszFormattedPath);
hr = MDConfig.MDGetPropPaths(pwszFormattedPath, MD_APP_ISOLATED, &pbBufferTemp, &dwBufferSizeTemp);
if (SUCCEEDED(hr) && pbBufferTemp)
{
WCHAR *pszString = NULL;
WCHAR *pszMetabasePath = NULL;
for (pszString = (LPWSTR)pbBufferTemp;
*pszString != (WCHAR)'\0' && SUCCEEDED(hr);
pszString += (wcslen(pszString) + 1))
{
hr = g_WamRegGlobal.ConstructFullPath(pwszFormattedPath,
dwSizePrefix,
pszString,
&pszMetabasePath
);
if (SUCCEEDED(hr))
{
if (!g_WamRegGlobal.FIsW3SVCRoot(pszMetabasePath))
{
hr = g_WamRegGlobal.RecoverApp(pszMetabasePath, TRUE);
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to Recover on path %S, hr = %08x\n",
pszMetabasePath,
hr));
break;
}
}
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
else
{
DBGPRINTF((DBG_CONTEXT, "Failed to Recover, hr = %08x\n",
pszString,
hr));
}
}
}
else
{
DBGPRINTF((DBG_CONTEXT, "Recover: GetPropPaths failed hr = %08x\n", hr));
}
if (pbBufferTemp != NULL)
{
delete [] pbBufferTemp;
pbBufferTemp = NULL;
}
}
else
{
hr = g_WamRegGlobal.RecoverApp(pwszFormattedPath, TRUE);
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to Recover on path %S, hr = %08x\n",
szMDPath,
hr));
}
}
if (SUCCEEDED(hr))
{
MDConfig.SaveData();
}
//
// Release the Lock
//
g_WamRegGlobal.ReleaseAdmWriteLock();
if (pwszFormattedPath != szMDPath)
{
delete [] pwszFormattedPath;
pwszFormattedPath = NULL;
}
return hr;
}
/*==================================================================
CWamAdmin::AppCreate2
Create an application on szMDPath. The dwAppMode indicates whether the
result application is in-proc or out-proc in a default pool or out proc isolated.
If the application exists with the desired mode, it will be a no op. Otherwise,
registration is done.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
dwAppMode
Return: HRESULT
===================================================================*/
STDMETHODIMP CWamAdmin::AppCreate2(LPCWSTR szMDPath, DWORD dwAppModeIn)
{
HRESULT hr = NOERROR;
DWORD dwAppMode = 0;
BOOL fCreateNewApp = FALSE;
BOOL fDeleteOldApp = FALSE;
WamRegMetabaseConfig MDConfig;
LPWSTR pwszFormattedPath = NULL;
if (szMDPath == NULL)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
//
// See FormatMetabasePath comment
//
hr = FormatMetabasePath(szMDPath, &pwszFormattedPath);
if (FAILED(hr))
{
return hr;
}
if (!g_WamRegGlobal.FAppPathAllowConfig(pwszFormattedPath))
{
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
// Acquire a Lock
g_WamRegGlobal.AcquireAdmWriteLock();
hr = MDConfig.MDGetDWORD(pwszFormattedPath, MD_APP_ISOLATED, &dwAppMode);
if (hr == MD_ERROR_DATA_NOT_FOUND)
{
fCreateNewApp = TRUE;
hr = NOERROR;
}
else if (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
{
hr = MDConfig.MDCreatePath(NULL, pwszFormattedPath);
fCreateNewApp = TRUE;
if(FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to create metabase path %S, hr = %08x",
szMDPath,
hr));
}
}
else if (SUCCEEDED(hr))
{
//
// if the input application mode is not the same as defined
// in the metabase, we need to delete the old application as
// defined in the metabase and create a new application as
// specified by dwAppModeIn, the in parameter.
//
if (dwAppMode != dwAppModeIn)
{
fDeleteOldApp = TRUE;
fCreateNewApp = TRUE;
}
}
else
{
DBGPRINTF((DBG_CONTEXT, "Failed to get DWORD on metabase path %S, hr = %08x",
szMDPath,
hr));
}
if (SUCCEEDED(hr))
{
if (fDeleteOldApp)
{
DBG_ASSERT(fCreateNewApp);
hr = g_WamRegGlobal.DeleteApp(pwszFormattedPath, FALSE, FALSE);
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to delete old application on path %S, hr = 08x\n",
szMDPath,
hr));
}
}
if (fCreateNewApp)
{
if (dwAppModeIn == eAppRunOutProcInDefaultPool)
{
hr = g_WamRegGlobal.CreatePooledApp(pwszFormattedPath, FALSE);
}
else if (dwAppModeIn == eAppRunInProc)
{
hr = g_WamRegGlobal.CreatePooledApp(pwszFormattedPath, TRUE);
}
else
{
hr = g_WamRegGlobal.CreateOutProcApp(pwszFormattedPath);
}
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Failed to create new application on path %S, hr = 08x\n",
szMDPath,
hr));
}
}
}
// Release a Lock
g_WamRegGlobal.ReleaseAdmWriteLock();
//
// if pwszFormattedPath is not same as szMDPath
// then FormatMetabasePath() did a memory allocation.
//
if (pwszFormattedPath != szMDPath)
{
delete [] pwszFormattedPath;
pwszFormattedPath = NULL;
}
return hr;
}
//===============================================================================
// Wam Admin Replication implementation
//
//===============================================================================
/*===================================================================
CWamAdmin::GetSignature
Get signature of application configurations. A signature in WAMREG is a checksum from
all the metabase paths that define application.
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdmin::GetSignature
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ unsigned char __RPC_FAR *pbBuffer,
/* [out */ DWORD __RPC_FAR *pdwMDRequiredBufferSize
)
{
HRESULT hr = NOERROR;
WCHAR *pbBufferTemp = NULL;
DWORD dwBufferSizeTemp = 0;
DWORD dwSignature = 0;
DWORD dwRequiredSize = 0;
WamRegMetabaseConfig MDConfig;
//
// Grab the Lock
//
g_WamRegGlobal.AcquireAdmWriteLock();
hr = MDConfig.MDGetPropPaths(WamRegGlobal::g_szMDW3SVCRoot, MD_APP_ISOLATED, &pbBufferTemp, &dwBufferSizeTemp);
if (SUCCEEDED(hr))
{
WCHAR *pszString = NULL;
WCHAR *pszMetabasePath = NULL;
DWORD dwSignatureofPath = 0;
for (pszString = (LPWSTR)pbBufferTemp;
*pszString != (WCHAR)'\0' && SUCCEEDED(hr);
pszString += (wcslen(pszString) + 1))
{
dwRequiredSize += sizeof(DWORD);
if (dwRequiredSize <= dwBufferSize)
{
hr = g_WamRegGlobal.ConstructFullPath(WamRegGlobal::g_szMDW3SVCRoot,
WamRegGlobal::g_cchMDW3SVCRoot,
pszString,
&pszMetabasePath
);
if (SUCCEEDED(hr))
{
dwSignatureofPath = 0;
hr = MDConfig.GetSignatureOnPath(pszMetabasePath, &dwSignatureofPath);
if (SUCCEEDED(hr))
{
// Add Signature
*(DWORD*)pbBuffer = dwSignatureofPath;
pbBuffer += sizeof(DWORD);
DBGPRINTF((DBG_CONTEXT, "Get Signature on path %S, signature = %08x\n",
pszMetabasePath,
dwSignatureofPath));
}
else
{
DBGPRINTF((DBG_CONTEXT, "Failed to get signature on path %S, hr = %08x\n",
pszString,
hr));
DBG_ASSERT(hr);
}
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
}
}
if (dwRequiredSize > dwBufferSize)
{
*pdwMDRequiredBufferSize = dwRequiredSize;
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
}
else
{
DBGPRINTF((DBG_CONTEXT, "GetSignature: GetPropPaths failed hr = %08x\n", hr));
}
if (SUCCEEDED(hr))
{
*pdwMDRequiredBufferSize = dwRequiredSize;
}
if (pbBufferTemp != NULL)
{
delete [] pbBufferTemp;
}
//
// Release the Lock
//
g_WamRegGlobal.ReleaseAdmWriteLock();
return hr;
}
/*===================================================================
CWamAdmin::Propagate
Unused in WAMREG. NOOP.
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdmin::Propagate
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pszBuffer
)
{
return NOERROR;
}
/*===================================================================
CWamAdmin::Propagate2
This function is called after IIS replication, and triggers MTS to start
replication pakcages, it calls IISComputerToComputer.
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdmin::Propagate2
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pszBuffer,
/* [in] */ DWORD dwSignatureMismatch
)
{
//
// IISComputerToComputer can not be called from inetinfo.exe, because IISComputerToComputer will
// make cross-machine RPC call, and inetinfo is set to Local system, therefore, IISComputerToComputer
// will fail at the authentication level.
// move IISComputerToComputer to iissync.exe. Where iissync.exe has some user account & password.
//
return NOERROR;
}
/*===================================================================
CWamAdmin::Serialize
This function packs all neccessary infomation (path + WAMCLSID) for a target
machine to prepare replication(DeSerialize).
The only applications that we really care about are isolated applications.
We need the path + WAMCLSID + APPID.
CODEWORK
See NT Bug 378371
Replication of IIS COM+ applications has been broken for a long time
but the all of the fixes I considered have some serious drawbacks.
1. Don't use comrepl to move the IIS applications. Serialize/Deserialize
all the data needed to create the isolated applications and then delete
and recreate them on the target. The problem here is that the packages
may in fact be modified by the user and these modifications should be
preserved.
2. Use comrepl as it is and replicate the IWAM_* account. This seems like
a bad idea. The IWAM_ account should ideally never exist on multiple
machines. Another issue is handling the password and account privileges.
3. Use a modified comrepl (or let comrepl fail and leave the package identity
as "interactive user"). Then do a fixup of the activation identity.
This doesn't work, because the Propogate/Propogate2 protocol is
essentially useless. Changing this protocol on the next release
is absolutely something that should be considered, although AppCenter
probably makes it a moot point.
The current implementation is option 1.
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdmin::Serialize
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ unsigned char __RPC_FAR *pbBuffer,
/* [out] */ DWORD __RPC_FAR *pdwMDRequiredBufferSize
)
{
HRESULT hr = NOERROR;
WCHAR *pbBufferTemp = NULL;
DWORD dwBufSizePath = 0;
DWORD dwSizeForReturn = sizeof(DWORD);
WamRegMetabaseConfig MDConfig;
//
// Grab the Lock
//
g_WamRegGlobal.AcquireAdmWriteLock();
hr = MDConfig.MDGetPropPaths( WamRegGlobal::g_szMDW3SVCRoot,
MD_APP_WAM_CLSID,
&pbBufferTemp,
&dwBufSizePath
);
if (SUCCEEDED(hr))
{
WCHAR *pszString = NULL;
WCHAR *pszMetabasePath = NULL;
WCHAR *pszAppName = NULL;
WCHAR szWAMCLSID[uSizeCLSID];
WCHAR szAppId[uSizeCLSID];
DWORD dwSizeofRecord;
DWORD cSizeMetabasePath = 0;
DWORD cSizeAppName = 0;
DWORD dwAppIsolated;
for( pszString = (LPWSTR)pbBufferTemp;
*pszString != (WCHAR)'\0';
pszString += (wcslen(pszString) + 1))
{
// Clean up allocations
if( pszMetabasePath != NULL )
{
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
if( pszAppName != NULL )
{
delete [] pszAppName;
pszAppName = NULL;
}
hr = g_WamRegGlobal.ConstructFullPath(
WamRegGlobal::g_szMDW3SVCRoot,
WamRegGlobal::g_cchMDW3SVCRoot,
pszString,
&pszMetabasePath
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"ConstructFullPath failed for base (%S) "
"partial (%S) hr=%08x\n",
WamRegGlobal::g_szMDW3SVCRoot,
pszString,
hr
));
break;
}
if( g_WamRegGlobal.FIsW3SVCRoot( pszMetabasePath ) )
{
// Don't consider the root application
continue;
}
hr = MDConfig.MDGetDWORD( pszMetabasePath,
MD_APP_ISOLATED,
&dwAppIsolated
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get MD_APP_ISOLATED, hr=%08x\n",
hr
));
break;
}
if( dwAppIsolated != eAppRunOutProcIsolated )
{
// Don't consider non-isolated applications
continue;
}
hr = MDConfig.MDGetIDs( pszMetabasePath,
szWAMCLSID,
szAppId,
dwAppIsolated
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get IDs for %S, hr = %08x\n",
pszMetabasePath,
hr
));
break;
}
hr = MDConfig.MDGetAppName( pszMetabasePath,
&pszAppName
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get AppName for %S, hr = %08x\n",
pszMetabasePath,
hr
));
break;
}
cSizeMetabasePath = wcslen(pszMetabasePath) + 1;
cSizeAppName = wcslen(pszAppName) + 1;
dwSizeofRecord = sizeof(DWORD) +
((2 * uSizeCLSID) * sizeof(WCHAR)) +
(cSizeMetabasePath * sizeof(WCHAR)) +
(cSizeAppName * sizeof(WCHAR));
dwSizeForReturn += dwSizeofRecord;
if (dwSizeForReturn <= dwBufferSize)
{
// Size
*(DWORD *)pbBuffer = dwSizeofRecord;
pbBuffer += sizeof(DWORD);
// WAMCLSID
memcpy( pbBuffer, szWAMCLSID, sizeof(WCHAR) * uSizeCLSID );
pbBuffer += sizeof(WCHAR) * uSizeCLSID;
// APPID
memcpy( pbBuffer, szAppId, sizeof(WCHAR) * uSizeCLSID );
pbBuffer += sizeof(WCHAR) * uSizeCLSID;
// PATH
memcpy( pbBuffer, pszMetabasePath, cSizeMetabasePath * sizeof(WCHAR) );
pbBuffer += cSizeMetabasePath * sizeof(WCHAR);
// APPNAME
memcpy( pbBuffer, pszAppName, cSizeAppName * sizeof(WCHAR) );
pbBuffer += cSizeAppName * sizeof(WCHAR);
}
}
if (SUCCEEDED(hr))
{
if (dwSizeForReturn <= dwBufferSize)
{
*(DWORD*)pbBuffer = 0x0; // Ending Signature
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
*pdwMDRequiredBufferSize = dwSizeForReturn;
}
// Clean up allocations
if( pszMetabasePath != NULL )
{
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
if( pszAppName != NULL )
{
delete [] pszAppName;
pszAppName = NULL;
}
}
else
{
DBGERROR(( DBG_CONTEXT,
"Serialize: GetPropPaths failed hr = %08x\n",
hr
));
}
//
// Release the Lock
//
g_WamRegGlobal.ReleaseAdmWriteLock();
if (pbBufferTemp)
{
delete [] pbBufferTemp;
}
return hr;
}
/*===================================================================
CWamAdmin::DeSerialize
This function unpacks all neccessary infomation (path + WAMCLSID) on a target
machine to prepare replication(DeSerialize).
The only applications that we really care about with replication are
isolated apps. This routine removes the existing out of process apps
and then recreates the applications that are sent over in pbBuffer.
CODEWORK - See comments in Serialize
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdmin::DeSerialize
(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pbBuffer
)
{
DWORD dwBufferSizeTemp= 0;
WCHAR* pbBufferTemp = NULL;
HRESULT hr = NOERROR;
WamRegMetabaseConfig MDConfig;
g_WamRegGlobal.AcquireAdmWriteLock();
hr = MDConfig.MDGetPropPaths( WamRegGlobal::g_szMDW3SVCRoot,
MD_APP_WAM_CLSID,
&pbBufferTemp,
&dwBufferSizeTemp
);
if (SUCCEEDED(hr))
{
//
// Remove all the existing isolated applications.
//
WCHAR * pszString = NULL;
WCHAR * pszMetabasePath = NULL;
DWORD dwAppIsolated;
for ( pszString = (LPWSTR)pbBufferTemp;
*pszString != (WCHAR)'\0';
pszString += (wcslen(pszString) + 1))
{
if( pszMetabasePath != NULL )
{
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
hr = g_WamRegGlobal.ConstructFullPath(
WamRegGlobal::g_szMDW3SVCRoot,
WamRegGlobal::g_cchMDW3SVCRoot,
pszString,
&pszMetabasePath
);
if( FAILED(hr) )
{
// This failure is fatal
DBGERROR(( DBG_CONTEXT,
"ConstructFullPath failed for base (%S) "
"partial (%S) hr=%08x\n",
WamRegGlobal::g_szMDW3SVCRoot,
pszString,
hr
));
break;
}
hr = MDConfig.MDGetDWORD( pszMetabasePath,
MD_APP_ISOLATED,
&dwAppIsolated
);
if( FAILED(hr) )
{
DBGWARN(( DBG_CONTEXT,
"Failed to get MD_APP_ISOLATED at (%S) hr=%08x\n",
pszMetabasePath,
hr
));
hr = NOERROR;
continue;
}
if( dwAppIsolated == eAppRunOutProcIsolated )
{
hr = g_WamRegGlobal.DeleteApp( pszMetabasePath, FALSE, FALSE );
if (FAILED(hr))
{
DBGWARN(( DBG_CONTEXT,
"Unable to delete app at %S, hr = %08x\n",
pszMetabasePath,
hr
));
hr = NOERROR;
continue;
}
}
}
if( pszMetabasePath != NULL )
{
delete [] pszMetabasePath;
pszMetabasePath = NULL;
}
}
//
// Now go through the serialized data and create the
// necessary new applications.
//
BYTE * pbTemp = pbBuffer;
DWORD cTotalBytes = 0;
DWORD cRecBytes = 0;
WCHAR * szWAMCLSID = NULL;
WCHAR * szPath = NULL;
WCHAR * szAppId = NULL;
WCHAR * szAppName = NULL;
DBGPRINTF(( DBG_CONTEXT,
"DeSerialize: buffer size %d, \n",
dwBufferSize
));
while( *((DWORD*)pbTemp) != 0x0 )
{
// SIZE
cRecBytes = *((DWORD*)pbTemp);
pbTemp += sizeof(DWORD);
// CLSID
szWAMCLSID = (WCHAR *)pbTemp;
pbTemp += uSizeCLSID * sizeof(WCHAR);
// APPID
szAppId = (WCHAR *)pbTemp;
pbTemp += uSizeCLSID * sizeof(WCHAR);
// PATH
szPath = (WCHAR *)pbTemp;
pbTemp += (wcslen(szPath) + 1) * sizeof(WCHAR);
// APPNAME
szAppName = (WCHAR *)pbTemp;
pbTemp += (wcslen(szAppName) + 1) * sizeof(WCHAR);
// TODO - This should really be output based on a flag
DBGPRINTF(( DBG_CONTEXT,
"Deserialize path = %S, WAMCLSID = %S.\n",
szPath,
szWAMCLSID
));
// Should never serialize the w3svc root
DBG_ASSERT( !g_WamRegGlobal.FIsW3SVCRoot(szPath) );
hr = g_WamRegGlobal.CreateOutProcAppReplica( szPath,
szAppName,
szWAMCLSID,
szAppId
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to create COM application. Path(%S) "
"Clsid(%S) AppId(%S). hr=%08x\n",
szPath,
szWAMCLSID,
szAppId,
hr
));
// ??? Should we be continuing here ???
// Don't report an error if we are continuing
hr = NOERROR;
}
}
if (pbBufferTemp)
{
delete [] pbBufferTemp;
}
//
// Release the Lock
//
g_WamRegGlobal.ReleaseAdmWriteLock();
return hr;
}
/*===================================================================
CWamAdmin::FormatMetabasePath
This function format the input metabase path. If the metabase path has an
ending '/', this function will allocate a memory block and make a new string
without the ending '/'. This function will return a pointer to newly allocated
memory block. Otherwise, the function will return the pointer to
the input metabase path.
Parameter:
pwszMetabasePathIn input metabase path
ppwszMetabasePathOut pointer to the resulting pointer that contains the formatted
metabase path.
Return: HRESULT
NOERROR if succeeds
NOTE: if ppwszMetabasePathOut == pwszMetabasePathIn, then no memory allocation.
Otherwise, there is a memory allocation happened, and caller needs to free the
memory block passed out in ppwszMetabasePathOut.
===================================================================*/
STDMETHODIMP CWamAdmin::FormatMetabasePath
(
/* [in] */ LPCWSTR pwszMetabasePathIn,
/* [out] */ LPWSTR *ppwszMetabasePathOut
)
{
HRESULT hr = NOERROR;
LPWSTR pResult = NULL;
DBG_ASSERT(pwszMetabasePathIn);
DBG_ASSERT(ppwszMetabasePathOut);
LONG cch = wcslen(pwszMetabasePathIn);
if (pwszMetabasePathIn[cch-1] == L'\\' ||
pwszMetabasePathIn[cch-1] == L'/')
{
//
// Need to start up with a new string, can not do it with old string.
//
pResult = new WCHAR[cch];
if (pResult != NULL)
{
wcsncpy(pResult, pwszMetabasePathIn, cch);
pResult[cch-1] = L'\0';
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
DBGPRINTF((DBG_CONTEXT, "FormatMetabasePath, failed to allocate memory. hr = %08x\n",
hr));
}
if (pResult != NULL)
{
*ppwszMetabasePathOut = pResult;
}
}
else
{
*ppwszMetabasePathOut = (LPWSTR)pwszMetabasePathIn;
}
return hr;
}
//===============================================================================
//
// IIISApplicationAdmin implementation
//
//===============================================================================
#ifdef _IIS_6_0
/*===================================================================
DoesAppPoolExist
Determine whether the AppPool passed exists
Parameter:
szAppPoolId a AppPoolId
pfRet where to place whether or not the appPool exists
Return: HRESULT
===================================================================*/
HRESULT
DoesAppPoolExist
(
LPCWSTR szAppPoolId,
BOOL * pfRet
)
{
DBG_ASSERT(pfRet);
WamRegMetabaseConfig MDConfig;
HRESULT hr = E_FAIL;
STACK_STRU(szPoolBuf, 64);
hr = szPoolBuf.Append(APPPOOLPATH);
if (FAILED(hr))
{
goto done;
}
hr = szPoolBuf.Append(szAppPoolId);
if (FAILED(hr))
{
goto done;
}
(*pfRet) = MDConfig.MDDoesPathExist(NULL, szPoolBuf.QueryStr());
hr = S_OK;
done:
return hr;
}
/*===================================================================
CWamAdmin::CreateApplication
Create an application on szMDPath, and add it to szAppPoolId AppPool.
Optionally create szAppPoolId
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
dwAppMode mode to create application in
szAppPoolId AppPool to setup app in.
fCreatePool Whether or not to create the pool
Return: HRESULT
===================================================================*/
STDMETHODIMP
CWamAdmin::CreateApplication
(
LPCWSTR szMDPath,
DWORD dwAppMode,
LPCWSTR szAppPoolId,
BOOL fCreatePool
)
{
HRESULT hr = S_OK;
WamRegMetabaseConfig MDConfig;
LPWSTR pwszFormattedPath = NULL;
if (NULL == szMDPath)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto done;
}
//
// See FormatMetabasePath comment
//
hr = FormatMetabasePath(szMDPath, &pwszFormattedPath);
if (FAILED(hr))
{
goto done;
}
// BUGBUG: Do We need locking around all of this? Why is locking present in other places?
hr = AppCreate2(pwszFormattedPath, dwAppMode);
if (FAILED(hr))
{
goto done;
}
if (FALSE == fCreatePool && NULL == szAppPoolId)
{
//
// We weren't told to create an application pool
// and NULL was passed as the application pool,
// therefore do nothing wil the application pool
//
hr = S_OK;
goto done;
}
if (TRUE == fCreatePool)
{
//
// create the application pool that we were passed
//
hr = CreateApplicationPool(szAppPoolId);
if (FAILED(hr) &&
HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) != hr)
{
goto done;
}
}
else
{
//
// We weren't told to create the application pool,
// but one was passed in. Verify that it exists.
//
DBG_ASSERT(NULL != szAppPoolId);
BOOL fRet;
hr = DoesAppPoolExist(szAppPoolId, &fRet);
if (FAILED(hr))
{
goto done;
}
if (FALSE == fRet)
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
goto done;
}
}
hr = MDConfig.MDSetStringProperty(NULL,
pwszFormattedPath,
MD_APP_APPPOOL_ID,
szAppPoolId,
IIS_MD_UT_SERVER);
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
//
// if pwszFormattedPath is not same as szMDPath
// then FormatMetabasePath() did a memory allocation.
//
if (pwszFormattedPath != szMDPath)
{
delete [] pwszFormattedPath;
pwszFormattedPath = NULL;
}
return hr;
}
/*===================================================================
CWamAdmin::DeleteApplication
Delete an application on a Metabase Path.
Parameter:
szMDPath a Metabase Path, in format of "/LM/W3SVC/..."
fRecursive TRUE if wants to deleteRecoverable applications from all sub nodes of szMDPath,
FALSE otherwise.
Return: HRESULT
===================================================================*/
STDMETHODIMP
CWamAdmin::DeleteApplication
(
LPCWSTR szMDPath,
BOOL fRecursive
)
{
return PrivateDeleteApplication(szMDPath,
fRecursive,
FALSE, // Recoverable?
TRUE); // RemoveAppPool?
}
/*===================================================================
CWamAdmin::CreateApplicationPool
Delete an application on a Metabase Path. If there is no application existed
before, it is no-op. It leaves AppIsolated untouched, because, this value is
needed in Recover operation.
Parameter:
szAppPool Application Pool to create
Return: HRESULT
===================================================================*/
STDMETHODIMP
CWamAdmin::CreateApplicationPool
(
LPCWSTR szAppPool
)
{
HRESULT hr = S_OK;
WamRegMetabaseConfig MDConfig;
STACK_STRU(szBuf, 64);
// Acquire a Lock
g_WamRegGlobal.AcquireAdmWriteLock();
if (NULL == szAppPool)
{
hr = E_INVALIDARG;
goto done;
}
// concatenate the path into a buffer
hr = szBuf.Append(APPPOOLPATH);
if (FAILED(hr))
{
goto done;
}
hr = szBuf.Append(szAppPool);
if (FAILED(hr))
{
goto done;
}
hr = MDConfig.MDCreatePath(NULL, szBuf.QueryStr());
if (FAILED(hr))
{
goto done;
}
hr = MDConfig.MDSetKeyType(NULL, szBuf.QueryStr(), L"IIsApplicationPool");
if (FAILED(hr))
{
goto done;
}
done:
// Release a Lock
g_WamRegGlobal.ReleaseAdmWriteLock();
return hr;
}
/*===================================================================
CWamAdmin::DeleteApplicationPool
Delete an application pool. First check to see if ApplicationPool is empty.
If not, return ERROR_NOT_EMPTY. Otherwise, remove apppool.
Parameter:
szAppPool Application Pool to remove
Return: HRESULT
===================================================================*/
STDMETHODIMP
CWamAdmin::DeleteApplicationPool
(
LPCWSTR szAppPool
)
{
HRESULT hr = S_OK;
UINT cchBstr = 0;
BOOL fRet = FALSE;
BSTR bstr = NULL;
WamRegMetabaseConfig MDConfig;
// BUGBUG: need locking around this?
if (NULL == szAppPool)
{
hr = E_INVALIDARG;
goto done;
}
hr = DoesAppPoolExist(szAppPool, &fRet);
if (FAILED(hr))
{
goto done;
}
if (FALSE == fRet)
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
goto done;
}
hr = EnumerateApplicationsInPool(szAppPool, &bstr);
if (FAILED(hr))
{
goto done;
}
cchBstr = SysStringLen(bstr);
// were there two terminating NULLs to be written into out buffer?
if (!(cchBstr >= 2 && '\0' == bstr[0] && '\0' == bstr[1]))
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_EMPTY);
goto done;
}
hr = MDConfig.MDDeleteKey(NULL, APPPOOLPATH, szAppPool);
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
if (bstr)
{
SysFreeString(bstr);
}
return hr;
}
/*===================================================================
DoesBeginWithLMW3SVCNRoot
Determine whether the string passed in starts with
/lm/w3svc/NNNN/root
where NNNN is greater than 0.
Parameter:
pszApp an metabase application path
pdwCharsAfter [OPTIONAL] - storage for the number of characters following /root
Return: BOOL
===================================================================*/
const WCHAR MB_W3SVC[] = L"/lm/w3svc/";
const int LEN_MB_W3SVC = (sizeof(MB_W3SVC) / sizeof(WCHAR)) - 1;
const WCHAR MB_W3SVC_1_ROOT[] = L"/LM/W3SVC/1/ROOT";
const int LEN_MB_W3SVC_1_ROOT = (sizeof(MB_W3SVC_1_ROOT) / sizeof(WCHAR)) - 1;
const WCHAR MB_ROOT[] = L"/Root";
const int LEN_MB_ROOT = (sizeof(MB_ROOT) / sizeof(WCHAR)) - 1;
BOOL
DoesBeginWithLMW3SVCNRoot
(
LPCWSTR pszApp,
DWORD * pdwCharsAfter = NULL
)
{
DBG_ASSERT(pszApp);
BOOL fRet = FALSE;
WCHAR pBuf[256] = {0};
int iSite;
// must have at least this many characters to have a chance
if (wcslen(pszApp) < LEN_MB_W3SVC_1_ROOT)
{
goto done;
}
// Applications must have \lm\w3svc\ at the front
if (0 != _wcsnicmp(MB_W3SVC, pszApp, LEN_MB_W3SVC))
{
goto done;
}
// Advance the pointer by enough characters
pszApp += LEN_MB_W3SVC;
// _wtoi returns as many characters as possible in a string before hitting a non-number or NULL
// if there is no number, the return is 0.
iSite = _wtoi(pszApp);
// Applications must then have a number that is >=1
if (0 == iSite)
{
goto done;
}
// get the count of numbers read from the string
_itow(iSite, pBuf, 10);
// advance the pointer by enough characters.
pszApp += wcslen(pBuf);
// Applications must them have "/Root"
if (0 != _wcsnicmp(pszApp, MB_ROOT, LEN_MB_ROOT))
{
goto done;
}
// if caller wants a count of characters following /Root
if (pdwCharsAfter)
{
// advance the pointer by enough characters
pszApp += LEN_MB_ROOT;
// get the remaining length
*pdwCharsAfter = wcslen(pszApp);
}
fRet = TRUE;
done:
return fRet;
}
/*===================================================================
IsRootApplication
Determine whether the string passed in is of the form:
/lm/w3svc/NNNN/root/
where NNNN is greater than 0.
And no additional characters following
Parameter:
pszApp an metabase application path
Return: BOOL
===================================================================*/
BOOL
IsRootApplication
(
LPCWSTR pszApp
)
{
DWORD dwCharsAfter = 0;
// Root applications must begin with /lm/w3svc/nnn/root
if (!DoesBeginWithLMW3SVCNRoot(pszApp, &dwCharsAfter))
{
return FALSE;
}
// we expect at most a trailing '/' after /lm/w3svc/nnn/root.
// If there is more, this was not a root application
if(1 < dwCharsAfter)
{
return FALSE;
}
return TRUE;
}
/*===================================================================
IsApplication
Determine whether the string passed in starts with
/lm/w3svc/NNNN/root
where NNNN is greater than 0.
With any additional characters following
Parameter:
pszApp an metabase application path
Return: BOOL
===================================================================*/
BOOL
IsApplication
(
LPCWSTR pszApp
)
{
return DoesBeginWithLMW3SVCNRoot(pszApp);
}
/*===================================================================
IsAppInAppPool
Determine whether the App is in Pool
Parameter:
pszApp an metabase application path
pszPool an applicationPool ID
Return: BOOL
===================================================================*/
BOOL
IsAppInAppPool
(
LPCWSTR pszApp,
LPCWSTR pszPool
)
{
DBG_ASSERT(pszApp);
DBG_ASSERT(pszPool);
HRESULT hr = E_FAIL;
BOOL fRet = FALSE;
LPWSTR pBuf = NULL;
WamRegMetabaseConfig MDConfig;
hr = MDConfig.MDGetStringAttribute(pszApp, MD_APP_APPPOOL_ID, &pBuf);
if (FAILED(hr) || NULL == pBuf)
{
goto done;
}
if (0 == _wcsicmp(pBuf, pszPool))
{
fRet = TRUE;
}
done:
delete [] pBuf;
return fRet;
}
/*===================================================================
CWamAdmin::EnumerateApplicationsInPool
Determine what applications are setup to point to the given pool.
Parameter:
szPool Application Pool enumerate
pbstrBuffer Where to store the pointer to allocated memory for application paths
Return: HRESULT
S_OK if buffer filled with a MULTISZ - if empty, double NULL at beginning
===================================================================*/
STDMETHODIMP
CWamAdmin::EnumerateApplicationsInPool
(
LPCWSTR szPool,
BSTR* pbstrBuffer
)
{
HRESULT hr = E_FAIL;
WamRegMetabaseConfig MDConfig;
MULTISZ mszApplicationsInPool;
WCHAR * pBuffer = NULL;
UINT cchMulti = 0;
DWORD dwBufferSize = 0;
if (NULL == szPool ||
NULL == pbstrBuffer
)
{
hr = E_INVALIDARG;
goto done;
}
*pbstrBuffer = NULL;
// First get all of the root applications
{
hr = MDConfig.MDGetAllSiteRoots(&pBuffer);
if (FAILED(hr))
{
goto done;
}
const WCHAR * pTestBuf = pBuffer;
while(pTestBuf && pTestBuf[0])
{
DBG_ASSERT(IsRootApplication(pTestBuf));
if ( IsAppInAppPool(pTestBuf, szPool) )
{
if (FALSE == mszApplicationsInPool.Append(pTestBuf))
{
hr = E_OUTOFMEMORY;
goto done;
}
}
// move pTestBuf beyond end of this string, including NULL terminator.
pTestBuf += wcslen(pTestBuf) + 1;
}
delete [] pBuffer;
pBuffer = NULL;
}
// now get any other applications that have APPISOLATED set
{
hr = MDConfig.MDGetPropPaths(NULL,
MD_APP_ISOLATED,
&pBuffer,
&dwBufferSize
);
if (FAILED(hr))
{
goto done;
}
{
const WCHAR * pTestBuf = pBuffer;
while (pTestBuf && pTestBuf[0])
{
// root applications have already been added
// the path needs to be an application
// and the application needs to be in the app pool
if ( !IsRootApplication(pTestBuf) &&
IsApplication(pTestBuf) &&
IsAppInAppPool(pTestBuf, szPool) )
{
if (FALSE == mszApplicationsInPool.Append(pTestBuf))
{
hr = E_OUTOFMEMORY;
goto done;
}
}
// move pTestBuf beyond end of this string, including NULL terminator.
pTestBuf += wcslen(pTestBuf) + 1;
}
}
}
// have the data in a MULTISZ - move it to the outgoing BSTR
cchMulti = mszApplicationsInPool.QueryCCH();
*pbstrBuffer = SysAllocStringLen(NULL, cchMulti);
if (NULL == *pbstrBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
dwBufferSize = cchMulti;
mszApplicationsInPool.CopyToBuffer(*pbstrBuffer, &dwBufferSize);
hr = S_OK;
done:
delete [] pBuffer;
pBuffer = NULL;
return hr;
}
/*===================================================================
QueryW3SVCStatus
Using the ServiceControlManager, determine the current state of W3SVC
pfRunning return bool value - TRUE if running, otherwise FALSE
Return: HRESULT
S_OK if able to read status. HRESULT_FROM_WIN32 error otherwise
===================================================================*/
HRESULT
QueryW3SVCStatus
(
BOOL * pfRunning
)
{
DBG_ASSERT(pfRunning);
*pfRunning = FALSE;
HRESULT hr = E_FAIL;
BOOL fRet = FALSE;
SC_HANDLE hSCM = 0;
SC_HANDLE hService = 0;
SERVICE_STATUS ssStatus;
ZeroMemory(&ssStatus, sizeof(ssStatus));
// first, get the service control manager
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (NULL == hSCM)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// now get the w3svc service
hService = OpenService(hSCM, "W3SVC", SERVICE_QUERY_STATUS);
if (NULL == hService)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// now ask for the status
fRet = QueryServiceStatus(hService, &ssStatus);
if (FALSE == fRet)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (SERVICE_RUNNING == ssStatus.dwCurrentState)
{
*pfRunning = TRUE;
}
hr = S_OK;
done:
if (0 != hService)
{
CloseServiceHandle(hService);
}
if (0 != hSCM)
{
CloseServiceHandle(hSCM);
}
return hr;
}
/*===================================================================
GetWASIfRunning
Get a pointer to WAS iff w3svc is already running.
ppiW3Control where to store the addref'ed pointer if it can be gotten
Return: HRESULT
S_OK if pointer retrieved
HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE) if W3SVC is not up
+other error codes
===================================================================*/
HRESULT
GetWASIfRunning
(
IW3Control ** ppiW3Control
)
{
DBG_ASSERT(ppiW3Control);
*ppiW3Control = NULL;
HRESULT hr = E_FAIL;
BOOL fRunning = FALSE;
hr = QueryW3SVCStatus(&fRunning);
if (FAILED(hr))
{
goto done;
}
if (FALSE == fRunning)
{
hr = HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE);
goto done;
}
hr = CoCreateInstance(CLSID_W3Control,
NULL,
CLSCTX_ALL,
IID_IW3Control,
reinterpret_cast<void**>(ppiW3Control));
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
return hr;
}
/*===================================================================
ValidateWriteAccessToMetabaseKey
Determine whether the caller has write access to the given metabase key
pPath - path to check write access on
Return: HRESULT
S_OK if access allowed
otherwise failure
===================================================================*/
HRESULT
ValidateWriteAccessToMetabaseKey(LPCWSTR pPath)
{
HRESULT hr = S_OK;
IMSAdminBase * pIMSAdminBase = NULL;
METADATA_HANDLE hMB = NULL;
METADATA_RECORD mdr;
DWORD dwTemp = 0x1234;
hr = CoImpersonateClient();
if (FAILED(hr))
{
goto done;
}
hr = CoCreateInstance(
CLSID_MSAdminBase, // CLSID
NULL, // controlling unknown
CLSCTX_SERVER, // desired context
IID_IMSAdminBase, // IID
( VOID * * ) ( &pIMSAdminBase ) // returned interface
);
if (FAILED(hr))
{
goto done;
}
hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
pPath,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
30000, // 30 seconds max
&hMB );
if (FAILED(hr))
{
goto done;
}
MD_SET_DATA_RECORD(&mdr,
MD_ISM_ACCESS_CHECK, // same identifier UI uses for this operation
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_FILE,
DWORD_METADATA,
sizeof(DWORD),
&dwTemp);
hr = pIMSAdminBase->SetData(hMB,
NULL,
&mdr
);
if (FAILED(hr))
{
goto done;
}
//
// don't leave goo hanging around
//
hr = pIMSAdminBase->DeleteData(hMB,
NULL,
MD_ISM_ACCESS_CHECK,
DWORD_METADATA
);
if (FAILED(hr))
{
goto done;
}
hr = CoRevertToSelf();
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
if ( hMB )
{
DBG_ASSERT( NULL != pIMSAdminBase );
DBG_REQUIRE( pIMSAdminBase->CloseKey( hMB ) == S_OK );
hMB = NULL;
}
if (pIMSAdminBase)
{
pIMSAdminBase->Release();
pIMSAdminBase = NULL;
}
return hr;
}
/*===================================================================
ValidateAccessToAppPool
Determine whether the caller has write access to the given apppool
szAppPool - AppPool to check access on
Return: HRESULT
S_OK if access allowed
otherwise failure
===================================================================*/
HRESULT
ValidateAccessToAppPool(LPCWSTR pAppPool)
{
HRESULT hr = S_OK;
STACK_STRU( strPath, 128 );
hr = strPath.Copy(L"\\LM\\W3SVC\\AppPools\\");
if (FAILED(hr))
{
goto done;
}
hr = strPath.Append(pAppPool);
if (FAILED(hr))
{
goto done;
}
hr = ValidateWriteAccessToMetabaseKey(strPath.QueryStr());
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
return hr;
}
/*===================================================================
CWamAdmin::RecycleApplicationPool
Restart the given application pool
szAppPool - AppPool to restart.
Return: HRESULT
S_OK if restarted
HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE) if W3SVC is not up
+other error codes
===================================================================*/
STDMETHODIMP
CWamAdmin::RecycleApplicationPool
(
LPCWSTR szAppPool
)
{
HRESULT hr = E_FAIL;
IW3Control* piW3Control = NULL;
if (NULL == szAppPool)
{
hr = E_INVALIDARG;
goto done;
}
hr = ValidateAccessToAppPool(szAppPool);
if (FAILED(hr))
{
goto done;
}
hr = GetWASIfRunning(&piW3Control);
if (FAILED(hr))
{
goto done;
}
hr = piW3Control->RecycleAppPool(szAppPool);
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
ReleaseInterface(piW3Control);
return hr;
}
/*===================================================================
CWamAdmin::GetProcessMode
Retrieve the current process mode
pdwMode - where to store the mode
Return: HRESULT
S_OK if retrieved
HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE) if W3SVC is not up
+other error codes
===================================================================*/
STDMETHODIMP
CWamAdmin::GetProcessMode
(
DWORD * pdwMode
)
{
HRESULT hr = E_FAIL;
IW3Control* piW3Control = NULL;
if (NULL == pdwMode)
{
hr = E_INVALIDARG;
goto done;
}
hr = ValidateWriteAccessToMetabaseKey(L"\\LM\\W3SVC");
if (FAILED(hr))
{
goto done;
}
hr = GetWASIfRunning(&piW3Control);
if (FAILED(hr))
{
goto done;
}
hr = piW3Control->GetCurrentMode(pdwMode);
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
ReleaseInterface(piW3Control);
return hr;
}
#endif // _IIS_6_0
/*
CWamAdminFactory: Class Factory IUnknown Implementation
*/
/*===================================================================
CWamAdminFactory::CWamAdminFactory
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
CWamAdminFactory::CWamAdminFactory()
: m_cRef(1)
{
InterlockedIncrement((long *)&g_dwRefCount);
}
/*===================================================================
CWamAdminFactory::~CWamAdminFactory
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
CWamAdminFactory::~CWamAdminFactory()
{
InterlockedDecrement((long *)&g_dwRefCount);
}
/*===================================================================
CWamAdminFactory::QueryInterface
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdminFactory::QueryInterface(REFIID riid, void ** ppv)
{
if (riid==IID_IUnknown || riid == IID_IClassFactory)
{
*ppv = static_cast<IClassFactory *>(this);
AddRef();
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return NOERROR;
}
/*===================================================================
CWamAdminFactory::AddRef
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP_(ULONG) CWamAdminFactory::AddRef( )
{
DWORD dwRefCount;
dwRefCount = InterlockedIncrement((long *)&m_cRef);
return dwRefCount;
}
/*===================================================================
CWamAdminFactory::Release
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP_(ULONG) CWamAdminFactory::Release( )
{
DWORD dwRefCount;
dwRefCount = InterlockedDecrement((long *)&m_cRef);
return dwRefCount;
}
/*===================================================================
CWamAdminFactory::CreateInstance
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdminFactory::CreateInstance(IUnknown * pUnknownOuter, REFIID riid, void ** ppv)
{
if (pUnknownOuter != NULL)
{
return CLASS_E_NOAGGREGATION;
}
CWamAdmin *pWamAdmin = new CWamAdmin;
if (pWamAdmin == NULL)
{
return E_OUTOFMEMORY;
}
HRESULT hrReturn = pWamAdmin->QueryInterface(riid, ppv);
pWamAdmin->Release();
return hrReturn;
}
/*===================================================================
CWamAdminFactory::LockServer
Parameter:
Return: HRESULT
NOERROR if succeeds
===================================================================*/
STDMETHODIMP CWamAdminFactory::LockServer(BOOL fLock)
{
if (fLock)
{
InterlockedIncrement((long *)&g_dwRefCount);
}
else
{
InterlockedDecrement((long *)&g_dwRefCount);
}
return NOERROR;
}
#if 0
// OBSOLETE - This fix (335422) was implemented in script but
// some of the code is general enough that it might be worth
// keeping around for a while.
STDMETHODIMP CWamAdmin::SynchWamAccountAll()
/*+
Routine Description:
Updates all out of process packages with the current IWAM_ account
values stored in the metabase.
There are a number of ways that the IWAM_ account information can get
out of sync between the metabase/sam/com+. The metabase contains code
to repair the IWAM_ and IUSR_ accounts on startup if there is a disconnect
with the SAM. If there is a disconnect with com+ calling this method will
repair it.
If the IWAM_ account does not match what is stored in the com catalog the
following error's will happen:
CoCreateInstance for WAM object returns CO_E_RUNAS_CREATEPROCESS_FAILURE
Event Log - DCOM 10004 - "Logon error"
Arguments:
None
Returns:
HRESULT
-*/
{
HRESULT hr = NOERROR;
// Get WAM user info from the metabase
WamRegMetabaseConfig mb;
// These are way too big...
WCHAR wszIWamUser[MAX_PATH];
WCHAR wszIWamPass[MAX_PATH];
hr = mb.MDGetIdentity( wszIWamUser,
sizeof(wszIWamUser),
wszIWamPass,
sizeof(wszIWamPass)
);
if( FAILED(hr) ) return hr;
// Init the com admin interface
WamRegPackageConfig comAdmin;
hr = comAdmin.CreateCatalog();
if( FAILED(hr) ) return hr;
//
// For each of the out of process applications,
// get the package and reset the metabase identity.
//
// After this failures cause a goto exit, which will release the lock
g_WamRegGlobal.AcquireAdmWriteLock();
WCHAR * wszPropPaths = NULL;
DWORD cbPropPaths = 0;
WCHAR * pwszPartialPath;
WCHAR * wszFullPath = NULL;
DWORD dwAppMode;
WCHAR wszWamClsid[uSizeCLSID];
WCHAR wszAppPackageId[uSizeCLSID];
// Reset the properties for the pooled package
hr = comAdmin.ResetPackageActivation(
g_WamRegGlobal.g_szIISOOPPoolPackageID,
wszIWamUser,
wszIWamPass
);
if( FAILED(hr) )
{
DBGPRINTF(( DBG_CONTEXT,
"comAdmin.ResetPackageActivation FAILED(%08x) on (%S)\n",
hr,
g_WamRegGlobal.g_szIISOOPPoolPackageID
));
goto exit;
}
// Reset the properties for each isolated application
hr = mb.MDGetPropPaths( g_WamRegGlobal.g_szMDW3SVCRoot,
MD_APP_ISOLATED,
&wszPropPaths,
&cbPropPaths
);
if( FAILED(hr) )
{
DBGPRINTF(( DBG_CONTEXT,
"mb.MDGetPropPaths FAILED(%08x)\n",
hr
));
goto exit;
}
if( SUCCEEDED(hr) )
{
for( pwszPartialPath = wszPropPaths;
*pwszPartialPath != L'\0';
pwszPartialPath += ( wcslen( pwszPartialPath ) + 1 )
)
{
if( wszFullPath )
{
delete [] wszFullPath;
wszFullPath = NULL;
}
hr = g_WamRegGlobal.ConstructFullPath(
WamRegGlobal::g_szMDW3SVCRoot,
WamRegGlobal::g_cchMDW3SVCRoot,
pwszPartialPath,
&wszFullPath
);
if( FAILED(hr) ) goto exit;
hr = mb.MDGetDWORD( wszFullPath, MD_APP_ISOLATED, &dwAppMode );
if( FAILED(hr) ) goto exit;
if( dwAppMode == eAppRunOutProcIsolated )
{
hr = mb.MDGetIDs( wszFullPath, wszWamClsid, wszAppPackageId, dwAppMode );
if( FAILED(hr) )
{
DBGPRINTF(( DBG_CONTEXT,
"mb.MDGetIDs FAILED(%08x) on (%S)\n",
hr,
wszFullPath
));
continue;
}
hr = comAdmin.ResetPackageActivation( wszAppPackageId, wszIWamUser, wszIWamPass );
if( FAILED(hr) )
{
DBGPRINTF(( DBG_CONTEXT,
"comAdmin.ResetPackageActivation FAILED(%08x) on (%S)\n",
hr,
wszFullPath
));
continue;
}
}
}
}
// goto exit on catastrophic failures, but if there is just
// an individual malformed application continue
exit:
g_WamRegGlobal.ReleaseAdmWriteLock();
if( FAILED(hr) )
{
DBGPRINTF(( DBG_CONTEXT,
"CWamAdmin::SynchWamAccountAll FAILED(%08x)\n",
hr
));
}
if( wszPropPaths ) delete [] wszPropPaths;
if( wszFullPath ) delete [] wszFullPath;
return hr;
}
// OBSOLETE
#endif