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.
 
 
 
 
 
 

4391 lines
121 KiB

//=============================================================================
//
// Copyright (c) 1996-1999, Microsoft Corporation, All rights reserved
//
// NSREP.CPP
//
// See nsrep.h for documentation
//
// History:
//
//=============================================================================
#include "precomp.h"
#include <stdio.h>
#include "ess.h"
#include "esssink.h"
#include "permbind.h"
#include "aggreg.h"
#include "persistcfg.h"
#include "WinMgmtR.h"
#include <ql.h>
#include <cominit.h>
#include <genutils.h>
#include "NCEvents.h" // For the non-COM event stuff
#include <tempbind.h>
#include <wbemutil.h>
#include <sddl.h>
#include <helper.h>
long g_lNumNamespaces = 0;
long g_lNumInternalTempSubscriptions = 0;
long g_lNumTempSubscriptions = 0;
#define ENSURE_INITIALIZED \
hres = EnsureInitPending(); \
if ( FAILED(hres) ) \
return hres; \
hres = WaitForInitialization(); \
if ( FAILED(hres) ) \
return hres; \
CInUpdate iu(this); \
if ( m_eState == e_Shutdown ) \
return WBEM_E_SHUTTING_DOWN;
// The use of this pointer to initialize parent class is valid in this context
#pragma warning(disable : 4355)
class CEnumSink : public CObjectSink
{
protected:
CEssNamespace* m_pNamespace;
HANDLE m_hEvent;
CEssThreadObject* m_pThreadObj;
public:
CEnumSink(CEssNamespace* pNamespace)
: m_pNamespace(pNamespace),
m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)),
m_pThreadObj(GetCurrentEssThreadObject())
{}
~CEnumSink(){SetEvent(m_hEvent);}
void ReleaseAndWait()
{
HANDLE h = m_hEvent;
Release();
WaitForSingleObject(h, INFINITE);
CloseHandle(h);
}
virtual HRESULT Process(IWbemClassObject* pObj) = 0;
STDMETHOD(Indicate)(long lNumObjects, IWbemClassObject** apObjects)
{
SetConstructedEssThreadObject(m_pThreadObj);
for(int i = 0; i < lNumObjects; i++)
Process(apObjects[i]);
return S_OK;
}
STDMETHOD(SetStatus)(long, HRESULT, BSTR, IWbemClassObject*)
{
return S_OK;
}
};
class CFilterEnumSink : public CEnumSink
{
public:
CFilterEnumSink(CEssNamespace* pNamespace) : CEnumSink(pNamespace){}
virtual HRESULT Process(IWbemClassObject* pObj)
{
return m_pNamespace->AddEventFilter(pObj, TRUE);
}
};
class CConsumerEnumSink : public CEnumSink
{
public:
CConsumerEnumSink(CEssNamespace* pNamespace) : CEnumSink(pNamespace){}
virtual HRESULT Process(IWbemClassObject* pObj)
{
return m_pNamespace->AddEventConsumer(pObj, 0, TRUE);
}
};
class CBindingEnumSink : public CEnumSink
{
public:
CBindingEnumSink(CEssNamespace* pNamespace) : CEnumSink(pNamespace){}
virtual HRESULT Process(IWbemClassObject* pObj)
{
return m_pNamespace->AddBinding(pObj);
}
};
class CPostponedReleaseRequest : public CPostponedRequest
{
protected:
IUnknown* m_pUnk;
public:
CPostponedReleaseRequest(IUnknown* pToRelease) : m_pUnk(pToRelease)
{
try
{
if(m_pUnk)
m_pUnk->AddRef();
}
catch(...)
{
}
}
HRESULT Execute(CEssNamespace* pNamespace)
{
try
{
if(m_pUnk)
m_pUnk->Release();
}
catch(...)
{
}
return WBEM_S_NO_ERROR;
}
~CPostponedReleaseRequest()
{
try
{
if(m_pUnk)
m_pUnk->Release();
}
catch(...)
{
}
}
};
class CPostponedRegisterNotificationSinkRequest : public CPostponedRequest
{
protected:
WString m_wsQuery;
WString m_wsQueryLanguage;
DWORD m_lFlags;
DWORD m_dwQosFlags;
CWbemPtr<IWbemObjectSink> m_pSink;
CWbemPtr<CEssNamespace> m_pNamespace;
CNtSid m_OwnerSid;
public:
HRESULT SetRegistration( CEssNamespace* pNamespace,
LPCWSTR wszQueryLanguage,
LPCWSTR wszQuery,
long lFlags,
DWORD dwQosFlags,
IWbemObjectSink* pSink,
PSID pOwnerSid )
{
m_pSink = pSink;
m_lFlags = lFlags;
m_dwQosFlags = dwQosFlags;
m_pNamespace = pNamespace;
try
{
m_wsQuery = wszQuery;
m_wsQueryLanguage = wszQueryLanguage;
m_OwnerSid = CNtSid(pOwnerSid);
}
catch( CX_MemoryException )
{
return WBEM_E_OUT_OF_MEMORY;
}
return WBEM_S_NO_ERROR;
}
HRESULT Execute( CEssNamespace* pNamespace )
{
HRESULT hr;
//
// we must set up a new thread object and then restore the
// old one where we're done. Reason for this is that we don't
// want our call into the other namespace to affect the postponed
// list of this one.
//
CEssThreadObject* pOldThreadObject = GetCurrentEssThreadObject();
SetCurrentEssThreadObject(NULL);
if ( GetCurrentEssThreadObject() != NULL )
{
{
CInUpdate iu( m_pNamespace );
if ( !m_pNamespace->IsShutdown() )
{
hr = m_pNamespace->InternalRegisterNotificationSink(
m_wsQueryLanguage,
m_wsQuery,
m_lFlags,
WMIMSG_QOS_FLAG(m_dwQosFlags),
NULL,
m_pSink,
TRUE,
m_OwnerSid.GetPtr() );
}
else
{
hr = WBEM_E_SHUTTING_DOWN;
}
}
if ( SUCCEEDED(hr) )
{
hr = m_pNamespace->FirePostponedOperations();
}
else
{
m_pNamespace->FirePostponedOperations();
}
delete GetCurrentEssThreadObject();
}
else
{
hr = WBEM_E_OUT_OF_MEMORY;
}
SetConstructedEssThreadObject( pOldThreadObject );
return hr;
}
};
class CPostponedRemoveNotificationSinkRequest : public CPostponedRequest
{
protected:
CWbemPtr<IWbemObjectSink> m_pSink;
CWbemPtr<CEssNamespace> m_pNamespace;
public:
CPostponedRemoveNotificationSinkRequest( CEssNamespace* pNamespace,
IWbemObjectSink* pSink )
: m_pSink( pSink ), m_pNamespace( pNamespace ) { }
HRESULT Execute( CEssNamespace* pNamespace )
{
HRESULT hr;
//
// we must set up a new thread object and then restore the
// old one where we're done. Reason for this is that we don't
// want our call into the other namespace to affect the postponed
// list of this one.
//
CEssThreadObject* pOldThreadObject = GetCurrentEssThreadObject();
SetCurrentEssThreadObject(NULL);
if ( GetCurrentEssThreadObject() != NULL )
{
{
CInUpdate iu( m_pNamespace );
if ( !m_pNamespace->IsShutdown() )
{
hr = m_pNamespace->InternalRemoveNotificationSink(m_pSink);
}
else
{
hr = WBEM_E_SHUTTING_DOWN;
}
}
if ( SUCCEEDED(hr) )
{
hr = m_pNamespace->FirePostponedOperations();
}
else
{
m_pNamespace->FirePostponedOperations();
}
delete GetCurrentEssThreadObject();
}
else
{
hr = WBEM_E_OUT_OF_MEMORY;
}
SetConstructedEssThreadObject( pOldThreadObject );
return hr;
}
};
class CFirePostponed : public CExecRequest
{
protected:
CEssNamespace* m_pNamespace;
CEssThreadObject* m_pThreadObj;
public:
CFirePostponed(CEssNamespace* pNamespace, CEssThreadObject* pThreadObj)
: m_pNamespace(pNamespace), m_pThreadObj(pThreadObj)
{
m_pNamespace->AddRef();
}
~CFirePostponed()
{
m_pNamespace->Release();
delete m_pThreadObj;
}
HRESULT Execute()
{
SetConstructedEssThreadObject(m_pThreadObj);
m_pNamespace->FirePostponedOperations();
ClearCurrentEssThreadObject();
return WBEM_S_NO_ERROR;
}
};
//******************************************************************************
// public
//
// See ess.h for documentation
//
//******************************************************************************
CEssNamespace::CEssNamespace(CEss* pEss) :
m_ClassDeletionSink(this), m_bInResync(FALSE),
m_Bindings(this), m_hInitComplete(INVALID_HANDLE_VALUE),
m_EventProviderCache(this), m_Poller(this),
m_ConsumerProviderCache(this), m_hresInit(WBEM_E_CRITICAL_ERROR),
m_ClassCache(this), m_eState(e_Quiet), m_pCoreEventProvider(NULL),
m_pEss(pEss), m_wszName(NULL), m_pCoreSvc(NULL), m_pFullSvc(NULL),
m_lRef(0), m_pInternalCoreSvc(NULL), m_pInternalFullSvc(NULL),
m_pProviderFactory(NULL), m_bStage1Complete(FALSE),
m_pAdminOnlySD(NULL), m_cAdminOnlySD(0)
{
PSID pRawSid;
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
g_lNumNamespaces++;
if(AllocateAndInitializeSid( &id, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0,&pRawSid))
{
m_sidAdministrators = CNtSid(pRawSid);
// We're done with this
FreeSid( pRawSid );
}
}
ULONG CEssNamespace::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
ULONG CEssNamespace::Release()
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
//
// this function is intended to be called in the same control path as
// the one which constructs the namespace object. Any initialization that
// cannot be defferred is done here.
//
HRESULT CEssNamespace::PreInitialize( LPCWSTR wszName )
{
HRESULT hres;
m_wszName = new WCHAR[wcslen(wszName)+1];
if(m_wszName == NULL)
{
hres = WBEM_E_OUT_OF_MEMORY;
return hres;
}
StringCchCopyW( m_wszName, wcslen(wszName)+1, wszName );
//
// create the event that will be used to signal any threads waiting
// for initialization to finish.
//
m_hInitComplete = CreateEvent( NULL, TRUE, FALSE, NULL );
if ( NULL == m_hInitComplete )
{
return WBEM_E_CRITICAL_ERROR;
}
//
// create admin only SD for raising core events that are AdminOnly.
//
static LPCWSTR wszAdminOnlyString = L"O:BAG:BAD:(A;;0x40;;;BA)";
if ( !ConvertStringSecurityDescriptorToSecurityDescriptorW(
wszAdminOnlyString,
SDDL_REVISION_1,
&m_pAdminOnlySD,
&m_cAdminOnlySD ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// Obtain repository only service. This is used for acessing all
// static ess objects.
//
hres = m_pEss->GetNamespacePointer( m_wszName, TRUE, &m_pCoreSvc );
if(FAILED(hres))
{
return WBEM_E_INVALID_NAMESPACE; // not there anymore!
}
hres = m_pCoreSvc->QueryInterface( IID_IWbemInternalServices,
(void**)&m_pInternalCoreSvc );
if(FAILED(hres))
{
return WBEM_E_CRITICAL_ERROR;
}
//
// Obtain full service. This is used accessing class objects
// ( which may involve accessing class providers.
//
hres = m_pEss->GetNamespacePointer( m_wszName, FALSE, &m_pFullSvc );
if(FAILED(hres))
{
return WBEM_E_INVALID_NAMESPACE; // not there anymore!
}
hres = m_pFullSvc->QueryInterface( IID_IWbemInternalServices,
(void**)&m_pInternalFullSvc );
if(FAILED(hres))
{
return WBEM_E_CRITICAL_ERROR;
}
//
// Get provider factory
//
hres = m_pEss->GetProviderFactory( m_wszName,
m_pFullSvc,
&m_pProviderFactory);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "No provider factory in %S: 0x%X\n",
m_wszName, hres));
}
//
// we want to ensure that core stays loaded between the PreInitialize()
// call and the Initialize() call. This is only ever an issue when the
// Initialize() call is defferred. Reason to ensure this is because we
// must keep core loaded when we have permanent subscriptions. If we
// haven't initialized yet, then we don't know if we have any. AddRef()
// core here and will then decrement in Initialize() to ensure this.
//
IncrementObjectCount();
//
// Namespace always starts out in the Quiet state. Caller must make
// a MarkAsInitPendingIfQuiet() call if they are going to schedule
// initialization.
//
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::EnsureInitPending()
{
{
CInCritSec ics(&m_csLevel1);
if ( m_eState != e_Quiet )
{
return WBEM_S_FALSE;
}
}
CWbemPtr<CEssNamespace> pNamespace;
return m_pEss->GetNamespaceObject( m_wszName, TRUE, &pNamespace );
}
BOOL CEssNamespace::MarkAsInitPendingIfQuiet()
{
CInCritSec ics( &m_csLevel1 );
if ( m_eState != e_Quiet )
{
return FALSE;
}
m_eState = e_InitializePending;
return TRUE;
};
HRESULT CEssNamespace::Initialize()
{
HRESULT hres;
DEBUGTRACE((LOG_ESS,"Initializing namespace %S\n", m_wszName ));
//
// need to modify level2 members. Grab namespace lock.
//
{
CInUpdate iu(this);
{
CInCritSec ics( &m_csLevel1 );
if ( m_eState == e_Shutdown )
{
return WBEM_E_SHUTTING_DOWN;
}
_DBG_ASSERT( m_eState == e_InitializePending );
}
//
// Load and process subscription objects
//
hres = PerformSubscriptionInitialization();
}
//
// execute postponed operations outside of namespace lock.
// if some of them fail to execute, it doesn't mean that the namespace
// can't be initialized. just log the error.
//
HRESULT hres2 = FirePostponedOperations();
if ( FAILED(hres2) )
{
ERRORTRACE((LOG_ESS,"Failed to execute postponed operations when "
"performing initialization in namespace %S. HR=0x%x\n",
m_wszName, hres2));
}
return hres;
}
HRESULT CEssNamespace::CompleteInitialization()
{
HRESULT hres;
DEBUGTRACE((LOG_ESS,"Completing Initialization for namespace %S\n",
m_wszName));
//
// need to modify level2 members. Grab namespace lock.
//
{
CInUpdate iu(this);
//
// stage 1 of initialization really isn't complete until we grab the
// lock to perform stage 2.
//
m_bStage1Complete = TRUE;
{
CInCritSec ics( &m_csLevel1 );
if ( m_eState == e_Shutdown )
{
return WBEM_E_SHUTTING_DOWN;
}
_DBG_ASSERT( m_eState == e_InitializePending );
}
//
// load and process all objects that deal with event providers.
//
hres = PerformProviderInitialization();
}
//
// execute postponed operations outside of namespace lock.
// if some of them fail to execute, it doesn't mean that the namespace
// can't be initialized. just log the error.
//
HRESULT hres2 = FirePostponedOperations();
if ( FAILED(hres2) )
{
ERRORTRACE((LOG_ESS,"Failed to execute postponed operations when "
"completing initialization in namespace %S. HR=0x%x\n",
m_wszName, hres2));
}
return hres;
}
void CEssNamespace::MarkAsInitialized( HRESULT hres )
{
//
// we need to grab the level1 critsec here because we're going to be
// modifying the state of the namespace and because we're going to be
// using the defferred events list.
//
CInCritSec ics( &m_csLevel1 );
if ( m_eState == e_Shutdown )
{
return;
}
_DBG_ASSERT( m_eState == e_InitializePending );
//
// transition to Initialized.
//
if ( SUCCEEDED(hres) && m_pCoreEventProvider != NULL )
{
//
// while holding level1, handle any deferred events
//
for( int i=0; i < m_aDeferredEvents.GetSize(); i++ )
{
//
// iterate through 1 by 1 because later we may propagate
// context for each event here.
//
HRESULT hr;
CEventContext Context;
hr = m_pCoreEventProvider->Fire( *m_aDeferredEvents[i], &Context );
if ( FAILED(hr) )
{
ERRORTRACE((LOG_ESS,"Could not fire deferred event in "
"namespace '%S'. HR=0x%x\n", m_wszName, hr ));
}
delete m_aDeferredEvents[i];
}
if ( m_aDeferredEvents.GetSize() > 0 )
{
DEBUGTRACE((LOG_ESS,"Fired %d deferred events after init "
"complete in namespace '%S'.\n",
m_aDeferredEvents.GetSize(),m_wszName));
}
m_aDeferredEvents.RemoveAll();
}
//
// release the ref we were holding to keep core loaded between PreInit()
// and now.
//
DecrementObjectCount();
m_eState = e_Initialized;
m_hresInit = hres;
SetEvent( m_hInitComplete );
}
HRESULT CEssNamespace::WaitForInitialization()
{
IWbemContext *pContext = GetCurrentEssContext( );
VARIANT vValue;
HRESULT hres;
do
{
if ( NULL == pContext )
{
break;
}
hres = pContext->GetValue( L"__ReentranceTestProp", 0, &vValue );
if ( WBEM_E_NOT_FOUND == hres )
{
break;
}
if ( FAILED( hres ) )
{
return hres;
}
if ( VARIANT_TRUE == V_BOOL( &vValue ) )
{
return S_OK;
}
}
while( FALSE );
//
// The level1 or level2 locks cannot be held when calling this function.
// The reason for this is because we may be we waiting on the initialize
// event.
//
CInCritSec ics(&m_csLevel1);
if ( m_eState == e_Shutdown )
{
return WBEM_E_SHUTTING_DOWN;
}
if ( m_eState == e_Initialized )
{
return m_hresInit;
}
_DBG_ASSERT( m_eState == e_InitializePending );
_DBG_ASSERT( m_hInitComplete != INVALID_HANDLE_VALUE )
//
// wait for initialization to complete.
//
LeaveCriticalSection( &m_csLevel1 );
m_pEss->TriggerDeferredInitialization();
DWORD dwRes = WaitForSingleObject( m_hInitComplete, 20*60*1000 );
EnterCriticalSection( &m_csLevel1 );
if ( dwRes != WAIT_OBJECT_0 )
{
return WBEM_E_CRITICAL_ERROR;
}
return m_hresInit;
}
BOOL CEssNamespace::DoesThreadOwnNamespaceLock()
{
return m_csLevel2.GetLockCount() != -1 &&
m_csLevel2.GetOwningThreadId() == GetCurrentThreadId();
}
void CEssNamespace::LogOp( LPCWSTR wszOp, IWbemClassObject* pObj )
{
if ( LoggingLevelEnabled(2) )
{
_DBG_ASSERT(pObj!=NULL);
BSTR bstrText;
if ( SUCCEEDED(pObj->GetObjectText( 0, &bstrText )) )
{
DEBUGTRACE((LOG_ESS,"%S in namespace %S. Object is %S\n",
wszOp, m_wszName, bstrText ));
SysFreeString( bstrText );
}
}
}
CQueueingEventSink* CEssNamespace::GetQueueingEventSink( LPCWSTR wszSinkName )
{
HRESULT hr;
//
// TODO: For now there is a 1 to 1 mapping between a sink and a consumer.
// ( consumer inherits from queueing sink ). This will not always be
// the case. Here, the sink name is really the standard path to the cons.
//
CEventConsumer* pCons;
hr = m_Bindings.FindEventConsumer( wszSinkName, &pCons );
if ( FAILED(hr) )
{
return NULL;
}
return pCons;
}
//******************************************************************************
// public
//
// See ess.h for documentation
//
//******************************************************************************
BOOL CEssNamespace::IsNeededOnStartup()
{
return m_Bindings.DoesHavePermanentConsumers();
}
void CEssNamespace::SetActive()
{
//
// Inform ESS of our newely active status so that it can make sure
// we are reloaded the next time around
//
m_pEss->SetNamespaceActive(m_wszName);
}
void CEssNamespace::SetInactive()
{
//
// Inform ESS of our newely inactive status so that it does not have to
// reload us the next time around
//
m_pEss->SetNamespaceInactive(m_wszName);
}
//
// This is a quick and dirty shutdown of the namespace that is used when the
// process is shutting down.
//
HRESULT CEssNamespace::Park()
{
{
CInUpdate iu(this);
m_Bindings.Clear( false );
}
FirePostponedOperations();
return S_OK;
}
//
// This is the slow and clean shutdown that is used when the namespace is
// purged.
//
HRESULT CEssNamespace::Shutdown()
{
{
//
// we want to wait until all update operations have completed, then
// we'll mark the namespace as shutdown.
//
CInUpdate iu(this);
//
// we will also be modifying the level1 members too so need level1
// lock.
//
CInCritSec ics(&m_csLevel1);
m_eState = e_Shutdown;
}
//
// at this point all new calls into the namespace will be rejected.
//
//
// wake up any threads waiting for Initialization.
//
SetEvent( m_hInitComplete );
InternalRemoveNotificationSink(&m_ClassDeletionSink);
m_EventProviderCache.Shutdown();
m_Bindings.Clear( false );
m_Poller.Clear();
m_ConsumerProviderCache.Clear();
FirePostponedOperations();
return WBEM_S_NO_ERROR;
}
CEssNamespace::~CEssNamespace()
{
//
// Do not call shutdown here. Shutdown() is an operation that incurrs
// postponed operations and triggering them to fire here is not usually
// expected by the caller. If the caller wants to call shutdown on their
// own then they are welcome to do so.
//
g_lNumNamespaces--;
delete [] m_wszName;
if(m_pCoreSvc)
m_pCoreSvc->Release();
if(m_pFullSvc)
m_pFullSvc->Release();
if(m_pInternalCoreSvc)
m_pInternalCoreSvc->Release();
if(m_pInternalFullSvc)
m_pInternalFullSvc->Release();
if(m_pProviderFactory)
m_pProviderFactory->Release();
if(m_pCoreEventProvider)
m_pCoreEventProvider->Release();
if ( m_hInitComplete != INVALID_HANDLE_VALUE )
CloseHandle( m_hInitComplete );
for( int i=0; i < m_aDeferredEvents.GetSize(); i++ )
delete m_aDeferredEvents[i];
if ( m_pAdminOnlySD != NULL )
LocalFree( m_pAdminOnlySD );
}
HRESULT CEssNamespace::GetNamespacePointer(
RELEASE_ME IWbemServices** ppNamespace)
{
//
// This function returns the full svc pointer for use outside this class.
// We want to ensure that we don't use the full service ptr until we've
// completed stage 1 initialization. Reason is that we don't want to
// load class providers until the second stage of initialization.
//
_DBG_ASSERT( m_bStage1Complete );
if(m_pFullSvc == NULL)
return WBEM_E_CRITICAL_ERROR;
*ppNamespace = m_pFullSvc;
(*ppNamespace)->AddRef();
return S_OK;
}
HRESULT CEssNamespace::ActOnSystemEvent(CEventRepresentation& Event,
long lFlags)
{
HRESULT hres;
// This macro will execute its parameter if updates are allowed at this time on
// this thread, and schedule it otherwise (in the case of an event provider
// calling back
#define PERFORM_IF_ALLOWED(OP) OP
// Check the type
// ==============
if(Event.IsInstanceEvent())
{
// Instance creation, deletion or modification event. Check class
// ==============================================================
if(!wbem_wcsicmp(CLASS_OF(Event), EVENT_FILTER_CLASS))
{
return PERFORM_IF_ALLOWED(ReloadEventFilter(OBJECT_OF(Event)));
}
else if(!wbem_wcsicmp(CLASS_OF(Event), BINDING_CLASS))
{
return PERFORM_IF_ALLOWED(ReloadBinding(OBJECT_OF(Event)));
}
else if(!wbem_wcsicmp(CLASS_OF(Event),
EVENT_PROVIDER_REGISTRATION_CLASS))
{
return PERFORM_IF_ALLOWED(
ReloadEventProviderRegistration(OBJECT_OF(Event)));
}
else if(!wbem_wcsicmp(CLASS_OF(Event),
CONSUMER_PROVIDER_REGISTRATION_CLASS))
{
return PERFORM_IF_ALLOWED(
ReloadConsumerProviderRegistration(OBJECT_OF(Event)));
}
else if(OBJECT_OF(Event)->InheritsFrom(PROVIDER_CLASS) == S_OK)
{
return PERFORM_IF_ALLOWED(ReloadProvider(OBJECT_OF(Event)));
}
else if(OBJECT_OF(Event)->InheritsFrom(CONSUMER_CLASS) == S_OK)
{
return PERFORM_IF_ALLOWED(ReloadEventConsumer(OBJECT_OF(Event),
lFlags));
}
else if(OBJECT_OF(Event)->InheritsFrom(TIMER_BASE_CLASS) == S_OK)
{
return PERFORM_IF_ALLOWED(ReloadTimerInstruction(OBJECT_OF(Event)));
}
else
{
return WBEM_S_FALSE;
}
}
else if(Event.type == e_EventTypeClassDeletion)
{
//
// For now --- only for deletions. Force-mode modifications are not
// properly handled at the moment.
//
return PERFORM_IF_ALLOWED(
HandleClassChange(CLASS_OF(Event), OBJECT_OF(Event)));
}
else if(Event.type == e_EventTypeClassCreation)
{
return PERFORM_IF_ALLOWED(
HandleClassCreation(CLASS_OF(Event), OBJECT_OF(Event)));
}
else if(Event.type == e_EventTypeNamespaceDeletion)
{
// Construct full namespace name (ours + child)
// =============================================
DWORD cLen = wcslen(m_wszName) + wcslen(Event.wsz2) + 2;
LPWSTR wszFullName = new WCHAR[cLen];
if(wszFullName == NULL)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<WCHAR> vdm( wszFullName );
StringCchPrintfW( wszFullName, cLen, L"%s\\%s", m_wszName, Event.wsz2);
// Get the main object to purge that namespace
// ===========================================
return m_pEss->PurgeNamespace(wszFullName);
}
else
{
// Not of interest
// ===============
return WBEM_S_FALSE;
}
}
HRESULT CEssNamespace::ValidateSystemEvent(CEventRepresentation& Event)
{
HRESULT hr;
// Check the type
// ==============
if(Event.IsInstanceEvent())
{
IWbemClassObject* pPrevObj = NULL;
IWbemClassObject* pObj = NULL;
if(Event.type == e_EventTypeInstanceCreation)
pObj = OBJECT_OF(Event);
else if(Event.type == e_EventTypeInstanceDeletion)
pPrevObj = OBJECT_OF(Event);
else if(Event.type == e_EventTypeInstanceModification)
{
pObj = OBJECT_OF(Event);
pPrevObj = OTHER_OBJECT_OF(Event);
}
// Instance creation, deletion or modification event. Check class
// ==============================================================
if(!wbem_wcsicmp(CLASS_OF(Event), EVENT_FILTER_CLASS))
{
hr = CheckEventFilter(pPrevObj, pObj);
}
else if(!wbem_wcsicmp(CLASS_OF(Event), BINDING_CLASS))
{
hr = CheckBinding(pPrevObj, pObj);
}
else if(!wbem_wcsicmp(CLASS_OF(Event),
EVENT_PROVIDER_REGISTRATION_CLASS))
{
hr = CheckEventProviderRegistration(OBJECT_OF(Event));
}
else if(OBJECT_OF(Event)->InheritsFrom(CONSUMER_CLASS) == S_OK)
{
hr = CheckEventConsumer(pPrevObj, pObj);
}
else if(OBJECT_OF(Event)->InheritsFrom(TIMER_BASE_CLASS) == S_OK)
{
hr = CheckTimerInstruction(pObj);
}
else
{
hr = WBEM_S_FALSE;
}
//
// even some of the validation routines use postponed operations.
//
FirePostponedOperations();
}
else
{
// Not of interest
// ===============
hr = WBEM_S_FALSE;
}
return hr;
}
HRESULT CEssNamespace::CheckEventConsumer(IWbemClassObject* pPrevConsumerObj,
IWbemClassObject* pConsumerObj)
{
HRESULT hres;
ENSURE_INITIALIZED
hres = CheckSecurity(pPrevConsumerObj, pConsumerObj);
return hres;
}
PSID CEssNamespace::GetSidFromObject(IWbemClassObject* pObj)
{
HRESULT hres;
VARIANT vSid;
VariantInit(&vSid);
CClearMe cm1(&vSid);
hres = pObj->Get(OWNER_SID_PROPNAME, 0, &vSid, NULL, NULL);
if(FAILED(hres) || V_VT(&vSid) != (VT_UI1 | VT_ARRAY))
{
return NULL;
}
// Construct an actual PSID from the SAFEARRAY
// ===========================================
PSID pOriginal = NULL;
hres = SafeArrayAccessData(V_ARRAY(&vSid), (void**)&pOriginal);
if(FAILED(hres))
{
return NULL;
}
CUnaccessMe uam(V_ARRAY(&vSid));
long cOriginal;
if ( FAILED(SafeArrayGetUBound( V_ARRAY(&vSid), 1, &cOriginal ) ))
{
return NULL;
}
cOriginal++; // SafeArrayGetUBound() is -1 based
//
// validate SID.
//
DWORD dwSidLength = GetLengthSid(pOriginal);
if ( dwSidLength > cOriginal || !IsValidSid(pOriginal) )
{
return NULL;
}
// Make a copy and return it
// =========================
PSID pCopy = (PSID)new BYTE[dwSidLength];
if(pCopy == NULL)
return NULL;
if(!CopySid(dwSidLength, pCopy, pOriginal))
{
delete [] (BYTE*)pCopy;
return NULL;
}
return pCopy;
}
HRESULT CEssNamespace::CheckSecurity(IWbemClassObject* pPrevObj,
IWbemClassObject* pObj)
{
HRESULT hres;
if(!IsNT())
return WBEM_S_NO_ERROR;
// Retrieve the SID of the calling user
// ====================================
hres = CoImpersonateClient();
if(FAILED(hres))
return hres;
CNtSid Sid;
hres = RetrieveSidFromCall(Sid);
CoRevertToSelf();
if(FAILED(hres))
return hres;
// If modifying an existing object, check override security
// ========================================================
if(pPrevObj)
{
hres = CheckOverwriteSecurity(pPrevObj, Sid);
if(FAILED(hres))
return hres;
}
// If creating a new version of an object, ensure Sid correctness
// ==============================================================
if(pObj)
{
hres = EnsureSessionSid(pObj, Sid);
if(FAILED(hres))
return hres;
}
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::CheckSidForPrivilege( PSID sid )
{
HRESULT hres;
//
// we must check that it will be possible to even perform access checks
// for this SID. If the SID is a domain account, then we need to see
// if this machine even has permission in the domain to enumerate the
// groups for this user ( necessary for performing access checks later
// on. ) If not, then we should fail now during subscription creation
// vs later on when performing the access checks.
//
IWbemToken* pTok;
hres = GetToken( sid, &pTok );
if ( FAILED(hres) )
{
if ( hres == HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ) )
return WBEMESS_E_AUTHZ_NOT_PRIVILEGED;
return hres;
}
pTok->Release();
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::EnsureSessionSid(IWbemClassObject* pObj, CNtSid& Sid)
{
HRESULT hres;
PSID pOldSid = GetSidFromObject(pObj);
if( pOldSid == NULL )
{
//
// No SID --- just put owner SID in there
//
hres = PutSidInObject(pObj, Sid);
if ( SUCCEEDED(hres) )
hres = CheckSidForPrivilege( Sid.GetPtr() );
return hres;
}
CVectorDeleteMe<BYTE> vdm((BYTE*)pOldSid);
//
// Check for the special case of administrators --- they get to use the
// Administrators SID instead of their own for off-line operations.
//
hres = IsCallerAdministrator();
if(FAILED(hres) && hres != WBEM_E_ACCESS_DENIED)
return hres;
BOOL bAdmin = hres == WBEM_S_NO_ERROR;
if ( EqualSid(pOldSid, Sid.GetPtr()) ||
( bAdmin && EqualSid(pOldSid, GetAdministratorsSid().GetPtr()) ) )
{
//
// the existing SID is o.k.
//
return CheckSidForPrivilege( pOldSid );
}
if ( !bAdmin )
{
CNtSid OldSid(pOldSid);
WCHAR achSid[130], achOldSid[130];
DWORD cSid = 130, cOldSid = 130;
if ( Sid.GetTextSid( achSid, &cSid ) &&
OldSid.GetTextSid( achOldSid, &cOldSid ) )
{
ERRORTRACE((LOG_ESS, "ERROR : "
"User %S has tried to put an incompatible SID "
"%S in a subscription instance. "
"Using User SID instead.\n",
achSid, achOldSid ));
}
}
//
// Invalid SID found --- replace with owner SID
//
hres = PutSidInObject( pObj, Sid );
if ( SUCCEEDED(hres) )
hres = CheckSidForPrivilege( Sid.GetPtr() );
return hres;
}
HRESULT CEssNamespace::PutSidInObject(IWbemClassObject* pObj, CNtSid& Sid)
{
HRESULT hres;
//
// Clear it first
//
VARIANT vSid;
VariantInit(&vSid);
V_VT(&vSid) = VT_NULL;
CClearMe cm1(&vSid);
hres = pObj->Put(OWNER_SID_PROPNAME, 0, &vSid, 0);
if(FAILED(hres))
return hres;
//
// Construct a safearray for it
//
V_VT(&vSid) = VT_ARRAY | VT_UI1;
SAFEARRAYBOUND sab;
sab.cElements = Sid.GetSize();
sab.lLbound = 0;
V_ARRAY(&vSid) = SafeArrayCreate(VT_UI1, 1, &sab);
if(V_ARRAY(&vSid) == NULL)
return WBEM_E_OUT_OF_MEMORY;
// Copy the SID in there
// =====================
BYTE* abSid = NULL;
hres = SafeArrayAccessData(V_ARRAY(&vSid), (void**)&abSid);
if(FAILED(hres))
return WBEM_E_OUT_OF_MEMORY;
CUnaccessMe uam(V_ARRAY(&vSid));
if(!CopySid(Sid.GetSize(), (PSID)abSid, Sid.GetPtr()))
return WBEM_E_OUT_OF_MEMORY;
// Put it into the consumer
// ========================
hres = pObj->Put(OWNER_SID_PROPNAME, 0, &vSid, 0);
return hres;
}
HRESULT CEssNamespace::CheckOverwriteSecurity(IWbemClassObject* pPrevObj,
CNtSid& ActingSid)
{
HRESULT hres;
if(!IsNT())
return WBEM_S_NO_ERROR;
// Retrieve owner SID from the old object
// ======================================
PSID pOwnerSid = GetSidFromObject(pPrevObj);
if(pOwnerSid == NULL)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<BYTE> vdm((BYTE*)pOwnerSid);
// Compare the owner sid with the acting SID. If same, allow access
// =================================================================
if(EqualSid(pOwnerSid, ActingSid.GetPtr()))
return WBEM_S_NO_ERROR;
// Not the same --- still hope that the acting SID is an Admin
// ===========================================================
hres = IsCallerAdministrator();
if(FAILED(hres))
return hres;
//
// OK --- an admin can overwrite
//
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::IsCallerAdministrator()
{
HRESULT hres;
hres = CoImpersonateClient();
if(FAILED(hres)) return hres;
OnDelete0<HRESULT(*)(void),CoRevertToSelf> RevertMe;
HANDLE hToken;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken))
return WBEM_E_FAILED;
CCloseMe ccm(hToken);
if(CNtSecurity::IsUserInGroup(hToken, GetAdministratorsSid()))
return WBEM_S_NO_ERROR;
return WBEM_E_ACCESS_DENIED;
}
HRESULT CEssNamespace::CheckEventFilter(IWbemClassObject* pOldFilterObj,
IWbemClassObject* pFilterObj)
{
HRESULT hres;
ENSURE_INITIALIZED
// Check security
// ==============
hres = CheckSecurity(pOldFilterObj, pFilterObj);
if(FAILED(hres))
return hres;
// Check everything else
// =====================
return CPermanentFilter::CheckValidity(pFilterObj);
}
HRESULT CEssNamespace::ReloadEventFilter(IWbemClassObject* pFilterObjTemplate)
{
HRESULT hres;
LogOp( L"ReloadEventFilter", pFilterObjTemplate );
ENSURE_INITIALIZED
// Start by deleting this event filter from our records, if there
// ==============================================================
hres = RemoveEventFilter(pFilterObjTemplate);
if(FAILED(hres))
return hres;
// Determine the current state of this filter in the database
// ==========================================================
IWbemClassObject* pFilterObj = NULL;
hres = GetCurrentState(pFilterObjTemplate, &pFilterObj);
if(FAILED(hres))
return hres;
if(pFilterObj == NULL)
{
// The filter has been deleted --- no further action is needed
// ===========================================================
return S_OK;
}
CReleaseMe rm1(pFilterObj);
// Now create it if necessary
// ==========================
hres = AddEventFilter(pFilterObj);
if(FAILED(hres))
return hres;
return hres;
}
//******************************************************************************
//
// Starting with the namespace locked and the filter deleted from the records,
// AddEventFilter updates the records to the state of this filter in the
// database.
//
//******************************************************************************
HRESULT CEssNamespace::AddEventFilter(IWbemClassObject* pFilterObj,
BOOL bInRestart)
{
HRESULT hres;
// Construct the new filter
// ========================
CPermanentFilter* pFilter = new CPermanentFilter(this);
if(pFilter == NULL)
return WBEM_E_OUT_OF_MEMORY;
pFilter->AddRef();
CReleaseMe rm2(pFilter);
// Initialize it
// =============
hres = pFilter->Initialize(pFilterObj);
if(FAILED(hres))
return hres;
// Add it to the table
// ===================
hres = m_Bindings.AddEventFilter(pFilter);
if(FAILED(hres))
return hres;
if(!bInRestart)
{
// Process all the bindings that this filter might have
// ====================================================
hres = AssertBindings(pFilterObj);
if(FAILED(hres))
return hres;
}
return hres;
}
//******************************************************************************
//
// Starting with the namespace locked, RemoveEventFilter updates the records
// to remove all mention of this filter. Note: this is *not* the function to be
// called in response to the database instance-deletion event, as the filter
// could have been recreated in the interim.
//
//******************************************************************************
HRESULT CEssNamespace::RemoveEventFilter(IWbemClassObject* pFilterObj)
{
HRESULT hres;
// Calculate the key for this filter
// =================================
BSTR strKey = CPermanentFilter::ComputeKeyFromObj(pFilterObj);
if(strKey == NULL)
return WBEM_E_OUT_OF_MEMORY;
CSysFreeMe sfm1(strKey);
// Remove it from the table, thus deactivating it
// ==============================================
hres = m_Bindings.RemoveEventFilter(strKey);
if(hres == WBEM_E_NOT_FOUND)
return S_FALSE;
return hres;
}
//*****************************************************************************
//
// Called in response to an instance operation event related to an event
// consumer object.
//
//*****************************************************************************
HRESULT CEssNamespace::ReloadEventConsumer(
IWbemClassObject* pConsumerObjTemplate,
long lFlags)
{
HRESULT hres;
LogOp( L"ReloadConsumer", pConsumerObjTemplate );
ENSURE_INITIALIZED
// Start by deleting this event consumer from our records, if there
// ================================================================
hres = RemoveEventConsumer(pConsumerObjTemplate);
if(FAILED(hres))
return hres;
// Determine the current state of this Consumer in the database
// ============================================================
IWbemClassObject* pConsumerObj = NULL;
hres = GetCurrentState(pConsumerObjTemplate, &pConsumerObj);
if(FAILED(hres))
return hres;
if(pConsumerObj == NULL)
{
// The Consumer has been deleted --- no further action is needed
// =============================================================
return S_OK;
}
CReleaseMe rm1(pConsumerObj);
// Now create it if necessary
// ==========================
hres = AddEventConsumer(pConsumerObjTemplate, lFlags, FALSE);
return hres;
}
//******************************************************************************
//
// Starting with the namespace locked and the consumer deleted from the records
// AddEventConsumer updates the records to the state of this consumer in the
// database.
//
//******************************************************************************
HRESULT CEssNamespace::AddEventConsumer(IWbemClassObject* pConsumerObj,
long lFlags,
BOOL bInRestart)
{
HRESULT hres;
// Construct the new Consumer
// ==========================
CPermanentConsumer* pConsumer = new CPermanentConsumer(this);
if(pConsumer == NULL)
return WBEM_E_OUT_OF_MEMORY;
pConsumer->AddRef();
CReleaseMe rm2(pConsumer);
// Initialize it
// =============
hres = pConsumer->Initialize(pConsumerObj);
if(FAILED(hres))
return hres;
//
// Validate if required
//
if(lFlags & WBEM_FLAG_STRONG_VALIDATION)
{
hres = pConsumer->Validate(pConsumerObj);
if(FAILED(hres))
{
return hres;
}
}
// Add it to the table
// ===================
hres = m_Bindings.AddEventConsumer(pConsumer);
if(FAILED(hres))
return hres;
if(!bInRestart)
{
// Process all the bindings that this consumer might have
// ======================================================
hres = AssertBindings(pConsumerObj);
if(FAILED(hres))
return hres;
}
return hres;
}
//******************************************************************************
//
// Starting with the namespace locked, RemoveEventConsumer updates the records
// to remove all mention of this consumer.
//
//******************************************************************************
HRESULT CEssNamespace::RemoveEventConsumer(IWbemClassObject* pConsumerObj)
{
HRESULT hres;
// Calculate the key for this filter
// =================================
BSTR strKey = CPermanentConsumer::ComputeKeyFromObj(this, pConsumerObj);
if(strKey == NULL)
return WBEM_E_OUT_OF_MEMORY;
CSysFreeMe sfm1(strKey);
// Remove it from the table
// ========================
hres = m_Bindings.RemoveEventConsumer(strKey);
if(hres == WBEM_E_NOT_FOUND)
return S_FALSE;
return hres;
}
HRESULT CEssNamespace::CheckBinding(IWbemClassObject* pPrevBindingObj,
IWbemClassObject* pBindingObj)
{
HRESULT hres;
ENSURE_INITIALIZED
//
// Check security
//
hres = CheckSecurity(pPrevBindingObj, pBindingObj);
if(FAILED(hres))
return hres;
//
// Construct a fake binding to test correctness
//
CPermanentBinding* pBinding = new CPermanentBinding;
if(pBinding == NULL)
return WBEM_E_OUT_OF_MEMORY;
pBinding->AddRef();
CTemplateReleaseMe<CPermanentBinding> trm(pBinding);
hres = pBinding->Initialize(pBindingObj);
if(FAILED(hres))
return hres;
return S_OK;
}
//******************************************************************************
//
// Called in response to an instance operation event related to a binding
// instance.
//
//******************************************************************************
HRESULT CEssNamespace::ReloadBinding(IWbemClassObject* pBindingObjTemplate)
{
HRESULT hres;
LogOp( L"ReloadBinding", pBindingObjTemplate );
ENSURE_INITIALIZED
// Retrieve consumer and provider keys from the binding
// ====================================================
BSTR strPrelimConsumerKey = NULL;
BSTR strFilterKey = NULL;
hres = CPermanentBinding::ComputeKeysFromObject(pBindingObjTemplate,
&strPrelimConsumerKey, &strFilterKey);
if(FAILED(hres))
return hres;
CSysFreeMe sfm1(strPrelimConsumerKey);
CSysFreeMe sfm2(strFilterKey);
// Get real paths from these possibly abbreviated ones
// ===================================================
BSTR strConsumerKey = NULL;
hres = m_pInternalCoreSvc->GetNormalizedPath( strPrelimConsumerKey,
&strConsumerKey);
if(FAILED(hres))
return hres;
CSysFreeMe sfm3(strConsumerKey);
// Start by deleting this binding from our records, if there
// =========================================================
hres = RemoveBinding(strFilterKey, strConsumerKey);
if(FAILED(hres) && hres != WBEM_E_NOT_FOUND)
return hres;
// Determine the current state of this binding in the database
// ============================================================
IWbemClassObject* pBindingObj = NULL;
hres = GetCurrentState(pBindingObjTemplate, &pBindingObj);
if(FAILED(hres))
return hres;
if(pBindingObj == NULL)
{
// The Binding has been deleted --- no further action is needed
// =============================================================
return S_OK;
}
CReleaseMe rm1(pBindingObj);
// Now create it if necessary
// ==========================
hres = AddBinding(strFilterKey, strConsumerKey, pBindingObjTemplate);
return hres;
}
HRESULT CEssNamespace::AddBinding(IWbemClassObject* pBindingObj)
{
HRESULT hres;
// Retrieve consumer and provider keys from the binding
// ====================================================
BSTR strPrelimConsumerKey = NULL;
BSTR strFilterKey = NULL;
hres = CPermanentBinding::ComputeKeysFromObject(pBindingObj,
&strPrelimConsumerKey, &strFilterKey);
if(FAILED(hres))
return hres;
CSysFreeMe sfm1(strPrelimConsumerKey);
CSysFreeMe sfm2(strFilterKey);
// Get real paths from these possibly abbreviated ones
// ===================================================
BSTR strConsumerKey = NULL;
hres = m_pInternalCoreSvc->GetNormalizedPath( strPrelimConsumerKey,
&strConsumerKey );
if(FAILED(hres))
return hres;
CSysFreeMe sfm3(strConsumerKey);
return AddBinding(strFilterKey, strConsumerKey, pBindingObj);
}
HRESULT CEssNamespace::AddBinding(LPCWSTR wszFilterKey, LPCWSTR wszConsumerKey,
IWbemClassObject* pBindingObj)
{
HRESULT hres;
// Create a new binding
// ====================
CPermanentBinding* pBinding = new CPermanentBinding;
if(pBinding == NULL)
return WBEM_E_OUT_OF_MEMORY;
pBinding->AddRef();
CReleaseMe rm1(pBinding);
// Initialize it with the information we have
// ==========================================
hres = pBinding->Initialize(pBindingObj);
if(FAILED(hres))
return hres;
// Extract its creator's SID
// ========================
PSID pSid = CPermanentBinding::GetSidFromObject(pBindingObj);
if ( pSid == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
hres = m_Bindings.Bind( wszFilterKey, wszConsumerKey, pBinding, pSid );
delete [] pSid;
return hres;
}
HRESULT CEssNamespace::RemoveBinding(LPCWSTR wszFilterKey,
LPCWSTR wszConsumerKey)
{
HRESULT hres;
hres = m_Bindings.Unbind(wszFilterKey, wszConsumerKey);
if(hres == WBEM_E_NOT_FOUND)
return S_FALSE;
return hres;
}
//******************************************************************************
//
// Reads all the bindings referencing a given objects from the database and
// asserts them.
//
//******************************************************************************
class CAssertBindingsSink : public CObjectSink
{
protected:
CEssNamespace* m_pNamespace;
public:
CAssertBindingsSink(CEssNamespace* pNamespace) : m_pNamespace(pNamespace)
{
AddRef();
}
STDMETHOD(Indicate)(long lNumObjects, IWbemClassObject** apObjects)
{
for(long i = 0; i < lNumObjects; i++)
{
m_pNamespace->AddBinding(apObjects[i]);
}
return S_OK;
}
};
HRESULT CEssNamespace::AssertBindings(IWbemClassObject* pEndpoint)
{
// Get the relative path of the endpoint
// =====================================
VARIANT vRelPath;
VariantInit(&vRelPath);
CClearMe cm1(&vRelPath);
HRESULT hres = pEndpoint->Get(L"__RELPATH", 0, &vRelPath, NULL, NULL);
if(FAILED(hres))
return hres;
if(V_VT(&vRelPath) != VT_BSTR)
return WBEM_E_INVALID_OBJECT;
BSTR strRelPath = V_BSTR(&vRelPath);
// Issue the query
// ===============
DWORD cLen = 200 + wcslen(strRelPath);
BSTR strQuery = SysAllocStringLen(NULL, cLen);
if(strQuery == NULL)
return WBEM_E_OUT_OF_MEMORY;
CSysFreeMe sfm1(strQuery);
StringCchPrintfW( strQuery, cLen, L"references of {%s} where "
L"ResultClass = __FilterToConsumerBinding", strRelPath);
CAssertBindingsSink Sink(this);
hres = ExecQuery(strQuery, 0, &Sink);
return hres;
}
HRESULT CEssNamespace::ReloadTimerInstruction(
IWbemClassObject* pInstObjTemplate)
{
HRESULT hres;
LogOp( L"ReloadTimerInstruction", pInstObjTemplate );
ENSURE_INITIALIZED
hres = RemoveTimerInstruction(pInstObjTemplate);
if(FAILED(hres))
return hres;
// Get the current version from the namespace
// ==========================================
IWbemClassObject* pInstObj = NULL;
hres = GetCurrentState(pInstObjTemplate, &pInstObj);
if(FAILED(hres))
return hres;
if(pInstObj == NULL)
{
// The instruction has been deleted --- no further action is needed
// ================================================================
return S_OK;
}
CReleaseMe rm1(pInstObj);
// Add it to the generator
// =======================
hres = AddTimerInstruction(pInstObj);
if(FAILED(hres))
return hres;
return hres;
}
HRESULT CEssNamespace::AddTimerInstruction(IWbemClassObject* pInstObj)
{
return m_pEss->GetTimerGenerator().
LoadTimerEventObject(m_wszName, pInstObj);
}
//******************************************************************************
// public
//
// See ess.h for documentation
//
//******************************************************************************
HRESULT CEssNamespace::RemoveTimerInstruction(IWbemClassObject* pOldObject)
{
HRESULT hres;
VARIANT vID;
VariantInit(&vID);
hres = pOldObject->Get(TIMER_ID_PROPNAME, 0, &vID, NULL, NULL);
if(FAILED(hres)) return hres;
if ( V_VT(&vID) != VT_BSTR )
{
VariantClear(&vID);
return WBEM_E_INVALID_OBJECT;
}
m_pEss->GetTimerGenerator().Remove(m_wszName, V_BSTR(&vID));
VariantClear(&vID);
return S_OK;
}
//******************************************************************************
// public
//
// See ess.h for documentation
//
//******************************************************************************
HRESULT CEssNamespace::SignalEvent( READ_ONLY CEventRepresentation& Event,
long lFlags,
BOOL bAdminOnly )
{
HRESULT hres;
//
// we cannot hold any turns in an exec line or hold the namespace lock
// when calling this function. This is because this function will
// aquire the proxy lock.
//
CPostponedList* pList;
_DBG_ASSERT( !DoesThreadOwnNamespaceLock() );
_DBG_ASSERT( !(pList=GetCurrentPostponedList()) ||
!pList->IsHoldingTurns() );
// BUGBUG: need to propagate security context to this function ?
CWbemPtr<CCoreEventProvider> pCoreEventProvider;
{
//
// we need to figure out if we need to deffer the event or signal it.
// we deffer events when we are in the init pending or init state.
//
CInCritSec ics( &m_csLevel1 );
if ( m_eState == e_Initialized )
{
pCoreEventProvider = m_pCoreEventProvider;
}
else if ( m_eState == e_InitializePending )
{
//
// Copy and add to defferred list.
//
CEventRepresentation* pEvRep = Event.MakePermanentCopy();
if ( pEvRep == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
if ( m_aDeferredEvents.Add( pEvRep ) < 0 )
{
delete pEvRep;
return WBEM_E_OUT_OF_MEMORY;
}
}
}
if ( pCoreEventProvider != NULL )
{
CEventContext Context;
if ( bAdminOnly )
Context.SetSD( m_cAdminOnlySD, PBYTE(m_pAdminOnlySD), FALSE );
hres = pCoreEventProvider->Fire( Event, &Context );
if(FAILED(hres))
{
return hres;
}
}
return WBEM_S_NO_ERROR;
}
//******************************************************************************
// public
//
// See ess.h for documentation
//
//******************************************************************************
HRESULT CEssNamespace::ProcessEvent(READ_ONLY CEventRepresentation& Event,
long lFlags)
{
// Ignore internal operations
// ==========================
if(Event.wsz2 != NULL &&
(!wbem_wcsicmp(Event.wsz2, L"__TimerNextFiring") ||
!wbem_wcsicmp(Event.wsz2, L"__ListOfEventActiveNamespaces")))
{
return WBEM_S_NO_ERROR;
}
HRESULT hres, hresReturn = WBEM_S_NO_ERROR;
// Analyze it for system changes
// =============================
hres = ActOnSystemEvent(Event, lFlags);
if(FAILED(hres))
{
//
// Check if this operation needs to be failed if invalid
//
if( lFlags & WBEM_FLAG_STRONG_VALIDATION )
{
hresReturn = hres;
}
else
{
ERRORTRACE((LOG_ESS, "Event subsystem was unable to perform the "
"necessary operations to accomodate a change to the system "
"state.\nThe state of the database may not reflect the state "
"of the event subsystem (%X)\n", hres));
}
}
// Fire postponed operations
// =========================
hres = FirePostponedOperations();
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,"Event subsystem was unable to perform the (post) "
"necessary operations to accomodate a change to the system state.\n"
"The state of the database may not reflect the state of the event "
"subsystem (%X)\n", hres));
}
// Deliver it to consumers
// =======================
hres = SignalEvent( Event, lFlags );
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Event subsystem was unable to deliver a "
"repository intrinsic event to some consumers (%X)\n", hres));
}
return hresReturn;
}
HRESULT CEssNamespace::ProcessQueryObjectSinkEvent( READ_ONLY CEventRepresentation& Event )
{
HRESULT hres;
hres = EnsureInitPending();
if ( FAILED(hres) )
return hres;
hres = WaitForInitialization();
if ( FAILED(hres) )
return hres;
if ( m_eState == e_Shutdown )
return WBEM_E_SHUTTING_DOWN;
CRefedPointerArray< CEventFilter > apEventFilters;
hres = S_FALSE;
if ( m_Bindings.GetEventFilters( apEventFilters ) )
{
if ( apEventFilters.GetSize( ) > 0 )
{
//
// Convert to real event
//
IWbemClassObject* pEvent;
HRESULT hr = Event.MakeWbemObject( this, &pEvent );
if( FAILED( hr ) )
{
return hr;
}
CReleaseMe rm1( pEvent );
ULONG cEventSD;
PBYTE pEventSD = (PBYTE)GetSD( pEvent, &cEventSD );
CEventContext Context;
if ( pEventSD != NULL )
{
if ( FALSE == Context.SetSD( cEventSD, pEventSD, TRUE ) )
{
return WBEM_E_OUT_OF_MEMORY;
}
if ( !IsValidSecurityDescriptor(
(PSECURITY_DESCRIPTOR)Context.GetSD() ) )
{
return WBEM_E_INVALID_OBJECT;
}
}
//
// Fire all matching filters
//
for( int i = 0; i < apEventFilters.GetSize( ); ++i )
{
CEventFilter* pEventFilter = apEventFilters[i];
hr = pEventFilter->Indicate( 1, &pEvent, &Context );
if ( FAILED( hr ) )
{
return hr;
}
//
// Return S_FALSE if all of the Indicates returns S_FALSE
//
if ( S_FALSE != hr )
{
hres = S_OK;
}
}
}
}
else
{
return E_FAIL;
}
return hres;
}
HRESULT CEssNamespace::RegisterNotificationSink( WBEM_CWSTR wszQueryLanguage,
WBEM_CWSTR wszQuery,
long lFlags,
WMIMSG_QOS_FLAG lQosFlags,
IWbemContext* pContext,
IWbemObjectSink* pSink )
{
HRESULT hres;
//
// Report the MSFT_WmiRegisterNotificationSink event.
//
FIRE_NCEVENT( g_hNCEvents[MSFT_WmiRegisterNotificationSink],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
(LPCWSTR) m_wszName,
wszQueryLanguage,
wszQuery,
(DWORD64) pSink);
DEBUGTRACE((LOG_ESS,"Registering notification sink with query %S in "
"namespace %S.\n", wszQuery, m_wszName ));
{
ENSURE_INITIALIZED
hres = InternalRegisterNotificationSink( wszQueryLanguage,
wszQuery,
lFlags,
lQosFlags,
pContext,
pSink,
FALSE,
NULL );
}
if(FAILED(hres))
{
// Clean up and return
FirePostponedOperations();
return hres;
}
// Filter and consumer are in place --- fire external operations
// =============================================================
hres = FirePostponedOperations();
if(FAILED(hres))
{
{
CInUpdate iu(this);
if ( m_eState == e_Shutdown )
return WBEM_E_SHUTTING_DOWN;
InternalRemoveNotificationSink( pSink );
}
//
// need to make sure that we fire postponed here too. Remember that
// we cannot hold the namespace lock when firing postponed ops.
//
FirePostponedOperations();
}
else
{
InterlockedIncrement(&g_lNumTempSubscriptions);
}
return hres;
}
HRESULT CEssNamespace::InternalRegisterNotificationSink(
WBEM_CWSTR wszQueryLanguage,
WBEM_CWSTR wszQuery,
long lFlags,
WMIMSG_QOS_FLAG lQosFlags,
IWbemContext* pContext,
IWbemObjectSink* pSink,
bool bInternal,
PSID pOwnerSid )
{
HRESULT hres;
if(wbem_wcsicmp(wszQueryLanguage, L"WQL"))
return WBEM_E_INVALID_QUERY_TYPE;
LPWSTR wszConsumerKey = NULL;
CVectorDeleteMe<WCHAR> vdm2(&wszConsumerKey);
wszConsumerKey = CTempConsumer::ComputeKeyFromSink(pSink);
if ( NULL == wszConsumerKey )
{
return WBEM_E_OUT_OF_MEMORY;
}
bool bInterNamespace = pOwnerSid != NULL;
LPWSTR wszFilterKey = NULL;
CVectorDeleteMe<WCHAR> vdm1(&wszFilterKey);
{
// Create a new temporary filter and add it to the binding table
// =============================================================
CTempFilter* pFilter = new CTempFilter(this);
if(pFilter == NULL)
return WBEM_E_OUT_OF_MEMORY;
hres = pFilter->Initialize( wszQueryLanguage,
wszQuery,
lFlags,
pOwnerSid,
bInternal,
pContext,
pSink );
if(FAILED(hres))
{
delete pFilter;
return hres;
}
hres = m_Bindings.AddEventFilter(pFilter);
if(FAILED(hres))
{
delete pFilter;
return hres;
}
wszFilterKey = pFilter->GetKey().CreateLPWSTRCopy();
if(wszFilterKey == NULL)
return WBEM_E_OUT_OF_MEMORY;
// Check if this sink has already been used by looking for it in the
// binding table
// =================================================================
CTempConsumer* pConsumer = NULL;
if(FAILED(m_Bindings.FindEventConsumer(wszConsumerKey, NULL)))
{
// Create a new temporary consumer and add it to the table
// =======================================================
pConsumer = _new CTempConsumer(this);
if(pConsumer == NULL)
return WBEM_E_OUT_OF_MEMORY;
hres = pConsumer->Initialize( bInterNamespace, pSink);
if(FAILED(hres))
return hres;
hres = m_Bindings.AddEventConsumer(pConsumer);
if(FAILED(hres))
{
// Undo filter creation
// ====================
m_Bindings.RemoveEventFilter(wszFilterKey);
return hres;
}
}
// Bind them together
// ==================
CBinding* pBinding = new CTempBinding( lFlags,
lQosFlags,
bInterNamespace );
if(pBinding == NULL)
return WBEM_E_OUT_OF_MEMORY;
pBinding->AddRef();
CReleaseMe rm1(pBinding);
//
// SPAGETTI WARNING: From this point on, we must flush the postponed
// operation cache, or we may leak memory. But not before all the
// CReleaseMe calls have fired.
//
hres = m_Bindings.Bind(wszFilterKey, wszConsumerKey, pBinding, NULL);
// Check that the filter is active --- otherwise activatioin must have
// failed.
// ===================================================================
if(SUCCEEDED(hres) && !pFilter->IsActive())
hres = pFilter->GetFilterError();
if(FAILED(hres))
{
//
// The core will deliver the SetStatus call to the consumer based
// on the return code from the ESS. Since we are failing, we should
// not call SetStatus ourselves.
//
if(pConsumer)
pConsumer->Shutdown(true); // quiet
m_Bindings.RemoveEventFilter(wszFilterKey);
m_Bindings.RemoveEventConsumer(wszConsumerKey);
}
else
{
InterlockedIncrement(&g_lNumInternalTempSubscriptions);
}
}
return hres;
}
HRESULT CEssNamespace::RemoveNotificationSink( IWbemObjectSink* pSink )
{
// Fire a MSFT_WmiCancelNotificationSink if necessary.
if (IS_NCEVENT_ACTIVE(MSFT_WmiCancelNotificationSink))
{
LPWSTR wszConsumerKey = CTempConsumer::ComputeKeyFromSink(pSink);
if (wszConsumerKey != NULL)
{
CVectorDeleteMe<WCHAR> vdm0(wszConsumerKey);
CInUpdate iu(this);
// Find the consumer in question
CEventConsumer *pConsumer = NULL;
if (SUCCEEDED(m_Bindings.FindEventConsumer(wszConsumerKey, &pConsumer)))
{
CRefedPointerSmallArray<CEventFilter>
apFilters;
CReleaseMe rm1(pConsumer);
// Make addrefed copies of all its associated filters
if (SUCCEEDED(pConsumer->GetAssociatedFilters(apFilters))
&& apFilters.GetSize())
{
int nFilters = apFilters.GetSize();
LPWSTR wszQuery = NULL,
wszQueryLanguage = NULL;
BOOL bExact;
apFilters[0]->
GetCoveringQuery(wszQueryLanguage, wszQuery, bExact, NULL);
CVectorDeleteMe<WCHAR> vdm1(wszQueryLanguage);
CVectorDeleteMe<WCHAR> vdm2(wszQuery);
//
// Report the MSFT_WmiRegisterNotificationSink event.
//
FIRE_NCEVENT(
g_hNCEvents[MSFT_WmiCancelNotificationSink],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
(LPCWSTR) m_wszName,
wszQueryLanguage,
wszQuery,
(DWORD64) pSink);
}
}
}
}
HRESULT hres;
{
CInUpdate iu( this );
if ( m_eState == e_Shutdown )
{
return WBEM_E_SHUTTING_DOWN;
}
hres = InternalRemoveNotificationSink( pSink );
}
FirePostponedOperations();
if ( SUCCEEDED(hres) )
{
InterlockedDecrement( &g_lNumTempSubscriptions );
}
return hres;
}
HRESULT CEssNamespace::InternalRemoveNotificationSink(IWbemObjectSink* pSink)
{
HRESULT hres;
LPWSTR wszKey = CTempConsumer::ComputeKeyFromSink(pSink);
if(wszKey == NULL)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<WCHAR> vdm1(wszKey);
// Find the consumer container
// ===========================
hres = m_Bindings.RemoveConsumerWithFilters(wszKey);
if(FAILED(hres))
return hres;
else
InterlockedDecrement( &g_lNumInternalTempSubscriptions );
return hres;
}
void CEssNamespace::FireNCFilterEvent(DWORD dwIndex, CEventFilter *pFilter)
{
if (IS_NCEVENT_ACTIVE(dwIndex))
{
LPWSTR wszQuery = NULL;
LPWSTR wszQueryLanguage = NULL;
BOOL bExact;
CWbemPtr<CEssNamespace> pNamespace;
GetFilterEventNamespace(pFilter, &pNamespace);
// I'll assume we should use the current namespace if it's null.
if (!pNamespace)
pNamespace = this;
pFilter->GetCoveringQuery(wszQueryLanguage, wszQuery, bExact, NULL);
CVectorDeleteMe<WCHAR> vdm1(wszQueryLanguage);
CVectorDeleteMe<WCHAR> vdm2(wszQuery);
//
// Report the event.
//
FIRE_NCEVENT(
g_hNCEvents[dwIndex],
WMI_SENDCOMMIT_SET_NOT_REQUIRED,
// Data follows...
pNamespace ? (LPCWSTR) pNamespace->GetName() : NULL,
(LPCWSTR) (WString) pFilter->GetKey(),
wszQueryLanguage,
wszQuery);
}
}
//*****************************************************************************
//
// Called by the filter when it notices that it has consumers. The filter is
// guaranteed to be either valid or temporarily invalid and not active. It is
// guaranteed that no more than 1 activation/deactivation can occur on the
// same filter at the same time.
//
//*****************************************************************************
HRESULT CEssNamespace::ActivateFilter(READ_ONLY CEventFilter* pFilter)
{
HRESULT hres, hresAttempt;
hresAttempt = AttemptToActivateFilter(pFilter);
if(FAILED(hresAttempt))
{
pFilter->MarkAsTemporarilyInvalid(hresAttempt);
//
// We need to log an event about our inability to activate the filter
// unless we shall report this failure to the caller. We can only
// report this to the caller if the filter is being created (not
// reactivated), and the caller is not using a force-mode
//
if(pFilter->DoesAllowInvalid() || pFilter->HasBeenValid())
{
LPWSTR wszQuery = NULL;
LPWSTR wszQueryLanguage = NULL;
BOOL bExact;
hres = pFilter->GetCoveringQuery( wszQueryLanguage,
wszQuery,
bExact,
NULL);
if(FAILED(hres))
return hres;
CVectorDeleteMe<WCHAR> vdm1(wszQueryLanguage);
CVectorDeleteMe<WCHAR> vdm2(wszQuery);
//
// Don't change this one: could be Nova customer dependencies
//
m_pEss->GetEventLog().Report( EVENTLOG_ERROR_TYPE,
WBEM_MC_CANNOT_ACTIVATE_FILTER,
m_wszName,
wszQuery,
(CHex)hresAttempt );
ERRORTRACE((LOG_ESS, "Could not activate filter %S in namespace "
"%S. HR=0x%x\n", wszQuery, m_wszName, hresAttempt ));
}
}
else
{
//
// Report the MSFT_WmiFilterActivated event.
//
FireNCFilterEvent(MSFT_WmiFilterActivated, pFilter);
pFilter->MarkAsValid();
}
return hresAttempt;
}
//******************************************************************************
//
// Worker for ActivateFilter --- does all the work but does not mark the filter
// status
//
//******************************************************************************
HRESULT CEssNamespace::AttemptToActivateFilter(READ_ONLY CEventFilter* pFilter)
{
HRESULT hres = WBEM_S_NO_ERROR;
//
// Get the query information from the filter
//
LPWSTR wszQueryLanguage = NULL;
LPWSTR wszQuery = NULL;
BOOL bExact;
QL_LEVEL_1_RPN_EXPRESSION* pExp = NULL;
hres = pFilter->GetCoveringQuery(wszQueryLanguage, wszQuery, bExact, &pExp);
if(FAILED(hres))
{
WMIESS_REPORT((WMIESS_CANNOT_GET_FILTER_QUERY, m_wszName, pFilter));
return hres;
}
CVectorDeleteMe<WCHAR> vdm1(wszQueryLanguage);
CVectorDeleteMe<WCHAR> vdm2(wszQuery);
CDeleteMe<QL_LEVEL_1_RPN_EXPRESSION> dm1(pExp);
if(!bExact)
{
//
// We don't support inexact filter, nor do we have any now
//
return WBEM_E_NOT_SUPPORTED;
}
//
// Check if the events are supposed to come from this namespace or some
// other one. Cross-namespace filters are all we are interested in the
// initialize phase, since we're going to reprocess normal filters
// after loading provider registrations ( In CompleteInitialization() )
//
CEssNamespace* pOtherNamespace = NULL;
hres = GetFilterEventNamespace(pFilter, &pOtherNamespace);
if(FAILED(hres))
return hres;
if( pOtherNamespace )
{
CTemplateReleaseMe<CEssNamespace> rm0(pOtherNamespace);
if ( m_bInResync )
{
//
// we don't need to do anything in the other namespace during
// resync of this one, so no work to do here. Actually, since
// resync doesn't do a deactivate, the registration is still there
// so be careful of double registration if removing this check.
//
return WBEM_S_FALSE;
}
DEBUGTRACE((LOG_ESS,"Activating cross-namespace filter %p with query "
"%S in namespace %S from namespace %S.\n", pFilter,
wszQuery, pOtherNamespace->GetName(), m_wszName ));
//
// Register this notification sink with the other namespace, as
// if it were a temporary consumer. Make the registration
// synchronous, as whatever asynchronicity we need will be
// provided by the ultimate consumer handling. This needs to be a
// postponed operation though, else we could have a deadlock
// scenario if at the same time cross namespace subscriptions
// were registered in both namespaces.
//
//
// BUGBUG: security propagation
//
CPostponedRegisterNotificationSinkRequest* pReq;
pReq = new CPostponedRegisterNotificationSinkRequest;
if ( pReq == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
hres = pReq->SetRegistration( pOtherNamespace,
wszQueryLanguage,
wszQuery,
pFilter->GetForceFlags(),
WMIMSG_FLAG_QOS_SYNCHRONOUS,
pFilter->GetNonFilteringSink(),
pFilter->GetOwner() );
if(FAILED(hres))
{
return hres;
}
CPostponedList* pList = GetCurrentPostponedList();
_DBG_ASSERT( pList != NULL );
hres = pList->AddRequest( this, pReq );
if ( FAILED(hres) )
{
return hres;
}
return WBEM_S_NO_ERROR;
}
else if ( m_bStage1Complete )
{
//
// Filter is being activated in this namespace. We must avoid
// processing filters before we're fully initialized. This can
// happen when one namespace is initializing its cross namespace
// subscription to one that is still initializing. We do not process
// filters before we're initialized because (1) we are not allowed to
// access class providers during stage1 init and (2) we're going to
// resync everything anyways during stage2 init.
//
DEBUGTRACE((LOG_ESS,"Activating filter %p with query %S "
"in namespace %S.\n", pFilter, wszQuery, m_wszName ));
// Retrieve its non-filtering sink
// ===============================
CAbstractEventSink* pNonFilter = pFilter->GetNonFilteringSink();
if(pNonFilter == NULL)
return WBEM_E_OUT_OF_MEMORY;
//
// Register for class modification events of relevance for this filter
//
hres = RegisterFilterForAllClassChanges(pFilter, pExp);
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS,"Unable to register for class changes related "
"to filter %S in namespace %S: 0x%x\n", wszQuery, GetName(),
hres));
return hres;
}
//
// Prepare filter for action
//
hres = pFilter->GetReady(wszQuery, pExp);
if( SUCCEEDED(hres) )
{
//
// Register it in the core tables
//
hres = m_EventProviderCache.LoadProvidersForQuery(wszQuery,
pExp, pNonFilter);
if(SUCCEEDED(hres))
{
hres = m_Poller.ActivateFilter(pFilter, wszQuery, pExp);
if(FAILED(hres))
{
// Need to deactivate providers
// ============================
m_EventProviderCache.ReleaseProvidersForQuery(
pNonFilter);
}
}
}
}
if(FAILED(hres))
{
//
// Keep this filter registered for its class change events, as one of
// them could make it valid!
//
}
return hres;
}
//*****************************************************************************
//
// Retrieves the namespace pointer for the event namespace for this filter.
// If current, returns NULL.
//
//*****************************************************************************
HRESULT CEssNamespace::GetFilterEventNamespace(CEventFilter* pFilter,
RELEASE_ME CEssNamespace** ppNamespace)
{
HRESULT hres;
*ppNamespace = NULL;
LPWSTR wszNamespace = NULL;
hres = pFilter->GetEventNamespace(&wszNamespace);
if(FAILED(hres))
{
WMIESS_REPORT((WMIESS_INVALID_FILTER_NAMESPACE, m_wszName, pFilter,
wszNamespace));
return hres;
}
CVectorDeleteMe<WCHAR> vdm0(wszNamespace);
if(wszNamespace && wbem_wcsicmp(wszNamespace, m_wszName))
{
//
// Different namespace: Find it in the list.
//
hres = m_pEss->GetNamespaceObject( wszNamespace, TRUE, ppNamespace );
if(FAILED(hres))
{
WMIESS_REPORT((WMIESS_CANNOT_OPEN_FILTER_NAMESPACE, m_wszName,
pFilter, wszNamespace));
return hres;
}
//
// Check if we got back our current namespace --- could happen if the
// spelling is different, etc
//
if(*ppNamespace == this)
{
(*ppNamespace)->Release();
*ppNamespace = NULL;
}
return S_OK;
}
else
{
// Same namespace
*ppNamespace = NULL;
return S_OK;
}
}
HRESULT CEssNamespace::RegisterFilterForAllClassChanges(CEventFilter* pFilter,
QL_LEVEL_1_RPN_EXPRESSION* pExpr)
{
HRESULT hres;
//
// Do nothing for class operation filters. They simply serve as their own
// "class change" filters
//
if(!wbem_wcsicmp(pExpr->bsClassName, L"__ClassOperationEvent") ||
!wbem_wcsicmp(pExpr->bsClassName, L"__ClassCreationEvent") ||
!wbem_wcsicmp(pExpr->bsClassName, L"__ClassDeletionEvent") ||
!wbem_wcsicmp(pExpr->bsClassName, L"__ClassModificationEvent"))
{
pFilter->MarkReconstructOnHit();
return WBEM_S_NO_ERROR;
}
//
// get the sink for class change notifications.
//
IWbemObjectSink* pClassChangeSink = pFilter->GetClassChangeSink(); // NOREF
_DBG_ASSERT( pClassChangeSink != NULL );
//
// since the class change sink will be modifying internal namespace
// structures, we must wrap with an internal operations sink. This is so
// the thread that performs the indicate will be guaranteed to have a
// valid thread object associated with it.
//
CWbemPtr<CEssInternalOperationSink> pInternalOpSink;
pInternalOpSink = new CEssInternalOperationSink( pClassChangeSink );
if ( pInternalOpSink == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
//
// store the new sink with the filter because we need it later to unreg
//
CWbemPtr<IWbemObjectSink> pOldInternalOpSink;
hres = pFilter->SetActualClassChangeSink( pInternalOpSink,
&pOldInternalOpSink );
if ( FAILED(hres) )
{
return hres;
}
_DBG_ASSERT( pOldInternalOpSink == NULL );
return RegisterSinkForAllClassChanges( pInternalOpSink, pExpr );
}
HRESULT CEssNamespace::RegisterSinkForAllClassChanges(IWbemObjectSink* pSink,
QL_LEVEL_1_RPN_EXPRESSION* pExpr)
{
HRESULT hres;
//
// First of all, the class we are looking for is of interest
//
hres = RegisterSinkForClassChanges(pSink, pExpr->bsClassName);
if(FAILED(hres))
return hres;
//
// Now, iterate over all the tokens looking for ISAs. We need those classes
// too.
//
for(int i = 0; i < pExpr->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN* pToken = pExpr->pArrayOfTokens + i;
if(pToken->nTokenType == QL1_OP_EXPRESSION &&
(pToken->nOperator == QL1_OPERATOR_ISA ||
pToken->nOperator == QL1_OPERATOR_ISNOTA) &&
V_VT(&pToken->vConstValue) == VT_BSTR)
{
hres = RegisterSinkForClassChanges(pSink,
V_BSTR(&pToken->vConstValue));
if(FAILED(hres))
{
UnregisterSinkFromAllClassChanges(pSink);
return hres;
}
}
}
// Somehow need to keep this filter subscribed to various events until all
// the classes show up
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::RegisterSinkForClassChanges(IWbemObjectSink* pSink,
LPCWSTR wszClassName)
{
//
// Do not register for changes to system classes --- they do not change!
//
if(wszClassName[0] == L'_')
{
return WBEM_S_NO_ERROR;
}
//
// Just issue the appropriate query against the namespace. The filter
// will know what to do when called
//
DWORD cLen = wcslen(wszClassName) + 100;
LPWSTR wszQuery = new WCHAR[cLen];
if ( wszQuery == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
StringCchPrintfW( wszQuery,
cLen,
L"select * from __ClassOperationEvent where "
L"TargetClass isa \"%s\"", wszClassName );
CVectorDeleteMe<WCHAR> vdm( wszQuery );
return InternalRegisterNotificationSink(L"WQL",
wszQuery, 0, WMIMSG_FLAG_QOS_SYNCHRONOUS,
GetCurrentEssContext(), pSink, true, NULL );
}
HRESULT CEssNamespace::RegisterProviderForClassChanges( LPCWSTR wszClassName,
LPCWSTR wszProvName )
{
try
{
CInCritSec ics(&m_csLevel1);
m_mapProviderInterestClasses[wszClassName].insert( wszProvName );
}
catch(CX_MemoryException)
{
return WBEM_E_OUT_OF_MEMORY;
}
return S_OK;
}
HRESULT CEssNamespace::UnregisterFilterFromAllClassChanges(
CEventFilter* pFilter)
{
HRESULT hres;
//
// unbind the filter from the actual class change sink and use it to unreg
//
CWbemPtr<IWbemObjectSink> pActualClassChangeSink;
hres = pFilter->SetActualClassChangeSink( NULL, &pActualClassChangeSink );
if ( FAILED(hres) )
{
return hres;
}
if ( pActualClassChangeSink != NULL )
{
hres = UnregisterSinkFromAllClassChanges( pActualClassChangeSink );
}
return hres;
}
HRESULT CEssNamespace::UnregisterSinkFromAllClassChanges(
IWbemObjectSink* pSink)
{
return InternalRemoveNotificationSink(pSink);
}
HRESULT CEssNamespace::DeactivateFilter( READ_ONLY CEventFilter* pFilter )
{
HRESULT hres;
DEBUGTRACE((LOG_ESS,"Deactivating filter %p\n", pFilter ));
HRESULT hresGlobal = WBEM_S_NO_ERROR;
//
// Check if the events are supposed to come from this namespace or some
// other one.
//
CEssNamespace* pOtherNamespace = NULL;
hres = GetFilterEventNamespace(pFilter, &pOtherNamespace);
if(FAILED(hres))
return hres;
if( pOtherNamespace )
{
CTemplateReleaseMe<CEssNamespace> rm0(pOtherNamespace);
//
// Unregister this notification sink with the other namespace,
// as if it were a temporary consumer. This needs to be a
// postponed operation though, else we could have a deadlock
// scenario if at the same time cross namespace subscriptions
// were registered in both namespaces.
//
CPostponedRemoveNotificationSinkRequest* pReq;
pReq = new CPostponedRemoveNotificationSinkRequest(
pOtherNamespace,
pFilter->GetNonFilteringSink() );
if ( pReq == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
CPostponedList* pList = GetCurrentPostponedList();
_DBG_ASSERT( pList != NULL );
hres = pList->AddRequest( this, pReq );
if ( FAILED(hres) )
{
delete pReq;
return hres;
}
return WBEM_S_NO_ERROR;
}
else
{
//
// Current namespace --- unregister for real
//
// Retrieve its non-filtering sink
// ===============================
CAbstractEventSink* pNonFilter = pFilter->GetNonFilteringSink();
if(pNonFilter == NULL)
return WBEM_E_OUT_OF_MEMORY;
//
// Report the MSFT_WmiFilterDeactivated event.
//
FireNCFilterEvent(MSFT_WmiFilterDeactivated, pFilter);
//
// Unregister it from class change notifications
//
hres = UnregisterFilterFromAllClassChanges(pFilter);
if(FAILED(hres))
hresGlobal = hres;
// Deactivate in providers, poller, and static search
// ==================================================
hres = m_EventProviderCache.ReleaseProvidersForQuery(pNonFilter);
if(FAILED(hres))
hresGlobal = hres;
hres = m_Poller.DeactivateFilter(pFilter);
if(FAILED(hres))
hresGlobal = hres;
pFilter->SetInactive();
return hres;
}
}
HRESULT CEssNamespace::HandleClassCreation( LPCWSTR wszClassName,
IWbemClassObject* pClass)
{
//
// Check if this is a class that a provider is waiting for.
//
ProviderSet setProviders;
{
CInCritSec ics( &m_csLevel1 );
ClassToProviderMap::iterator it;
it = m_mapProviderInterestClasses.find( wszClassName );
if ( it != m_mapProviderInterestClasses.end() )
{
//
// copy the interested provider list.
//
setProviders = it->second;
//
// remove the entry from the map.
//
m_mapProviderInterestClasses.erase( it );
}
}
if ( setProviders.size() > 0 )
{
//
// reload interested providers.
//
DEBUGTRACE((LOG_ESS,"Reloading some providers in namespace %S due to "
"creation of %S class\n", m_wszName, wszClassName ));
ProviderSet::iterator itProv;
for( itProv=setProviders.begin(); itProv!=setProviders.end(); itProv++)
{
ReloadProvider( 0, *itProv );
}
}
return S_OK;
}
//*****************************************************************************
//
// Updates internal structures to reflect a change to this class. Assumes that
// the namespace is already locked.
// Very few errors are reported from this function, since class changes cannot
// be vetoed.
//
//*****************************************************************************
HRESULT CEssNamespace::HandleClassChange(LPCWSTR wszClassName,
IWbemClassObject* pClass)
{
// Check if the class in question is an event consumer class
// =========================================================
if(pClass->InheritsFrom(CONSUMER_CLASS) == S_OK)
{
CInUpdate iu(this);
if ( IsShutdown() )
return WBEM_E_SHUTTING_DOWN;
HandleConsumerClassDeletion(wszClassName);
}
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::HandleConsumerClassDeletion(LPCWSTR wszClassName)
{
// There are two varieties: non-singleton and singleton
// ====================================================
DWORD cLen = wcslen(wszClassName) + 2;
LPWSTR wszPrefix = new WCHAR[cLen];
if(wszPrefix == NULL)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<WCHAR> vdm( wszPrefix );
StringCchPrintfW(wszPrefix, cLen, L"%s.", wszClassName);
m_Bindings.RemoveConsumersStartingWith(wszPrefix);
StringCchPrintfW(wszPrefix, cLen, L"%s=", wszClassName);
m_Bindings.RemoveConsumersStartingWith(wszPrefix);
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::ReloadProvider( long lFlags, LPCWSTR wszProvider )
{
HRESULT hres;
WString wsRelpath;
//
// we only have to do this for event providers. Check to see if
// we know about this try to see if we even have any event providers to reload ...
//
try
{
wsRelpath = L"__Win32Provider.Name='";
wsRelpath += wszProvider;
wsRelpath += L"'";
}
catch( CX_MemoryException )
{
return WBEM_E_OUT_OF_MEMORY;
}
CWbemPtr<_IWmiObject> pObj;
hres = GetInstance( wsRelpath, &pObj );
if ( SUCCEEDED(hres) )
{
{
ENSURE_INITIALIZED
CInResync ir(this);
//
// note : in case of reloading event provider due to notification
// from provss, we only need to handle event providers since
// consumer providers already have a reloading mechanism.
//
m_EventProviderCache.RemoveProvider(pObj);
hres = AddProvider( pObj );
ir.Commit();
}
if ( SUCCEEDED(hres) )
{
hres = FirePostponedOperations();
}
else
{
FirePostponedOperations();
}
}
return hres;
}
HRESULT CEssNamespace::ReloadProvider(IWbemClassObject* pProvObjTemplate)
{
HRESULT hres;
LogOp( L"ReloadProvider", pProvObjTemplate );
ENSURE_INITIALIZED
CInResync ir(this);
// Start by deleting this provider from our records, if there
// ==========================================================
hres = RemoveProvider(pProvObjTemplate);
if(FAILED(hres))
return hres;
// Determine the current state of this provider in the database
// ============================================================
IWbemClassObject* pProvObj = NULL;
hres = GetCurrentState(pProvObjTemplate, &pProvObj);
if(FAILED(hres))
return hres;
if(pProvObj == NULL)
{
// The provider has been deleted --- no further action is needed
// =============================================================
return S_OK;
}
CReleaseMe rm1(pProvObj);
// Now create it if necessary
// ==========================
hres = AddProvider(pProvObj);
if(FAILED(hres))
return hres;
ir.Commit();
return hres;
}
HRESULT CEssNamespace::ReloadEventProviderRegistration(
IWbemClassObject* pProvRegObjTemplate)
{
HRESULT hres;
LogOp( L"ReloadEventProviderRegistration", pProvRegObjTemplate );
ENSURE_INITIALIZED
CInResync ir(this);
// Start by deleting this provider from our records, if there
// ==========================================================
hres = RemoveEventProviderRegistration(pProvRegObjTemplate);
if(FAILED(hres))
return hres;
// Determine the current state of this registration in the database
// ================================================================
IWbemClassObject* pProvRegObj = NULL;
hres = GetCurrentState(pProvRegObjTemplate, &pProvRegObj);
if(FAILED(hres))
return hres;
if(pProvRegObj == NULL)
{
// The registration has been deleted --- no further action is needed
// =================================================================
return S_OK;
}
CReleaseMe rm1(pProvRegObj);
// Now create it if necessary
// ==========================
hres = AddEventProviderRegistration(pProvRegObj);
if(FAILED(hres))
return hres;
ir.Commit();
return hres;
}
HRESULT CEssNamespace::ReloadConsumerProviderRegistration(
IWbemClassObject* pProvRegObjTemplate)
{
CInUpdate iu(this);
// Reset consumer provider info in all the consumers using this consumer
// provider. That's all we need to do --- they will simply pick up the new
// data on next delivery. We don't even need to get the current version,
// since all we need is the key
// ========================================================================
return RemoveConsumerProviderRegistration(pProvRegObjTemplate);
}
//*****************************************************************************
//
// Assumes that the namespace is locked and PrepareForResync has been called
// Adds this provider to the records. Expects ReactivateAllFilters and
// CommitResync to be called later
//
//*****************************************************************************
HRESULT CEssNamespace::AddProvider(READ_ONLY IWbemClassObject* pProv)
{
HRESULT hres;
hres = m_EventProviderCache.AddProvider(pProv);
return hres;
}
HRESULT CEssNamespace::CheckEventProviderRegistration(IWbemClassObject* pReg)
{
HRESULT hres;
ENSURE_INITIALIZED
hres = m_EventProviderCache.CheckProviderRegistration(pReg);
return hres;
}
HRESULT CEssNamespace::CheckTimerInstruction(IWbemClassObject* pInst)
{
HRESULT hres;
ENSURE_INITIALIZED
hres = GetTimerGenerator().CheckTimerInstruction(pInst);
return hres;
}
//*****************************************************************************
//
// Assumes that the namespace is locked and PrepareForResync has been called
// Adds this event provider registration to the records. Expects
// ReactivateAllFilters and CommitResync to be called later
//
//*****************************************************************************
HRESULT CEssNamespace::AddEventProviderRegistration(
IWbemClassObject* pReg)
{
HRESULT hres;
hres = m_EventProviderCache.AddProviderRegistration(pReg);
return hres;
}
//*****************************************************************************
//
// Assumes that the namespace is locked and PrepareForResync has been called
// Removes this provider from the records. Expects ReactivateAllFilters and
// CommitResync to be called later
//
//*****************************************************************************
HRESULT CEssNamespace::RemoveProvider(READ_ONLY IWbemClassObject* pProv)
{
HRESULT hres;
// Handle event consumer providers
// ===============================
IWbemClassObject* pConsProvReg;
hres = m_ConsumerProviderCache.
GetConsumerProviderRegFromProviderReg(pProv, &pConsProvReg);
if(SUCCEEDED(hres))
{
RemoveConsumerProviderRegistration(pConsProvReg);
pConsProvReg->Release();
}
// Handle event providers
// ======================
hres = m_EventProviderCache.RemoveProvider(pProv);
return hres;
}
//*****************************************************************************
//
// Assumes that the namespace is locked and PrepareForResync has been called
// Adds this event provider registration to the records. Expects
// ReactivateAllFilters and CommitResync to be called later
//
//*****************************************************************************
HRESULT CEssNamespace::RemoveEventProviderRegistration(
READ_ONLY IWbemClassObject* pReg)
{
HRESULT hres;
hres = m_EventProviderCache.RemoveProviderRegistration(pReg);
return hres;
}
DWORD CEssNamespace::GetProvidedEventMask(IWbemClassObject* pClass)
{
return m_EventProviderCache.GetProvidedEventMask(pClass);
}
//*****************************************************************************
//
// This function is called before a major update to the records. Without any
// calls to external components, it "deactivates" all the filters, in a sense
// that when all of them are "reactivated", the system will arrive in a
// consistent state (usage counts, etc). CommitResync will then perform any
// necessary activations/deactivations based on the new state
//
//*****************************************************************************
HRESULT CEssNamespace::PrepareForResync()
{
m_bInResync = TRUE;
// Ask the poller to "virtually" stop all polling instructions, without
// actually stopping them physically
// ====================================================================
m_Poller.VirtuallyStopPolling();
// Ask provider cache to "virtually" release all its providers, without
// actually releasing them physically
// ====================================================================
m_EventProviderCache.VirtuallyReleaseProviders();
// Ask core search to mark all filters so that it would know which ones are
// gone after the resync
// ========================================================================
DEBUGTRACE((LOG_ESS,"Prepared resync in namespace %S\n", m_wszName ));
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::ReactivateAllFilters()
{
DEBUGTRACE((LOG_ESS,"Reactivating all filters in namespace %S\n",
m_wszName ));
return m_Bindings.ReactivateAllFilters();
}
HRESULT CEssNamespace::CommitResync()
{
m_bInResync = FALSE;
// Tell provider cache to perform all the loadings and unloadings it
// needs to perform based on the new data
// =================================================================
m_EventProviderCache.CommitProviderUsage();
// Tell the poller to cancel unnecessary instructions
// ==================================================
m_Poller.CancelUnnecessaryPolling();
DEBUGTRACE((LOG_ESS,"Committed resync in namespace %S\n", m_wszName ));
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::RemoveConsumerProviderRegistration(
IWbemClassObject* pReg)
{
// Get the name of the consumer provider being deleteed
// ====================================================
BSTR strProvRef = CConsumerProviderCache::GetProviderRefFromRecord(pReg);
if(strProvRef == NULL)
{
ERRORTRACE((LOG_ESS, "Invalid consumer provider record is being deleted"
"\n"));
return WBEM_S_FALSE;
}
CSysFreeMe sfm1(strProvRef);
// Reset it in all the consumers
// =============================
m_Bindings.ResetProviderRecords(strProvRef);
// Remove it from the cache
// ========================
m_ConsumerProviderCache.RemoveConsumerProvider(strProvRef);
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::ScheduleDelivery(CQueueingEventSink* pDest)
{
return m_pEss->EnqueueDeliver(pDest);
}
HRESULT CEssNamespace::DecorateObject(IWbemClassObject* pObj)
{
return m_pEss->DecorateObject(pObj, m_wszName);
}
HRESULT CEssNamespace::EnsureConsumerWatchInstruction()
{
return m_Bindings.EnsureConsumerWatchInstruction();
}
HRESULT CEssNamespace::AddSleepCharge(DWORD dwSleep)
{
return m_pEss->AddSleepCharge(dwSleep);
}
HRESULT CEssNamespace::AddCache()
{
return m_pEss->AddCache();
}
HRESULT CEssNamespace::RemoveCache()
{
return m_pEss->RemoveCache();
}
HRESULT CEssNamespace::AddToCache(DWORD dwAdd, DWORD dwMemberTotal,
DWORD* pdwSleep)
{
return m_pEss->AddToCache(dwAdd, dwMemberTotal, pdwSleep);
}
HRESULT CEssNamespace::RemoveFromCache(DWORD dwRemove)
{
return m_pEss->RemoveFromCache(dwRemove);
}
HRESULT CEssNamespace::PerformSubscriptionInitialization()
{
HRESULT hres;
DWORD dwRead;
//
// must use repository only svc ptr here, else we can deadlock when
// class providers try to call back in.
//
// Enumerator all EventFilters
// ===========================
CFilterEnumSink* pFilterSink = new CFilterEnumSink(this);
if ( NULL == pFilterSink )
{
return WBEM_E_OUT_OF_MEMORY;
}
pFilterSink->AddRef();
m_pInternalCoreSvc->InternalCreateInstanceEnum( EVENT_FILTER_CLASS, 0,
pFilterSink);
pFilterSink->ReleaseAndWait();
// Enumerator all consumers
// ========================
CConsumerEnumSink* pConsumerSink = new CConsumerEnumSink(this);
if ( NULL == pConsumerSink )
{
return WBEM_E_OUT_OF_MEMORY;
}
pConsumerSink->AddRef();
m_pInternalCoreSvc->InternalCreateInstanceEnum( CONSUMER_CLASS, 0,
pConsumerSink);
pConsumerSink->ReleaseAndWait();
// Enumerator all bindings
// =======================
CBindingEnumSink* pBindingSink = new CBindingEnumSink(this);
if ( NULL == pBindingSink )
{
return WBEM_E_OUT_OF_MEMORY;
}
pBindingSink->AddRef();
m_pInternalCoreSvc->InternalCreateInstanceEnum( BINDING_CLASS, 0,
pBindingSink);
pBindingSink->ReleaseAndWait();
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::PerformProviderInitialization()
{
HRESULT hres;
DWORD dwRead;
//
// make sure that we resync all subscriptions after we've processed
// provider objs
//
CInResync ir( this );
//
// Enumerate all the providers
//
IEnumWbemClassObject* penumProvs;
hres = m_pCoreSvc->CreateInstanceEnum( CWbemBSTR( PROVIDER_CLASS ),
WBEM_FLAG_DEEP,
GetCurrentEssContext(),
&penumProvs );
if ( SUCCEEDED(hres) )
{
CReleaseMe rm1(penumProvs);
// Add them all to ESS
// ===================
IWbemClassObject* pProvObj;
while((hres=penumProvs->Next(INFINITE, 1, &pProvObj, &dwRead)) == S_OK)
{
hres = AddProvider(pProvObj);
pProvObj->Release();
if(FAILED(hres))
{
// Already logged.
}
}
}
if ( FAILED(hres) )
{
ERRORTRACE((LOG_ESS, "Error 0x%X occurred enumerating event providers "
"in namespace %S. Some event providers may not be active\n", hres,
m_wszName));
}
//
// Enumerate all the provider registrations
//
IEnumWbemClassObject* penumRegs;
hres = m_pCoreSvc->CreateInstanceEnum( CWbemBSTR( EVENT_PROVIDER_REGISTRATION_CLASS ),
WBEM_FLAG_DEEP,
GetCurrentEssContext(),
&penumRegs);
if ( SUCCEEDED(hres) )
{
CReleaseMe rm2(penumRegs);
// Add them all to ESS
// ===================
IWbemClassObject* pRegObj;
while((hres = penumRegs->Next(INFINITE, 1, &pRegObj, &dwRead)) == S_OK)
{
hres = AddEventProviderRegistration(pRegObj);
pRegObj->Release();
if(FAILED(hres))
{
// Already logged
}
}
}
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Error 0x%X occurred enumerating event providers "
"registrations in namespace %S. "
"Some event providers may not be active\n", hres, m_wszName));
}
//
// Create and initialize the core provider.
//
CWbemPtr<CCoreEventProvider> pCoreEventProvider = new CCoreEventProvider;
if ( pCoreEventProvider != NULL )
{
hres = pCoreEventProvider->SetNamespace(this);
if ( SUCCEEDED(hres) )
{
LPCWSTR awszQuery[5] =
{
L"select * from __InstanceOperationEvent",
L"select * from __ClassOperationEvent",
L"select * from __NamespaceOperationEvent",
L"select * from __SystemEvent",
L"select * from __TimerEvent"
};
hres = m_EventProviderCache.AddSystemProvider(pCoreEventProvider,
L"$Core",
5,
awszQuery );
}
}
else
{
hres = WBEM_E_OUT_OF_MEMORY;
}
if ( SUCCEEDED(hres) )
{
pCoreEventProvider->AddRef();
m_pCoreEventProvider = pCoreEventProvider;
}
else
{
ERRORTRACE((LOG_ESS, "Core event provider cannot initialize due "
"to critical errors. HR=0x%x\n", hres));
}
// Initialize timer generator
// ==========================
hres = InitializeTimerGenerator();
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Error 0x%X occurred initializing the timer "
"in namespace %S. Some timer instructions may not be active\n",
hres, m_wszName));
}
ir.Commit();
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::InitializeTimerGenerator()
{
return m_pEss->InitializeTimerGenerator( m_wszName, m_pCoreSvc );
}
HRESULT CEssNamespace::ScheduleFirePostponed()
{
//
// Save and detach the current thread object --- we need to pass it to the
// other thread, as well as make sure than nobody else fires our postponed
// operations!
//
CEssThreadObject* pThreadObj = GetCurrentEssThreadObject();
ClearCurrentEssThreadObject();
SetCurrentEssThreadObject(NULL);
if ( GetCurrentEssThreadObject() == NULL )
{
SetConstructedEssThreadObject( pThreadObj );
return WBEM_E_OUT_OF_MEMORY;
}
CFirePostponed* pReq = new CFirePostponed(this, pThreadObj);
if(pReq == NULL)
{
SetConstructedEssThreadObject( pThreadObj );
return WBEM_E_OUT_OF_MEMORY;
}
HRESULT hr = m_pEss->Enqueue(pReq);
if (FAILED(hr)) delete pReq;
return hr;
}
HRESULT CEssNamespace::FirePostponedOperations()
{
IWbemContext *pContext = GetCurrentEssContext( );
VARIANT vValue;
HRESULT hr;
do
{
if ( NULL == pContext )
{
break;
}
hr = pContext->GetValue( L"__ReentranceTestProp", 0, &vValue );
if ( WBEM_E_NOT_FOUND == hr )
{
break;
}
if ( FAILED( hr ) )
{
return hr;
}
if ( VARIANT_TRUE == V_BOOL( &vValue ) )
{
//
// Reentrance
//
hr = pContext->DeleteValue( L"__ReentranceTestProp", 0 );
if ( FAILED( hr ) )
{
return hr;
}
ScheduleFirePostponed( );
return S_OK;
}
}
while( FALSE );
HRESULT hrReturn = WBEM_S_NO_ERROR;
//
// Update lock cannot be held when calling this function and there
// are operations to execute.
//
_DBG_ASSERT( !DoesThreadOwnNamespaceLock() );
//
// execute both primary and event postponed ops until empty.
//
CPostponedList* pList = GetCurrentPostponedList();
CPostponedList* pEventList = GetCurrentPostponedEventList();
do
{
//
// execute the primary postponed ops.
//
if( pList != NULL )
{
hr = pList->Execute(this, CPostponedList::e_ReturnOneError);
if ( SUCCEEDED(hrReturn) )
{
hrReturn = hr;
}
}
//
// now execute postponed events
//
if ( pEventList != NULL )
{
hr = pEventList->Execute(this, CPostponedList::e_ReturnOneError);
if ( SUCCEEDED(hrReturn) )
{
hrReturn = hr;
}
}
}
while( pList != NULL && !pList->IsEmpty() );
return hrReturn;
}
HRESULT CEssNamespace::PostponeRelease(IUnknown* pUnk)
{
CPostponedList* pList = GetCurrentPostponedList();
if(pList == NULL)
{
//
// Just execute it
//
pUnk->Release();
return WBEM_S_NO_ERROR;
}
CPostponedReleaseRequest* pReq = new CPostponedReleaseRequest(pUnk);
if(pReq == NULL)
return WBEM_E_OUT_OF_MEMORY;
//
// this is a namespace agnostic postponed request, so specify null.
//
return pList->AddRequest( NULL, pReq );
}
HRESULT CEssNamespace::GetProviderNamespacePointer(IWbemServices** ppServices)
{
IWbemServices* pServices = NULL;
HRESULT hres = m_pEss->GetNamespacePointer(m_wszName, FALSE, &pServices);
if(FAILED(hres))
return hres;
*ppServices = pServices;
return WBEM_S_NO_ERROR;
}
void CEssNamespace::IncrementObjectCount()
{
m_pEss->IncrementObjectCount();
}
void CEssNamespace::DecrementObjectCount()
{
m_pEss->DecrementObjectCount();
}
HRESULT CEssNamespace::LockForUpdate()
{
m_csLevel2.Enter();
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::UnlockForUpdate()
{
m_ClassCache.Clear();
m_csLevel2.Leave();
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::GetCurrentState( IWbemClassObject* pTemplate,
IWbemClassObject** ppObj)
{
HRESULT hres;
*ppObj = NULL;
// Retrieve the path
// =================
VARIANT vPath;
hres = pTemplate->Get(L"__RELPATH", 0, &vPath, NULL, NULL);
if(FAILED(hres))
return hres;
CClearMe cm1(&vPath);
if(V_VT(&vPath) != VT_BSTR)
return WBEM_E_INVALID_OBJECT;
// Get it from the namespace
// =========================
_IWmiObject* pObj;
hres = GetInstance( V_BSTR(&vPath), &pObj );
if( hres == WBEM_E_NOT_FOUND )
return WBEM_S_FALSE;
*ppObj = pObj;
return hres;
}
CWinMgmtTimerGenerator& CEssNamespace::GetTimerGenerator()
{
return m_pEss->GetTimerGenerator();
}
HRESULT CEssNamespace::RaiseErrorEvent(IWbemEvent* pEvent, BOOL bAdminOnly )
{
CEventRepresentation Event;
Event.type = e_EventTypeSystem;
Event.nObjects = 1;
Event.apObjects = &pEvent;
HRESULT hres;
hres = SignalEvent( Event, 0, bAdminOnly );
if(FAILED(hres))
{
ERRORTRACE((LOG_ESS, "Event subsystem was unable to deliver an "
"error event to some consumers (%X)\n", hres));
}
return S_OK;
}
HRESULT CEssNamespace::GetClassFromCore( LPCWSTR wszClassName,
_IWmiObject** ppClass )
{
HRESULT hres;
CWbemPtr<IWbemClassObject> pClass;
*ppClass = NULL;
//
// want to ensure that we don't use the full service ptr until we've
// completed stage 1 initialization. Reason is that we don't want to
// load class providers until the second stage of initialization.
//
_DBG_ASSERT( m_bStage1Complete );
//
// must use full service because will need to support dynamic classes.
//
hres = m_pInternalFullSvc->InternalGetClass( wszClassName, &pClass );
if ( FAILED(hres) )
{
return hres;
}
return pClass->QueryInterface( IID__IWmiObject, (void**)ppClass );
}
HRESULT CEssNamespace::GetInstance( LPCWSTR wszPath,
_IWmiObject** ppInstance )
{
HRESULT hres;
CWbemPtr<IWbemClassObject> pInstance;
*ppInstance = NULL;
hres = m_pInternalCoreSvc->InternalGetInstance( wszPath, &pInstance );
if ( FAILED(hres) )
{
return hres;
}
return pInstance->QueryInterface( IID__IWmiObject, (void**)ppInstance );
}
HRESULT CEssNamespace::GetDbInstance( LPCWSTR wszDbKey,
_IWmiObject** ppInstance)
{
HRESULT hres;
CWbemPtr<IWbemClassObject> pInstance;
*ppInstance = NULL;
hres = m_pInternalCoreSvc->GetDbInstance( wszDbKey, &pInstance );
if ( FAILED(hres) )
{
return hres;
}
return pInstance->QueryInterface( IID__IWmiObject, (void**)ppInstance );
}
HRESULT CEssNamespace::CreateInstanceEnum(LPCWSTR wszClass, long lFlags,
IWbemObjectSink* pSink)
{
return m_pInternalCoreSvc->InternalCreateInstanceEnum(wszClass, lFlags, pSink);
}
HRESULT CEssNamespace::ExecQuery(LPCWSTR wszQuery, long lFlags,
IWbemObjectSink* pSink)
{
return m_pInternalCoreSvc->InternalExecQuery(L"WQL", wszQuery, lFlags, pSink);
}
HRESULT CEssNamespace::GetToken(PSID pSid, IWbemToken** ppToken)
{
return m_pEss->GetToken(pSid, ppToken);
}
void CEssNamespace::DumpStatistics(FILE* f, long lFlags)
{
CInUpdate iu(this);
fprintf(f, "------- Namespace '%S' ----------\n", m_wszName);
m_Bindings.DumpStatistics(f, lFlags);
m_ConsumerProviderCache.DumpStatistics(f, lFlags);
m_EventProviderCache.DumpStatistics(f, lFlags);
m_Poller.DumpStatistics(f, lFlags);
}
HRESULT CEssMetaData::GetClass( LPCWSTR wszName, IWbemContext* pContext,
_IWmiObject** ppClass)
{
return m_pNamespace->m_ClassCache.GetClass(wszName, pContext, ppClass);
}
STDMETHODIMP CEssNamespace::CConsumerClassDeletionSink::Indicate(
long lNumObjects,
IWbemClassObject** apObjects)
{
HRESULT hres;
for(long i = 0; i < lNumObjects; i++)
{
_IWmiObject* pEvent = NULL;
apObjects[i]->QueryInterface(IID__IWmiObject, (void**)&pEvent);
CReleaseMe rm1(pEvent);
//
// Get the class name of the class being deleted
//
VARIANT vObj;
hres = pEvent->Get(L"TargetClass", 0, &vObj, NULL, NULL);
if(SUCCEEDED(hres))
{
CClearMe cm1(&vObj);
IWbemClassObject* pClass;
V_UNKNOWN(&vObj)->QueryInterface(IID_IWbemClassObject,
(void**)&pClass);
CReleaseMe rm2(pClass);
VARIANT vClass;
hres = pClass->Get(L"__CLASS", 0, &vClass, NULL, NULL);
if(SUCCEEDED(hres))
{
CClearMe cm(&vClass);
m_pOuter->HandleConsumerClassDeletion(V_BSTR(&vClass));
}
}
}
return WBEM_S_NO_ERROR;
}
HRESULT CEssNamespace::LoadEventProvider(LPCWSTR wszProviderName,
IWbemEventProvider** ppProv)
{
HRESULT hres;
*ppProv = NULL;
//
// Get provider pointer from the provider subsystem
//
if(m_pProviderFactory == NULL)
return WBEM_E_CRITICAL_ERROR;
WmiInternalContext t_InternalContext ;
ZeroMemory ( & t_InternalContext , sizeof ( t_InternalContext ) ) ;
hres = m_pProviderFactory->GetProvider(
t_InternalContext ,
0, // lFlags
GetCurrentEssContext(),
0,
NULL,
NULL,
0,
wszProviderName,
IID_IWbemEventProvider,
(LPVOID *) ppProv
);
return hres;
}
HRESULT CEssNamespace::LoadConsumerProvider(LPCWSTR wszProviderName,
IUnknown** ppProv)
{
HRESULT hres;
*ppProv = NULL;
//
// Get provider pointer from the provider subsystem
//
if(m_pProviderFactory == NULL)
return WBEM_E_CRITICAL_ERROR;
WmiInternalContext t_InternalContext ;
ZeroMemory ( & t_InternalContext , sizeof ( t_InternalContext ) ) ;
hres = m_pProviderFactory->GetProvider(
t_InternalContext ,
0, // lFlags
GetCurrentEssContext(),
0,
NULL,
NULL,
0,
wszProviderName,
IID_IUnknown,
(LPVOID *) ppProv
);
return hres;
}