windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/esscli/filtprox.cpp
2020-09-26 16:20:57 +08:00

2594 lines
67 KiB
C++

//******************************************************************************
//
// FILTPROX.CPP
//
// Copyright (C) 1996-1999 Microsoft Corporation
//
//******************************************************************************
#include "precomp.h"
#include <stdio.h>
#include "pragmas.h"
#include <wbemcomn.h>
#include "filtprox.h"
#include <eventrep.h>
#include <evtools.h>
#include <wbemdcpl.h>
#include <newnew.h>
#define MAX_TOKENS_IN_DNF 100
#ifdef DBG
#define _ESSCLI_ASSERT(X) { if (!(X)) { DebugBreak(); } }
#else
#define _ESSCLI_ASSERT(X)
#endif
CTempMemoryManager g_TargetsManager;
bool TempSetTargets(WBEM_REM_TARGETS* pTargets, CSortedArray* pTrues)
{
int nSize = pTrues->Size();
pTargets->m_lNumTargets = nSize;
pTargets->m_aTargets = (WBEM_REMOTE_TARGET_ID_TYPE*)
g_TargetsManager.Allocate(
sizeof(WBEM_REMOTE_TARGET_ID_TYPE) * nSize);
if(pTargets->m_aTargets == NULL)
return false;
for(int i = 0; i < nSize; i++)
{
pTargets->m_aTargets[i] = (WBEM_REMOTE_TARGET_ID_TYPE)pTrues->GetAt(i);
}
return true;
}
void TempClearTargets(WBEM_REM_TARGETS* pTargets)
{
g_TargetsManager.Free(pTargets->m_aTargets,
sizeof(WBEM_REMOTE_TARGET_ID_TYPE) * pTargets->m_lNumTargets);
}
//#define DUMP_DEBUG_TREES 1
bool CTimeKeeper::DecorateObject(_IWmiObject* pObj)
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
{
CInCritSec ics(&m_cs);
if(ft.dwLowDateTime == m_ftLastEvent.dwLowDateTime &&
ft.dwHighDateTime == m_ftLastEvent.dwHighDateTime)
{
//
// This event has the same timestamp as the previous one ---
// let's add the counter to it.
//
if(0xFFFFFFFF - ft.dwLowDateTime > m_dwEventCount)
{
ft.dwLowDateTime += m_dwEventCount++;
}
else
{
ft.dwLowDateTime += m_dwEventCount++;
ft.dwHighDateTime++;
}
}
else
{
//
// Different timestamp --- reset the counter
//
m_dwEventCount = 1; // 0 has been used by us
m_ftLastEvent = ft;
}
}
__int64 i64Stamp = ft.dwLowDateTime + ((__int64)ft.dwHighDateTime << 32);
if(m_lTimeHandle == 0 && !m_bHandleInit)
{
HRESULT hres =
pObj->GetPropertyHandleEx(L"TIME_CREATED", 0, NULL, &m_lTimeHandle);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to retrieve TIME_CREATED handle: 0x%X\n",
hres));
m_lTimeHandle=0;
}
m_bHandleInit = true;
}
if(m_lTimeHandle)
{
pObj->SetPropByHandle(m_lTimeHandle, 0, sizeof(__int64),
&i64Stamp);
return true;
}
else
return false;
}
//******************************************************************************
//******************************************************************************
// META DATA
//******************************************************************************
//******************************************************************************
HRESULT CWrappingMetaData::GetClass(LPCWSTR wszName, IWbemContext* pContext,
_IWmiObject** ppClass)
{
HRESULT hres;
IWbemClassObject* pObj;
*ppClass = NULL;
hres = m_pDest->GetClass(wszName, pContext, &pObj);
if ( FAILED(hres) )
{
return hres;
}
CReleaseMe rm1(pObj);
return pObj->QueryInterface(IID__IWmiObject, (void**)ppClass);
}
//******************************************************************************
//******************************************************************************
// FILTER PROXY MANAGER
//******************************************************************************
//******************************************************************************
CFilterProxyManager::CFilterProxyManager(CLifeControl* pControl)
: CFilterProxyManagerBase( pControl ), m_lRef(0), m_pStub(NULL),
m_pMetaData(NULL), m_pMultiTarget(NULL), m_pSpecialContext(NULL),
m_XProxy(this), m_lExtRef(0),
m_hthreadSend(NULL),
m_heventDone(NULL),
m_heventBufferNotFull(NULL),
m_heventBufferFull(NULL),
m_heventEventsPending(NULL),
m_dwLastSentStamp(0),
m_pMultiTargetStream( NULL )
{
}
CFilterProxyManager::~CFilterProxyManager()
{
StopSendThread();
if(m_pMetaData)
m_pMetaData->Release();
if(m_pStub)
m_pStub->Release();
if(m_pMultiTarget)
m_pMultiTarget->Release();
}
ULONG STDMETHODCALLTYPE CFilterProxyManager::AddRef()
{
// This is an AddRef from a client. Increment a special counter as well
// ====================================================================
InterlockedIncrement(&m_lExtRef);
return InterlockedIncrement(&m_lRef);
}
ULONG STDMETHODCALLTYPE CFilterProxyManager::Release()
{
// This is a Release from a client. Check if the client has released all
// references to the proxy, in which case we need to disconnect ourselves
// ======================================================================
if(InterlockedDecrement(&m_lExtRef) == 0)
{
EnterCriticalSection(&m_cs);
IWbemFilterStub* pStub = m_pStub;
InterlockedIncrement(&m_lRef);
LeaveCriticalSection(&m_cs);
if(pStub)
pStub->UnregisterProxy(&m_XProxy);
InterlockedDecrement(&m_lRef);
}
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0) delete this;
return lRef;
}
ULONG STDMETHODCALLTYPE CFilterProxyManager::AddRefProxy()
{
// AddRef from proxy.
return InterlockedIncrement(&m_lRef);
}
ULONG STDMETHODCALLTYPE CFilterProxyManager::ReleaseProxy()
{
// Release from proxy.
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0) delete this;
return lRef;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IUnknown)
*ppv = (IUnknown*)this;
else if(riid == IID_IMarshal)
*ppv = (IMarshal*)this;
else if(riid == IID_IWbemFilterProxy || riid == IID_IWbemLocalFilterProxy)
*ppv = (IWbemLocalFilterProxy*)&m_XProxy;
else
return E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::Initialize(IWbemMetaData* pMetaData,
IWbemMultiTarget* pMultiTarget)
{
CInCritSec ics(&m_cs);
if(m_pMetaData)
m_pMetaData->Release();
m_pMetaData = new CWrappingMetaData(pMetaData);
if(m_pMetaData)
m_pMetaData->AddRef();
else
return WBEM_E_OUT_OF_MEMORY;
if(m_pMultiTarget)
m_pMultiTarget->Release();
m_pMultiTarget = pMultiTarget;
if(m_pMultiTarget)
m_pMultiTarget->AddRef();
if(GetMainProxy() == NULL)
return WBEM_E_OUT_OF_MEMORY;
// Leave ourselves locked for further initialization
// =================================================
Lock();
return S_OK;
}
HRESULT CFilterProxyManager::SetStub(IWbemFilterStub* pStub)
{
if(m_pStub)
m_pStub->Release();
m_pStub = pStub;
if(m_pStub)
m_pStub->AddRef();
// Initialize ourselves
// ====================
HRESULT hres = m_pStub->RegisterProxy(&m_XProxy);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Failed to register proxy with stub: %X\n", hres));
return hres;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::Lock()
{
if(m_Lock.Enter()) // old implementation: == WAIT_OBJECT_0)
{
return S_OK;
}
else
return WBEM_E_CRITICAL_ERROR;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::Unlock()
{
m_Lock.Leave();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::AddFilter(IWbemContext* pContext,
LPCWSTR wszQuery,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
// Parse the query
// ===============
CTextLexSource Source((LPWSTR)wszQuery);
QL1_Parser Parser(&Source);
QL_LEVEL_1_RPN_EXPRESSION* pExp;
if(Parser.Parse(&pExp) != QL1_Parser::SUCCESS)
{
ERRORTRACE((LOG_ESS, "Filter proxy unable to parse %S\n", wszQuery));
return WBEM_E_UNPARSABLE_QUERY;
}
CDeleteMe<QL_LEVEL_1_RPN_EXPRESSION> dm(pExp);
return AddFilter(pContext, wszQuery, pExp, idFilter);
}
HRESULT CFilterProxyManager::AddFilter(IWbemContext* pContext,
LPCWSTR wszQuery,
QL_LEVEL_1_RPN_EXPRESSION* pExp,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
CInCritSec ics(&m_cs);
//
// Record the filter in our array
//
m_mapQueries[idFilter] = wszQuery;
//
// Add the filter to all our subproxies
//
HRESULT hresGlobal = S_OK;
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
HRESULT hres;
if(m_apProxies[i] == NULL)
continue;
hres = m_apProxies[i]->AddFilter(GetProperContext(pContext),
wszQuery, pExp, idFilter);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to add filter %S to sub-proxy in "
"process %d.\n", wszQuery, GetCurrentProcessId()));
hresGlobal = hres;
}
}
return hresGlobal;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::RemoveFilter(
IWbemContext* pContext,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
CInCritSec ics(&m_cs);
//
// Remove the filter from our array
//
m_mapQueries.erase(idFilter);
//
// Remove the filter from all our subproxies
//
HRESULT hresGlobal = S_OK;
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
HRESULT hres;
if(m_apProxies[i] == NULL)
continue;
hres = m_apProxies[i]->RemoveFilter(GetProperContext(pContext),
idFilter);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to remove filter from sub-proxy in "
"process %d.\n", GetCurrentProcessId()));
hresGlobal = hres;
}
}
return hresGlobal;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::RemoveAllFilters(IWbemContext* pContext)
{
CInCritSec ics(&m_cs);
//
// Clear our filter array
//
m_mapQueries.clear();
//
// Remove all filters from all our subproxies
//
HRESULT hresGlobal = S_OK;
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
HRESULT hres;
if(m_apProxies[i] == NULL)
continue;
hres = m_apProxies[i]->RemoveAllFilters(GetProperContext(pContext));
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to remove all filters from sub-proxy "
"in process %d.\n", GetCurrentProcessId()));
hresGlobal = hres;
}
}
return hresGlobal;
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::AllowUtilizeGuarantee()
{
CInCritSec ics(&m_cs);
//
// Definition queries should be sent to the main (first) proxy only
//
if(m_apProxies.GetSize() == 0)
return WBEM_E_UNEXPECTED;
if(m_apProxies[0] == NULL)
return WBEM_S_FALSE;
return m_apProxies[0]->AllowUtilizeGuarantee();
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::AddDefinitionQuery(
IWbemContext* pContext, LPCWSTR wszQuery)
{
CInCritSec ics(&m_cs);
//
// Definition queries should be sent to the main (first) proxy only
//
if(m_apProxies.GetSize() == 0)
return WBEM_E_UNEXPECTED;
if(m_apProxies[0] == NULL)
return WBEM_S_FALSE;
return m_apProxies[0]->AddDefinitionQuery(GetProperContext(pContext),
wszQuery);
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::RemoveAllDefinitionQueries(
IWbemContext* pContext)
{
//
// Definition queries should be sent to the main (first) proxy only
//
if(m_apProxies.GetSize() == 0)
return WBEM_E_UNEXPECTED;
if(m_apProxies[0] == NULL)
return WBEM_S_FALSE;
return m_apProxies[0]->RemoveAllDefinitionQueries(
GetProperContext(pContext));
}
HRESULT STDMETHODCALLTYPE CFilterProxyManager::Disconnect()
{
// We must make sure that once Disconnect returns, no events will be
// delivered
// =================================================================
CInLock<CFilterProxyManager> il(this);
{
CInCritSec ics(&m_cs);
if(m_pMetaData)
m_pMetaData->Release();
m_pMetaData = NULL;
if(m_pStub)
m_pStub->Release();
m_pStub = NULL;
if(m_pMultiTarget)
m_pMultiTarget->Release();
m_pMultiTarget = NULL;
}
return WBEM_S_NO_ERROR;
}
IWbemContext* CFilterProxyManager::GetProperContext(IWbemContext* pCurrentContext)
{
// If we are a real, out-of-proc, proxy, we should not use this context,
// because the thread that owns it is currently stuck in an RPC call to us
// and will not be able to process dependent requests. Instead, we must
// use the "special" context that will cause the thread pool to always
// create a new thread if needed
// =======================================================================
if(m_pSpecialContext)
return m_pSpecialContext;
else
return pCurrentContext;
}
HRESULT CFilterProxyManager::SetStatus(long lFlags, HRESULT hResult,
BSTR strResult, IWbemClassObject* pErrorObj)
{
HRESULT hres;
if(m_pMultiTarget == NULL)
return WBEM_E_UNEXPECTED;
//
// There is only one reason we support this call: to re-check all
// subscriptions for validity/security
//
if(lFlags != WBEM_STATUS_REQUIREMENTS ||
hResult != WBEM_REQUIREMENTS_RECHECK_SUBSCRIPTIONS)
{
return WBEM_E_INVALID_PARAMETER;
}
//
// Retrieve "special" interface we use for this purpose
//
IWbemEventProviderRequirements* pReq = NULL;
hres = m_pMultiTarget->QueryInterface(IID_IWbemEventProviderRequirements,
(void**)&pReq);
if(FAILED(hres))
return hres;
CReleaseMe rm1(pReq);
return pReq->DeliverProviderRequest(hResult);
}
STDMETHODIMP CFilterProxyManager::GetRestrictedSink(
long lNumQueries,
const LPCWSTR* awszQueries,
IUnknown* pCallback,
IWbemEventSink** ppSink)
{
// Basic parameter validation
if(lNumQueries < 1)
return WBEM_E_INVALID_PARAMETER;
if(ppSink == NULL)
return WBEM_E_INVALID_PARAMETER;
*ppSink = NULL;
HRESULT hres;
//
// Construct a new filter proxy
//
CFilterProxy* pNewProxy = new CFilterProxy(this, pCallback);
if(pNewProxy == NULL)
return WBEM_E_OUT_OF_MEMORY;
//
// Add all the definition queries
//
for(long i = 0; i < lNumQueries; i++)
{
LPCWSTR wszQuery = awszQueries[i];
if(wszQuery == NULL)
{
delete pNewProxy;
return WBEM_E_INVALID_PARAMETER;
}
hres = pNewProxy->AddDefinitionQuery(NULL, wszQuery);
if(FAILED(hres))
{
delete pNewProxy;
return hres;
}
}
//
// if we made it here, then all definition queries were correctly added
// and we can now utilize these definitions for optimizing the filter.
//
pNewProxy->AllowUtilizeGuarantee();
{
CInCritSec ics(&m_cs);
for(TIterator it = m_mapQueries.begin(); it != m_mapQueries.end(); it++)
{
// Parse the query
// ===============
LPCWSTR wszQuery = it->second;
WBEM_REMOTE_TARGET_ID_TYPE idFilter = it->first;
CTextLexSource Source(wszQuery);
QL1_Parser Parser(&Source);
QL_LEVEL_1_RPN_EXPRESSION* pExp;
if(Parser.Parse(&pExp) != QL1_Parser::SUCCESS)
{
ERRORTRACE((LOG_ESS, "Filter proxy unable to parse %S\n",
wszQuery));
continue;
}
CDeleteMe<QL_LEVEL_1_RPN_EXPRESSION> dm(pExp);
pNewProxy->AddFilter(NULL, wszQuery, pExp, idFilter);
}
if(m_apProxies.Add(pNewProxy) < 0)
{
delete pNewProxy;
return WBEM_E_OUT_OF_MEMORY;
}
pNewProxy->SetRunning();
}
return pNewProxy->QueryInterface(IID_IWbemEventSink, (void**)ppSink);
}
STDMETHODIMP CFilterProxyManager::GetUnmarshalClass(REFIID riid, void* pv,
DWORD dwDestContext, void* pvReserved, DWORD mshlFlags,
CLSID* pClsid)
{
*pClsid = CLSID_WbemFilterProxy;
return WBEM_S_NO_ERROR;
}
STDMETHODIMP CFilterProxyManager::GetMarshalSizeMax(REFIID riid, void* pv,
DWORD dwDestContext, void* pvReserved, DWORD mshlFlags,
ULONG* plSize)
{
return CoGetMarshalSizeMax(plSize, IID_IWbemFilterStub, m_pStub,
dwDestContext, pvReserved, mshlFlags);
}
STDMETHODIMP CFilterProxyManager::MarshalInterface(IStream* pStream, REFIID riid,
void* pv, DWORD dwDestContext, void* pvReserved,
DWORD mshlFlags)
{
return CoMarshalInterface(pStream, IID_IWbemFilterStub,
m_pStub, dwDestContext, pvReserved, mshlFlags);
}
STDMETHODIMP CFilterProxyManager::UnmarshalInterface(IStream* pStream, REFIID riid,
void** ppv)
{
// Unmarshal the stub pointer
// ==========================
HRESULT hres = CoUnmarshalInterface(pStream, IID_IWbemFilterStub,
(void**)&m_pStub);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to unmarshal filter stub: %X\n", hres));
return hres;
}
// Since we are unmarshalling, this must be a real proxy. Real proxies
// should use a "special" context when calling back into CIMOM to make sure
// that they do not cause a deadlock, because a thread in CIMOM is stuck in
// an RPC call to this proxy and is not processing dependent requests.
// ========================================================================
IWbemCausalityAccess* pCausality = NULL;
hres = CoCreateInstance(CLSID_WbemContext, NULL, CLSCTX_INPROC_SERVER,
IID_IWbemCausalityAccess, (void**)&pCausality);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to create a context object in proxy: "
"error code 0x%X\n", hres));
return hres;
}
CReleaseMe rm1(pCausality);
hres = pCausality->MakeSpecial();
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to construct special context object in "
"proxy: error code 0x%X\n", hres));
return hres;
}
hres = pCausality->QueryInterface(IID_IWbemContext,
(void**)&m_pSpecialContext);
if(FAILED(hres))
{
// Out of memory?
// ==============
return hres;
}
// Initialize ourselves
// ====================
hres = m_pStub->RegisterProxy(&m_XProxy);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Failed to register proxy with stub: %X\n", hres));
return hres;
}
//
// What we must return is our main proxy
//
if(GetMainProxy())
return GetMainProxy()->QueryInterface(riid, ppv);
else
return WBEM_E_CRITICAL_ERROR;
}
INTERNAL IWbemEventSink* CFilterProxyManager::GetMainProxy()
{
//
// We are being asked for the sink to give to the provider. It is possible
// that we do not have a sink --- that will be the case if the provider
// has unloaded. In that case, we must be sure to create it!
//
if(m_apProxies.GetSize() == 0)
{
CFilterProxy* pMainProxy = new CFilterProxy(this);
if(pMainProxy == NULL)
return NULL;
pMainProxy->SetRunning();
if(m_apProxies.Add(pMainProxy) < 0)
{
delete pMainProxy;
return NULL;
}
return pMainProxy;
}
else
{
if(m_apProxies[0] == NULL)
{
CFilterProxy* pMainProxy = new CFilterProxy(this);
if(pMainProxy == NULL)
return NULL;
pMainProxy->SetRunning();
m_apProxies.SetAt(0, pMainProxy);
}
return m_apProxies[0];
}
}
HRESULT CFilterProxyManager::GetMetaData(RELEASE_ME CWrappingMetaData** ppMeta)
{
*ppMeta = m_pMetaData;
(*ppMeta)->AddRef();
return S_OK;
}
HRESULT CFilterProxyManager::RemoveProxy(CFilterProxy* pProxy)
{
//
// Called when a proxy is fully released by the client, and calls on the
// manager to self-destruct
//
CFilterProxy* pOldProxy = NULL;
{
CInCritSec ics(&m_cs);
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
if(m_apProxies[i] == pProxy)
{
RemoveProxyLatency(pProxy);
m_apProxies.RemoveAt(i, &pOldProxy);
break;
}
}
}
if(pOldProxy)
{
// We don't do a release because pProxy's refcount is already 0 (which
// is why we're in this function). Normally RemoveAt would have
// deleted it, but since we passed in &pOldProxy, it didn't. We do this
// so pOldProxy doesn't do its final release of the manager which could
// destruct the manager while we're holding onto the manager's lock.
delete pOldProxy;
return WBEM_S_NO_ERROR;
}
else
return WBEM_E_NOT_FOUND;
}
STDMETHODIMP CFilterProxyManager::ReleaseMarshalData(IStream* pStream)
{
return CoReleaseMarshalData(pStream);
}
STDMETHODIMP CFilterProxyManager::DisconnectObject(DWORD dwReserved)
{
// BUGBUG
return WBEM_E_UNEXPECTED;
}
HRESULT CFilterProxyManager::DeliverEvent(long lNumToSend,
IWbemClassObject** apEvents,
WBEM_REM_TARGETS* aTargets,
long lSDLength, BYTE* pSD)
{
//
// we need to hold the proxy lock when signalling an event.
// the reason for this is that when a call to disconnect() returns,
// we can be absolutely sure that no events will be delivered to the
// stub. Without locking here, just after the check for multitarget,
// disconnect could be called setting multitarget to null and then
// returning, however, just after the DeliverEvent call is made.
//
CInLock<CFilterProxyManager> il(this);
if(m_pMultiTarget)
return m_pMultiTarget->DeliverEvent(lNumToSend, apEvents, aTargets,
lSDLength, pSD);
else
return CO_E_OBJNOTCONNECTED;
}
HRESULT CFilterProxyManager::DeliverEventMT(long lNumToSend,
IWbemClassObject** apEvents,
WBEM_REM_TARGETS* aTargets,
long lSDLength, BYTE* pSD,
IWbemMultiTarget * pMultiTarget)
{
//
// we need to hold the proxy lock when signalling an event. There are
// two reasons for this. The first is that during resync of ess, it
// must ensure that no events are delivered, else they would be lost.
// the way ess ensures this is by grabbing the locks of all the proxies.
// The other reason is so that when a call to disconnect() returns,
// we can be absolutely sure that no events will be delivered to the
// stub. Without locking here, just after the check for multitarget,
// disconnect could be called setting multitarget to null and then
// returning, however, just after the DeliverEvent call is made.
//
// This assertion is for this func to be called in other place than
// SendThreadProc in the future.
_DBG_ASSERT( pMultiTarget );
CInLock<CFilterProxyManager> il(this);
return pMultiTarget->DeliverEvent(lNumToSend, apEvents, aTargets, lSDLength, pSD);
}
ULONG CFilterProxyManager::XProxy::AddRef()
{
return m_pObject->AddRefProxy();
}
ULONG CFilterProxyManager::XProxy::Release()
{
return m_pObject->ReleaseProxy();
}
HRESULT CFilterProxyManager::XProxy::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IUnknown || riid == IID_IWbemFilterProxy ||
riid == IID_IWbemLocalFilterProxy)
{
*ppv = (IWbemLocalFilterProxy*)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
HRESULT CFilterProxyManager::XProxy::Initialize(IWbemMetaData* pMetaData,
IWbemMultiTarget* pMultiTarget)
{
return m_pObject->Initialize(pMetaData, pMultiTarget);
}
HRESULT CFilterProxyManager::XProxy::Lock() // Deprecated ?
{
return m_pObject->Lock();
}
HRESULT CFilterProxyManager::XProxy::Unlock() // Deprecated ?
{
return m_pObject->Unlock();
}
HRESULT CFilterProxyManager::XProxy::AddFilter(IWbemContext* pContext,
LPCWSTR wszQuery,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
return m_pObject->AddFilter(pContext, wszQuery, idFilter);
}
HRESULT CFilterProxyManager::XProxy::RemoveFilter(IWbemContext* pContext,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
return m_pObject->RemoveFilter(pContext, idFilter);
}
HRESULT CFilterProxyManager::XProxy::RemoveAllFilters(IWbemContext* pContext)
{
return m_pObject->RemoveAllFilters(pContext);
}
HRESULT CFilterProxyManager::XProxy::AddDefinitionQuery(IWbemContext* pContext,
LPCWSTR wszQuery)
{
return m_pObject->AddDefinitionQuery(pContext, wszQuery);
}
HRESULT CFilterProxyManager::XProxy::AllowUtilizeGuarantee()
{
return m_pObject->AllowUtilizeGuarantee();
}
HRESULT CFilterProxyManager::XProxy::RemoveAllDefinitionQueries(
IWbemContext* pContext)
{
return m_pObject->RemoveAllDefinitionQueries(pContext);
}
HRESULT CFilterProxyManager::XProxy::Disconnect()
{
return m_pObject->Disconnect();
}
HRESULT CFilterProxyManager::XProxy::SetStub(IWbemFilterStub* pStub)
{
return m_pObject->SetStub(pStub);
}
HRESULT CFilterProxyManager::XProxy::LocalAddFilter(IWbemContext* pContext,
LPCWSTR wszQuery,
void* pExp,
WBEM_REMOTE_TARGET_ID_TYPE Id)
{
return m_pObject->AddFilter(pContext, wszQuery,
(QL_LEVEL_1_RPN_EXPRESSION*)pExp, Id);
}
HRESULT CFilterProxyManager::XProxy::GetMainSink(IWbemEventSink** ppSink)
{
*ppSink = m_pObject->GetMainProxy();
if(*ppSink)
{
(*ppSink)->AddRef();
return S_OK;
}
else
return E_UNEXPECTED;
}
void CFilterProxyManager::CalcMaxSendLatency()
{
LockBatching();
DWORD dwLatency = 0xFFFFFFFF;
for (CLatencyMapItor i = m_mapLatencies.begin();
i != m_mapLatencies.end();
i++)
{
if ((*i).second < dwLatency)
dwLatency = (*i).second;
}
m_dwMaxSendLatency = dwLatency;
UnlockBatching();
}
HRESULT CFilterProxyManager::SetProxyLatency(CFilterProxy *pProxy, DWORD dwLatency)
{
LockBatching();
BOOL bWasEmpty = m_mapLatencies.size() == 0;
// Add this proxy.
m_mapLatencies[pProxy] = dwLatency;
HRESULT hr = S_OK;
// If our map was previously empty, start the send thread.
if ( bWasEmpty )
{
m_dwMaxSendLatency = dwLatency;
_DBG_ASSERT( NULL == m_hthreadSend );
if ( NULL == m_hthreadSend )
{
_DBG_ASSERT( NULL == m_pMultiTargetStream );
//
// IWbemMultiTarget interface pointer is mashaled to make the
// interface pointer available for cross apartment access
//
hr = CoMarshalInterThreadInterfaceInStream( IID_IWbemMultiTarget,
m_pMultiTarget,
&m_pMultiTargetStream );
if ( SUCCEEDED( hr ) )
{
if ( FALSE == StartSendThread( ) )
{
ERRORTRACE((LOG_ESS, "Failed to set proxy latency due to thread creation error : 0x%X\n", GetLastError( ) ) );
m_pMultiTargetStream->Release( );
m_pMultiTargetStream = NULL;
hr = E_FAIL;
}
}
else
{
m_pMultiTargetStream = NULL;
ERRORTRACE((LOG_ESS, "Failed to set proxy latency due to marshaling error : 0x%X\n", hr ) );
}
}
}
else
{
// If dwLatency is smaller than m_dwMaxSendLatency, set
// m_dwMaxSendLatency to the new smallest value.
if (dwLatency < m_dwMaxSendLatency)
m_dwMaxSendLatency = dwLatency;
}
UnlockBatching();
return hr;
}
void CFilterProxyManager::RemoveProxyLatency(CFilterProxy *pProxy)
{
LockBatching();
// Try to find the proxy.
CLatencyMapItor item = m_mapLatencies.find(pProxy);
// Did we find it?
if (item != m_mapLatencies.end())
{
// Remove it.
m_mapLatencies.erase(item);
// If there are no more proxies that care about batching, stop the
// send thread.
if (m_mapLatencies.size() == 0)
StopSendThread();
else
{
DWORD dwLatency = (*item).second;
// If the latency value we just removed is the same as
// m_dwMaxSendLatency, recalc m_dwMaxSendLatency.
if (dwLatency == m_dwMaxSendLatency)
CalcMaxSendLatency();
}
}
UnlockBatching();
}
BOOL CFilterProxyManager::StartSendThread()
{
LockBatching();
if ( NULL == m_hthreadSend )
{
DWORD dwID;
do
{
m_heventDone = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( NULL == m_heventDone )
{
break;
}
m_heventBufferNotFull = CreateEvent( NULL, TRUE, TRUE, NULL );
if ( NULL == m_heventBufferNotFull )
{
break;
}
m_heventBufferFull = CreateEvent( NULL, TRUE, FALSE, NULL );
if ( NULL == m_heventBufferFull )
{
break;
}
m_heventEventsPending = CreateEvent( NULL, TRUE, FALSE, NULL );
if ( NULL == m_heventEventsPending )
{
break;
}
m_hthreadSend = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE) SendThreadProc,
this,
0,
&dwID );
}
while( FALSE );
if ( NULL == m_hthreadSend )
{
if (m_heventDone)
{
CloseHandle(m_heventDone);
m_heventDone = NULL;
}
if (m_heventBufferNotFull)
{
CloseHandle(m_heventBufferNotFull);
m_heventBufferNotFull = NULL;
}
if (m_heventBufferFull)
{
CloseHandle(m_heventBufferFull);
m_heventBufferFull = NULL;
}
if (m_heventEventsPending)
{
CloseHandle(m_heventEventsPending);
m_heventEventsPending = NULL;
}
}
}
UnlockBatching();
return ( NULL != m_hthreadSend );
}
void CFilterProxyManager::StopSendThread()
{
LockBatching();
if (m_hthreadSend && m_heventDone)
{
SetEvent(m_heventDone);
WaitForSingleObject(m_hthreadSend, 3000);
CloseHandle(m_hthreadSend);
m_hthreadSend = NULL;
}
if (m_heventDone)
{
CloseHandle(m_heventDone);
m_heventDone = NULL;
}
if (m_heventBufferNotFull)
{
CloseHandle(m_heventBufferNotFull);
m_heventBufferNotFull = NULL;
}
if (m_heventBufferFull)
{
CloseHandle(m_heventBufferFull);
m_heventBufferFull = NULL;
}
if (m_heventEventsPending)
{
CloseHandle(m_heventEventsPending);
m_heventEventsPending = NULL;
}
UnlockBatching();
}
DWORD WINAPI CFilterProxyManager::SendThreadProc(CFilterProxyManager *pThis)
{
HANDLE hWait[2] = { pThis->m_heventDone, pThis->m_heventEventsPending },
hwaitSendLatency[2] = { pThis->m_heventDone, pThis->m_heventBufferFull },
heventBufferNotFull = pThis->m_heventBufferNotFull;
HRESULT hres;
IWbemMultiTarget * pMultiTarget = NULL;
_DBG_ASSERT( pThis->m_pMultiTargetStream );
if ( NULL == pThis->m_pMultiTargetStream )
{
return 1;
}
CoInitializeEx( NULL, COINIT_MULTITHREADED );
//
// IWbemMultiTarget interface pointer is unmarshaled to use in this
// thread (in case of cross apartment).
//
hres = CoGetInterfaceAndReleaseStream( pThis->m_pMultiTargetStream,
IID_IWbemMultiTarget,
( void ** )&pMultiTarget );
if( FAILED( hres ) )
{
ERRORTRACE((LOG_ESS, "Failed to run batching thread due to unmarshaling errors: 0x%X\n", hres));
// pThis->m_pMultiTargetStream->Release( );
pThis->m_pMultiTargetStream = NULL;
CoUninitialize( );
return 1;
}
pThis->m_pMultiTargetStream = NULL;
_DBG_ASSERT( pMultiTarget );
while (WaitForMultipleObjects(2, hWait, FALSE, INFINITE) != 0)
{
// If we have a send latency, wait for that time or until the send
// buffer is full. If the done event fires, get out.
if (pThis->m_dwMaxSendLatency)
{
if (WaitForMultipleObjects(2, hwaitSendLatency, FALSE,
pThis->m_dwMaxSendLatency) == 0)
break;
// Reset m_heventBufferFull.
ResetEvent(hwaitSendLatency[1]);
}
CInCritSec csBuffer(&pThis->m_csBuffer);
int nItems = pThis->m_batch.GetItemCount();
hres = pThis->DeliverEventMT(
nItems,
pThis->m_batch.GetObjs(),
pThis->m_batch.GetTargets(),
0,
&CFilterProxy::mstatic_EmptySD,
pMultiTarget);
// Increment this so the filter proxies will know to clear out their
// buffer size when they next get an event to batch.
pThis->m_dwLastSentStamp++;
pThis->m_batch.RemoveAll();
SetEvent(heventBufferNotFull);
// Reset m_heventEventsPending
ResetEvent(hWait[1]);
}
// Make sure our batch buffer is empty before we exit.
CInCritSec csBuffer(&pThis->m_csBuffer);
int nItems = pThis->m_batch.GetItemCount();
if ( nItems )
{
pThis->DeliverEventMT(
nItems,
pThis->m_batch.GetObjs(),
pThis->m_batch.GetTargets(),
0,
&CFilterProxy::mstatic_EmptySD,
pMultiTarget);
}
CoUninitialize( );
return 0;
}
DWORD CFilterProxyManager::GetLastSentStamp()
{
return m_dwLastSentStamp;
}
//*****************************************************************************
//*****************************************************************************
//
// FILTER PROXY
//
//*****************************************************************************
//*****************************************************************************
CTimeKeeper CFilterProxy::mstatic_TimeKeeper;
BYTE CFilterProxy::mstatic_EmptySD = 0;
CFilterProxy::CFilterProxy(CFilterProxyManager* pManager, IUnknown* pCallback)
: m_lRef(0), m_pManager(pManager), m_pMetaData(NULL),
m_lSDLength(0), m_pSD(&mstatic_EmptySD), m_pProvider(NULL),
m_pQuerySink(NULL), m_bRunning(false),
m_typeBatch(WBEM_FLAG_MUST_NOT_BATCH), m_bUtilizeGuarantee(false),
m_dwCurrentBufferSize(0), m_bBatching(FALSE),
m_wSourceVersion(0), m_wAppliedSourceVersion(0)
{
m_SourceDefinition.SetBool(FALSE);
if(m_pManager)
{
m_pManager->AddRef();
m_pManager->GetMetaData(&m_pMetaData);
}
if(pCallback)
{
pCallback->QueryInterface(IID_IWbemEventProvider, (void**)&m_pProvider);
pCallback->QueryInterface(IID_IWbemEventProviderQuerySink,
(void**)&m_pQuerySink);
}
}
CFilterProxy::~CFilterProxy()
{
if (m_pManager)
m_pManager->Release();
if (m_pMetaData)
m_pMetaData->Release();
}
ULONG STDMETHODCALLTYPE CFilterProxy::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
ULONG STDMETHODCALLTYPE CFilterProxy::Release()
{
//
// CFilterProxy is deleted by CFilterProxyManager --- it never goes away
// on a Release
//
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
{
//
// Inform the manager that we are no longer needed. This call can
// destroy this object!
//
m_pManager->RemoveProxy(this);
}
return lRef;
}
HRESULT STDMETHODCALLTYPE CFilterProxy::QueryInterface(REFIID riid, void** ppv)
{
if( riid == IID_IUnknown ||
riid == IID_IWbemObjectSink ||
riid == IID_IWbemEventSink)
{
*ppv = (IWbemEventSink*)this;
}
else if(riid == IID_IMarshal)
{
*ppv = (IMarshal*)this;
}
else
return E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
HRESULT CFilterProxy::Lock()
{
return m_pManager->Lock();
}
HRESULT CFilterProxy::Unlock()
{
return m_pManager->Unlock();
}
HRESULT CFilterProxy::SetRunning()
{
HRESULT hres;
bool bActive = false;
IWbemEventProvider* pProvider = NULL;
{
CInCritSec ics(&m_cs);
if(m_bRunning)
return WBEM_S_FALSE;
else
{
m_bRunning = true;
if(m_pProvider)
{
bActive = (IsActive() == WBEM_S_NO_ERROR);
pProvider = m_pProvider;
pProvider->AddRef();
}
}
}
//
// If here, we are just now marking it for running. Notify the callback if
// there are any sinks
//
if(bActive && pProvider)
{
hres = pProvider->ProvideEvents(NULL, WBEM_FLAG_START_PROVIDING);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Restricted sink refused to stop "
"error code 0x%X\n", hres));
}
}
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::AddFilter(IWbemContext* pContext,
LPCWSTR wszQuery,
QL_LEVEL_1_RPN_EXPRESSION* pExp,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
HRESULT hres;
// Compile the query
// =================
CContextMetaData MetaData(m_pMetaData, pContext);
CEvalTree Tree;
hres = Tree.CreateFromQuery(&MetaData, pExp, WBEM_FLAG_MANDATORY_MERGE,
MAX_TOKENS_IN_DNF);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Filter proxy unable to parse %S, "
"error code: %X\n", wszQuery, hres));
return hres;
}
//
// merge the query into the rest of the filter.
//
{
CInCritSec ics(&m_cs);
if ( m_bUtilizeGuarantee )
{
//
// Utilize source definition
// =========================
//
// assert that our source definition hasn't changed since the last
// time a filter was added. This would be bad, since the tree
// doesn't account for the new source queries. Also assert that
// the source tree is valid and is not empty. ( These last two may
// have to be removed in the future. For now they shouldn't be
// false )
//
_ESSCLI_ASSERT( m_wAppliedSourceVersion == 0 ||
m_wAppliedSourceVersion == m_wSourceVersion );
_ESSCLI_ASSERT( m_SourceDefinition.IsValid() );
_ESSCLI_ASSERT( !m_SourceDefinition.IsFalse() );
hres = Tree.UtilizeGuarantee(m_SourceDefinition, &MetaData);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Filter proxy unable to utilize guarantee for %S, "
"error code: %X\n", wszQuery, hres));
return hres;
}
//
// Check if anything is left of it
//
if(!Tree.IsValid())
{
//
// Utilization of the guarantee shows that this filter cannot
// be satisftied by events coming through this proxy
//
return WBEM_S_FALSE;
}
}
//
// Add consumer information to it
//
Tree.Rebase((QueryID)idFilter);
#ifdef DUMP_DEBUG_TREES
FILE* f = fopen("c:\\try.log", "a");
fprintf(f, "\n\nAdding filter\n");
Tree.Dump(f);
fprintf(f, " to existing filter: \n");
m_Filter.Dump(f);
#endif
hres = m_Filter.CombineWith(Tree, &MetaData, EVAL_OP_COMBINE);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Filter proxy unable to combine %S with the "
"rest, error code: %X\n", wszQuery, hres));
return hres;
}
m_wAppliedSourceVersion = m_wSourceVersion;
}
//
// Now, we need to notify the provider of a new filter being issued
//
IWbemEventProviderQuerySink* pQuerySink = NULL;
IWbemEventProvider* pProvider = NULL;
{
CInCritSec ics(&m_cs);
if(m_pQuerySink)
{
pQuerySink = m_pQuerySink;
pQuerySink->AddRef();
}
if(m_pProvider)
{
pProvider = m_pProvider;
pProvider->AddRef();
}
}
//
// Call provider's NewQuery, if supported
//
if(pQuerySink)
{
hres = pQuerySink->NewQuery(idFilter, L"WQL", (LPWSTR)wszQuery);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Restricted sink refused consumer "
"registration query %S: error code 0x%X\n",
wszQuery, hres));
// Too bad --- restricted sinks cannot veto subscriptions
}
}
//
// If we are adding this filter to a running proxy, and this is the very
// first filter on it, we should call ProvideEvents immediately. Not so if
// we are configuring a proxy that is not running yet --- in that case, we
// need to wait until all outstanding filters have been put in place
//
if(m_bRunning && (IsActive() == WBEM_S_FALSE) && pProvider)
{
hres = pProvider->ProvideEvents((IWbemObjectSink*)this,
WBEM_FLAG_START_PROVIDING);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Restricted sink refused a call to "
"ProvideEvents with 0x%X\n", hres));
}
}
#ifdef DUMP_DEBUG_TREES
fprintf(f, " to obtain: \n");
m_Filter.Dump(f);
fclose(f);
#endif
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::RemoveFilter(IWbemContext* pContext,
WBEM_REMOTE_TARGET_ID_TYPE idFilter)
{
HRESULT hres;
IWbemEventProviderQuerySink* pQuerySink = NULL;
IWbemEventProvider* pProvider = NULL;
bool bActive;
{
CInCritSec ics(&m_cs);
if(m_pQuerySink)
{
pQuerySink = m_pQuerySink;
pQuerySink->AddRef();
}
if(m_pProvider)
{
pProvider = m_pProvider;
pProvider->AddRef();
}
hres = m_Filter.RemoveIndex(idFilter);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to remove index %d from the filter "
"proxy\n", idFilter));
return hres;
}
CContextMetaData MetaData(m_pMetaData, pContext);
hres = m_Filter.Optimize(&MetaData);
if(FAILED(hres))
return hres;
bActive = (IsActive() == WBEM_S_NO_ERROR);
}
//
// Call provider's NewQuery, if supported
//
if(pQuerySink)
{
hres = pQuerySink->CancelQuery(idFilter);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Restricted sink refused consumer "
"registration query cancellation: error code 0x%X\n",
hres));
}
}
//
// If we are left with no queries, notify provider of that fact
//
if(!bActive && pProvider)
{
hres = pProvider->ProvideEvents(NULL, WBEM_FLAG_STOP_PROVIDING);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Restricted sink refused to stop "
"error code 0x%X\n", hres));
}
}
#ifdef DUMP_DEBUG_TREES
FILE* f = fopen("c:\\try.log", "a");
fprintf(f, "Removed at %d to obtain: \n", idFilter);
m_Filter.Dump(f);
fclose(f);
#endif
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::RemoveAllFilters(IWbemContext* pContext)
{
CInCritSec ics(&m_cs);
m_wAppliedSourceVersion = 0;
if(!m_Filter.Clear())
return WBEM_E_OUT_OF_MEMORY;
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::AllowUtilizeGuarantee()
{
//
// ess shouldn't be calling this function if the tree is invalid.
//
_DBG_ASSERT( m_SourceDefinition.IsValid() );
//
// ess thinks its o.k. to utilize the guarantee, however there are cases
// where the soruce definition could still be false ( e.g. when there are
// no source definition queries or when all of the source definition
// queries are contradictions ).
//
CInCritSec ics(&m_cs);
if ( !m_SourceDefinition.IsFalse() )
{
m_bUtilizeGuarantee = true;
}
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::AddDefinitionQuery( IWbemContext* pContext,
LPCWSTR wszQuery )
{
HRESULT hres;
// Compile the query
// =================
CContextMetaData MetaData(m_pMetaData, pContext);
CEvalTree Tree;
hres = Tree.CreateFromQuery( &MetaData,
wszQuery,
WBEM_FLAG_MANDATORY_MERGE,
0x7FFFFFFF ); // no limit
if(FAILED(hres))
{
return hres;
}
{
CInCritSec ics(&m_cs);
//
// we shouldn't be adding definition queries when there are currently
// existing filters.
//
_ESSCLI_ASSERT( m_Filter.IsFalse() );
// Merge the query into the rest
// =============================
hres = m_SourceDefinition.CombineWith(Tree, &MetaData, EVAL_OP_OR,
WBEM_FLAG_MANDATORY_MERGE);
if(FAILED(hres))
return hres;
m_wSourceVersion++;
}
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::RemoveAllDefinitionQueries( IWbemContext* pContext)
{
CInCritSec ics(&m_cs);
m_wSourceVersion = 0;
m_SourceDefinition.SetBool(FALSE);
m_bUtilizeGuarantee = false;
return WBEM_S_NO_ERROR;
}
HRESULT CFilterProxy::ProcessOne( IUnknown* pUnk,
long lSDLength,
BYTE* pSD )
{
//
// NOTE: not in a critical section yet
//
HRESULT hres;
//
// Check overall validity
//
if( pUnk == NULL )
{
ERRORTRACE((LOG_ESS, "Event provider returned a NULL event!\n"));
return WBEM_E_INVALID_PARAMETER;
}
CWbemObject* pObj = (CWbemObject*)(IWbemClassObject*)pUnk;
if( pObj->IsObjectInstance() != WBEM_S_NO_ERROR )
{
ERRORTRACE((LOG_ESS, "CLASS object received from event provider!\n"));
return WBEM_E_INVALID_PARAMETER;
}
//
// Run the event through the filter
//
CSortedArray aTrues, aSourceTrues;
CFilterProxyManager* pManager = NULL;
{
CInCritSec ics(&m_cs);
hres = FilterEvent( pObj, aTrues );
if ( hres == WBEM_S_NO_ERROR )
{
_DBG_ASSERT( aTrues.Size() > 0 );
}
else
{
return hres;
}
pManager = m_pManager;
if( pManager )
{
pManager->AddRef();
}
else
{
return WBEM_S_FALSE;
}
}
CReleaseMe rm2(pManager);
//
// the event has made it through the filter ..
//
SetGenerationTime(pObj);
if (IsBatching())
{
BatchEvent((IWbemClassObject*) pUnk, &aTrues);
hres = S_OK;
}
else
{
// Some delivery is required --- construct the blob and the targets
// ================================================================
WBEM_REM_TARGETS RemTargets;
if(!TempSetTargets(&RemTargets, &aTrues))
return WBEM_E_OUT_OF_MEMORY;
hres = pManager->DeliverEvent(1, (IWbemClassObject**)&pObj,
&RemTargets, lSDLength, pSD);
TempClearTargets(&RemTargets);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Filter stub failed to process an event: "
"0x%X\n", hres));
}
}
return hres;
}
void CFilterProxy::SetGenerationTime(_IWmiObject* pObj)
{
mstatic_TimeKeeper.DecorateObject(pObj);
}
void CFilterProxyManager::AddEvent(
IWbemClassObject *pObj,
CSortedArray *pTrues)
{
LockBatching();
BOOL bWasEmpty = m_batch.GetItemCount() == 0;
m_batch.AddEvent(pObj, pTrues);
if (bWasEmpty)
SetEvent(m_heventEventsPending);
UnlockBatching();
}
void CFilterProxyManager::WaitForEmptyBatch()
{
LockBatching();
// Once we get the lock and the batch has already been cleared out, we
// don't need to do anything else.
if (m_batch.GetItemCount() == 0)
{
UnlockBatching();
return;
}
// We need to wait for the send thread to finish sending what's
// in our buffer.
// Wake up the send latency thread if necessary.
if (m_dwMaxSendLatency)
SetEvent(m_heventBufferFull);
// So we'll block until the send thread sets the event.
ResetEvent(m_heventBufferNotFull);
UnlockBatching();
WaitForSingleObject(m_heventBufferNotFull, INFINITE);
}
void CFilterProxy::BatchEvent(
IWbemClassObject *pObj,
CSortedArray *pTrues)
{
BOOL bRet = FALSE;
_IWmiObject *pWmiObj = (_IWmiObject*) pObj;
DWORD dwObjSize = 0;
pWmiObj->GetObjectMemory(
NULL,
0,
&dwObjSize);
CInCritSec ics(&m_cs);
// See if the manager has sent off its batch of events since we last
// batched an event.
if (m_dwLastSentStamp != m_pManager->GetLastSentStamp())
m_dwCurrentBufferSize = 0;
// See if we have enough room to add our event.
if (m_dwCurrentBufferSize >= m_dwMaxBufferSize)
{
m_pManager->WaitForEmptyBatch();
m_dwCurrentBufferSize = 0;
}
m_dwCurrentBufferSize += dwObjSize;
m_dwLastSentStamp = m_pManager->GetLastSentStamp();
m_pManager->AddEvent(pObj, pTrues);
}
HRESULT CFilterProxy::FilterEvent( _IWmiObject* pObj, CSortedArray& raTrues )
{
HRESULT hr;
//
// evaluate
//
try
{
//
// this code is in a try catch because if a provider generates
// events that it has not registered to, then we do bad things to
// class objects. A potential fix could be do extra checking on
// our part, but is expensive when using the public interfaces. A
// more advantageous fix should be making the class object code
// perform the checking for us ( e.g. when we ask for a property
// using an invalid handle, etc ). It can do this checking much
// faster. When this checking is performed by the class object
// code, we should remove this try catch. see RAID 166026
//
hr = m_Filter.Evaluate( pObj, raTrues );
}
catch( ... )
{
//
// check to see if the provider is generating an event its not
// supposed to. If so, then handle AV and return error, else
// rethrow - there's something else wrong.
//
CSortedArray aSourceTrues;
hr = m_SourceDefinition.Evaluate( pObj, aSourceTrues );
if ( SUCCEEDED(hr) && aSourceTrues.Size() == 0 )
{
ERRORTRACE((LOG_ESS, "Filter Proxy encountered case where "
"event provider is signaling events that are not "
"covered by its registration!!\n"));
hr = WBEM_E_INVALID_OBJECT;
}
else
{
throw;
}
}
//
// check events that make it through the filter against source definition.
// if we're not utilizing guarantee, then there's no need to check the
// event against the source definition because its already factored into
// the filter.
//
if (SUCCEEDED(hr) && raTrues.Size() == 0 )
{
hr = WBEM_S_FALSE;
}
else if ( SUCCEEDED(hr) && m_bUtilizeGuarantee )
{
//
// run the event through the source tree to ensure that the
// provider is providing the events its supposed to.
//
CSortedArray aSourceTrues;
hr = m_SourceDefinition.Evaluate( pObj, aSourceTrues );
if ( SUCCEEDED(hr) && aSourceTrues.Size() == 0 )
{
ERRORTRACE((LOG_ESS, "Filter Proxy encountered case where "
"event provider is signaling events that are not "
"covered by its registration!!\n"));
hr = WBEM_E_INVALID_OBJECT;
}
}
return hr;
}
HRESULT CFilterProxy::BatchMany(long nEvents, IUnknown **ppObjects)
{
HRESULT hr = S_OK;
for ( long i = 0; i < nEvents && SUCCEEDED(hr); i++ )
{
//
// Check overall validity
//
if( ppObjects[i] == NULL )
{
ERRORTRACE((LOG_ESS, "Event provider returned a NULL event!\n"));
return WBEM_E_INVALID_PARAMETER;
}
CWbemObject *pObj = (CWbemObject*)(IWbemClassObject*)ppObjects[i];
if( pObj->IsObjectInstance() != WBEM_S_NO_ERROR )
{
ERRORTRACE((LOG_ESS, "CLASS object received from event provider!\n"));
return WBEM_E_INVALID_PARAMETER;
}
//
// Run the event through the filter
//
CInCritSec ics(&m_cs);
CSortedArray aTrues;
hr = FilterEvent( pObj, aTrues );
if ( hr == WBEM_S_NO_ERROR )
{
_DBG_ASSERT( aTrues.Size() > 0 );
//
// Delivery is required --- add this event to the list
//
SetGenerationTime(pObj);
BatchEvent(pObj, &aTrues);
}
}
return hr;
}
class CDeleteTargetsArray
{
protected:
WBEM_REM_TARGETS *m_pData;
int *m_piSize;
public:
CDeleteTargetsArray(WBEM_REM_TARGETS *pData, int *piSize) :
m_pData(pData),
m_piSize(piSize)
{
}
~CDeleteTargetsArray()
{
int nSize = *m_piSize;
for (DWORD i = 0; i < nSize; i++)
TempClearTargets(m_pData + i);
}
};
HRESULT CFilterProxy::ProcessMany(long lNumObjects,
IUnknown** apObjects,
long lSDLength, BYTE* pSD)
{
//
// NOTE: not in critical section
//
HRESULT hres;
if (IsBatching())
return BatchMany(lNumObjects, apObjects);
//
// Allocate appropriate arrays on the stack
//
CTempArray<IWbemClassObject*> apEventsToSend;
INIT_TEMP_ARRAY(apEventsToSend, lNumObjects);
if(apEventsToSend == NULL)
return WBEM_E_OUT_OF_MEMORY;
CTempArray<WBEM_REM_TARGETS> aTargetsToSend;
INIT_TEMP_ARRAY(aTargetsToSend, lNumObjects);
if(aTargetsToSend == NULL)
return WBEM_E_OUT_OF_MEMORY;
int lNumToSend = 0;
// Make sure the array gets cleaned up.
CDeleteTargetsArray deleteMe(aTargetsToSend, &lNumToSend);
//
// Iterate over events supplied and move those that need to be delivered
// into delivery arrays
//
CFilterProxyManager* pManager = NULL;
{
//
// We could choose a smaller window, but I am betting that the cost of
// entering and exiting the cs many times will outweigh the benefits
// of slightly smaller windows
//
CInCritSec ics(&m_cs);
for(long i = 0; i < lNumObjects; i++)
{
//
// Check overall validity
//
if( apObjects[i] == NULL )
{
ERRORTRACE((LOG_ESS, "Event provider returned a NULL event!\n"));
return WBEM_E_INVALID_PARAMETER;
}
CWbemObject *pObj = (CWbemObject*)(IWbemClassObject*)apObjects[i];
if( pObj->IsObjectInstance() != WBEM_S_NO_ERROR )
{
ERRORTRACE((LOG_ESS, "CLASS object received from event provider!\n"));
return WBEM_E_INVALID_PARAMETER;
}
//
// Run the event through the filter
//
CSortedArray aTrues;
hres = FilterEvent( pObj, aTrues );
if ( hres == WBEM_S_FALSE )
{
;
}
else if ( hres == WBEM_S_NO_ERROR )
{
_DBG_ASSERT( aTrues.Size() > 0 );
//
// Delivery is required --- add this event to the list
//
SetGenerationTime(pObj);
apEventsToSend[lNumToSend] = pObj;
if(!TempSetTargets(aTargetsToSend + lNumToSend, &aTrues))
return WBEM_E_OUT_OF_MEMORY;
lNumToSend++;
}
else
{
return hres;
}
}
//
// If any events need to be delivered, get the delivery pointer
//
if(lNumToSend > 0)
{
pManager = m_pManager;
if(pManager)
pManager->AddRef();
else
return WBEM_S_FALSE;
}
}
CReleaseMe rm1(pManager);
//
// If any events need to be delivered, deliver
//
if(lNumToSend > 0)
{
hres = pManager->DeliverEvent(lNumToSend, apEventsToSend,
aTargetsToSend,
lSDLength, pSD);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Filter stub failed to process an event: "
"error code %X\n", hres));
}
return hres;
}
return WBEM_S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilterProxy::Indicate(long lNumObjects,
IWbemClassObject** apObjects)
{
return IndicateWithSD(lNumObjects, (IUnknown**)apObjects,
m_lSDLength, m_pSD);
}
HRESULT STDMETHODCALLTYPE CFilterProxy::SetStatus(long lFlags, HRESULT hResult,
BSTR strResult, IWbemClassObject* pErrorObj)
{
return m_pManager->SetStatus(lFlags, hResult, strResult, pErrorObj);
}
STDMETHODIMP CFilterProxy::IndicateWithSD(long lNumObjects,
IUnknown** apObjects, long lSDLength, BYTE* pSD)
{
if(lNumObjects <= 0 || apObjects == NULL)
return WBEM_E_INVALID_PARAMETER;
if(pSD == NULL)
{
//
// Use proxy defaults
//
lSDLength = m_lSDLength;
pSD = m_pSD;
}
try
{
//
// Special-case single event
//
if(lNumObjects == 1)
return ProcessOne(*apObjects, lSDLength, pSD);
else
return ProcessMany(lNumObjects, apObjects, lSDLength, pSD);
}
catch(...)
{
return WBEM_E_OUT_OF_MEMORY;
}
}
STDMETHODIMP CFilterProxy::IsActive()
{
CInCritSec ics(&m_cs);
return (m_Filter.IsFalse()?WBEM_S_FALSE:WBEM_S_NO_ERROR);
return WBEM_S_NO_ERROR;
}
STDMETHODIMP CFilterProxy::SetSinkSecurity(
long lSDLength,
BYTE* pSD)
{
CInCritSec ics(&m_cs);
//
// Check for validity
//
if(lSDLength < 0)
return WBEM_E_INVALID_PARAMETER;
if(lSDLength > 0)
{
SECURITY_DESCRIPTOR* pDesc = (SECURITY_DESCRIPTOR*)pSD;
if(!IsValidSecurityDescriptor(pDesc))
return WBEM_E_INVALID_PARAMETER;
if(pDesc->Owner == NULL || pDesc->Group == NULL)
return WBEM_E_INVALID_PARAMETER;
if(GetSecurityDescriptorLength(pSD) != (DWORD)lSDLength)
return WBEM_E_INVALID_PARAMETER;
}
else
{
if(pSD != NULL)
return WBEM_E_INVALID_PARAMETER;
}
//
// Store the SD in the proxy
//
if(m_pSD && m_pSD != &mstatic_EmptySD)
delete [] m_pSD;
if(lSDLength)
{
m_pSD = new BYTE[lSDLength];
if(m_pSD == NULL)
return WBEM_E_OUT_OF_MEMORY;
memcpy(m_pSD, pSD, lSDLength);
}
else
{
//
// Cannot let m_pSD be NULL
//
m_pSD = &mstatic_EmptySD;
}
m_lSDLength = lSDLength;
return WBEM_S_NO_ERROR;
}
STDMETHODIMP CFilterProxy::GetRestrictedSink(
long lNumQueries,
const LPCWSTR* awszQueries,
IUnknown* pCallback,
IWbemEventSink** ppSink)
{
return m_pManager->GetRestrictedSink(lNumQueries, awszQueries,
pCallback, ppSink);
}
STDMETHODIMP CFilterProxy::SetBatchingParameters(
LONG lFlags,
DWORD dwMaxBufferSize,
DWORD dwMaxSendLatency)
{
HRESULT hr = S_OK;
CInCritSec ics(&m_cs);
switch(lFlags)
{
// TODO: WBEM_FLAG_BATCH_IF_NEEDED currently works the same as
// WBEM_FLAG_MUST_NOT_BATCH. At some point this needs allow
// subscriptions to determine the batching behavior.
case WBEM_FLAG_BATCH_IF_NEEDED:
case WBEM_FLAG_MUST_NOT_BATCH:
m_typeBatch = (WBEM_BATCH_TYPE) lFlags;
m_pManager->RemoveProxyLatency(this);
m_bBatching = FALSE;
break;
case WBEM_FLAG_MUST_BATCH:
m_typeBatch = (WBEM_BATCH_TYPE) lFlags;
m_dwMaxSendLatency = dwMaxSendLatency;
m_dwMaxBufferSize = dwMaxBufferSize;
m_dwLastSentStamp = m_pManager->GetLastSentStamp();
hr = m_pManager->SetProxyLatency(this, dwMaxSendLatency);
m_bBatching = TRUE;
break;
default:
hr = WBEM_E_INVALID_PARAMETER;
break;
}
return hr;
}
// Assumes pMainProxy is locked
HRESULT CFilterProxy::TransferFiltersFromMain(CFilterProxy* pMain)
{
HRESULT hres;
//
// Move all the normal filters
//
try
{
m_Filter = pMain->m_Filter;
}
catch(CX_MemoryException)
{
return WBEM_E_OUT_OF_MEMORY;
}
if ( m_bUtilizeGuarantee )
{
// Utilize source definition
// =========================
CContextMetaData MetaData(m_pMetaData, NULL);
hres = m_Filter.UtilizeGuarantee(m_SourceDefinition, &MetaData);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Filter proxy unable to utilize guarantee for"
" a new proxy; error code: %X\n", hres));
return hres;
}
}
return WBEM_S_NO_ERROR;
}
STDMETHODIMP CFilterProxy::GetUnmarshalClass(REFIID riid, void* pv,
DWORD dwDestContext, void* pvReserved, DWORD mshlFlags,
CLSID* pClsid)
{
return m_pManager->GetUnmarshalClass(riid, pv, dwDestContext, pvReserved,
mshlFlags, pClsid);
}
STDMETHODIMP CFilterProxy::GetMarshalSizeMax(REFIID riid, void* pv,
DWORD dwDestContext, void* pvReserved, DWORD mshlFlags,
ULONG* plSize)
{
return m_pManager->GetMarshalSizeMax(riid, pv, dwDestContext, pvReserved,
mshlFlags, plSize);
}
STDMETHODIMP CFilterProxy::MarshalInterface(IStream* pStream, REFIID riid,
void* pv, DWORD dwDestContext, void* pvReserved,
DWORD mshlFlags)
{
return m_pManager->MarshalInterface(pStream, riid, pv, dwDestContext,
pvReserved, mshlFlags);
}
/////////////////////////////////////////////////////////////////////////////
// CEventBatch
#define INIT_SIZE 32
#define GROW_SIZE 32
CEventBatch::CEventBatch() :
m_ppObjs(NULL),
m_pTargets(NULL),
m_nItems(0),
m_dwSize(0)
{
m_ppObjs = new IWbemClassObject*[INIT_SIZE];
if (!m_ppObjs)
throw CX_MemoryException();
m_pTargets = new WBEM_REM_TARGETS[INIT_SIZE];
if (!m_pTargets)
throw CX_MemoryException();
m_dwSize = INIT_SIZE;
}
CEventBatch::~CEventBatch()
{
RemoveAll();
if (m_ppObjs)
delete [] m_ppObjs;
if (m_pTargets)
delete [] m_pTargets;
}
BOOL CEventBatch::EnsureAdditionalSize(DWORD nAdditionalNeeded)
{
if (m_nItems + nAdditionalNeeded > m_dwSize)
{
DWORD nNewSize = m_nItems + nAdditionalNeeded + GROW_SIZE;
IWbemClassObject **ppNewObjs;
WBEM_REM_TARGETS *pNewTargets;
ppNewObjs = new IWbemClassObject*[nNewSize];
if (!ppNewObjs)
throw CX_MemoryException();
pNewTargets = new WBEM_REM_TARGETS[nNewSize];
if (!pNewTargets)
{
delete [] ppNewObjs;
throw CX_MemoryException();
}
// Copy the data from the old pointers to the new pointers.
memcpy(ppNewObjs, m_ppObjs, m_nItems * sizeof(ppNewObjs[0]));
memcpy(pNewTargets, m_pTargets, m_nItems * sizeof(pNewTargets[0]));
// Get rid of the old pointers.
delete [] m_ppObjs;
delete [] m_pTargets;
// Set our member pointers with the new pointers.
m_ppObjs = ppNewObjs;
m_pTargets = pNewTargets;
m_dwSize = nNewSize;
}
return TRUE;
}
BOOL CEventBatch::AddEvent(IWbemClassObject *pObj, CSortedArray *pTrues)
{
BOOL bRet = FALSE;
if (EnsureAdditionalSize(1))
{
if (SUCCEEDED(pObj->Clone(&m_ppObjs[m_nItems])))
{
if(!TempSetTargets(m_pTargets + m_nItems, pTrues))
return FALSE;
m_nItems++;
bRet = TRUE;
}
}
return bRet;
}
void CEventBatch::RemoveAll()
{
for (DWORD i = 0; i < m_nItems; i++)
{
m_ppObjs[i]->Release();
TempClearTargets(m_pTargets + i);
}
m_nItems = 0;
}