//****************************************************************************** // // POLLER.CPP // // Copyright (C) 1996-1999 Microsoft Corporation // //****************************************************************************** #include "precomp.h" #include #include "ess.h" #include #include #include "Quota.h" #include #include long g_lNumPollingCachedObjects = 0; long g_lNumPollingInstructions = 0; // {2ECF39D0-2B26-11d2-AEC8-00C04FB68820} const GUID IID_IWbemCallSecurity = { 0x2ecf39d0, 0x2b26, 0x11d2, {0xae, 0xc8, 0x0, 0xc0, 0x4f, 0xb6, 0x88, 0x20}}; void CBasePollingInstruction::AddRef() { InterlockedIncrement(&m_lRef); } void CBasePollingInstruction::Release() { if(InterlockedDecrement(&m_lRef) == 0) { if(DeleteTimer()) { delete this; } else { // // Deep trouble --- cannot delete timer, so it will execute again. // This means I must leak this instruction (to prevent a crash) // } } } CBasePollingInstruction::CBasePollingInstruction(CEssNamespace* pNamespace) : m_pNamespace(pNamespace), m_strLanguage(NULL), m_strQuery(NULL), m_pSecurity(NULL), m_lRef(0), m_bUsedQuota(false), m_bCancelled(false), m_hTimer(NULL) { pNamespace->AddRef(); } CBasePollingInstruction::~CBasePollingInstruction() { Destroy(); } void CBasePollingInstruction::Destroy() { // // The timer is guaranteed to have been deleted by the Release // _DBG_ASSERT(m_hTimer == NULL); if(m_pNamespace) m_pNamespace->Release(); SysFreeString(m_strLanguage); SysFreeString(m_strQuery); if (m_bUsedQuota) { if (m_pSecurity) m_pSecurity->ImpersonateClient(); g_quotas.DecrementQuotaIndex( ESSQ_POLLING_INSTRUCTIONS, NULL, 1); if (m_pSecurity) m_pSecurity->RevertToSelf(); } if(m_pSecurity) m_pSecurity->Release(); } bool CBasePollingInstruction::DeleteTimer() { HANDLE hTimer = NULL; { CInCritSec ics(&m_cs); hTimer = m_hTimer; m_bCancelled = true; } if(hTimer) { if(!DeleteTimerQueueTimer(NULL, hTimer, INVALID_HANDLE_VALUE)) { return false; } m_hTimer = NULL; // no need for cs --- it's cancelled! } return true; } CWbemTime CBasePollingInstruction::GetNextFiringTime(CWbemTime LastFiringTime, OUT long* plFiringCount) const { *plFiringCount = 1; CWbemTime Next = LastFiringTime + m_Interval; if(Next < CWbemTime::GetCurrentTime()) { // We missed a poll. No problem --- reschedule for later // ===================================================== return CWbemTime::GetCurrentTime() + m_Interval; } else { return Next; } } CWbemTime CBasePollingInstruction::GetFirstFiringTime() const { // The first time is a random function of the interval // =================================================== double dblFrac = (double)rand() / RAND_MAX; return CWbemTime::GetCurrentTime() + m_Interval * dblFrac; } HRESULT CBasePollingInstruction::Initialize(LPCWSTR wszLanguage, LPCWSTR wszQuery, DWORD dwMsInterval, bool bAffectsQuota) { m_strLanguage = SysAllocString(wszLanguage); m_strQuery = SysAllocString(wszQuery); if(m_strLanguage == NULL || m_strQuery == NULL) return WBEM_E_OUT_OF_MEMORY; m_Interval.SetMilliseconds(dwMsInterval); // // Retrieve the current security object. Even though it is ours, we cannot // keep it, since it is shared by other threads // HRESULT hres = WBEM_S_NO_ERROR; m_pSecurity = CWbemCallSecurity::MakeInternalCopyOfThread(); if (bAffectsQuota) { if ( m_pSecurity ) { hres = m_pSecurity->ImpersonateClient(); if ( FAILED(hres) ) { ERRORTRACE((LOG_ESS, "Polling instruction for query %S failed " "to impersonate client during initialization.\n", wszQuery )); return hres; } } hres = g_quotas.IncrementQuotaIndex( ESSQ_POLLING_INSTRUCTIONS, NULL, 1); if (m_pSecurity) m_pSecurity->RevertToSelf(); if (SUCCEEDED(hres)) m_bUsedQuota = true; } return hres; } void CBasePollingInstruction::staticTimerCallback(void* pParam, BOOLEAN) { CBasePollingInstruction* pInst = (CBasePollingInstruction*)pParam; try { pInst->ExecQuery(); } catch( CX_MemoryException ) { } // // Reschedule the timer, if needed // { CInCritSec ics(&pInst->m_cs); // // First, check if the instruction has been cancelled // if(pInst->m_bCancelled) return; // // Delete ourselves // _DBG_ASSERT(pInst->m_hTimer != NULL); DeleteTimerQueueTimer(NULL, pInst->m_hTimer, NULL); CreateTimerQueueTimer(&pInst->m_hTimer, NULL, (WAITORTIMERCALLBACK)&staticTimerCallback, pParam, pInst->m_Interval.GetMilliseconds(), 0, WT_EXECUTELONGFUNCTION); } } HRESULT CBasePollingInstruction::Fire(long lNumTimes, CWbemTime NextFiringTime) { return ExecQuery(); } void CBasePollingInstruction::Cancel() { m_bCancelled = true; } HRESULT CBasePollingInstruction::ExecQuery() { HRESULT hres; // Impersonate // =========== if(m_pSecurity) { hres = m_pSecurity->ImpersonateClient(); if(FAILED(hres) && (hres != E_NOTIMPL)) { ERRORTRACE((LOG_ESS, "Impersonation failed with error code %X for " "polling query %S. Will retry at next polling interval\n", hres, m_strQuery)); return hres; } } // Execute the query synchrnously (TBD: async would be better) // ============================== IWbemServices* pServices = NULL; hres = m_pNamespace->GetNamespacePointer(&pServices); if(FAILED(hres)) { if(m_pSecurity) m_pSecurity->RevertToSelf(); return hres; } CReleaseMe rm1(pServices); DEBUGTRACE((LOG_ESS, "Executing polling query '%S' in namespace '%S'\n", m_strQuery, m_pNamespace->GetName())); IEnumWbemClassObject* pEnum; hres = pServices->ExecQuery(m_strLanguage, m_strQuery, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_KEEP_SHAPE, GetCurrentEssContext(), &pEnum); if(m_pSecurity) m_pSecurity->RevertToSelf(); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Polling query %S failed with error code %X. " "Will retry at next polling interval\n", m_strQuery, hres)); return hres; } CReleaseMe rm2(pEnum); // Get the results into an array // ============================= IWbemClassObject* aBuffer[100]; DWORD dwNumRet; while(1) { hres = pEnum->Next(1000, 100, aBuffer, &dwNumRet); if(FAILED(hres)) break; bool bDone = false; if(hres == WBEM_S_FALSE) bDone = true; // // Check if this query has been cancelled // if(m_bCancelled) { DEBUGTRACE((LOG_ESS, "Aborting polling query '%S' because its " "subscription is cancelled\n", m_strQuery)); return WBEM_E_CALL_CANCELLED; } for(DWORD dw = 0; dw < dwNumRet; dw++) { _IWmiObject* pObj = NULL; aBuffer[dw]->QueryInterface(IID__IWmiObject, (void**)&pObj); CReleaseMe rm(pObj); hres = ProcessObject(pObj); if(FAILED(hres)) break; } for( dw=0; dw < dwNumRet; dw++ ) { aBuffer[dw]->Release(); } if(dw < dwNumRet || FAILED(hres)) break; if(bDone) break; } ProcessQueryDone(hres, NULL); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Polling query '%S' failed with error code 0x%X. " "Will retry at next polling interval\n", m_strQuery, hres)); return hres; } else { DEBUGTRACE((LOG_ESS, "Polling query '%S' done\n", m_strQuery)); } return WBEM_S_NO_ERROR; } BOOL CBasePollingInstruction::CompareTo(CBasePollingInstruction* pOther) { if(wcscmp(pOther->m_strLanguage, m_strLanguage)) return FALSE; if(wcscmp(pOther->m_strQuery, m_strQuery)) return FALSE; if(pOther->m_Interval.GetMilliseconds() != m_Interval.GetMilliseconds()) return FALSE; return TRUE; } //*************************************************************************** //*************************************************************************** //*************************************************************************** CPollingInstruction::CCachedObject::CCachedObject(_IWmiObject* pObject) : m_pObject(pObject), m_strPath(NULL) { g_lNumPollingCachedObjects++; // Extract the path // ================ VARIANT v; VariantInit(&v); if (SUCCEEDED(pObject->Get(L"__RELPATH", 0, &v, NULL, NULL)) && (V_VT(&v) == VT_BSTR)) m_strPath = V_BSTR(&v); // Variant intentionally not cleared pObject->AddRef(); } CPollingInstruction::CCachedObject::~CCachedObject() { g_lNumPollingCachedObjects--; if(m_pObject) m_pObject->Release(); if(NULL != m_strPath) SysFreeString(m_strPath); } int __cdecl CPollingInstruction::CCachedObject::compare(const void* pelem1, const void* pelem2) { CCachedObject* p1 = *(CCachedObject**)pelem1; CCachedObject* p2 = *(CCachedObject**)pelem2; if((p1->m_strPath != NULL) && (p2->m_strPath != NULL)) return wbem_wcsicmp(p1->m_strPath, p2->m_strPath); else if(p1->m_strPath == p2->m_strPath) return 0; else return 1; } CPollingInstruction::CPollingInstruction(CEssNamespace* pNamespace) : CBasePollingInstruction(pNamespace), m_papCurrentObjects(NULL), m_dwEventMask(0), m_pDest(NULL), m_papPrevObjects(NULL), m_pUser(NULL) { g_lNumPollingInstructions++; } CPollingInstruction::~CPollingInstruction() { g_lNumPollingInstructions--; SubtractMemory(m_papCurrentObjects); delete m_papCurrentObjects; ResetPrevious(); if(m_pDest) m_pDest->Release(); if(m_pUser) g_quotas.FreeUser(m_pUser); } // This class represents a postponed request to execute a query class CPostponedQuery : public CPostponedRequest { protected: CPollingInstruction* m_pInst; public: CPostponedQuery(CPollingInstruction* pInst) : m_pInst(pInst) { m_pInst->AddRef(); } ~CPostponedQuery() { m_pInst->Release(); } HRESULT Execute(CEssNamespace* pNamespace) { return m_pInst->FirstExecute(); } }; HRESULT CPollingInstruction::FirstExecute() { // // Check if our filter has any hope // if(FAILED(m_pDest->GetPollingError())) { DEBUGTRACE((LOG_ESS, "Polling query '%S' will not be attempted as \n" "another polling query related to the same subscription has failed " "to start with error code 0x%X, deactivating subscription\n", m_strQuery, m_pDest->GetPollingError())); return m_pDest->GetPollingError(); } if(m_bCancelled) { DEBUGTRACE((LOG_ESS, "Aborting polling query '%S' because its " "subscription is cancelled\n", m_strQuery)); return WBEM_E_CALL_CANCELLED; } // note that if this function fails, then it will be destroyed when // the postponed query releases it reference. If this function succeedes // then tss will hold onto a reference and keep it alive. // m_papCurrentObjects = _new CCachedArray; if( m_papCurrentObjects == NULL ) { return WBEM_E_OUT_OF_MEMORY; } HRESULT hres = ExecQuery(); if ( FAILED(hres) ) { ERRORTRACE((LOG_ESS, "Polling query '%S' failed on the first try with " "error code 0x%X.\nDeactivating subscription\n", m_strQuery, hres)); m_pDest->SetPollingError(hres); return hres; } // // add this instruction to the scheduler // if(!CreateTimerQueueTimer(&m_hTimer, NULL, (WAITORTIMERCALLBACK)&staticTimerCallback, (void*)(CBasePollingInstruction*)this, m_Interval.GetMilliseconds(), 0, WT_EXECUTELONGFUNCTION)) { long lRes = GetLastError(); ERRORTRACE((LOG_ESS, "ESS is unable to schedule a timer instruction " "with the system (error code %d). This operation will be " "aborted.\n", lRes)); return WBEM_E_FAILED; } return WBEM_S_NO_ERROR; } HRESULT CPollingInstruction::Initialize(LPCWSTR wszLanguage, LPCWSTR wszQuery, DWORD dwMsInterval, DWORD dwEventMask, CEventFilter* pDest) { HRESULT hres; hres = CBasePollingInstruction::Initialize(wszLanguage, wszQuery, dwMsInterval); if(FAILED(hres)) return hres; m_dwEventMask = dwEventMask; m_pDest = pDest; pDest->AddRef(); hres = g_quotas.FindUser(pDest, &m_pUser); if(FAILED(hres)) return hres; return WBEM_S_NO_ERROR; } HRESULT CPollingInstruction::ProcessObject(_IWmiObject* pObj) { HRESULT hres; // // Make sure that the current object list exists // if(m_papCurrentObjects == NULL) { m_papCurrentObjects = new CCachedArray; if(m_papCurrentObjects == NULL) return WBEM_E_OUT_OF_MEMORY; } // // Check if this query has been cancelled // if(m_bCancelled) { DEBUGTRACE((LOG_ESS, "Aborting polling query '%S' because its " "subscription is cancelled\n", m_strQuery)); return WBEM_E_CALL_CANCELLED; } // // Check quotas // DWORD dwSize = ComputeObjectMemory(pObj); hres = g_quotas.IncrementQuotaIndexByUser(ESSQ_POLLING_MEMORY, m_pUser, dwSize); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Aborting polling query '%S' because the quota " "for memory used by polling is exceeded\n", m_strQuery)); return hres; } // // Add the object to the current list // CCachedObject* pRecord = _new CCachedObject(pObj); if(pRecord == NULL || !pRecord->IsValid()) { delete pRecord; return WBEM_E_OUT_OF_MEMORY; } if(m_papCurrentObjects->Add(pRecord) < 0) { delete pRecord; return WBEM_E_OUT_OF_MEMORY; } return WBEM_S_NO_ERROR; } DWORD CPollingInstruction::ComputeObjectMemory(_IWmiObject* pObj) { DWORD dwSize = 0; HRESULT hres = pObj->GetObjectMemory( NULL, 0, &dwSize ); if (FAILED(hres) && hres != WBEM_E_BUFFER_TOO_SMALL ) { return hres; } return dwSize; } HRESULT CPollingInstruction::ProcessQueryDone( HRESULT hresQuery, IWbemClassObject* pErrorObj) { HRESULT hres; if(FAILED(hresQuery)) { // // If the query failed, retain the previous poll // result --- that's the best we can do // SubtractMemory(m_papCurrentObjects); delete m_papCurrentObjects; m_papCurrentObjects = NULL; // // Report subscription error // return WBEM_S_FALSE; } else if ( m_papCurrentObjects == NULL ) { // // Query came back empty --- emulate by creating an empty // m_papCurrentObjects // m_papCurrentObjects = new CCachedArray; if(m_papCurrentObjects == NULL) return WBEM_E_OUT_OF_MEMORY; } // // Sort the objects by path // qsort((void*)m_papCurrentObjects->GetArrayPtr(), m_papCurrentObjects->GetSize(), sizeof(CCachedObject*), CCachedObject::compare); // // At this point, m_papCurrentObjects contains the sorted results of the // current query. If this is not the first time, m_papPrevObjects // contains the previous result. If first time, then all done for now. // if( m_papPrevObjects == NULL ) { m_papPrevObjects = m_papCurrentObjects; m_papCurrentObjects = NULL; return WBEM_S_NO_ERROR; } // // Now is the time to compare // long lOldIndex = 0, lNewIndex = 0; while(lNewIndex < m_papCurrentObjects->GetSize() && lOldIndex < m_papPrevObjects->GetSize()) { int nCompare = 1; BSTR bstr1 = m_papCurrentObjects->GetAt(lNewIndex)->m_strPath, bstr2 = m_papPrevObjects->GetAt(lOldIndex)->m_strPath; if((bstr1 != NULL) && (bstr2 != NULL)) nCompare = wbem_wcsicmp(bstr1, bstr2); else if (bstr1 == bstr2) nCompare = 0; else nCompare = 1; if(nCompare < 0) { // The _new object is not in the old array --- object created // ========================================================= if(m_dwEventMask & (1 << e_EventTypeInstanceCreation)) { RaiseCreationEvent(m_papCurrentObjects->GetAt(lNewIndex)); } lNewIndex++; } else if(nCompare > 0) { // The old object is not in the _new array --- object deleted // ========================================================= if(m_dwEventMask & (1 << e_EventTypeInstanceDeletion)) { RaiseDeletionEvent(m_papPrevObjects->GetAt(lOldIndex)); } lOldIndex++; } else { if(m_dwEventMask & (1 << e_EventTypeInstanceModification)) { // Compare the objects themselves // ============================== hres = m_papCurrentObjects->GetAt(lNewIndex)->m_pObject-> CompareTo( WBEM_FLAG_IGNORE_CLASS | WBEM_FLAG_IGNORE_OBJECT_SOURCE, m_papPrevObjects->GetAt(lOldIndex)->m_pObject); if(hres != S_OK) { // The objects are not the same --- object changed // =============================================== RaiseModificationEvent( m_papCurrentObjects->GetAt(lNewIndex), m_papPrevObjects->GetAt(lOldIndex)); } } lOldIndex++; lNewIndex++; } } if(m_dwEventMask & (1 << e_EventTypeInstanceDeletion)) { while(lOldIndex < m_papPrevObjects->GetSize()) { RaiseDeletionEvent(m_papPrevObjects->GetAt(lOldIndex)); lOldIndex++; } } if(m_dwEventMask & (1 << e_EventTypeInstanceCreation)) { while(lNewIndex < m_papCurrentObjects->GetSize()) { RaiseCreationEvent(m_papCurrentObjects->GetAt(lNewIndex)); lNewIndex++; } } // Replace the cached array with the new one // ========================================= ResetPrevious(); m_papPrevObjects = m_papCurrentObjects; m_papCurrentObjects = NULL; return S_OK; } HRESULT CPollingInstruction::RaiseCreationEvent(CCachedObject* pNewObj) { IWbemClassObject* _pObj = pNewObj->m_pObject; CEventRepresentation Event; Event.type = e_EventTypeInstanceCreation; Event.wsz1 = (LPWSTR)m_pNamespace->GetName(); BSTR strTemp = GetObjectClass(pNewObj); Event.wsz2 = (LPWSTR)strTemp; Event.wsz3 = NULL; Event.nObjects = 1; Event.apObjects = &_pObj; CWbemPtr pEventObj; if(FAILED(Event.MakeWbemObject(m_pNamespace, &pEventObj))) return WBEM_E_OUT_OF_MEMORY; // BUGBUG: context HRESULT hres = m_pDest->Indicate(1, &pEventObj, NULL); SysFreeString(strTemp); return hres; } HRESULT CPollingInstruction::RaiseDeletionEvent(CCachedObject* pOldObj) { IWbemClassObject* _pObj = pOldObj->m_pObject; CEventRepresentation Event; Event.type = e_EventTypeInstanceDeletion; Event.wsz1 = (LPWSTR)m_pNamespace->GetName(); BSTR strTemp = GetObjectClass(pOldObj); Event.wsz2 = (LPWSTR)strTemp; Event.wsz3 = NULL; Event.nObjects = 1; Event.apObjects = &_pObj; CWbemPtr pEventObj; if(FAILED(Event.MakeWbemObject(m_pNamespace, &pEventObj))) return WBEM_E_OUT_OF_MEMORY; // BUGBUG: context HRESULT hres = m_pDest->Indicate(1, &pEventObj, NULL); SysFreeString(strTemp); return hres; } HRESULT CPollingInstruction::RaiseModificationEvent(CCachedObject* pNewObj, CCachedObject* pOldObj) { IWbemClassObject* apObjects[2]; CEventRepresentation Event; Event.type = e_EventTypeInstanceModification; Event.wsz1 = (LPWSTR)m_pNamespace->GetName(); BSTR strTemp = GetObjectClass(pNewObj); Event.wsz2 = (LPWSTR)strTemp; Event.wsz3 = NULL; Event.nObjects = 2; Event.apObjects = (IWbemClassObject**)apObjects; Event.apObjects[0] = pNewObj->m_pObject; Event.apObjects[1] = (pOldObj?pOldObj->m_pObject:NULL); CWbemPtr pEventObj; if(FAILED(Event.MakeWbemObject(m_pNamespace, &pEventObj))) return WBEM_E_OUT_OF_MEMORY; // BUGBUG: context HRESULT hres = m_pDest->Indicate(1, &pEventObj, NULL); SysFreeString(strTemp); return hres; } HRESULT CPollingInstruction::ResetPrevious() { HRESULT hres; SubtractMemory(m_papPrevObjects); delete m_papPrevObjects; m_papPrevObjects = NULL; return S_OK; } HRESULT CPollingInstruction::SubtractMemory(CCachedArray* pArray) { HRESULT hres; if(pArray == NULL) return S_FALSE; for(int i = 0; i < pArray->GetSize(); i++) { _IWmiObject* pObj = pArray->GetAt(i)->m_pObject; DWORD dwSize = ComputeObjectMemory(pObj); hres = g_quotas.DecrementQuotaIndexByUser(ESSQ_POLLING_MEMORY, m_pUser, dwSize); if(FAILED(hres)) return hres; } return S_OK; } SYSFREE_ME BSTR CPollingInstruction::GetObjectClass(CCachedObject* pObj) { VARIANT v; VariantInit(&v); if ( FAILED( pObj->m_pObject->Get(L"__CLASS", 0, &v, NULL, NULL) ) ) { return NULL; } return V_BSTR(&v); } //***************************************************************************** //***************************************************************************** // // P o l l e r // //***************************************************************************** //***************************************************************************** CPoller::CPoller(CEssNamespace* pNamespace) : m_pNamespace(pNamespace), m_bInResync(FALSE) { } CPoller::~CPoller() { } void CPoller::Clear() { CInstructionMap::iterator it = m_mapInstructions.begin(); while(it != m_mapInstructions.end()) { // Release the refcount this holds on the instructioin // =================================================== it->first->Cancel(); it->first->DeleteTimer(); it->first->Release(); it = m_mapInstructions.erase(it); } } HRESULT CPoller::ActivateFilter(CEventFilter* pDest, LPCWSTR wszQuery, QL_LEVEL_1_RPN_EXPRESSION* pExpr) { // Check what kind of events it is looking for // =========================================== DWORD dwEventMask = CEventRepresentation::GetTypeMaskFromName( pExpr->bsClassName); if((dwEventMask & ((1 << e_EventTypeInstanceCreation) | (1 << e_EventTypeInstanceDeletion) | (1 << e_EventTypeInstanceModification) ) ) == 0 ) { // This registration does not involve instance-related events and // therefore there is no polling involved // ============================================================== return WBEM_S_FALSE; } // The query is looking for instance-change events. See what classes // of objects it is interested in. // ================================================================= CClassInfoArray* paInfos; HRESULT hres = m_Analyser.GetPossibleInstanceClasses(pExpr, paInfos); if(FAILED(hres)) return hres; CDeleteMe dm2(paInfos); if(!paInfos->IsLimited()) { // Analyser could not find any limits on the possible classes. // Rephrase that as all children of "" // =========================================================== CClassInformation* pNewInfo = _new CClassInformation; if(pNewInfo == NULL) return WBEM_E_OUT_OF_MEMORY; pNewInfo->m_wszClassName = NULL; pNewInfo->m_bIncludeChildren = TRUE; paInfos->AddClass(pNewInfo); paInfos->SetLimited(TRUE); } // See if it is looking for any dynamic classes. // ============================================= for(int i = 0; i < paInfos->GetNumClasses(); i++) { CClassInfoArray aNonProvided; hres = ListNonProvidedClasses( paInfos->GetClass(i), dwEventMask, aNonProvided); if(FAILED(hres)) { ERRORTRACE((LOG_ESS,"Failed searching for classes to poll.\n" "Class name: %S, Error code: %X\n\n", paInfos->GetClass(i)->m_wszClassName, hres)); return hres; } // Increment our quotas if necessary. DWORD nClasses = aNonProvided.GetNumClasses(); if (nClasses) { PSID pSID = pDest->GetOwner( ); if ( pSID && STATUS_SUCCESS != IsUserAdministrator( pSID ) ) { return WBEM_E_ACCESS_DENIED; } if (FAILED(hres = g_quotas.IncrementQuotaIndex( ESSQ_POLLING_INSTRUCTIONS, pDest, nClasses))) { return hres; } } // Institute polling for each class // ================================ for(int j = 0; j < nClasses; j++) { // We have an instance-change event registration where dynamic // instances are involved. Check if tolerance is specified // =========================================================== if(pExpr->Tolerance.m_bExact || pExpr->Tolerance.m_fTolerance == 0) { return WBEMESS_E_REGISTRATION_TOO_PRECISE; } // Tolerance is there. Get the right query for this class // ====================================================== LPWSTR wszThisQuery = NULL; hres = m_Analyser.GetLimitingQueryForInstanceClass( pExpr, *aNonProvided.GetClass(j), wszThisQuery); CVectorDeleteMe vdm1(wszThisQuery); if(FAILED(hres)) { ERRORTRACE((LOG_ESS,"ERROR: Limiting query extraction failed.\n" "Original query: %S\nClass: %S\nError code: %X\n", wszQuery, aNonProvided.GetClass(j)->m_wszClassName, hres)); return hres; } DEBUGTRACE((LOG_ESS,"Instituting polling query %S to satisfy event" " query %S\n", wszThisQuery, wszQuery)); DWORD dwMs = pExpr->Tolerance.m_fTolerance * 1000; CWbemPtr pInst; pInst = _new CPollingInstruction(m_pNamespace); if(pInst == NULL) return WBEM_E_OUT_OF_MEMORY; hres = pInst->Initialize( L"WQL", wszThisQuery, dwMs, aNonProvided.GetClass(j)->m_dwEventMask, pDest); if ( SUCCEEDED(hres) ) { hres = AddInstruction( (DWORD_PTR)pDest, pInst ); } if ( FAILED(hres) ) { ERRORTRACE((LOG_ESS, "ERROR: Polling instruction initialization failed\n" "Query: %S\nError code: %X\n\n", wszThisQuery, hres)); return hres; } } } return WBEM_S_NO_ERROR; } HRESULT CPoller::AddInstruction( DWORD_PTR dwKey, CPollingInstruction* pInst ) { HRESULT hr; CInCritSec ics(&m_cs); if( m_bInResync ) { // Search for the instruction in the map // ===================================== CInstructionMap::iterator it; for( it=m_mapInstructions.begin(); it != m_mapInstructions.end(); it++) { // // if the filter key is the same and the instructions have the // same queries, then there is a match. It is not enough to // do just the filter key, since there can be multiple instructions // per filter, and it is not enough to do just the instruction // comparison since multiple filters can have the same polling // instruction queries. Since there can never be multiple // instructions with the same query for the same filter, // comparing both works. // if( it->second.m_dwFilterId == dwKey && it->first->CompareTo( pInst ) ) { // // Found it, set to active but DO NOT add to the generator. // it is already there // it->second.m_bActive = TRUE; return WBEM_S_FALSE; } } } // // add to the instruction to the map. // FilterInfo Info; Info.m_dwFilterId = dwKey; Info.m_bActive = TRUE; try { m_mapInstructions[pInst] = Info; } catch(CX_MemoryException) { return WBEM_E_OUT_OF_MEMORY; } pInst->AddRef(); // // Postpone the first execution of the query. // 1. Execution may not be done here, because the namespace is // locked // 2. Execution may not be done asynchronously, because we // must get a baseline reading before returning to the // client. // CPostponedList* pList = GetCurrentPostponedList(); _DBG_ASSERT( pList != NULL ); CPostponedQuery* pReq = new CPostponedQuery( pInst ); if ( pReq && pList ) { hr = pList->AddRequest( m_pNamespace, pReq ); if ( FAILED(hr) ) { delete pReq; } } else { hr = WBEM_E_OUT_OF_MEMORY; delete pReq; } if ( FAILED(hr) ) { pInst->Release(); m_mapInstructions.erase( pInst ); } return hr; } HRESULT CPoller::DeactivateFilter(CEventFilter* pDest) { CInCritSec ics(&m_cs); DWORD_PTR dwKey = (DWORD_PTR)pDest; // Remove it from the map // ====================== CInstructionMap::iterator it = m_mapInstructions.begin(); DWORD nItems = 0; while(it != m_mapInstructions.end()) { if(it->second.m_dwFilterId == dwKey) { CBasePollingInstruction* pInst = it->first; // // First, cancel the instruction so that if it is executing, it will // abort at the earliest convenience // pInst->Cancel(); // // Then, deactivate the timer. This will block until the // instruction has finished executing, if it is currently doing so // pInst->DeleteTimer(); // // Now we are safe --- release the instruction. // it = m_mapInstructions.erase(it); pInst->Release(); nItems++; } else it++; } // Release our quotas if needed. if (nItems) g_quotas.DecrementQuotaIndex(ESSQ_POLLING_INSTRUCTIONS, pDest, nItems); return WBEM_S_NO_ERROR; } HRESULT CPoller::ListNonProvidedClasses(IN CClassInformation* pInfo, IN DWORD dwDesiredMask, OUT CClassInfoArray& aNonProvided) { HRESULT hres; aNonProvided.Clear(); // Get the class itself // ==================== IWbemServices* pNamespace; hres = m_pNamespace->GetNamespacePointer(&pNamespace); if(FAILED(hres)) return hres; CReleaseMe rm0(pNamespace); IWbemClassObject* pClass = NULL; hres = pNamespace->GetObject( CWbemBSTR( pInfo->m_wszClassName ), 0, GetCurrentEssContext(), &pClass, NULL); if(FAILED(hres)) return hres; CReleaseMe rm1(pClass); if(IsClassDynamic(pClass)) { AddDynamicClass(pClass, dwDesiredMask, aNonProvided); return WBEM_S_NO_ERROR; } // Enumerate all its descendants // ============================= IEnumWbemClassObject* pEnum; hres = pNamespace->CreateClassEnum( CWbemBSTR( pInfo->m_wszClassName ), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY | ((pInfo->m_bIncludeChildren)?WBEM_FLAG_DEEP:WBEM_FLAG_SHALLOW), GetCurrentEssContext(), &pEnum); if(FAILED(hres)) return hres; CReleaseMe rm3(pEnum); IWbemClassObject* pChild = NULL; DWORD dwNumRet; while(SUCCEEDED(pEnum->Next(INFINITE, 1, &pChild, &dwNumRet)) && dwNumRet > 0) { // Check if this one is dynamic // ============================ if(IsClassDynamic(pChild)) { AddDynamicClass(pChild, dwDesiredMask, aNonProvided); } pChild->Release(); pChild = NULL; } return WBEM_S_NO_ERROR; } BOOL CPoller::AddDynamicClass(IWbemClassObject* pClass, DWORD dwDesiredMask, OUT CClassInfoArray& aNonProvided) { // Check to see if all desired events are provided // =============================================== DWORD dwProvidedMask = m_pNamespace->GetProvidedEventMask(pClass); DWORD dwRemainingMask = ((~dwProvidedMask) & dwDesiredMask); if(dwRemainingMask) { // Add it to the array of classes to poll // ====================================== CClassInformation* pNewInfo = _new CClassInformation; if(pNewInfo == NULL) return WBEM_E_OUT_OF_MEMORY; _variant_t v; if (FAILED(pClass->Get(L"__CLASS", 0, &v, NULL, NULL))) { delete pNewInfo; return WBEM_E_OUT_OF_MEMORY; } pNewInfo->m_wszClassName = CloneWstr(V_BSTR(&v)); if(pNewInfo->m_wszClassName == NULL) { delete pNewInfo; return WBEM_E_OUT_OF_MEMORY; } pNewInfo->m_bIncludeChildren = FALSE; pNewInfo->m_dwEventMask = dwRemainingMask; pNewInfo->m_pClass = pClass; pClass->AddRef(); if(!aNonProvided.AddClass(pNewInfo)) { delete pNewInfo; return WBEM_E_OUT_OF_MEMORY; } return TRUE; } return FALSE; } BOOL CPoller::IsClassDynamic(IWbemClassObject* pClass) { HRESULT hres; IWbemQualifierSet* pSet; hres = pClass->GetQualifierSet(&pSet); if(FAILED(hres)) return TRUE; VARIANT v; VariantInit(&v); hres = pSet->Get(L"dynamic", 0, &v, NULL); pSet->Release(); if(FAILED(hres)) return FALSE; BOOL bRes = V_BOOL(&v); VariantClear(&v); return bRes; } HRESULT CPoller::VirtuallyStopPolling() { CInCritSec ics(&m_cs); // Mark all polling instructions in the map with the key of 0xFFFFFFFF // This will not stop them from working, but will separate them from the // new ones. // ===================================================================== for(CInstructionMap::iterator it = m_mapInstructions.begin(); it != m_mapInstructions.end(); it++) { it->second.m_bActive = FALSE; } m_bInResync = TRUE; return WBEM_S_NO_ERROR; } HRESULT CPoller::CancelUnnecessaryPolling() { CInCritSec ics(&m_cs); // Remove it from the map // ====================== CInstructionMap::iterator it = m_mapInstructions.begin(); while(it != m_mapInstructions.end()) { if( !it->second.m_bActive ) { CBasePollingInstruction* pInst = it->first; // // First, cancel the instruction so that if it is executing, it will // abort at the earliest convenience // pInst->Cancel(); // // Then, deactivate the timer. This will block until the // instruction has finished executing, if it is currently doing so // pInst->DeleteTimer(); // // Now we are safe --- release the instruction. // it = m_mapInstructions.erase(it); pInst->Release(); } else it++; } m_bInResync = FALSE; return WBEM_S_NO_ERROR; } void CPoller::DumpStatistics(FILE* f, long lFlags) { fprintf(f, "%d polling instructions\n", m_mapInstructions.size()); }