//****************************************************************************** // // FILTPROX.CPP // // Copyright (C) 1996-1999 Microsoft Corporation // //****************************************************************************** #include "precomp.h" #include #include "pragmas.h" #include #include "filtprox.h" #include #include #include #include #include #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); } //****************************************************************************** //****************************************************************************** // 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; 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 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 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; CWbemPtr pMultiTarget; { CInLock il(this); if(m_pMultiTarget == NULL) return WBEM_E_UNEXPECTED; pMultiTarget = m_pMultiTarget; } // // 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 = 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 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) { // // before unmarshaling, we must first check we are unmarshaling due to // a call from wmi ( or local system rather ). Because we're going to // be calling back 'blindly' to the passed in reference, we need to // ensure we're not going to 'give away the farm'. Remember that this // custom marshaler can be used in any DCOM server, so we must ensure // that it can't be misused to get an arbitrary process to call you back // at potentially impersonate level. // CWbemPtr pSec; HRESULT hr = CoGetCallContext( IID_IServerSecurity, (void**)&pSec ); if ( SUCCEEDED(hr) ) { CNtSid ProcessSid( CNtSid::CURRENT_USER ); if ( ProcessSid.GetStatus() != CNtSid::NoError ) return WBEM_E_ACCESS_DENIED; hr = pSec->ImpersonateClient(); if ( FAILED(hr) ) return hr; CNtSid CallerSid( CNtSid::CURRENT_THREAD ); pSec->RevertToSelf(); if ( CallerSid.GetStatus() != CNtSid::NoError ) return WBEM_E_ACCESS_DENIED; PSID pLocalSystemSid; SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY; if ( AllocateAndInitializeSid( &id, 1, SECURITY_LOCAL_SYSTEM_RID, 0,0,0,0,0,0,0, &pLocalSystemSid ) ) { BOOL bOk = EqualSid( CallerSid.GetPtr(), pLocalSystemSid ) || EqualSid( CallerSid.GetPtr(), ProcessSid.GetPtr() ); FreeSid( pLocalSystemSid ); if ( !bOk ) return WBEM_E_ACCESS_DENIED; } else { return WBEM_E_OUT_OF_MEMORY; } } // 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 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 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) { ENTER_API_CALL return m_pObject->Initialize(pMetaData, pMultiTarget); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::Lock() // Deprecated ? { ENTER_API_CALL return m_pObject->Lock(); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::Unlock() // Deprecated ? { ENTER_API_CALL return m_pObject->Unlock(); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::AddFilter(IWbemContext* pContext, LPCWSTR wszQuery, WBEM_REMOTE_TARGET_ID_TYPE idFilter) { ENTER_API_CALL return m_pObject->AddFilter(pContext, wszQuery, idFilter); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::RemoveFilter(IWbemContext* pContext, WBEM_REMOTE_TARGET_ID_TYPE idFilter) { ENTER_API_CALL return m_pObject->RemoveFilter(pContext, idFilter); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::RemoveAllFilters(IWbemContext* pContext) { ENTER_API_CALL return m_pObject->RemoveAllFilters(pContext); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::AddDefinitionQuery(IWbemContext* pContext, LPCWSTR wszQuery) { ENTER_API_CALL return m_pObject->AddDefinitionQuery(pContext, wszQuery); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::AllowUtilizeGuarantee() { ENTER_API_CALL return m_pObject->AllowUtilizeGuarantee(); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::RemoveAllDefinitionQueries( IWbemContext* pContext) { ENTER_API_CALL return m_pObject->RemoveAllDefinitionQueries(pContext); EXIT_API_CALL } HRESULT CFilterProxyManager::XProxy::Disconnect() { ENTER_API_CALL return m_pObject->Disconnect(); EXIT_API_CALL } 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_pMetaData) m_pMetaData->Release(); if (m_pManager) m_pManager->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 ); 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 apEventsToSend; INIT_TEMP_ARRAY(apEventsToSend, lNumObjects); if(apEventsToSend == NULL) return WBEM_E_OUT_OF_MEMORY; CTempArray 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; }