|
|
//******************************************************************************
//
// POLLER.CPP
//
// Copyright (C) 1996-1999 Microsoft Corporation
//
//******************************************************************************
#include "precomp.h"
#include <stdio.h>
#include "ess.h"
#include <cominit.h>
#include <callsec.h>
#include "Quota.h"
#include <comdef.h>
#include <GroupsForUser.h>
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<IWbemEvent> 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<IWbemEvent> 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<IWbemEvent> 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<CClassInfoArray> 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<WCHAR> 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<CPollingInstruction> 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()); }
|