Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4217 lines
119 KiB

//******************************************************************************
//
// PROVREG.CPP
//
// Copyright (C) 1996-1999 Microsoft Corporation
//
//******************************************************************************
#include "precomp.h"
#include <stdio.h>
#include <parmdefs.h>
#include <ql.h>
#include "ess.h"
#include <wbemutil.h>
#include <cominit.h>
#include <objpath.h>
#include <provinit.h>
#include <winmgmtr.h>
#include <comutl.h>
#include "NCEvents.h"
_IWmiObject* g_pCopy;
#define WBEM_MAX_FILTER_ID 0x80000000
inline BOOL IsRpcError( HRESULT hr )
{
//
// we'll consider any error but a wbem error to be an rpc error.
//
return HRESULT_FACILITY(hr) != FACILITY_ITF;
}
CWbemInterval CEventProviderWatchInstruction::mstatic_Interval;
CEventProviderWatchInstruction::CEventProviderWatchInstruction(
CEventProviderCache* pCache)
: CBasicUnloadInstruction(mstatic_Interval), m_pCache(pCache)
{
}
void CEventProviderWatchInstruction::staticInitialize(IWbemServices* pRoot)
{
mstatic_Interval = CBasicUnloadInstruction::staticRead(pRoot, GetCurrentEssContext(),
L"__EventProviderCacheControl=@");
}
HRESULT CEventProviderWatchInstruction::Fire(long, CWbemTime)
{
CInCritSec ics(&m_cs);
if(!m_bTerminate)
{
SetCurrentEssThreadObject(NULL);
if ( GetCurrentEssThreadObject() != NULL )
{
m_pCache->UnloadUnusedProviders(m_Interval);
delete GetCurrentEssThreadObject();
ClearCurrentEssThreadObject();
}
}
return WBEM_S_FALSE;
}
//******************************************************************************
//******************************************************************************
//
// PROVIDER SINK (SERVER)
//
//******************************************************************************
//******************************************************************************
CProviderSinkServer::CEventDestination::CEventDestination(
WBEM_REMOTE_TARGET_ID_TYPE id,
CAbstractEventSink* pSink)
: m_id(id), m_pSink(pSink)
{
if(m_pSink)
m_pSink->AddRef();
}
CProviderSinkServer::CEventDestination::CEventDestination(
const CEventDestination& Other)
: m_id(Other.m_id), m_pSink(Other.m_pSink)
{
if(m_pSink)
m_pSink->AddRef();
}
CProviderSinkServer::CEventDestination::~CEventDestination()
{
if(m_pSink)
m_pSink->Release();
}
CProviderSinkServer::CProviderSinkServer()
: m_lRef(0), m_pNamespace(NULL), m_pMetaData(NULL), m_Stub(this), m_idNext(0),
m_pPseudoProxy(NULL), m_pPseudoSink(NULL), m_pReqSink(NULL), m_lLocks(0)
{
}
HRESULT CProviderSinkServer::Initialize( CEssNamespace* pNamespace,
IWbemEventProviderRequirements* pReqSink )
{
HRESULT hres;
//
// This sink owns us, so we intentionally do not AddRef it
//
m_pReqSink = pReqSink;
m_pMetaData = new CEssMetaData(pNamespace);
if(m_pMetaData == NULL)
return WBEM_E_OUT_OF_MEMORY;
m_pMetaData->AddRef();
m_pNamespace = pNamespace;
m_pNamespace->AddRef();
//
// create the pseudo proxy and sink.
//
hres = WbemCoCreateInstance( CLSID_WbemFilterProxy,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWbemLocalFilterProxy,
(void**)&m_pPseudoProxy );
if( FAILED(hres) )
{
return hres;
}
hres = m_pPseudoProxy->SetStub( &m_Stub );
if(FAILED(hres))
{
return hres;
}
return m_pPseudoProxy->GetMainSink(&m_pPseudoSink);
}
HRESULT CProviderSinkServer::GetMainProxy(IWbemEventSink** ppSink)
{
_DBG_ASSERT( m_pPseudoSink != NULL );
m_pPseudoSink->AddRef();
*ppSink = m_pPseudoSink;
return WBEM_S_NO_ERROR;
}
CProviderSinkServer::~CProviderSinkServer()
{
if(m_pPseudoProxy)
m_pPseudoProxy->Release();
if(m_pPseudoSink)
m_pPseudoSink->Release();
if(m_pMetaData)
m_pMetaData->Release();
if(m_pNamespace)
m_pNamespace->Release();
}
ULONG STDMETHODCALLTYPE CProviderSinkServer::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
ULONG STDMETHODCALLTYPE CProviderSinkServer::Release()
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
HRESULT STDMETHODCALLTYPE CProviderSinkServer::QueryInterface(REFIID riid,
void** ppv)
{
if(riid == IID_IUnknown || riid == IID_IMarshal)
*ppv = (IMarshal*)this;
else
return E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CProviderSinkServer::DeliverEvent(
DWORD dwNumEvents,
IWbemClassObject** apEvents,
WBEM_REM_TARGETS* aTargets,
CEventContext* pContext)
{
if(aTargets == NULL || aTargets->m_aTargets == NULL || apEvents == NULL)
{
ERRORTRACE((LOG_ESS, "NULL parameter received from a "
"filter proxy for an event provider. Either an internal "
"error has occurred, or a DENIAL OF SERVICE ATTACK has "
"been thwarted\n"));
return WBEM_E_INVALID_PARAMETER;
}
//
// ensure that there is no ess thread object associated with this thread.
// to avoid making the design more complicated we are not going to support
// postpone operations being used on event signaling threads. If we did
// we have to start creating thread objects and firing postponed ops on
// every event signaling - it would be rather messy and inefficient to
// say the least.
//
CEssThreadObject* pThreadObj = GetCurrentEssThreadObject();
if ( pThreadObj != NULL )
{
ClearCurrentEssThreadObject();
}
for(DWORD i = 0; i < dwNumEvents; i++)
{
DeliverOneEvent(apEvents[i], aTargets + i, pContext);
}
if ( pThreadObj != NULL )
{
SetConstructedEssThreadObject( pThreadObj );
}
return WBEM_S_NO_ERROR;
}
HRESULT CProviderSinkServer::DeliverOneEvent( IWbemClassObject* pEvent,
WBEM_REM_TARGETS* pTargets,
CEventContext* pContext )
{
HRESULT hres;
_DBG_ASSERT( pContext != NULL );
if(pEvent == NULL)
{
ERRORTRACE((LOG_ESS, "NULL parameter received from a "
"filter proxy for an event provider. Either an internal "
"error has occurred, or a DENIAL OF SERVICE ATTACK has "
"been thwarted\n"));
return WBEM_E_INVALID_PARAMETER;
}
//
// allocate the context to be used if we need to switch to a per event
// context ( e.g. when the event has a SD ).
//
CEventContext PerEventContext;
//
// take care of the event SD here. If there is an SD associated with the
// context, then we always use that one. If not, then we take the one
// associated with the event. In the latter case, it is important to
// pull the SD out here because sometimes we perform the access check
// after the SD has been projected out from the event ( this happens in
// cross-namespace subscriptions.
//
if ( pContext->GetSD() == NULL )
{
ULONG cEventSD;
PBYTE pEventSD = (PBYTE)GetSD( pEvent, &cEventSD );
if ( pEventSD != NULL )
{
//
// must use a different context for the event,
// since it has its own SD
//
pContext = &PerEventContext;
//
// we must copy the SD here because it is not guaranteed to be
// aligned properly since it is a ptr to the direct event object
// data. The bytes MUST NOT be treated as an SD until it has
// been copied.
//
if ( !pContext->SetSD( cEventSD, pEventSD, TRUE ) )
{
return WBEM_E_OUT_OF_MEMORY;
}
if ( !IsValidSecurityDescriptor(
(PSECURITY_DESCRIPTOR)pContext->GetSD() ) )
{
return WBEM_E_INVALID_OBJECT;
}
}
}
else
{
if ( !IsValidSecurityDescriptor(
(PSECURITY_DESCRIPTOR)pContext->GetSD() ) )
{
return WBEM_E_INVALID_PARAMETER;
}
}
//
// clone the event
//
IWbemEvent* pClone = NULL;
hres = pEvent->Clone(&pClone);
if(FAILED(hres))
return hres;
CReleaseMe rm1(pClone);
if(pTargets->m_lNumTargets > 1)
return MultiTargetDeliver(pClone, pTargets, pContext);
// Single target
// =============
// Check validity
// ==============
long lDestId = pTargets->m_aTargets[0];
CAbstractEventSink* pDest = NULL;
{
CInCritSec ics(&m_cs);
hres = FindDestinations(1, pTargets->m_aTargets, &pDest);
if(FAILED(hres))
return hres;
if(!pDest)
// No longer there --- that's OK
return WBEM_S_FALSE;
}
hres = pDest->Indicate(1, &pClone, pContext);
pDest->Release();
return hres;
}
HRESULT CProviderSinkServer::MultiTargetDeliver(IWbemEvent* pEvent,
WBEM_REM_TARGETS* pTargets,
CEventContext* pContext)
{
HRESULT hres;
// Convert the target IDs to the actual targets
// ============================================
CTempArray<CAbstractEventSink*> apSinks;
if(!INIT_TEMP_ARRAY(apSinks, pTargets->m_lNumTargets))
return WBEM_E_OUT_OF_MEMORY;
{
CInCritSec ics(&m_cs);
hres = FindDestinations(pTargets->m_lNumTargets, pTargets->m_aTargets,
(CAbstractEventSink**)apSinks);
if(FAILED(hres))
return hres;
}
HRESULT hresGlobal = WBEM_S_NO_ERROR;
for(int i = 0; i < pTargets->m_lNumTargets; i++)
{
if(apSinks[i])
{
hres = apSinks[i]->Indicate(1, &pEvent, pContext);
if(FAILED(hres))
hresGlobal = hres;
apSinks[i]->Release();
}
}
// DEBUGTRACE((LOG_ESS, "Done delivering\n"));
return hresGlobal;
}
// assumes: locked
HRESULT CProviderSinkServer::FindDestinations(long lNum,
IN WBEM_REMOTE_TARGET_ID_TYPE* aidTargets,
RELEASE_ME CAbstractEventSink** apSinks)
{
//
// Do a binary search for each one. The range will be getting progressively
// smaller with each element we find
//
long lLastFoundIndex = -1;
for(long i = 0; i < lNum; i++)
{
long lMinIndex = lLastFoundIndex+1;
long lMaxIndex = m_apDestinations.GetSize() - 1;
long lFound = -1;
WBEM_REMOTE_TARGET_ID_TYPE idCurrent = aidTargets[i];
//
// Search the remaining portion of the array
//
while(lMinIndex <= lMaxIndex)
{
long lMidIndex = (lMinIndex + lMaxIndex) / 2;
WBEM_REMOTE_TARGET_ID_TYPE idMid = m_apDestinations[lMidIndex]->m_id;
if(idMid == idCurrent)
{
lFound = lMidIndex;
break;
}
else if(idCurrent < idMid)
{
lMaxIndex = lMidIndex - 1;
}
else
{
lMinIndex = lMidIndex + 1;
}
}
if(lFound < 0)
{
//
// Invalid target ID -- OK, so NULL target then
//
apSinks[i] = NULL;
}
else
{
apSinks[i] = m_apDestinations[lFound]->m_pSink;
(apSinks[i])->AddRef();
//
// The rest of the IDs can only be found to the right of this one
// because the targets are sorted
//
lLastFoundIndex = lFound;
}
}
return WBEM_S_NO_ERROR;
}
HRESULT STDMETHODCALLTYPE CProviderSinkServer::DeliverStatus(long lFlags,
HRESULT hresStatus,
LPCWSTR wszStatus, IWbemClassObject* pErrorObj,
WBEM_REM_TARGETS* pTargets,
CEventContext* pContext)
{
return WBEM_E_UNEXPECTED;
}
HRESULT STDMETHODCALLTYPE CProviderSinkServer::DeliverProviderRequest(
long lFlags)
{
if(m_pReqSink)
return m_pReqSink->DeliverProviderRequest(lFlags);
else
return WBEM_E_UNEXPECTED;
}
// assumes: locked
HRESULT CProviderSinkServer::GetDestinations(
CUniquePointerArray<CEventDestination>& apDestinations)
{
for(int i = 0; i < m_apDestinations.GetSize(); i++)
{
CEventDestination* pNew = new CEventDestination(*m_apDestinations[i]);
if(pNew == NULL)
return WBEM_E_OUT_OF_MEMORY;
if(apDestinations.Add(pNew) < 0)
{
delete pNew;
return WBEM_E_OUT_OF_MEMORY;
}
}
return WBEM_S_NO_ERROR;
}
// assumes in m_cs;
HRESULT CProviderSinkServer::AddDestination(CAbstractEventSink* pDest,
WBEM_REMOTE_TARGET_ID_TYPE* pID)
{
HRESULT hres = WBEM_S_NO_ERROR;
//
// Allocate a new destination ID
//
WBEM_REMOTE_TARGET_ID_TYPE idNew = m_idNext++;
if(m_idNext > WBEM_MAX_FILTER_ID / 2)
{
//
// 32-bit integer roll-over! This provider has processed over
// 4000000000 filter creations! Canfetti is falling from the ceiling
//
DEBUGTRACE((LOG_ESS, "Filter ID rollover!!!\n"));
// BUGBUG: Postpone a call to reactivate all filters!
}
//
// Add a new destination entry
//
CEventDestination* pDestRecord = new CEventDestination(idNew, pDest);
if(pDestRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
m_apDestinations.Add(pDestRecord);
// Record the ID in the TARGETS
// ============================
*pID = idNew;
return hres;
}
HRESULT CProviderSinkServer::AddFilter(LPCWSTR wszQuery,
QL_LEVEL_1_RPN_EXPRESSION* pExp,
CAbstractEventSink* pDest,
WBEM_REMOTE_TARGET_ID_TYPE* pidRequest)
{
HRESULT hres;
WBEM_REMOTE_TARGET_ID_TYPE idDest;
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
// Copy proxies
// ============
if(!GetProxies(apProxies))
return WBEM_E_OUT_OF_MEMORY;
// Add to the list of destinations registered with the provider and
// construct the target identification for the proxies
// ================================================================
hres = AddDestination(pDest, &idDest);
if(FAILED(hres))
return hres;
}
if(pidRequest)
*pidRequest = idDest;
// Go through all the proxies and schedule calls
// =============================================
HRESULT hresReal = WBEM_S_NO_ERROR;
for(int i = 0; i < apProxies.GetSize(); i++)
{
IWbemLocalFilterProxy *pLocalProxy = NULL;
// See if the proxy will allow us to call LocalAddFilter (in which case
// it's the pseudo proxy).
if (SUCCEEDED(apProxies[i]->QueryInterface(
IID_IWbemLocalFilterProxy, (LPVOID*) &pLocalProxy)))
{
CReleaseMe rm1(pLocalProxy);
hres = pLocalProxy->LocalAddFilter( GetCurrentEssContext(),
wszQuery,
pExp,
idDest );
hresReal = hres; // other errors do not matter
}
else
{
hres = apProxies[i]->AddFilter( GetCurrentEssContext(),
wszQuery,
idDest );
}
if( FAILED(hres) )
{
if ( IsRpcError(hres) )
{
UnregisterProxy( apProxies[i] );
}
ERRORTRACE((LOG_ESS, "Unable to add query %S to a remote provider "
"proxy. Error code: %X\n", wszQuery, hres));
}
}
if ( FAILED(hresReal) )
{
CInCritSec ics(&m_cs);
//
// guaranteed no destinations can be added since we added the last
// one, so remove the last one.
//
m_apDestinations.RemoveAt( m_apDestinations.GetSize()-1 );
}
return hresReal;
}
HRESULT CProviderSinkServer::RemoveFilter(CAbstractEventSink* pDest,
WBEM_REMOTE_TARGET_ID_TYPE* pidRequest)
{
HRESULT hres;
// Find and invalidate the filter in the list of destinations
// ==========================================================
CEventDestination* pToRemove = NULL;
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
// Copy the proxies
// ================
if(!GetProxies(apProxies))
return WBEM_E_OUT_OF_MEMORY;
// Search for it in the array of destinations
// ==========================================
for(int i = 0; i < m_apDestinations.GetSize(); i++)
{
if(m_apDestinations[i]->m_pSink == pDest)
{
m_apDestinations.RemoveAt(i, &pToRemove);
break;
}
}
if(pToRemove == NULL)
return WBEM_E_NOT_FOUND;
}
if(pidRequest)
*pidRequest = pToRemove->m_id;
// The filter is invalidated, but not removed. We are outside of the CS, so
// events can be delivered (but no other changes can occur)
// =========================================================================
// Instruct all proxies to (later) remove this filter from consideration
// =====================================================================
for(int i = 0; i < apProxies.GetSize(); i++)
{
hres = apProxies[i]->RemoveFilter(GetCurrentEssContext(),
pToRemove->m_id);
if(FAILED(hres))
{
if ( IsRpcError(hres) )
{
UnregisterProxy( apProxies[i] );
}
ERRORTRACE((LOG_ESS, "Unable to remove filter %I64d from an event "
"provider proxy: 0x%X\n", pToRemove->m_id, hres));
}
}
//
// Delete the destination in question
//
delete pToRemove;
return WBEM_S_NO_ERROR;
}
// assumes all proxies are locked
void CProviderSinkServer::RemoveAllFilters()
{
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
// Copy the proxies
// ================
if(!GetProxies(apProxies))
return;
//
// Clear out both the list of destinations
//
m_apDestinations.RemoveAll();
}
//
// Remove all filters from all proxies
//
for(int i = 0; i < apProxies.GetSize(); i++)
{
HRESULT hres =
apProxies[i]->RemoveAllFilters(GetCurrentEssContext());
if(FAILED(hres))
{
if ( IsRpcError(hres) )
{
UnregisterProxy( apProxies[i] );
}
ERRORTRACE((LOG_ESS, "Unable to remove all queries from a "
"remote provider proxy. Error code: %X\n", hres));
}
}
}
//
// Only allow utilization of the guarantee if the proxy's definition
// matches the provider's definition. In other words, only when
// the provider's registration has been successfully processed,
// and the proxies are set up to reflect it, should utilization of
// the guarantee be allowed. The reason for this is that an incomplete
// source definition can cause bad things to happen when events are
// evaluated using a filter that was optimized for that definition.
//
HRESULT CProviderSinkServer::AllowUtilizeGuarantee()
{
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
if ( !GetProxies( apProxies ) )
{
return WBEM_E_OUT_OF_MEMORY;
}
}
for(int i = 0; i < apProxies.GetSize(); i++)
{
HRESULT hr = apProxies[i]->AllowUtilizeGuarantee();
if ( FAILED(hr) && IsRpcError(hr) )
{
UnregisterProxy( apProxies[i] );
}
}
return WBEM_S_NO_ERROR;
}
HRESULT CProviderSinkServer::AddDefinitionQuery(LPCWSTR wszQuery)
{
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
GetProxies(apProxies);
if ( m_awsDefinitionQueries.Add(wszQuery) < 0 )
{
return WBEM_E_OUT_OF_MEMORY;
}
}
//
// we always try to add the definition to all proxies, but if there's an
// error ( other than RPC ) we return it to the caller.
//
HRESULT hresReturn = WBEM_S_NO_ERROR;
for(int i = 0; i < apProxies.GetSize(); i++)
{
HRESULT hres = apProxies[i]->AddDefinitionQuery(
GetCurrentEssContext(), wszQuery);
if( FAILED(hres) )
{
if ( IsRpcError(hres) )
{
UnregisterProxy( apProxies[i] );
}
else
{
hresReturn = hres;
}
ERRORTRACE((LOG_ESS, "Unable to add definition query %S to a "
"provider proxy. Error code: %X\n", wszQuery, hres));
}
}
return hresReturn;
}
// assumes: all proxies are locked
void CProviderSinkServer::RemoveAllDefinitionQueries()
{
CInCritSec ics(&m_cs);
m_awsDefinitionQueries.Empty();
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
HRESULT hres = m_apProxies[i]->RemoveAllDefinitionQueries(
GetCurrentEssContext());
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to remove all definition queries from"
" a provider proxy. Error code: %X\n", hres));
}
}
}
void CProviderSinkServer::Clear()
{
// Provider is being removed. First, we disconnect all proxies, ensuring
// that no more events are delivered
// ======================================================================
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
GetProxies(apProxies);
m_apProxies.RemoveAll();
m_awsDefinitionQueries.Empty();
}
//
// since we are going to disconnect the proxy it is illegal to own the
// namespace lock. Reason is that disconnecting takes ownership of the
// proxy lock.
//
_DBG_ASSERT( !m_pNamespace->DoesThreadOwnNamespaceLock() );
for(int i = 0; i < apProxies.GetSize(); i++)
{
apProxies[i]->Disconnect();
}
// Now we clean up
// ===============
RemoveAllFilters();
RemoveAllDefinitionQueries();
m_pReqSink = NULL;
CWbemPtr<IUnknown> pStubUnk;
HRESULT hr = m_Stub.QueryInterface( IID_IUnknown, (void**)&pStubUnk );
_DBG_ASSERT( SUCCEEDED(hr) );
hr = CoDisconnectObject( pStubUnk, 0 );
if ( FAILED( hr ) )
{
ERRORTRACE((LOG_ESS,"Failed Disconnecting Stub.\n"));
}
}
HRESULT CProviderSinkServer::Lock()
{
//
// it is illegal to lock proxies while holding the namespace lock.
//
_DBG_ASSERT( !m_pNamespace->DoesThreadOwnNamespaceLock() );
// DEBUGTRACE((LOG_ESS, "Server %p locking all proxies\n", this));
// First we lock all the proxies. In the interim, events are still
// delivered. Once done, events are blocked in proxies
// ================================================================
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
//
// First, check if we are already locked. If so, no need to bother
// the proxies. Not only that, but since proxies are out-of-proc, we
// would be re-locking them on a different thread, causing a deadlock.
//
if(m_lLocks++ > 0)
return WBEM_S_NO_ERROR;
GetProxies(apProxies);
}
for(int i = 0; i < apProxies.GetSize(); i++)
{
// DEBUGTRACE((LOG_ESS, "Server %p locking proxy %p\n", this,
// apProxies[i]));
HRESULT hres = apProxies[i]->Lock();
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to lock a remote provider proxy. "
"Error code: %X\n", hres));
//
// if we couldn't lock it because of an RPC Error, simply
// unregister, else we have big problems and should unlock all
// the proxies and return the error.
//
if ( IsRpcError( hres ) )
{
UnregisterProxy( apProxies[i] );
}
else
{
for(int j = 0; j < i; j++)
apProxies[j]->Unlock();
return hres;
}
}
}
return WBEM_S_NO_ERROR;
}
BOOL CProviderSinkServer::GetProxies(
CRefedPointerArray<IWbemFilterProxy>& apProxies)
{
CInCritSec ics(&m_cs);
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
if(apProxies.Add(m_apProxies[i]) < 0)
return FALSE;
}
return TRUE;
}
void CProviderSinkServer::Unlock()
{
// DEBUGTRACE((LOG_ESS, "Server %p unlocking all proxies\n", this));
CRefedPointerArray<IWbemFilterProxy> apProxies;
{
CInCritSec ics(&m_cs);
//
// First, check if this is the last unlock. If not, we didn't forward
// this lock, so we shouldn't forward this unlock either
//
if(--m_lLocks != 0)
return;
GetProxies(apProxies);
}
for(int i = 0; i < apProxies.GetSize(); i++)
{
// DEBUGTRACE((LOG_ESS, "Server %p unlocking proxy %p\n", this,
// apProxies[i]));
HRESULT hres = apProxies[i]->Unlock();
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to unlock a remote provider proxy. "
"Error code: %X\n", hres));
if ( IsRpcError(hres) )
{
UnregisterProxy( apProxies[i] );
}
}
}
}
HRESULT STDMETHODCALLTYPE CProviderSinkServer::RegisterProxy(
IWbemFilterProxy* pProxy)
{
// Initialize it with ourselves
// ============================
HRESULT hres = pProxy->Initialize(m_pMetaData, &m_Stub);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to initialize remote proxy: %X\n", hres));
return hres;
}
//
// aquire ns lock here because we need to ensure that we have a
// consistent set of definition queries and filters to initialize the
// proxy with. The cs member of this class is not good enough by
// itself because definition queries and filters are all manipulated
// separately but are always consistent outside of a ess namespace
// operation - which is why we grab the ns lock. It is likely that
// the provider lock is held in this control path already, but it is
// allowed for provider lock to be held when aquiring the ns lock (
// but not the other way around ). We need to ensure that the
// update lock is ALWAYS aquired before the cs to avoid deadlocks.
// Furthermore, we need to hold the lock while populating the provider
// with the filters. This is consistent with all other modifications to
// the provider's filter.
//
CInUpdate iu( m_pNamespace );
{
CInCritSec ics(&m_cs);
if(m_apProxies.Add(pProxy) < 0)
return WBEM_E_OUT_OF_MEMORY;
}
//
// Add all the definition queries to this proxy
//
int i;
BOOL bUtilizeGuarantee = TRUE;
for(i = 0; i < m_awsDefinitionQueries.Size(); i++)
{
hres = pProxy->AddDefinitionQuery( GetCurrentEssContext(),
m_awsDefinitionQueries[i]);
if(FAILED(hres))
{
//
// TODO : We need to mark the provider as inactive.
//
ERRORTRACE((LOG_ESS, "Unable to add definition query '%S' to "
"provider sink: 0x%X.\n",
m_awsDefinitionQueries[i], hres));
bUtilizeGuarantee = FALSE;
}
}
if ( bUtilizeGuarantee )
{
pProxy->AllowUtilizeGuarantee();
}
//
// Add all the filters to this proxy
//
for(i = 0; i < m_apDestinations.GetSize(); i++)
{
// Retrieve the filter from the event sink
// =======================================
CEventDestination* pDest = m_apDestinations[i];
CEventFilter* pFilter = pDest->m_pSink->GetEventFilter();
if(pFilter == NULL)
{
ERRORTRACE((LOG_ESS, "Internal error: non-filter sink in "
"proxy\n"));
continue;
}
LPWSTR wszQuery;
LPWSTR wszQueryLanguage;
BOOL bExact;
if(SUCCEEDED(pFilter->GetCoveringQuery(wszQueryLanguage, wszQuery,
bExact, NULL)) && bExact)
{
// Add this filter to this proxy
// =============================
hres = pProxy->AddFilter(GetCurrentEssContext(), wszQuery,
pDest->m_id);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to add query %S to a remote "
"provider proxy. Error code: %X\n", wszQuery, hres));
}
delete [] wszQuery;
delete [] wszQueryLanguage;
}
}
return WBEM_S_NO_ERROR;
}
HRESULT STDMETHODCALLTYPE CProviderSinkServer::UnregisterProxy(
IWbemFilterProxy* pProxy)
{
CInCritSec ics(&m_cs);
// Look for it
// ===========
for(int i = 0; i < m_apProxies.GetSize(); i++)
{
if(m_apProxies[i] == pProxy)
{
// It is safe to release it, since the caller has a ref-count
// ==========================================================
m_apProxies.RemoveAt(i);
return WBEM_S_NO_ERROR;
}
}
return WBEM_S_FALSE;
}
ULONG STDMETHODCALLTYPE CFilterStub::AddRef()
{
return m_pSink->AddRef();
}
ULONG STDMETHODCALLTYPE CFilterStub::Release()
{
return m_pSink->Release();
}
HRESULT STDMETHODCALLTYPE CFilterStub::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IUnknown || riid == IID_IWbemFilterStub)
{
*ppv = (IWbemFilterStub*)this;
}
else if(riid == IID_IWbemMultiTarget)
{
*ppv = (IWbemMultiTarget*)this;
}
else if ( riid == IID_IWbemFetchSmartMultiTarget )
{
*ppv = (IWbemFetchSmartMultiTarget*)this;
}
else if ( riid == IID_IWbemSmartMultiTarget )
{
*ppv = (IWbemSmartMultiTarget*)this;
}
else if( riid == IID_IWbemEventProviderRequirements)
{
*ppv = (IWbemEventProviderRequirements*)this;
}
else return E_NOINTERFACE;
AddRef();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilterStub::RegisterProxy(IWbemFilterProxy* pProxy)
{
return m_pSink->RegisterProxy(pProxy);
}
HRESULT STDMETHODCALLTYPE CFilterStub::UnregisterProxy(IWbemFilterProxy* pProxy)
{
return m_pSink->UnregisterProxy(pProxy);
}
HRESULT STDMETHODCALLTYPE CFilterStub::DeliverEvent(DWORD dwNumEvents,
IWbemClassObject** apEvents,
WBEM_REM_TARGETS* aTargets,
long lSDLength, BYTE* pSD)
{
CEventContext Context;
Context.SetSD( lSDLength, pSD, FALSE );
return m_pSink->DeliverEvent( dwNumEvents, apEvents, aTargets, &Context );
}
HRESULT STDMETHODCALLTYPE CFilterStub::DeliverStatus(long lFlags,
HRESULT hresStatus,
LPCWSTR wszStatus, IWbemClassObject* pErrorObj,
WBEM_REM_TARGETS* pTargets,
long lSDLength, BYTE* pSD)
{
CEventContext Context;
Context.SetSD( lSDLength, pSD, FALSE );
return m_pSink->DeliverStatus(lFlags, hresStatus, wszStatus, pErrorObj,
pTargets, &Context);
}
HRESULT STDMETHODCALLTYPE CFilterStub::DeliverProviderRequest(long lFlags)
{
return m_pSink->DeliverProviderRequest(lFlags);
}
HRESULT STDMETHODCALLTYPE CFilterStub::GetSmartMultiTarget( IWbemSmartMultiTarget** ppSmartMultiTarget )
{
return QueryInterface( IID_IWbemSmartMultiTarget, (void**) ppSmartMultiTarget );
}
HRESULT STDMETHODCALLTYPE CFilterStub::DeliverEvent(ULONG dwNumEvents,
ULONG dwBuffSize,
BYTE* pBuffer,
WBEM_REM_TARGETS* pTargets,
long lSDLength, BYTE* pSD)
{
// Unwind the buffer into an object. Note that because m_ClassCache is
// STL based, it is intrinsically thread-safe. Also, calling proxies are
// serialized, so we shouldn't have any thread-safety problems here.
CWbemMtgtDeliverEventPacket packet( (LPBYTE) pBuffer, dwBuffSize );
long lObjectCount;
IWbemClassObject ** pObjArray;
HRESULT hr = packet.UnmarshalPacket( lObjectCount, pObjArray, m_ClassCache );
if ( SUCCEEDED( hr ) )
{
// Number must be dwNumEvents
if(lObjectCount == dwNumEvents)
{
// Now call the standard deliver event function and hand it the
// object
hr = DeliverEvent(dwNumEvents, pObjArray, pTargets, lSDLength, pSD);
}
else
{
hr = WBEM_E_UNEXPECTED;
}
// Release the objects in the array and clean up pObjArray
for ( int lCtr = 0; lCtr < lObjectCount; lCtr++ )
{
pObjArray[lCtr]->Release();
}
delete [] pObjArray;
} // IF UnmarshalPacket
return hr;
}
void CProviderSinkServer::GetStatistics(long* plProxies, long* plDestinations,
long* plFilters, long* plTargetLists, long* plTargets,
long* plPostponed)
{
*plProxies = m_apProxies.GetSize();
*plDestinations = m_apDestinations.GetSize();
/* BUGBUG: do properly for all sinks
((CFilterProxy*)m_pSink)->GetStatistics(plFilters, plTargetLists,
plTargets, plPostponed);
*/
}
//******************************************************************************
//******************************************************************************
//
// CRECORD :: CQUERY RECORD
//
//******************************************************************************
//******************************************************************************
CEventProviderCache::CRecord::CQueryRecord::CQueryRecord()
: m_strQuery(NULL), m_pEventClass(NULL),
m_dwEventMask(0), m_paInstanceClasses(NULL), m_pExpr(NULL)
{
}
HRESULT CEventProviderCache::CRecord::CQueryRecord::EnsureClasses(
CEssNamespace* pNamespace )
{
HRESULT hres = WBEM_S_NO_ERROR;
_IWmiObject* pClass;
if ( m_pEventClass == NULL )
{
if ( SUCCEEDED( pNamespace->GetClass( m_pExpr->bsClassName,
&pClass ) ) )
{
m_pEventClass = pClass;
}
else
{
hres = WBEM_S_FALSE;
}
}
if ( m_paInstanceClasses != NULL )
{
for(int i = 0; i < m_paInstanceClasses->GetNumClasses(); i++)
{
CClassInformation* pInfo = m_paInstanceClasses->GetClass(i);
if ( pInfo->m_pClass == NULL )
{
if ( SUCCEEDED( pNamespace->GetClass( pInfo->m_wszClassName,
&pClass) ) )
{
pInfo->m_pClass = pClass;
}
else
{
hres = WBEM_S_FALSE;
}
}
}
}
else
{
hres = WBEM_S_FALSE;
}
return hres;
}
void CEventProviderCache::CRecord::CQueryRecord::ReleaseClasses()
{
if ( m_pEventClass != NULL )
{
m_pEventClass->Release();
m_pEventClass = NULL;
}
if ( m_paInstanceClasses != NULL )
{
for(int i = 0; i < m_paInstanceClasses->GetNumClasses(); i++)
{
CClassInformation* pInfo = m_paInstanceClasses->GetClass(i);
if ( pInfo->m_pClass != NULL )
{
pInfo->m_pClass->Release();
pInfo->m_pClass = NULL;
}
}
}
}
HRESULT CEventProviderCache::CRecord::CQueryRecord::Initialize(
LPCWSTR wszQuery,
LPCWSTR wszProvName,
CEssNamespace* pNamespace,
bool bSystem)
{
HRESULT hres;
m_strQuery = SysAllocString(wszQuery);
if(m_strQuery == NULL)
return WBEM_E_OUT_OF_MEMORY;
// Parse the query
// ===============
CTextLexSource Source((LPWSTR)wszQuery);
QL1_Parser Parser(&Source);
if(Parser.Parse(&m_pExpr) != QL1_Parser::SUCCESS)
{
ERRORTRACE((LOG_ESS,
"Invalid query in provider registration: %S\n", wszQuery));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_INVALID_EVENT_PROVIDER_QUERY,
wszQuery);
return WBEM_E_UNPARSABLE_QUERY;
}
if(!bSystem)
{
if(!wbem_wcsicmp(m_pExpr->bsClassName, L"__Event") ||
!wbem_wcsicmp(m_pExpr->bsClassName, L"__ExtrinsicEvent"))
{
ERRORTRACE((LOG_ESS,
"Provider claims to provide all events with "
"query: %S\n"
"We don't believe it, so we ignore the registration\n\n",
wszQuery));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_EVENT_PROVIDER_QUERY_TOO_BROAD,
wszQuery);
return WBEMESS_E_REGISTRATION_TOO_BROAD;
}
}
// Determine its event mask
// ========================
m_dwEventMask = CEventRepresentation::GetTypeMaskFromName(
m_pExpr->bsClassName);
// Check if the mask mentions any pollable events
// ==============================================
if(m_dwEventMask & INTRINSIC_EVENTS_MASK)
{
// Yes. Get instance classes for which it providers these events
// =============================================================
hres = CQueryAnalyser::GetDefiniteInstanceClasses(m_pExpr,
m_paInstanceClasses);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Unable to determine instance classes for which events"
"are provided by this query: %S\n", wszQuery));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_INVALID_EVENT_PROVIDER_INTRINSIC_QUERY,
wszQuery);
return WBEM_E_UNINTERPRETABLE_PROVIDER_QUERY;
}
if(!bSystem && !m_paInstanceClasses->IsLimited())
{
ERRORTRACE((LOG_ESS,
"Provider claims to provide all intrinsic events with "
"query: %S\n"
"We don't believe it, so we ignore the registration\n\n",
wszQuery));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_EVENT_PROVIDER_QUERY_TOO_BROAD,
wszQuery);
return WBEMESS_E_REGISTRATION_TOO_BROAD;
}
// Get the actual classes from the namespace
// =========================================
for(int i = 0; i < m_paInstanceClasses->GetNumClasses(); i++)
{
CClassInformation* pInfo = m_paInstanceClasses->GetClass(i);
_IWmiObject* pClass = NULL;
hres = pNamespace->GetClass(pInfo->m_wszClassName, &pClass);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Could not get class %S for which provider claims"
" to provider events. Error code: %X\n",
pInfo->m_wszClassName, hres));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_EVENT_PROVIDER_QUERY_NOT_FOUND,
wszQuery, pInfo->m_wszClassName);
//
// Before continuing, we register for class creation event on
// this class. This way, when it is finally created, we will
// reactivate stuff and bring the system back on track
//
hres = pNamespace->RegisterProviderForClassChanges(
pInfo->m_wszClassName,
wszProvName );
// ignore error code --- what can we do?
return WBEM_S_FALSE;
}
//
// don't store, we'll retrieve it later as necessary. This
// will require that the user call EnsureClasses() before calling
// any function that needs those classes.
//
pClass->Release();
}
}
// Get the event class
// ===================
_IWmiObject* pClass = NULL;
hres = pNamespace->GetClass(m_pExpr->bsClassName, &pClass);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Invalid event class %S in provider registration \n"
"Query was: %S\n\n", m_pExpr->bsClassName, wszQuery));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_EVENT_PROVIDER_QUERY_NOT_FOUND,
wszQuery, m_pExpr->bsClassName);
//
// Before continuing, we register for class creation event on this
// class. This way, when it is finally created, we will reactivate
// stuff and bring the system back on track
//
hres = pNamespace->RegisterProviderForClassChanges(
m_pExpr->bsClassName,
wszProvName );
// ignore error code --- what can we do?
return WBEM_S_FALSE;
}
//
// don't store, we'll retrieve it later as necessary. This
// will require that the user call EnsureClasses() before calling
// any function that needs those classes.
//
CReleaseMe rmpClass( pClass );
if( pClass->InheritsFrom(L"__Event") != S_OK)
{
ERRORTRACE((LOG_ESS,
"Invalid event class %S in provider registration \n"
"Query was: %S\n\n", m_pExpr->bsClassName, wszQuery));
CEventLog Log; Log.Open();
Log.Report(EVENTLOG_ERROR_TYPE,
WBEM_MC_EVENT_PROVIDER_QUERY_NOT_EVENT,
wszQuery, m_pExpr->bsClassName);
return WBEM_S_FALSE;
}
return WBEM_S_NO_ERROR;
}
CEventProviderCache::CRecord::CQueryRecord::~CQueryRecord()
{
SysFreeString(m_strQuery);
if(m_pEventClass)
m_pEventClass->Release();
delete m_paInstanceClasses;
delete m_pExpr;
}
HRESULT CEventProviderCache::CRecord::CQueryRecord::Update(LPCWSTR wszClassName,
IWbemClassObject* pClass)
{
HRESULT hres = WBEM_S_FALSE;
// Check the event class
// =====================
if(!wbem_wcsicmp(wszClassName, m_pExpr->bsClassName))
{
if(pClass == NULL)
{
// This query record is hereby invalid
// ===================================
ERRORTRACE((LOG_ESS,
"Event provider query, %S, is invalidated by class "
"deletion of %S\n", m_strQuery, m_pExpr->bsClassName));
if(m_pEventClass)
m_pEventClass->Release();
m_pEventClass = NULL;
delete m_paInstanceClasses;
m_paInstanceClasses = NULL;
}
else
{
// Change the class definition
// ===========================
if(m_pEventClass)
{
m_pEventClass->Release();
m_pEventClass = NULL;
hres = pClass->Clone(&m_pEventClass);
if ( FAILED(hres) )
return hres;
}
}
hres = WBEM_S_NO_ERROR;
}
if(m_paInstanceClasses)
{
// Check the instance classes
// ==========================
for(int i = 0; i < m_paInstanceClasses->GetNumClasses(); i++)
{
CClassInformation* pInfo = m_paInstanceClasses->GetClass(i);
if(!wbem_wcsicmp(wszClassName, pInfo->m_wszClassName))
{
if(pClass)
{
// This class is no longer there
// =============================
ERRORTRACE((LOG_ESS,
"Class %S for which provider claims to provide"
" events is deleted", pInfo->m_wszClassName));
m_paInstanceClasses->RemoveClass(i);
i--;
}
else
{
// Change the class definition
// ===========================
if(pInfo->m_pClass)
{
pInfo->m_pClass->Release();
pInfo->m_pClass = NULL;
hres = pClass->Clone(&pInfo->m_pClass);
if ( FAILED(hres) )
return hres;
}
}
hres = WBEM_S_NO_ERROR;
}
}
}
return hres;
}
HRESULT CEventProviderCache::CRecord::CQueryRecord::DoesIntersectWithQuery(
IN CRequest& Request, CEssNamespace* pNamespace)
{
HRESULT hres;
if(m_pEventClass == NULL)
{
// Inactive record
return WBEM_S_FALSE;
}
// Check that the classes are related --- one is derived from another
// ==================================================================
if(m_pEventClass->InheritsFrom(Request.GetQueryExpr()->bsClassName)
!= WBEM_S_NO_ERROR &&
Request.GetEventClass(pNamespace)->InheritsFrom(m_pExpr->bsClassName)
!= WBEM_S_NO_ERROR
)
{
// Not the right class.
// ====================
return WBEM_S_FALSE;
}
// For extrinsic providers, this is good enough. But for
// intrinsic providers, we need to check if the requested
// instance classes intersect with the provided ones
// ======================================================
if(Request.GetEventMask() & INSTANCE_EVENTS_MASK)
{
INTERNAL CClassInfoArray* pClasses = NULL;
hres = Request.GetInstanceClasses(pNamespace, &pClasses);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Failed to determine instance classes required by query '%S':"
"0x%X\n", Request.GetQuery(), hres));
return hres;
}
if(!CQueryAnalyser::CompareRequestedToProvided(
*pClasses,
*m_paInstanceClasses))
{
// This intrinsic provider does not need activation
// ================================================
return WBEM_S_FALSE;
}
}
// All test have been passed
// =========================
return WBEM_S_NO_ERROR;
}
DWORD CEventProviderCache::CRecord::CQueryRecord::GetProvidedEventMask(
IWbemClassObject* pClass,
BSTR strClassName)
{
if(m_pEventClass == NULL || m_paInstanceClasses == NULL)
{
// Not active as an intrinsic provider record
// ==========================================
return 0;
}
// Check that we supply intrinsic events
// =====================================
if((m_dwEventMask & INSTANCE_EVENTS_MASK) == 0)
return 0;
// Go through all the instance classes for which it provides events
// ================================================================
for(int k = 0; k < m_paInstanceClasses->GetNumClasses(); k++)
{
CClassInformation* pInfo = m_paInstanceClasses->GetClass(k);
if(pInfo->m_pClass == NULL)
{
// Non-existent class
// ==================
return 0;
}
//
// If desired class is derived from the provided class, then we are
// covered. If it is the other way around, we are not
//
if(pClass->InheritsFrom(pInfo->m_wszClassName) == S_OK)
return m_dwEventMask;
}
return 0;
}
//******************************************************************************
//******************************************************************************
//
// CRECORD
//
//******************************************************************************
//******************************************************************************
CEventProviderCache::CRecord::CRecord()
: m_strName(NULL), m_lRef(0), m_bStarted(false), m_lPermUsageCount(0),
m_bProviderSet(FALSE), m_lUsageCount(0), m_pProvider(NULL),
m_pQuerySink(NULL), m_pMainSink(NULL), m_pSecurity(NULL),
m_LastUse(CWbemTime::GetCurrentTime()), m_bRecorded(FALSE),
m_bNeedsResync(TRUE), m_strNamespace(NULL)
{
}
HRESULT CEventProviderCache::CRecord::Initialize( LPCWSTR wszName,
CEssNamespace* pNamespace )
{
m_pNamespace = pNamespace;
m_pNamespace->AddRef();
m_pMainSink = new CProviderSinkServer();
if(m_pMainSink == NULL)
return WBEM_E_OUT_OF_MEMORY;
m_pMainSink->AddRef();
m_strNamespace = SysAllocString(pNamespace->GetName());
if(m_strNamespace == NULL)
return WBEM_E_OUT_OF_MEMORY;
if ( wszName != NULL )
{
m_strName = SysAllocString(wszName);
if(m_strName == NULL)
return WBEM_E_OUT_OF_MEMORY;
}
return m_pMainSink->Initialize(pNamespace, this);
}
CEventProviderCache::CRecord::~CRecord()
{
if(m_pNamespace)
m_pNamespace->Release();
if( m_pMainSink )
{
//
// shutdown and release the sink server. We must postpone the
// shutdown though because it will release any outstanding
// proxies which cannot be done while holding the namespace lock.
//
CPostponedList* pList = GetCurrentPostponedList();
_DBG_ASSERT( pList != NULL );
CPostponedSinkServerShutdown* pReq;
pReq = new CPostponedSinkServerShutdown( m_pMainSink );
if ( pReq != NULL )
{
if ( FAILED(pList->AddRequest( m_pNamespace, pReq ) ) )
{
delete pReq;
}
}
m_pMainSink->Release();
}
UnloadProvider();
SysFreeString(m_strNamespace);
SysFreeString(m_strName);
}
ULONG CEventProviderCache::CRecord::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
ULONG CEventProviderCache::CRecord::Release()
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
BOOL CEventProviderCache::CRecord::IsEmpty()
{
return ( !m_bProviderSet && m_apQueries.GetSize() == 0);
}
HRESULT CEventProviderCache::CRecord::SetProvider(IWbemClassObject* pWin32Prov)
{
HRESULT hres;
// Clean out the old data
// ======================
m_bProviderSet = FALSE;
VARIANT v;
VariantInit(&v);
CClearMe cm1(&v);
// Verity object validity
// ======================
if(pWin32Prov->InheritsFrom(WIN32_PROVIDER_CLASS) != WBEM_S_NO_ERROR)
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
// removed doublecheck - a decoupled provider does not have a clsid
// if(FAILED(pWin32Prov->Get(PROVIDER_CLSID_PROPNAME, 0, &v, NULL, NULL)) ||
// V_VT(&v) != VT_BSTR)
// return WBEM_E_INVALID_PROVIDER_REGISTRATION;
if(m_pProvider)
{
UnloadProvider();
}
// Store object for later use
// ==========================
m_bProviderSet = TRUE;
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::ResetProvider()
{
if(m_bProviderSet)
{
m_bProviderSet = FALSE;
return WBEM_S_NO_ERROR;
}
else
{
return WBEM_S_FALSE;
}
}
HRESULT CEventProviderCache::CRecord::GetProviderInfo(
IWbemClassObject* pRegistration,
BSTR& strName)
{
VARIANT v;
VariantInit(&v);
strName = NULL;
if(FAILED(pRegistration->Get(PROVIDER_NAME_PROPNAME, 0, &v, NULL, NULL)) ||
V_VT(&v) != VT_BSTR)
{
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
}
strName = V_BSTR(&v);
// VARIANT intentionally not cleared
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::GetRegistrationInfo(
IWbemClassObject* pRegistration,
BSTR& strName)
{
VARIANT v;
VariantInit(&v);
CClearMe cm1(&v);
strName = NULL;
if(FAILED(pRegistration->Get(EVPROVREG_PROVIDER_REF_PROPNAME, 0, &v,
NULL, NULL)) || V_VT(&v) != VT_BSTR)
{
ERRORTRACE((LOG_ESS, "NULL provider reference in event provider "
"registration! Registration is invalid\n"));
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
}
// Parse the path
// ==============
CObjectPathParser Parser;
ParsedObjectPath* pPath;
int nRes = Parser.Parse(V_BSTR(&v), &pPath);
if(nRes != CObjectPathParser::NoError)
{
ERRORTRACE((LOG_ESS, "Unparsable provider reference in event provider "
"registration: %S. Registration is invalid\n", V_BSTR(&v)));
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
}
//
// It would be good to check that the class specified here is valid, but
// we cannot just compare the name since this may be a derived class of
// __Win32Provider. And getting the class definition and comparing would
// be too expensive, so we'll just trust the provider here
//
//
// if(wbem_wcsicmp(pPath->m_pClass, WIN32_PROVIDER_CLASS))
// {
// Parser.Free(pPath);
// return WBEM_E_INVALID_PROVIDER_REGISTRATION;
// }
if(pPath->m_dwNumKeys != 1)
{
Parser.Free(pPath);
ERRORTRACE((LOG_ESS, "Wrong number of keys in provider reference in "
"event provider registration: %S. Registration is invalid\n",
V_BSTR(&v)));
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
}
if(V_VT(&pPath->m_paKeys[0]->m_vValue) != VT_BSTR)
{
Parser.Free(pPath);
ERRORTRACE((LOG_ESS, "Wrong key type in provider reference in event "
"provider registration: %S. Registration is invalid\n",
V_BSTR(&v)));
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
}
strName = SysAllocString(V_BSTR(&pPath->m_paKeys[0]->m_vValue));
Parser.Free(pPath);
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::SetQueries(CEssNamespace* pNamespace,
IWbemClassObject* pRegistration)
{
HRESULT hres;
// Get the list of class names
// ===========================
VARIANT v;
VariantInit(&v);
if(FAILED(pRegistration->Get(EVPROVREG_QUERY_LIST_PROPNAME, 0, &v,
NULL, NULL)) || V_VT(&v) != (VT_BSTR | VT_ARRAY))
{
ResetQueries();
return WBEM_E_INVALID_PROVIDER_REGISTRATION;
}
CClearMe cm(&v);
SAFEARRAY* psa = V_ARRAY(&v);
long lLBound, lUBound;
SafeArrayGetLBound(psa, 1, &lLBound);
SafeArrayGetUBound(psa, 1, &lUBound);
long lElements = lUBound - lLBound + 1;
BSTR* astrQueries;
SafeArrayAccessData(psa, (void**)&astrQueries);
CUnaccessMe um(psa);
return SetQueries(pNamespace, lElements, (LPCWSTR*)astrQueries);
}
HRESULT CEventProviderCache::CRecord::SetQueries(CEssNamespace* pNamespace,
long lNumQueries,
LPCWSTR* awszQueries)
{
HRESULT hres;
ResetQueries();
// Create a record for each query
// ==============================
BOOL bUtilizeGuarantee = TRUE;
for(long lQueryIndex = 0; lQueryIndex < lNumQueries; lQueryIndex++)
{
hres = AddDefinitionQuery(pNamespace, awszQueries[lQueryIndex]);
if ( FAILED(hres) )
{
bUtilizeGuarantee = FALSE;
}
if( hres == WBEM_E_OUT_OF_MEMORY )
{
return hres;
}
}
if ( bUtilizeGuarantee )
{
m_pMainSink->AllowUtilizeGuarantee();
}
return WBEM_S_NO_ERROR;
}
// assumes: CProviderSinkServer locked!
HRESULT CEventProviderCache::CRecord::AddDefinitionQuery(
CEssNamespace* pNamespace,
LPCWSTR wszQuery)
{
HRESULT hres;
CQueryRecord* pNewQueryRecord = new CQueryRecord;
if(pNewQueryRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
hres = pNewQueryRecord->Initialize( wszQuery, m_strName, pNamespace, IsSystem());
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Skipping provider %S invalid registration query %S\n",
m_strName, wszQuery));
}
else
{
hres = m_pMainSink->AddDefinitionQuery(wszQuery);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Skipping provider %S registration query %S\n"
" failed to merge: %X\n",
m_strName, wszQuery, hres));
}
if(m_apQueries.Add(pNewQueryRecord) < 0)
{
delete pNewQueryRecord;
hres = WBEM_E_OUT_OF_MEMORY;
}
}
return hres;
}
HRESULT CEventProviderCache::CRecord::ResetQueries()
{
m_apQueries.RemoveAll();
m_pMainSink->RemoveAllDefinitionQueries();
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::PostponeNewQuery(CExecLine::CTurn* pTurn,
DWORD dwId, LPCWSTR wszQueryLanguage, LPCWSTR wszQuery,
CAbstractEventSink* pDest)
{
CPostponedList* pList = GetCurrentPostponedList();
//
// if null, then no thread object associated with thread. caller may
// need to use an CEssInternalOperationSink.
//
_DBG_ASSERT( pList != NULL );
CPostponedNewQuery* pReq = new CPostponedNewQuery(this, dwId,
wszQueryLanguage, wszQuery, pTurn, pDest);
if(pReq == NULL)
{
return WBEM_E_OUT_OF_MEMORY;
}
HRESULT hr = pList->AddRequest( m_pNamespace, pReq);
if ( FAILED(hr) )
{
delete pReq;
return hr;
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::PostponeCancelQuery(
CExecLine::CTurn* pTurn, DWORD dwId)
{
CPostponedList* pList = GetCurrentPostponedList();
//
// if null, then no thread object associated with thread. caller may
// need to use an CEssInternalOperationSink.
//
_DBG_ASSERT( pList != NULL );
CPostponedCancelQuery* pReq = new CPostponedCancelQuery(this, pTurn, dwId);
if( pReq == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
HRESULT hr = pList->AddRequest( m_pNamespace, pReq );
if ( FAILED(hr) )
{
delete pReq;
return hr;
}
return WBEM_S_NO_ERROR;
}
// assumes: no locks are held
HRESULT CEventProviderCache::CRecord::Exec_LoadProvider(
CEssNamespace* pNamespace)
{
HRESULT hres;
// Having locked the namespace, retrieve the necessary parameters
// ==============================================================
CLSID clsid;
IWbemObjectSink* pEventSink = NULL;
{
CInUpdate iu(pNamespace);
if(pNamespace->IsShutdown())
return WBEM_E_INVALID_NAMESPACE;
// Check if it is already loaded
// =============================
if(m_pProvider)
return WBEM_S_FALSE;
}
IWbemEventProvider* pProvider = NULL;
hres = m_pNamespace->LoadEventProvider(m_strName, &pProvider);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Unable to load event provider '%S' in namespace "
"'%S': 0x%X\n", m_strName, m_pNamespace->GetName(), hres));
return hres;
}
CReleaseMe rm1(pProvider);
/* TAKEN CARE OF BY THE PROVSS
//
// In case this is a "framework" provider, inform it of its registration
//
IWbemProviderIdentity* pIdent = NULL;
hres = pProvider->QueryInterface(IID_IWbemProviderIdentity,
(void**)&pIdent);
if(SUCCEEDED(hres))
{
CReleaseMe rm(pIdent);
hres = pIdent->SetRegistrationObject(0, m_pWin32Prov);
if(hres == WBEM_E_PROVIDER_NOT_CAPABLE)
hres = WBEM_S_SUBJECT_TO_SDS;
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Event provider %S failed to accept its "
"registration object with error code 0x%X\n", m_strName, hres));
return hres;
}
}
*/
//
// Deposit this and other provider pointers into the record
//
hres = SetProviderPointer(pNamespace, pProvider);
if(FAILED(hres))
return hres;
//
// Report the MSFT_WmiEventProviderLoaded event.
//
FIRE_NCEVENT(
g_hNCEvents[MSFT_WmiEventProviderLoaded],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
pNamespace->GetName(),
m_strName);
//
// Postpone start until all activations are done
//
CPostponedList* pList = GetCurrentPostponedList();
//
// if null, then no thread object associated with thread. caller may
// need to use an CEssInternalOperationSink.
//
_DBG_ASSERT( pList != NULL );
CPostponedProvideEvents* pReq = new CPostponedProvideEvents(this);
if(pReq == NULL)
{
return WBEM_E_OUT_OF_MEMORY;
}
hres = pList->AddRequest( m_pNamespace, pReq);
if ( FAILED(hres) )
{
delete pReq;
}
return hres;
}
HRESULT CEventProviderCache::CRecord::SetProviderPointer(
CEssNamespace* pNamespace,
IWbemEventProvider* pProvider)
{
HRESULT hres;
//
// Check the "smart provider" interface
//
IWbemEventProviderQuerySink* pQuerySink = NULL;
hres = pProvider->QueryInterface(IID_IWbemEventProviderQuerySink,
(void**)&pQuerySink);
CReleaseMe rm4(pQuerySink);
//
// Check the security interface
//
IWbemEventProviderSecurity* pSecurity = NULL;
hres = pProvider->QueryInterface(IID_IWbemEventProviderSecurity,
(void**)&pSecurity);
CReleaseMe rm5(pSecurity);
// Having locked the namespace, deposit the pointers into the record
// =================================================================
{
CInUpdate iu(pNamespace);
if(pNamespace->IsShutdown())
return WBEM_E_INVALID_NAMESPACE;
m_pProvider = pProvider;
pProvider->AddRef();
m_pQuerySink = pQuerySink;
if(pQuerySink)
pQuerySink->AddRef();
m_pSecurity = pSecurity;
if(pSecurity)
pSecurity->AddRef();
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::Exec_StartProvider(
CEssNamespace* pNamespace)
{
IWbemEventProvider* pProvider = NULL;
IWbemEventSink* pEventSink = NULL;
HRESULT hres;
{
CInUpdate iu(pNamespace);
if(m_bStarted)
return WBEM_S_NO_ERROR;
m_bStarted = true;
pProvider = m_pProvider;
if(pProvider)
pProvider->AddRef();
// Retrieve the sink to give to the provider
// =========================================
hres = m_pMainSink->GetMainProxy(&pEventSink);
if(FAILED(hres))
return hres;
}
CReleaseMe rm1(pProvider);
CReleaseMe rm2(pEventSink);
if(pProvider)
{
//
// all calls to provider ( except AccessCheck ) should be made as
// the system. It is incorrect to propagate the client's identity
// along in this call.
//
IUnknown *pOldCtx, *pTmpCtx;
hres = CoSwitchCallContext( NULL, &pOldCtx );
if ( FAILED( hres ) )
return hres;
hres = pProvider->ProvideEvents(pEventSink, 0);
HRESULT hr = CoSwitchCallContext( pOldCtx, &pTmpCtx );
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Could not start provider %S. Error: %X\n", m_strName, hres));
CInUpdate iu( pNamespace );
UnloadProvider();
return WBEM_E_PROVIDER_FAILURE;
}
else if ( FAILED(hr) ) // propagate ProvideEvents code in success case.
{
hres = hr;
}
return hres;
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::AddActiveProviderEntryToRegistry()
{
LONG lRes;
HKEY hkeyEss, hkeyNamespace, hkeyProvider;
DEBUGTRACE((LOG_ESS,"Adding provider %S from namespace %S to "
" registry as active provider\n", m_strName, m_strNamespace));
//
// open ess key. It is expected that this key is already created.
//
lRes = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
WBEM_REG_ESS,
0,
KEY_ALL_ACCESS,
&hkeyEss );
if ( lRes == ERROR_SUCCESS )
{
//
// open namespace key. It is expected that this key is already created.
//
lRes = RegOpenKeyExW( hkeyEss,
m_strNamespace,
0,
KEY_ALL_ACCESS,
&hkeyNamespace );
if ( lRes == ERROR_SUCCESS )
{
//
// create the provider sub key.
//
lRes = RegCreateKeyExW( hkeyNamespace,
m_strName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hkeyProvider,
NULL );
if ( lRes == ERROR_SUCCESS )
{
RegCloseKey( hkeyProvider );
}
RegCloseKey( hkeyNamespace );
}
RegCloseKey( hkeyEss );
}
return HRESULT_FROM_WIN32( lRes );
}
HRESULT CEventProviderCache::CRecord::RemoveActiveProviderEntryFromRegistry()
{
LONG lRes;
HKEY hkeyEss, hkeyNamespace;
DEBUGTRACE((LOG_ESS,"Removing provider %S from namespace %S from "
" registry as active provider\n", m_strName, m_strNamespace));
//
// open ess key.
//
lRes = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
WBEM_REG_ESS,
0,
KEY_ALL_ACCESS,
&hkeyEss );
if ( lRes == ERROR_SUCCESS )
{
//
// open namespace key. It is expected that this key is already created.
//
lRes = RegOpenKeyExW( hkeyEss,
m_strNamespace,
0,
KEY_ALL_ACCESS,
&hkeyNamespace );
if ( lRes == ERROR_SUCCESS )
{
//
// delete the provider sub key.
//
lRes = RegDeleteKeyW( hkeyNamespace, m_strName );
RegCloseKey( hkeyNamespace );
}
RegCloseKey( hkeyEss );
}
return HRESULT_FROM_WIN32( lRes );
}
void CEventProviderCache::CRecord::UnloadProvider()
{
HRESULT hr;
DEBUGTRACE((LOG_ESS,"Unloading Provider %S in namespace %S\n",
m_strName, m_strNamespace ));
//
// make sure the provider is removed from the provider cache. This is
// so if we load the provider again in the near future, we don't call
// ProvideEvents() on it twice.
//
if ( m_pProvider != NULL )
{
CWbemPtr<_IWmiProviderCache> pProvCache;
hr = m_pProvider->QueryInterface( IID__IWmiProviderCache,
(void**)&pProvCache );
if ( SUCCEEDED(hr) )
{
hr = pProvCache->Expel( 0, GetCurrentEssContext() );
if ( FAILED(hr) )
{
ERRORTRACE((LOG_ESS,"Could not expel provider %S from "
"provider cache in namespace %S. HR=0x%x\n",
m_strName,m_strNamespace,hr));
}
}
m_pNamespace->PostponeRelease(m_pProvider);
m_pProvider = NULL;
}
if(m_pQuerySink)
m_pNamespace->PostponeRelease(m_pQuerySink);
m_pQuerySink = NULL;
if(m_pSecurity)
m_pNamespace->PostponeRelease(m_pSecurity);
m_pSecurity = NULL;
m_bStarted = false;
//
// Report the MSFT_WmiEventProviderUnloaded event.
//
FIRE_NCEVENT(
g_hNCEvents[MSFT_WmiEventProviderUnloaded],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
m_strNamespace,
m_strName);
}
HRESULT CEventProviderCache::CRecord::Exec_NewQuery(CEssNamespace* pNamespace,
CExecLine::CTurn* pTurn,
DWORD dwID, LPCWSTR wszLanguage, LPCWSTR wszQuery,
CAbstractEventSink* pDest)
{
HRESULT hres;
// Wait for our turn to make changes
// =================================
CExecLine::CInTurn it(&m_Line, pTurn);
hres = ActualExecNewQuery(pNamespace, dwID, wszLanguage, wszQuery, pDest);
if(FAILED(hres))
{
//
// Check: it could be provider needs to be restarted
//
if(HRESULT_FACILITY(hres) != FACILITY_ITF)
{
ERRORTRACE((LOG_ESS, "Non-WMI error code recieved from provider "
"%S: 0x%x. WMI will attempt to re-activate\n", m_strName,
hres));
{
CInUpdate iu( pNamespace );
UnloadProvider();
}
hres = ActualExecNewQuery(pNamespace, dwID, wszLanguage, wszQuery,
pDest);
}
}
if(FAILED(hres))
{
// Filter activation failed: deactivate
// =====================================
CInUpdate iu(pNamespace);
if(pNamespace->IsShutdown())
return WBEM_E_INVALID_NAMESPACE;
pNamespace->DeactivateFilter(pDest->GetEventFilter());
}
return hres;
}
HRESULT CEventProviderCache::CRecord::ActualExecNewQuery(
CEssNamespace* pNamespace,
DWORD dwID, LPCWSTR wszLanguage, LPCWSTR wszQuery,
CAbstractEventSink* pDest)
{
HRESULT hres;
// Ensure provider is loaded
// =========================
hres = Exec_LoadProvider(pNamespace);
if(FAILED(hres))
return hres;
// With namespace locked, check if the provider is loaded
// ======================================================
IWbemEventProviderQuerySink* pSink = NULL;
IWbemEventProviderSecurity* pSecurity = NULL;
PSID pCopySid = NULL;
{
CInUpdate iu(pNamespace);
if(pNamespace->IsShutdown())
return WBEM_E_INVALID_NAMESPACE;
if(m_pQuerySink != NULL)
{
pSink = m_pQuerySink;
pSink->AddRef();
}
if(m_pSecurity != NULL)
{
pSecurity = m_pSecurity;
pSecurity->AddRef();
// Make a copy of the filter's owner SID
// =====================================
PSID pActualSid = pDest->GetEventFilter()->GetOwner();
if(pActualSid != NULL)
{
pCopySid = new BYTE[GetLengthSid(pActualSid)];
if(pCopySid == NULL)
return WBEM_E_OUT_OF_MEMORY;
if(!CopySid(GetLengthSid(pActualSid), pCopySid, pActualSid))
{
delete [] pCopySid;
return WBEM_E_OUT_OF_MEMORY;
}
}
}
}
CReleaseMe rm1(pSink);
CReleaseMe rm2(pSecurity);
CVectorDeleteMe<BYTE> vdm((BYTE*)pCopySid);
//
// Check security, if possible. If provider does not support the interface,
// interpret as "check SDs", as this may be a new-model-only provider
//
hres = WBEM_S_SUBJECT_TO_SDS;
if(pSecurity)
{
DWORD dwSidLen = pCopySid ? GetLengthSid(pCopySid) : 0;
// Check security based on the SID or thread
// =========================================
if ( dwSidLen == 0 )
{
//
// Check security based on the thread. First save the current
// call context, then switch it back after we're done.
//
IUnknown *pOldCtx, *pTmpCtx;
hres = CoSwitchCallContext( NULL, &pOldCtx );
if ( FAILED( hres ) )
{
return hres;
}
CWbemPtr<IUnknown> pNewCtx;
hres = pDest->GetEventFilter()->SetThreadSecurity( &pNewCtx );
if ( FAILED( hres ) )
{
return hres;
}
hres = pSecurity->AccessCheck( wszLanguage,
wszQuery,
0,
NULL );
HRESULT hr = CoSwitchCallContext( pOldCtx, &pTmpCtx );
if ( SUCCEEDED( hres ) && FAILED( hr ) )
{
hres = hr;
}
}
else
{
hres = pSecurity->AccessCheck( wszLanguage,
wszQuery,
dwSidLen,
(BYTE*)pCopySid);
}
//
// Report the MSFT_WmiEventProviderAccessCheck event.
//
FIRE_NCEVENT(
g_hNCEvents[MSFT_WmiEventProviderAccessCheck],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
m_strNamespace,
m_strName,
wszLanguage,
wszQuery,
pCopySid, dwSidLen,
hres);
}
if(hres == WBEM_E_PROVIDER_NOT_CAPABLE)
hres = WBEM_S_NO_ERROR;
if(SUCCEEDED(hres))
{
// Security check has been passed: decrement remaining count
// =========================================================
pDest->GetEventFilter()->DecrementRemainingSecurityChecks(hres);
}
else
{
ERRORTRACE((LOG_ESS, "Event provider refused consumer registration "
"query %S for security reasons: 0x%X\n", wszQuery, hres));
}
// Call "NewQuery" if required
// ===========================
if(SUCCEEDED(hres) && pSink)
{
IUnknown *pOldCtx, *pTmpCtx;
hres = CoSwitchCallContext( NULL, &pOldCtx );
if ( SUCCEEDED(hres) )
{
hres = pSink->NewQuery(dwID, (LPWSTR)wszLanguage,(LPWSTR)wszQuery);
if(hres == WBEM_E_PROVIDER_NOT_CAPABLE)
hres = WBEM_S_NO_ERROR;
HRESULT hr = CoSwitchCallContext( pOldCtx, &pTmpCtx );
if ( SUCCEEDED(hres) && FAILED(hr) )
hres = hr;
}
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Event provider refused consumer registration "
"query %S: error code 0x%X\n", wszQuery, hres));
}
//
// Report the MSFT_WmiEventProviderNewQuery event.
//
FIRE_NCEVENT(
g_hNCEvents[MSFT_WmiEventProviderNewQuery],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
m_strNamespace,
m_strName,
wszLanguage,
wszQuery,
dwID,
hres);
}
return hres;
}
HRESULT CEventProviderCache::CRecord::Exec_CancelQuery(
CEssNamespace* pNamespace, CExecLine::CTurn* pTurn,
DWORD dwId)
{
CExecLine::CInTurn it(&m_Line, pTurn);
// With namespace locked, check if the provider is loaded
// ======================================================
IWbemEventProviderQuerySink* pSink = NULL;
{
CInUpdate iu(pNamespace);
if(pNamespace->IsShutdown())
return WBEM_E_INVALID_NAMESPACE;
if(m_pQuerySink == NULL)
return WBEM_S_FALSE;
pSink = m_pQuerySink;
pSink->AddRef();
}
CReleaseMe rm1(pSink);
// Make the call
// =============
HRESULT hr;
IUnknown *pOldCtx, *pTmpCtx;
hr = CoSwitchCallContext( NULL, &pOldCtx );
if ( SUCCEEDED(hr) )
{
hr = pSink->CancelQuery(dwId);
HRESULT hr2 = CoSwitchCallContext( pOldCtx, &pTmpCtx );
if ( SUCCEEDED(hr) && FAILED(hr2) )
hr = hr2;
}
//
// Report the MSFT_WmiEventProviderCancelQuery event.
//
FIRE_NCEVENT(
g_hNCEvents[MSFT_WmiEventProviderCancelQuery],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
m_strNamespace,
m_strName,
dwId,
hr);
return hr;
}
HRESULT CEventProviderCache::CRecord::DeliverProviderRequest(
long lFlags)
{
HRESULT hres;
//
// The only requirement we support is WBEM_REQUIREMENT_RECHECK_SUBSCRIPTIONS
//
if(lFlags != WBEM_REQUIREMENTS_RECHECK_SUBSCRIPTIONS)
return WBEM_E_INVALID_PARAMETER;
//
// With this object locked, retrieve all the filters for this provider.
// Get provider pointers, as well
//
CProviderSinkServer::TDestinationArray apDestinations;
IWbemEventProviderQuerySink* pSink = NULL;
IWbemEventProviderSecurity* pSecurity = NULL;
{
CInUpdate iu(m_pMainSink->GetNamespace());
hres = m_pMainSink->GetDestinations(apDestinations);
if(FAILED(hres))
return hres;
if(m_pQuerySink != NULL)
{
pSink = m_pQuerySink;
pSink->AddRef();
}
if(m_pSecurity != NULL)
{
pSecurity = m_pSecurity;
pSecurity->AddRef();
}
}
CReleaseMe rm1(pSink);
CReleaseMe rm2(pSecurity);
//
// Iterate over them all, rechecking each with the provider
//
for(int i = 0; i < apDestinations.GetSize(); i++)
{
CProviderSinkServer::CEventDestination* pEventDest = apDestinations[i];
CAbstractEventSink* pDest = pEventDest->m_pSink;
//
// Retrieve the event filter associated with this sink
//
CEventFilter* pFilter = pDest->GetEventFilter();
if(pFilter == NULL)
{
ERRORTRACE((LOG_ESS, "Internal error: non-filter sink in proxy\n"));
continue;
}
//
// Retrieve the query from this filter.
//
LPWSTR wszQuery;
LPWSTR wszQueryLanguage;
BOOL bExact;
hres = pFilter->GetCoveringQuery(wszQueryLanguage, wszQuery,
bExact, NULL);
if(FAILED(hres) || !bExact)
continue;
CVectorDeleteMe<WCHAR> vdm1(wszQuery);
CVectorDeleteMe<WCHAR> vdm2(wszQueryLanguage);
//
// Check security first
//
if(pSecurity)
{
PSID pSid = pFilter->GetOwner();
if(pSid)
{
// Check security based on SID
hres = pSecurity->AccessCheck(wszQueryLanguage, wszQuery,
GetLengthSid(pSid),
(BYTE*)pSid);
}
else
{
//
// Check security based on the thread. First save the current
// call context, then switch it back after we're done.
//
IUnknown *pOldCtx, *pTmpCtx;
hres = CoSwitchCallContext( NULL, &pOldCtx );
if ( FAILED( hres ) )
{
return hres;
}
CWbemPtr<IUnknown> pNewCtx;
hres = pFilter->SetThreadSecurity( &pNewCtx );
if ( FAILED(hres) )
{
return hres;
}
hres = pSecurity->AccessCheck( wszQueryLanguage,
wszQuery,
0,
NULL );
HRESULT hr = CoSwitchCallContext( pOldCtx, &pTmpCtx );
if ( SUCCEEDED( hres ) && FAILED( hr ) )
{
hres = hr;
}
}
if(FAILED(hres))
{
//
// Increment remaining security checks, thus disabling filter
//
ERRORTRACE((LOG_ESS, "Disabling filter %S as provider denies "
" access for this user: 0x%X\n", wszQuery, hres));
pFilter->IncrementRemainingSecurityChecks();
pDest->SetStatus( 0, WBEM_E_CALL_CANCELLED, NULL, NULL );
}
}
if(SUCCEEDED(hres) && pSink)
{
//
// Check everything else --- do a NewQuery
//
hres = pSink->NewQuery(pEventDest->m_id, (LPWSTR)wszQueryLanguage,
(LPWSTR)wszQuery);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Disabling filter %S as provider refuses "
"registration: error code 0x%X\n", wszQuery, hres));
}
}
}
return hres;
}
CExecLine::CTurn* CEventProviderCache::CRecord::GetInLine()
{
return m_Line.GetInLine();
}
void CEventProviderCache::CRecord::DiscardTurn(CExecLine::CTurn* pTurn)
{
m_Line.DiscardTurn(pTurn);
}
HRESULT CEventProviderCache::CRecord::Activate(CEssNamespace* pNamespace,
CRequest* pRequest,
WBEM_REMOTE_TARGET_ID_TYPE idRequest)
{
CExecLine::CTurn* pTurn = GetInLine();
if(pTurn == NULL)
return WBEM_E_OUT_OF_MEMORY;
m_lUsageCount++;
m_LastUse = CWbemTime::GetCurrentTime();
if ( pRequest->GetDest()->GetEventFilter()->IsPermanent() )
{
m_lPermUsageCount++;
CheckPermanentUsage();
}
// Notify him of the new query, if required
// ========================================
HRESULT hr;
hr = PostponeNewQuery( pTurn,
idRequest,
L"WQL",
pRequest->GetQuery(),
pRequest->GetDest() );
if ( FAILED(hr) )
{
DiscardTurn( pTurn );
return hr;
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::Deactivate( CAbstractEventSink* pDest,
WBEM_REMOTE_TARGET_ID_TYPE idRequest )
{
if( !m_bProviderSet )
{
// Provider is not registered.
// ===========================
return WBEM_S_FALSE;
}
// Notify him of the cancellation, if required
// ===========================================
CExecLine::CTurn* pTurn = GetInLine();
if(pTurn == NULL)
{
return WBEM_E_OUT_OF_MEMORY;
}
HRESULT hr = PostponeCancelQuery(pTurn, idRequest);
if ( FAILED(hr) )
{
DiscardTurn( pTurn );
return hr;
}
m_lUsageCount--;
m_LastUse = CWbemTime::GetCurrentTime();
if ( pDest->GetEventFilter()->IsPermanent() )
{
//
// TODO: Out usage counts can easily get out of wack because of
// mismatched Activate/Deactivates in the presence of failures.
// _DBG_ASSERT( m_lPermUsageCount > 0 );
//
m_lPermUsageCount--;
CheckPermanentUsage();
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::DeactivateFilter(
CAbstractEventSink* pDest)
{
HRESULT hres;
// Try to remove it from our stub
// ==============================
WBEM_REMOTE_TARGET_ID_TYPE idRequest;
hres = m_pMainSink->RemoveFilter(pDest, &idRequest);
if(hres == WBEM_E_NOT_FOUND) // not there --- no problem
return WBEM_S_FALSE;
else if(FAILED(hres))
return hres;
hres = Deactivate( pDest, idRequest);
return hres;
}
HRESULT CEventProviderCache::CRecord::ActivateIfNeeded(CRequest& Request,
IN CEssNamespace* pNamespace)
{
HRESULT hres;
// Go through all the classes supplied by the provider and see if ours
// is an ancestor of any of them.
// ===================================================================
for(int j = 0; j < m_apQueries.GetSize(); j++)
{
CQueryRecord* pQueryRecord = m_apQueries[j];
_DBG_ASSERT( pQueryRecord != NULL );
pQueryRecord->EnsureClasses( pNamespace );
hres = pQueryRecord->DoesIntersectWithQuery(Request, pNamespace);
pQueryRecord->ReleaseClasses();
if(FAILED(hres))
{
// Something is wrong with the query itself --- no point in
// continuing to other registrations
// ========================================================
return hres;
}
else if(hres == WBEM_S_NO_ERROR)
{
DEBUGTRACE((LOG_ESS,"Activating filter '%S' with provider %S\n",
Request.GetQuery(), m_strName ));
// First, increment the number of remaining security checks on this
// filter, since, even though we are adding it to the proxy, we do
// not want events reaching it until the provider said OK
// ================================================================
Request.GetDest()->GetEventFilter()->
IncrementRemainingSecurityChecks();
// Add this filter to the proxies
// ==============================
WBEM_REMOTE_TARGET_ID_TYPE idRequest;
hres = m_pMainSink->AddFilter(Request.GetQuery(),
Request.GetQueryExpr(),
Request.GetDest(),
&idRequest);
if(FAILED(hres)) return hres;
// Schedule activation of this record, which will involve loading
// and notifying the provider. Also at that time, filter's security
// check count will be reduced and events can start flowing
// ================================================================
hres = Activate(pNamespace, &Request, idRequest);
if(hres != WBEM_S_NO_ERROR) // S_FALSE means no provider
{
m_pMainSink->RemoveFilter(Request.GetDest());
return hres;
}
// No point in continuing --- the provider has been activated
// ==========================================================
break;
}
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CRecord::CancelAllQueries()
{
HRESULT hres;
//
// With this object locked, retrieve all the filters for this provider.
// Get provider pointers, as well
//
CProviderSinkServer::TDestinationArray apDestinations;
IWbemEventProviderQuerySink* pSink = NULL;
{
CInUpdate iu(m_pMainSink->GetNamespace());
if(m_pQuerySink == NULL)
{
//
// Nothing to cancel!
//
return WBEM_S_FALSE;
}
hres = m_pMainSink->GetDestinations(apDestinations);
if(FAILED(hres))
return hres;
pSink = m_pQuerySink;
pSink->AddRef();
}
CReleaseMe rm1(pSink);
//
// Iterate over them all, rechecking each with the provider
//
for(int i = 0; i < apDestinations.GetSize(); i++)
{
CProviderSinkServer::CEventDestination* pEventDest = apDestinations[i];
//
// Notify the provider of the cancellation
//
CExecLine::CTurn* pTurn = GetInLine();
if( pTurn == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
hres = PostponeCancelQuery(pTurn, pEventDest->m_id);
if( FAILED(hres) )
{
DiscardTurn( pTurn );
return hres;
}
}
return S_OK;
}
//
// takes care of storing permanently the 'permanent' usage state of a provider
//
void CEventProviderCache::CRecord::CheckPermanentUsage()
{
HRESULT hr;
if ( IsSystem() )
{
return;
}
if ( m_lPermUsageCount == 0 && m_bRecorded )
{
hr = RemoveActiveProviderEntryFromRegistry();
//
// no matter what the outcome, make sure to set recorded to false.
//
m_bRecorded = FALSE;
//
// since a namespace is deactivated before the filters are deactivated,
// ( because filter deactivation is postponed ), it is possible that
// the namespace key will be deleted by the time we get here.
// this happens when we're deactivating the last permanent consumer
// in the namespace.
//
if ( FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) )
{
ERRORTRACE((LOG_ESS,"Error removing active provider entry "
"from registry in namespace %S. HR=0x%x\n",
m_pNamespace->GetName(), hr ));
}
}
else if ( m_lPermUsageCount > 0 && !m_bRecorded )
{
hr = AddActiveProviderEntryToRegistry();
if ( SUCCEEDED(hr) )
{
m_bRecorded = TRUE;
}
else
{
ERRORTRACE((LOG_ESS,"Error adding active provider entry "
"to registry in namespace %S. HR=0x%x\n",
m_pNamespace->GetName(), hr ));
}
}
}
void CEventProviderCache::CRecord::ResetUsage()
{
DEBUGTRACE((LOG_ESS,"Resetting provider '%S' in namespace '%S' to prepare "
"for resync.\n", m_strName, m_strNamespace ));
//
// set a flag so that when all filters are reactivated we know to
// process them for this record.
//
m_bNeedsResync = TRUE;
CancelAllQueries();
m_lUsageCount = 0;
//
// when the changes to the event provider cache is committed, we will
// enumerate all records and remove ones having a perm usage count still 0
// from the registry.
//
m_lPermUsageCount = 0;
m_pMainSink->RemoveAllFilters();
}
bool CEventProviderCache::CRecord::DeactivateIfNotUsed()
{
if(m_lUsageCount == 0 && m_pProvider)
{
// Stop the provider
// =================
UnloadProvider();
DEBUGTRACE((LOG_ESS, "Unloading event provider %S\n", m_strName));
return true;
}
else
return false;
}
bool CEventProviderCache::CRecord::IsUnloadable()
{
return (IsActive() && GetUsageCount() == 0);
}
DWORD CEventProviderCache::CRecord::GetProvidedEventMask(
IWbemClassObject* pClass,
BSTR strClassName)
{
DWORD dwEventMask = 0;
// Go through all its registered queries
// =====================================
for(int j = 0; j < m_apQueries.GetSize(); j++)
{
CRecord::CQueryRecord* pQueryRecord = m_apQueries.GetAt(j);
_DBG_ASSERT( pQueryRecord != NULL );
pQueryRecord->EnsureClasses( m_pNamespace );
dwEventMask |= pQueryRecord->GetProvidedEventMask(pClass, strClassName);
pQueryRecord->ReleaseClasses();
}
return dwEventMask;
}
bool CEventProviderCache::CSystemRecord::DeactivateIfNotUsed()
{
//
// System providers cannot be deactivated
//
return false;
}
bool CEventProviderCache::CSystemRecord::IsUnloadable()
{
//
// System providers cannot be deactivated
//
return false;
}
/*
HRESULT CEventProviderCache::CSystemRecord::PostponeNewQuery(
CExecLine::CTurn* pTurn, DWORD dwId,
LPCWSTR wszQueryLanguage, LPCWSTR wszQuery,
CAbstractEventSink* pDest)
{
//
// System providers do not need calls to them postponed!
//
return Exec_NewQuery(m_pNamespace, pTurn, dwId, wszQueryLanguage, wszQuery,
pDest);
}
HRESULT CEventProviderCache::CSystemRecord::PostponeCancelQuery(
CExecLine::CTurn* pTurn, DWORD dwId)
{
//
// System providers do not need calls to them postponed!
//
return Exec_CancelQuery(m_pNamespace, pTurn, dwId);
}
*/
//******************************************************************************
//******************************************************************************
//
// REQUEST
//
//******************************************************************************
//******************************************************************************
CEventProviderCache::CRequest::CRequest(IN CAbstractEventSink* pDest,
LPWSTR wszQuery, QL_LEVEL_1_RPN_EXPRESSION* pExp)
: m_pDest(pDest),
m_wszQuery(wszQuery), m_pExpr(pExp),
m_dwEventMask(0), m_papInstanceClasses(NULL), m_pEventClass(NULL)
{
}
CEventProviderCache::CRequest::~CRequest()
{
// Do not delete namespace, language, and query, and QL -- they were STOREd
// ========================================================================
if(m_papInstanceClasses)
delete m_papInstanceClasses;
if(m_pEventClass)
m_pEventClass->Release();
}
INTERNAL QL_LEVEL_1_RPN_EXPRESSION* CEventProviderCache::CRequest::
GetQueryExpr()
{
return m_pExpr;
}
DWORD CEventProviderCache::CRequest::GetEventMask()
{
if(m_dwEventMask == 0)
{
QL_LEVEL_1_RPN_EXPRESSION* pExpr = GetQueryExpr();
if(pExpr == NULL)
return 0;
m_dwEventMask =
CEventRepresentation::GetTypeMaskFromName(pExpr->bsClassName);
}
return m_dwEventMask;
}
HRESULT CEventProviderCache::CRequest::GetInstanceClasses(
CEssNamespace* pNamespace,
INTERNAL CClassInfoArray** ppClasses)
{
*ppClasses = NULL;
if(!m_papInstanceClasses)
{
QL_LEVEL_1_RPN_EXPRESSION* pExpr = GetQueryExpr();
if(pExpr == NULL)
return WBEM_E_OUT_OF_MEMORY;
HRESULT hres = CQueryAnalyser::GetPossibleInstanceClasses(
pExpr, m_papInstanceClasses);
if(FAILED(hres))
{
return hres;
}
if(m_papInstanceClasses == NULL)
return WBEM_E_OUT_OF_MEMORY;
// Get the actual classes from the namespace
// =========================================
for(int i = 0; i < m_papInstanceClasses->GetNumClasses(); i++)
{
CClassInformation* pInfo = m_papInstanceClasses->GetClass(i);
_IWmiObject* pClass = NULL;
hres = pNamespace->GetClass(pInfo->m_wszClassName, &pClass);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,
"Could not get class %S for which intrinsic events"
" are requested. Error code: %X\n",
pInfo->m_wszClassName, hres));
delete m_papInstanceClasses;
m_papInstanceClasses = NULL;
if(hres == WBEM_E_NOT_FOUND)
hres = WBEM_E_INVALID_CLASS;
return hres;
}
pInfo->m_pClass = pClass;
}
}
*ppClasses = m_papInstanceClasses;
return WBEM_S_NO_ERROR;
}
INTERNAL IWbemClassObject* CEventProviderCache::CRequest::GetEventClass(
CEssNamespace* pNamespace)
{
HRESULT hres;
if(m_pEventClass == NULL)
{
QL_LEVEL_1_RPN_EXPRESSION* pExpr = GetQueryExpr();
if(pExpr == NULL)
return NULL;
_IWmiObject* pClass = NULL;
hres = pNamespace->GetClass(pExpr->bsClassName, &pClass);
if(FAILED(hres))
{
return NULL;
}
m_pEventClass = pClass;
}
return m_pEventClass;
}
HRESULT CEventProviderCache::CRequest::CheckValidity(CEssNamespace* pNamespace)
{
if(GetQueryExpr() == NULL)
return WBEM_E_INVALID_QUERY;
if(GetEventClass(pNamespace) == NULL)
return WBEM_E_INVALID_CLASS;
return WBEM_S_NO_ERROR;
}
//******************************************************************************
//******************************************************************************
//
// PROVIDER CACHE
//
//******************************************************************************
//******************************************************************************
CEventProviderCache::CEventProviderCache(CEssNamespace* pNamespace)
: m_pNamespace(pNamespace), m_pInstruction(NULL), m_bInResync(FALSE)
{
}
CEventProviderCache::~CEventProviderCache()
{
Shutdown();
}
// assumes: in m_cs
long CEventProviderCache::FindRecord(LPCWSTR wszName)
{
for(long l = 0; l < m_aRecords.GetSize(); l++)
{
if(!wbem_wcsicmp(wszName, m_aRecords[l]->m_strName))
{
return l;
}
}
return -1;
}
HRESULT CEventProviderCache::AddProvider(IWbemClassObject* pWin32Prov)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
// Determine provider's name
// =========================
BSTR strName;
hres = CRecord::GetProviderInfo(pWin32Prov, strName);
if(FAILED(hres))
return hres;
CSysFreeMe sfm1(strName);
// Check if it exists
// ==================
long lIndex = FindRecord(strName);
if(lIndex != -1)
{
// Already there
// =============
hres = m_aRecords[lIndex]->SetProvider(pWin32Prov);
if(FAILED(hres))
return hres;
return WBEM_S_FALSE;
}
// Create a new provider record
// ============================
CRecord* pNewRecord = _new CRecord;
if(pNewRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
pNewRecord->AddRef();
CTemplateReleaseMe<CRecord> rm1(pNewRecord);
hres = pNewRecord->Initialize( strName, m_pNamespace );
if(FAILED(hres))
return hres;
hres = pNewRecord->SetProvider(pWin32Prov);
if(FAILED(hres))
return hres;
// Store it
// ========
if(m_aRecords.Add(pNewRecord) < 0)
{
delete pNewRecord;
return WBEM_E_OUT_OF_MEMORY;
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::AddSystemProvider(IWbemEventProvider* pProvider,
LPCWSTR wszName,
long lNumQueries,
LPCWSTR* awszQueries)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
//
// First of all, construct a system provider record and add it
//
CSystemRecord* pNewRecord = new CSystemRecord;
if(pNewRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
pNewRecord->AddRef();
CTemplateReleaseMe<CSystemRecord> rm1(pNewRecord);
hres = pNewRecord->Initialize( wszName, m_pNamespace);
if(FAILED(hres))
return hres;
//
// Now, add all the queries in
//
hres = pNewRecord->SetQueries(m_pNamespace, lNumQueries, awszQueries);
if(FAILED(hres))
return hres;
//
// Populate it with the provider pointer
//
hres = pNewRecord->SetProviderPointer(m_pNamespace, pProvider);
if(FAILED(hres))
return hres;
//
// Launch it
//
hres = pNewRecord->Exec_StartProvider(m_pNamespace);
if(FAILED(hres))
return hres;
if(m_aRecords.Add(pNewRecord) < 0)
{
return WBEM_E_OUT_OF_MEMORY;
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::RemoveProvider(IWbemClassObject* pWin32Prov)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
// Determine provider's name
// =========================
BSTR strName;
hres = CRecord::GetProviderInfo(pWin32Prov, strName);
if(FAILED(hres))
return hres;
// Find this record
// ================
long lIndex = FindRecord(strName);
SysFreeString(strName);
if(lIndex == -1)
{
return WBEM_S_FALSE;
}
else
{
m_aRecords[lIndex]->ResetUsage();
m_aRecords[lIndex]->ResetProvider();
if(m_aRecords[lIndex]->IsEmpty())
{
m_aRecords.RemoveAt(lIndex);
}
return WBEM_S_NO_ERROR;
}
}
HRESULT CEventProviderCache::CheckProviderRegistration(
IWbemClassObject* pRegistration)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
// Create a new provider record
// ============================
CRecord* pRecord = new CRecord;
if(pRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
CDeleteMe<CRecord> dm1(pRecord);
hres = pRecord->Initialize( NULL, m_pNamespace );
if(FAILED(hres))
return hres;
// Set the queries into it
// =======================
hres = pRecord->SetQueries(m_pNamespace, pRegistration);
return hres;
}
HRESULT CEventProviderCache::AddProviderRegistration(
IWbemClassObject* pRegistration)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
BSTR strName;
hres = CRecord::GetRegistrationInfo( pRegistration, strName );
if( FAILED(hres) )
{
return hres;
}
CSysFreeMe sfm( strName );
long lIndex = FindRecord( strName );
if(lIndex == -1)
{
CRecord* pRecord = new CRecord;
if(pRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
hres = pRecord->Initialize( strName, m_pNamespace );
if(FAILED(hres))
{
delete pRecord;
return hres;
}
hres = pRecord->SetQueries(m_pNamespace, pRegistration);
if ( FAILED(hres) )
{
delete pRecord;
return hres;
}
lIndex = m_aRecords.Add(pRecord);
if(lIndex == -1)
{
delete pRecord;
return WBEM_E_OUT_OF_MEMORY;
}
}
else
{
hres = m_aRecords[lIndex]->SetQueries(m_pNamespace, pRegistration);
}
return hres;
}
HRESULT CEventProviderCache::RemoveProviderRegistration(
IWbemClassObject* pRegistration)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
BSTR strName;
hres = CRecord::GetRegistrationInfo( pRegistration, strName );
if( FAILED(hres) )
{
return hres;
}
CSysFreeMe sfm( strName );
long lIndex = FindRecord( strName );
if(lIndex == -1)
return WBEM_S_FALSE;
// Set registration info
// =====================
m_aRecords[lIndex]->ResetUsage();
m_aRecords[lIndex]->ResetQueries();
if(m_aRecords[lIndex]->IsEmpty())
{
m_aRecords.RemoveAt(lIndex);
}
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::ReleaseProvidersForQuery(CAbstractEventSink* pDest)
{
HRESULT hres;
//
// it is possible that this one function can be called without ns set
//
if ( m_pNamespace != NULL )
{
_DBG_ASSERT( m_pNamespace->IsShutdown() ||
m_pNamespace->DoesThreadOwnNamespaceLock() );
}
// Search all the providers
// ========================
for(int i = 0; i < m_aRecords.GetSize(); i++)
{
CRecord* pRecord = m_aRecords[i];
hres = pRecord->DeactivateFilter(pDest);
// If failures occur, they are logged. Continue.
}
// Make sure unload instruction is running
// =======================================
EnsureUnloadInstruction();
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::LoadProvidersForQuery(LPWSTR wszQuery,
QL_LEVEL_1_RPN_EXPRESSION* pExp, CAbstractEventSink* pDest)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
// DEBUGTRACE((LOG_ESS, "Activating providers for %S (%p)\n",
// wszQuery, pDest));
// Create a request record
// =======================
CRequest Request(pDest, wszQuery, pExp);
// Check query validity
// ====================
hres = Request.CheckValidity(m_pNamespace);
if(FAILED(hres))
return hres;
// Search all the providers
// ========================
HRESULT hresGlobal = WBEM_S_NO_ERROR;
for(int i = 0; i < m_aRecords.GetSize(); i++)
{
CRecord* pRecord = m_aRecords[i];
if ( !m_bInResync || pRecord->NeedsResync() )
{
HRESULT hr = pRecord->ActivateIfNeeded(Request, m_pNamespace);
if(FAILED(hr))
hresGlobal = hr;
}
}
return hresGlobal;
}
void CEventProviderCache::EnsureUnloadInstruction()
{
if(m_pInstruction == NULL && m_pNamespace != NULL)
{
m_pInstruction = new CEventProviderWatchInstruction(this);
if(m_pInstruction != NULL)
{
m_pInstruction->AddRef();
m_pNamespace->GetTimerGenerator().Set(m_pInstruction);
}
}
}
DWORD CEventProviderCache::GetProvidedEventMask(IWbemClassObject* pClass)
{
HRESULT hres;
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
VARIANT v;
VariantInit(&v);
hres = pClass->Get(L"__CLASS", 0, &v, NULL, NULL);
if(FAILED(hres))
return hres;
CClearMe cm1(&v);
DWORD dwProvidedMask = 0;
// Search all the providers
// ========================
for(int i = 0; i < m_aRecords.GetSize(); i++)
{
CRecord* pRecord = m_aRecords[i];
dwProvidedMask |= pRecord->GetProvidedEventMask(pClass, V_BSTR(&v));
}
return dwProvidedMask;
}
HRESULT CEventProviderCache::VirtuallyReleaseProviders()
{
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
//
// just need to record the fact that we are in resync. This allows us to
// handle reactivation of filters differently than when not in resync.
// For example, during resync we only process reactivations for provider
// records that had changed causing the resync in the first place.
//
m_bInResync = TRUE;
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::CommitProviderUsage()
{
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( m_pNamespace->DoesThreadOwnNamespaceLock() );
// Called after VirtuallyReleaseProviders and re-activating all filters
// to actually deactivate all the providers whose usage count went to 0.
// =====================================================================
//
// need to process all records and ensure that any having a perm usage
// count of 0 be removed from the registry. Also make sure that we
// reset each records resync flag.
//
for( int i=0; i < m_aRecords.GetSize(); i++ )
{
m_aRecords[i]->ResetNeedsResync();
m_aRecords[i]->CheckPermanentUsage();
}
// At this point, there is nothing to be done. When unload instruction
// executes, providers that are no longer needed will be unloaded. All we
// need to do is allow the unload instruction to proceed.
// ======================================================================
m_bInResync = FALSE;
EnsureUnloadInstruction();
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::UnloadUnusedProviders(CWbemInterval Interval)
{
_DBG_ASSERT( m_pNamespace != NULL );
_DBG_ASSERT( !m_pNamespace->DoesThreadOwnNamespaceLock() );
{
CInUpdate iu(m_pNamespace);
if( m_pNamespace->IsShutdown())
return WBEM_S_FALSE;
if(m_bInResync)
{
// Usage counters are not up-to-date --- wait for the next time
// ============================================================
return WBEM_S_FALSE;
}
BOOL bDeactivated = FALSE;
BOOL bActiveLeft = FALSE;
for(int i = 0; i < m_aRecords.GetSize(); i++)
{
CRecord* pRecord = m_aRecords[i];
if(pRecord->IsActive() &&
CWbemTime::GetCurrentTime() - pRecord->m_LastUse > Interval)
{
if(pRecord->DeactivateIfNotUsed())
bDeactivated = TRUE;
}
//
// Check if we need to come back for this one
//
if(pRecord->IsUnloadable())
bActiveLeft = TRUE;
}
if(bDeactivated)
m_pNamespace->GetTimerGenerator().ScheduleFreeUnusedLibraries();
if(!bActiveLeft && m_pInstruction)
{
m_pInstruction->Terminate();
m_pInstruction->Release();
m_pInstruction = NULL;
}
}
m_pNamespace->FirePostponedOperations();
return WBEM_S_NO_ERROR;
}
HRESULT CEventProviderCache::Shutdown()
{
//
// check if we've already been shutdown. we don't need a cs here because
// this method is not multithread safe anyhow and is the only method that
// modifies the namespace member.
//
if ( m_pNamespace == NULL )
return WBEM_S_FALSE;
//
// namespace lock cannot be held when calling this method. This is
// because we will block here waiting for the timer instruction to
// shutdown and this thread aquires the namespace lock.
//
_DBG_ASSERT( !m_pNamespace->DoesThreadOwnNamespaceLock() );
if(m_pInstruction)
{
m_pInstruction->Terminate();
m_pInstruction->Release();
m_pInstruction = NULL;
}
m_aRecords.RemoveAll();
//
// we don't need a cs here to modify the namespace because we are
// assured that the only other thread that can access us concurrently,
// (the timer thread) has been shutdown.
//
m_pNamespace = NULL;
return WBEM_S_NO_ERROR;
}
void CEventProviderCache::DumpStatistics(FILE* f, long lFlags)
{
long lLoaded = 0;
long lQueries = 0;
long lProxies = 0;
long lFilters = 0;
long lDestinations = 0;
long lTargetLists = 0;
long lTargets = 0;
long lPostponed = 0;
for(int i = 0; i < m_aRecords.GetSize(); i++)
{
CRecord* pRecord = m_aRecords[i];
if(pRecord->m_pProvider)
lLoaded++;
lQueries += pRecord->m_apQueries.GetSize();
long lThisProxies = 0;
long lThisFilters = 0;
long lThisTargetLists = 0;
long lThisTargets = 0;
long lThisPostponed = 0;
long lThisDestinations = 0;
pRecord->m_pMainSink->GetStatistics(&lThisProxies, &lThisDestinations,
&lThisFilters, &lThisTargetLists,
&lThisTargets, &lThisPostponed);
lProxies += lThisProxies;
lDestinations += lThisDestinations;
lFilters += lThisFilters;
lTargetLists += lThisTargetLists;
lTargets += lThisTargets;
lPostponed += lThisPostponed;
}
fprintf(f, "%d provider records, %d definition queries, %d proxies\n"
"%d destinations, %d proxy filters, %d proxy target lists\n"
"%d proxy targets, %d postponed in proxies\n",
m_aRecords.GetSize(), lQueries, lProxies, lDestinations, lFilters,
lTargetLists, lTargets, lPostponed);
}
CPostponedNewQuery::CPostponedNewQuery(CEventProviderCache::CRecord* pRecord,
DWORD dwId, LPCWSTR wszQueryLanguage, LPCWSTR wszQuery,
CExecLine::CTurn* pTurn, CAbstractEventSink* pDest)
: m_pRecord(pRecord), m_dwId(dwId), m_pTurn(pTurn), m_pcsQuery(NULL),
m_pDest(pDest)
{
m_pRecord->AddRef();
m_pDest->AddRef();
// Figure out how much space we need
// =================================
int nSpace = CCompressedString::ComputeNecessarySpace(wszQuery);
// Allocate this string on the temporary heap
// ==========================================
m_pcsQuery = (CCompressedString*)CTemporaryHeap::Alloc(nSpace);
if(m_pcsQuery == NULL)
return;
m_pcsQuery->SetFromUnicode(wszQuery);
}
CPostponedNewQuery::~CPostponedNewQuery()
{
if(m_pTurn)
m_pRecord->DiscardTurn(m_pTurn);
if(m_pcsQuery)
CTemporaryHeap::Free(m_pcsQuery, m_pcsQuery->GetLength());
if(m_pDest)
m_pDest->Release();
m_pRecord->Release();
}
HRESULT CPostponedNewQuery::Execute(CEssNamespace* pNamespace)
{
if(m_pcsQuery == NULL)
return WBEM_E_OUT_OF_MEMORY;
HRESULT hres = m_pRecord->Exec_NewQuery(pNamespace, m_pTurn, m_dwId,
L"WQL", m_pcsQuery->CreateWStringCopy(),
m_pDest);
m_pTurn = NULL;
return hres;
}
void* CPostponedNewQuery::operator new(size_t nSize)
{
return CTemporaryHeap::Alloc(nSize);
}
void CPostponedNewQuery::operator delete(void* p)
{
CTemporaryHeap::Free(p, sizeof(CPostponedNewQuery));
}