//============================================================================= // // Copyright (c) 1996-1999, Microsoft Corporation, All rights reserved // // STDCONS.CPP // // This file implements the class for standard event consumer. // // History: // // 11/27/96 a-levn Compiles. // //============================================================================= #include "precomp.h" #include #include "pragmas.h" #include "permcons.h" #include "ess.h" #include #include "wbemutil.h" #include #include #include "NCEvents.h" #define HRESULT_ERROR_MASK (0x0000FFFF) #define HRESULT_ERROR_FUNC(X) (X&HRESULT_ERROR_MASK) #define HRESULT_ERROR_SERVER_UNAVAILABLE 1722L #define HRESULT_ERROR_CALL_FAILED_DNE 1727L long CPermanentConsumer::mstatic_lMaxQueueSizeHandle = 0; long CPermanentConsumer::mstatic_lSidHandle = 0; bool CPermanentConsumer::mstatic_bHandlesInitialized = false; // static HRESULT CPermanentConsumer::InitializeHandles( _IWmiObject* pObject) { if(mstatic_bHandlesInitialized) return S_FALSE; CIMTYPE ct; pObject->GetPropertyHandle(CONSUMER_MAXQUEUESIZE_PROPNAME, &ct, &mstatic_lMaxQueueSizeHandle); pObject->GetPropertyHandleEx(OWNER_SID_PROPNAME, 0, &ct, &mstatic_lSidHandle); mstatic_bHandlesInitialized = true; return S_OK; } //****************************************************************************** // public // // See stdcons.h for documentation // //****************************************************************************** CPermanentConsumer::CPermanentConsumer(CEssNamespace* pNamespace) : CEventConsumer(pNamespace), m_pCachedSink(NULL), m_pLogicalConsumer(NULL), m_dwLastDelivery(GetTickCount()) { pNamespace->IncrementObjectCount(); } HRESULT CPermanentConsumer::Initialize(IWbemClassObject* pObj) { HRESULT hres; CWbemPtr<_IWmiObject> pActualConsumer; hres = pObj->QueryInterface( IID__IWmiObject, (void**)&pActualConsumer ); if ( FAILED(hres) ) { return WBEM_E_CRITICAL_ERROR; } InitializeHandles(pActualConsumer); // Get the "database key" --- unique identifier // ============================================ BSTR strStandardPath; hres = pActualConsumer->GetNormalizedPath( 0, &strStandardPath ); if(FAILED(hres)) return hres; CSysFreeMe sfm1(strStandardPath); if(!(m_isKey = strStandardPath)) return WBEM_E_OUT_OF_MEMORY; // // set the queueing sink name to the consumer name. // TODO : this is temporary and will go away when the consumer no longer // inherits from queueing sink. // hres = SetName( strStandardPath ); if ( FAILED(hres) ) { return hres; } // Get the maximum queue size, if specified // ======================================== DWORD dwMaxQueueSize; hres = pActualConsumer->ReadDWORD(mstatic_lMaxQueueSizeHandle, &dwMaxQueueSize); if(hres == S_OK) SetMaxQueueSize(dwMaxQueueSize); // Get the SID // =========== if(IsNT()) { PSID pSid; ULONG ulNumElements; hres = pActualConsumer->GetArrayPropAddrByHandle( mstatic_lSidHandle, 0, &ulNumElements, &pSid ); if ( hres != S_OK ) { return WBEM_E_INVALID_OBJECT; } m_pOwnerSid = new BYTE[ulNumElements]; if ( m_pOwnerSid == NULL ) { return WBEM_E_OUT_OF_MEMORY; } memcpy( m_pOwnerSid, pSid, ulNumElements ); } return WBEM_S_NO_ERROR; } //****************************************************************************** // public // // See stdcons.h for documentation // //****************************************************************************** CPermanentConsumer::~CPermanentConsumer() { if(m_pCachedSink) { FireSinkUnloadedEvent(); m_pCachedSink->Release(); } if(m_pNamespace) m_pNamespace->DecrementObjectCount(); if(m_pLogicalConsumer) m_pLogicalConsumer->Release(); } HRESULT CPermanentConsumer::RetrieveProviderRecord( RELEASE_ME CConsumerProviderRecord** ppRecord, RELEASE_ME IWbemClassObject** ppLogicalConsumer) { HRESULT hres; // Retrieve our logical consumer instance // ====================================== _IWmiObject* pLogicalConsumer = NULL; WString wsKey = m_isKey; hres = m_pNamespace->GetDbInstance((LPCWSTR)wsKey, &pLogicalConsumer); if(FAILED(hres)) return hres; CReleaseMe rm1(pLogicalConsumer); *ppRecord = m_pNamespace->GetConsumerProviderCache().GetRecord( pLogicalConsumer); if(*ppRecord == NULL) { return WBEM_E_INVALID_PROVIDER_REGISTRATION; } else { if(pLogicalConsumer && ppLogicalConsumer) { *ppLogicalConsumer = pLogicalConsumer; (*ppLogicalConsumer)->AddRef(); } } return WBEM_S_NO_ERROR; } //****************************************************************************** // // RetrieveConsumer // // Have consumer provider produce a sink for this logical consumer // //****************************************************************************** HRESULT CPermanentConsumer::RetrieveSink( RELEASE_ME IWbemUnboundObjectSink** ppSink, RELEASE_ME IWbemClassObject** ppLogicalConsumer) { // Check if one is cached // ====================== { CInCritSec ics(&m_cs); if(m_pCachedSink) { *ppSink = m_pCachedSink; (*ppSink)->AddRef(); *ppLogicalConsumer = m_pLogicalConsumer; if(*ppLogicalConsumer) (*ppLogicalConsumer)->AddRef(); return WBEM_S_NO_ERROR; } } // Not cached. Retrieve one // ======================== HRESULT hres = ObtainSink(ppSink, ppLogicalConsumer); if(FAILED(hres)) return hres; m_pNamespace->EnsureConsumerWatchInstruction(); // Cache it, if needed // =================== { CInCritSec ics(&m_cs); if(m_pCachedSink) { if(m_pCachedSink != (*ppSink)) { // Drop ours, and use the one that's there // ======================================= (*ppSink)->Release(); *ppSink = m_pCachedSink; (*ppSink)->AddRef(); } if(m_pLogicalConsumer != *ppLogicalConsumer) { if(*ppLogicalConsumer) (*ppLogicalConsumer)->Release(); *ppLogicalConsumer = m_pLogicalConsumer; if(*ppLogicalConsumer) (*ppLogicalConsumer)->AddRef(); } return WBEM_S_NO_ERROR; } else { // Cache it // ======== m_pCachedSink = *ppSink; m_pCachedSink->AddRef(); m_pLogicalConsumer = *ppLogicalConsumer; if(m_pLogicalConsumer) m_pLogicalConsumer->AddRef(); } } return WBEM_S_NO_ERROR; } HRESULT CPermanentConsumer::ObtainSink( RELEASE_ME IWbemUnboundObjectSink** ppSink, RELEASE_ME IWbemClassObject** ppLogicalConsumer) { *ppSink = NULL; CConsumerProviderRecord* pRecord = NULL; IWbemClassObject* pLogicalConsumer = NULL; HRESULT hres = RetrieveProviderRecord(&pRecord, &pLogicalConsumer); if(FAILED(hres)) return hres; CTemplateReleaseMe rm1(pRecord); CReleaseMe rm2(pLogicalConsumer); // Check for global sink shortcut // ============================== hres = pRecord->GetGlobalObjectSink(ppSink, pLogicalConsumer); if(FAILED(hres)) return hres; if(*ppSink != NULL) { // That's it --- this consumer provider provides itself! // ===================================================== *ppLogicalConsumer = pLogicalConsumer; if(pLogicalConsumer) pLogicalConsumer->AddRef(); return S_OK; } hres = pRecord->FindConsumer(pLogicalConsumer, ppSink); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Event consumer provider is unable to instantiate " "event consumer %S: error code 0x%X\n", (LPCWSTR)(WString)m_isKey, hres)); return hres; } else { if(hres == WBEM_S_FALSE) { // Consumer provider says: don't need logical consumer! // ==================================================== *ppLogicalConsumer = NULL; } else { *ppLogicalConsumer = pLogicalConsumer; (*ppLogicalConsumer)->AddRef(); } } return hres; } //****************************************************************************** // // ClearCache // // Releases cached event consumer pointers. // //****************************************************************************** HRESULT CPermanentConsumer::ClearCache() { // // First, clear consumer provider record // CConsumerProviderRecord* pRecord = NULL; IWbemClassObject* pLogicalConsumer = NULL; HRESULT hres = RetrieveProviderRecord(&pRecord, &pLogicalConsumer); if(SUCCEEDED(hres)) { pLogicalConsumer->Release(); pRecord->Invalidate(); pRecord->Release(); } // // Need to PostponeRelease outside of the critical section, since // it will not actually postpone if done on a delivery thread // IWbemUnboundObjectSink* pSink = NULL; { CInCritSec ics(&m_cs); if(m_pCachedSink) { FireSinkUnloadedEvent(); pSink = m_pCachedSink; m_pCachedSink = NULL; } if(m_pLogicalConsumer) { m_pLogicalConsumer->Release(); m_pLogicalConsumer = NULL; } } _DBG_ASSERT( m_pNamespace != NULL ); if(pSink) m_pNamespace->PostponeRelease(pSink); return S_OK; } HRESULT CPermanentConsumer::Indicate(IWbemUnboundObjectSink* pSink, IWbemClassObject* pLogicalConsumer, long lNumEvents, IWbemEvent** apEvents, BOOL bSecure) { HRESULT hres; try { hres = pSink->IndicateToConsumer(pLogicalConsumer, lNumEvents, apEvents); } catch(...) { ERRORTRACE((LOG_ESS, "Event consumer threw an exception!\n")); hres = WBEM_E_PROVIDER_FAILURE; } return hres; } //****************************************************************************** // public // // See stdcons.h for documentation // //****************************************************************************** HRESULT CPermanentConsumer::ActuallyDeliver(long lNumEvents, IWbemEvent** apEvents, BOOL bSecure, CEventContext* pContext) { HRESULT hres; // Mark "last-delivery" time // ========================= m_dwLastDelivery = GetTickCount(); // Retrieve the sink to deliver the event into // =========================================== IWbemUnboundObjectSink* pSink = NULL; IWbemClassObject* pLogicalConsumer = NULL; hres = RetrieveSink(&pSink, &pLogicalConsumer); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed the first attempt to retrieve the sink to " "deliver an event to event consumer %S with error code %X.\n" "WMI will reload and retry.\n", (LPCWSTR)(WString)m_isKey, hres)); return Redeliver(lNumEvents, apEvents, bSecure); } CReleaseMe rm1(pSink); CReleaseMe rm2(pLogicalConsumer); // Try to deliver (m_pLogicalConsumer is immutable, so no cs is needed) // ==================================================================== hres = Indicate(pSink, pLogicalConsumer, lNumEvents, apEvents, bSecure); if(FAILED(hres)) { // decide whether it's an RPC error code DWORD shiftedRPCFacCode = FACILITY_RPC << 16; if ( ( ( hres & 0x7FF0000 ) == shiftedRPCFacCode ) || ( HRESULT_ERROR_FUNC(hres) == HRESULT_ERROR_SERVER_UNAVAILABLE ) || ( HRESULT_ERROR_FUNC(hres) == HRESULT_ERROR_CALL_FAILED_DNE ) || ( hres == RPC_E_DISCONNECTED ) ) { ERRORTRACE((LOG_ESS, "Failed the first attempt to deliver an event to " "event consumer %S with error code 0x%X.\n" "WMI will reload and retry.\n", (LPCWSTR)(WString)m_isKey, hres)); return Redeliver(lNumEvents, apEvents, bSecure); } else { ReportConsumerFailure(lNumEvents, apEvents, hres); ERRORTRACE((LOG_ESS, "Failed to deliver an event to " "event consumer %S with error code 0x%X. Dropping event.\n", (LPCWSTR)(WString)m_isKey, hres)); return hres; } } return hres; } HRESULT CPermanentConsumer::Redeliver(long lNumEvents, IWbemEvent** apEvents, BOOL bSecure) { HRESULT hres; // Clear everything // ================ ClearCache(); // Re-retrieve the sink // ==================== IWbemUnboundObjectSink* pSink = NULL; IWbemClassObject* pLogicalConsumer = NULL; hres = RetrieveSink(&pSink, &pLogicalConsumer); if(SUCCEEDED(hres)) { CReleaseMe rm1(pSink); CReleaseMe rm2(pLogicalConsumer); // Re-deliver // ========== hres = Indicate(pSink, pLogicalConsumer, lNumEvents, apEvents, bSecure); } if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed the second attempt to deliver an event to " "event consumer %S with error code %X.\n" "This event is dropped for this consumer.\n", (LPCWSTR)(WString)m_isKey, hres)); ReportConsumerFailure(lNumEvents, apEvents, hres); } return hres; } BOOL CPermanentConsumer::IsFullyUnloaded() { return (m_pCachedSink == NULL); } HRESULT CPermanentConsumer::Validate(IWbemClassObject* pLogicalConsumer) { HRESULT hres; // // Retrieve our consumer provider record // CConsumerProviderRecord* pRecord = NULL; hres = RetrieveProviderRecord(&pRecord); if(FAILED(hres)) return hres; CTemplateReleaseMe rm1(pRecord); // // Get it to validate our logical consumer // hres = pRecord->ValidateConsumer(pLogicalConsumer); return hres; } BOOL CPermanentConsumer::UnloadIfUnusedFor(CWbemInterval Interval) { CInCritSec ics(&m_cs); if(m_pCachedSink && GetTickCount() - m_dwLastDelivery > Interval.GetMilliseconds()) { FireSinkUnloadedEvent(); _DBG_ASSERT( m_pNamespace != NULL ); m_pNamespace->PostponeRelease(m_pCachedSink); m_pCachedSink = NULL; if(m_pLogicalConsumer) m_pLogicalConsumer->Release(); m_pLogicalConsumer = NULL; DEBUGTRACE((LOG_ESS, "Unloading event consumer sink %S\n", (LPCWSTR)(WString)m_isKey)); return TRUE; } else { return FALSE; } } HRESULT CPermanentConsumer::ResetProviderRecord(LPCWSTR wszProviderRef) { HRESULT hres; // Check if anything is even cached // ================================ { CInCritSec ics(&m_cs); if(m_pCachedSink == NULL) return WBEM_S_FALSE; } // Locate our consumer provider record // =================================== CConsumerProviderRecord* pRecord = NULL; hres = RetrieveProviderRecord(&pRecord); if(FAILED(hres)) return hres; CTemplateReleaseMe rm1(pRecord); if(!wbem_wcsicmp(pRecord->GetProviderRef(), wszProviderRef)) { ClearCache(); return WBEM_S_NO_ERROR; } else { return WBEM_S_FALSE; } } SYSFREE_ME BSTR CPermanentConsumer::ComputeKeyFromObj( CEssNamespace* pNamespace, IWbemClassObject* pObj) { HRESULT hres; CWbemPtr<_IWmiObject> pConsumerObj; hres = pObj->QueryInterface( IID__IWmiObject, (void**)&pConsumerObj ); if ( FAILED(hres) ) { return NULL; } BSTR strStandardPath = NULL; hres = pConsumerObj->GetNormalizedPath( 0, &strStandardPath ); if(FAILED(hres)) return NULL; return strStandardPath; } HRESULT CPermanentConsumer::ReportQueueOverflow(IWbemEvent* pEvent, DWORD dwQueueSize) { HRESULT hres; if(CEventConsumer::ReportEventDrop(pEvent) != S_OK) return S_FALSE; // Construct event instance // ======================== IWbemEvent* pErrorEvent = NULL; hres = ConstructErrorEvent(QUEUE_OVERFLOW_CLASS, pEvent, &pErrorEvent); if(FAILED(hres)) return hres; CReleaseMe rm1(pErrorEvent); // Fill in the queue size // ====================== VARIANT v; V_VT(&v) = VT_I4; V_I4(&v) = dwQueueSize; hres = pErrorEvent->Put(QUEUE_OVERFLOW_SIZE_PROPNAME, 0, &v, 0); if(FAILED(hres)) return hres; // Raise it // ======== hres = m_pNamespace->RaiseErrorEvent(pErrorEvent,TRUE); return hres; } HRESULT CPermanentConsumer::ReportConsumerFailure(long lNumEvents, IWbemEvent** apEvents, HRESULT hresError) { HRESULT hres; // // Compute the error object to use // _IWmiObject* pErrorObj = NULL; // // Get it from the thread // IErrorInfo* pErrorInfo = NULL; hres = GetErrorInfo(0, &pErrorInfo); if(hres != S_OK) { pErrorInfo = NULL; } else { hres = pErrorInfo->QueryInterface(IID__IWmiObject, (void**)&pErrorObj); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Non-WMI error object found returned by event " "consumer. Error object ignored\n")); pErrorObj = NULL; } } CReleaseMe rm1(pErrorObj); for(long l = 0; l < lNumEvents; l++) { ReportConsumerFailure(apEvents[l], hresError, pErrorObj); } return S_OK; } HRESULT CPermanentConsumer::ReportConsumerFailure(IWbemEvent* pEvent, HRESULT hresError, _IWmiObject* pErrorObj) { HRESULT hres; if(CEventConsumer::ReportEventDrop(pEvent) != S_OK) return S_FALSE; // // Construct event instance // IWbemEvent* pErrorEvent = NULL; hres = ConstructErrorEvent(CONSUMER_FAILURE_CLASS, pEvent, &pErrorEvent); if(FAILED(hres)) return hres; CReleaseMe rm1(pErrorEvent); // // Fill in the error code // VARIANT v; V_VT(&v) = VT_I4; V_I4(&v) = hresError; hres = pErrorEvent->Put(CONSUMER_FAILURE_ERROR_PROPNAME, 0, &v, 0); if(FAILED(hres)) return hres; if(pErrorObj) { // // Fill in the error object // V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = pErrorObj; hres = pErrorEvent->Put(CONSUMER_FAILURE_ERROROBJ_PROPNAME, 0, &v, 0); if(FAILED(hres)) { // // That's OK, sometimes error objects are not supported // } } // Raise it // ======== hres = m_pNamespace->RaiseErrorEvent(pErrorEvent,TRUE); return hres; } HRESULT CPermanentConsumer::ReportQosFailure( IWbemEvent* pEvent, HRESULT hresError ) { HRESULT hres; if(CEventConsumer::ReportEventDrop(pEvent) != S_OK) return S_FALSE; // Construct event instance // ======================== IWbemEvent* pErrorEvent = NULL; hres = ConstructErrorEvent(QOS_FAILURE_CLASS, pEvent, &pErrorEvent); if(FAILED(hres)) return hres; CReleaseMe rm1(pErrorEvent); // Fill in the error code // ====================== VARIANT v; V_VT(&v) = VT_I4; V_I4(&v) = hresError; hres = pErrorEvent->Put(QOS_FAILURE_ERROR_PROPNAME, 0, &v, 0); if(FAILED(hres)) return hres; // Raise it // ======== hres = m_pNamespace->RaiseErrorEvent(pErrorEvent,TRUE); return hres; } HRESULT CPermanentConsumer::ConstructErrorEvent(LPCWSTR wszEventClass, IWbemEvent* pEvent, IWbemEvent** ppErrorEvent) { HRESULT hres; _IWmiObject* pClass = NULL; hres = m_pNamespace->GetClass(wszEventClass, &pClass); if(FAILED(hres)) return hres; CReleaseMe rm2(pClass); IWbemClassObject* pErrorEvent = NULL; hres = pClass->SpawnInstance(0, &pErrorEvent); if(FAILED(hres)) return hres; CReleaseMe rm3(pErrorEvent); VARIANT v; VariantInit(&v); V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = pEvent; hres = pErrorEvent->Put(EVENT_DROP_EVENT_PROPNAME, 0, &v, 0); if(FAILED(hres)) return hres; BSTR strTemp = SysAllocString((WString)m_isKey); if ( NULL == strTemp ) { return WBEM_E_OUT_OF_MEMORY; } V_VT(&v) = VT_BSTR; V_BSTR(&v) = strTemp; hres = pErrorEvent->Put(EVENT_DROP_CONSUMER_PROPNAME, 0, &v, 0); VariantClear(&v); if(FAILED(hres)) return hres; *ppErrorEvent = pErrorEvent; pErrorEvent->AddRef(); return S_OK; } void CPermanentConsumer::FireSinkUnloadedEvent() { CConsumerProviderRecord *pRecord = NULL; IWbemClassObject *pLogicalConsumer = NULL; if (SUCCEEDED(RetrieveProviderRecord(&pRecord, &pLogicalConsumer))) { CTemplateReleaseMe rm1(pRecord); CReleaseMe rm2(pLogicalConsumer); // // Report the MSFT_WmiConsumerProviderSinkUnloaded event. // pRecord->FireNCSinkEvent( MSFT_WmiConsumerProviderSinkUnloaded, pLogicalConsumer); } }