windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/ess3/poller.cpp
2020-09-26 16:20:57 +08:00

1386 lines
37 KiB
C++

//******************************************************************************
//
// 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"
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))
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)))
m_strPath = V_BSTR(&v);
// Variant intentionally not cleared
pObject->AddRef();
}
CPollingInstruction::CCachedObject::~CCachedObject()
{
g_lNumPollingCachedObjects--;
if(m_pObject)
m_pObject->Release();
SysFreeString(m_strPath);
}
int __cdecl CPollingInstruction::CCachedObject::compare(const void* pelem1,
const void* pelem2)
{
CCachedObject* p1 = *(CCachedObject**)pelem1;
CCachedObject* p2 = *(CCachedObject**)pelem2;
return wbem_wcsicmp(p1->m_strPath, p2->m_strPath);
}
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 = wbem_wcsicmp(
m_papCurrentObjects->GetAt(lNewIndex)->m_strPath,
m_papPrevObjects->GetAt(lOldIndex)->m_strPath);
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();
Event.wsz2 = GetObjectClass(pNewObj);
Event.wsz3 = NULL;
Event.nObjects = 1;
Event.apObjects = &_pObj;
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);
pEventObj->Release();
SysFreeString(Event.wsz2);
return hres;
}
HRESULT CPollingInstruction::RaiseDeletionEvent(CCachedObject* pOldObj)
{
IWbemClassObject* _pObj = pOldObj->m_pObject;
CEventRepresentation Event;
Event.type = e_EventTypeInstanceDeletion;
Event.wsz1 = (LPWSTR)m_pNamespace->GetName();
Event.wsz2 = GetObjectClass(pOldObj);
Event.wsz3 = NULL;
Event.nObjects = 1;
Event.apObjects = &_pObj;
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);
pEventObj->Release();
SysFreeString(Event.wsz2);
return hres;
}
HRESULT CPollingInstruction::RaiseModificationEvent(CCachedObject* pNewObj,
CCachedObject* pOldObj)
{
IWbemClassObject* apObjects[2];
CEventRepresentation Event;
Event.type = e_EventTypeInstanceModification;
Event.wsz1 = (LPWSTR)m_pNamespace->GetName();
Event.wsz2 = GetObjectClass(pNewObj);
Event.wsz3 = NULL;
Event.nObjects = 2;
Event.apObjects = (IWbemClassObject**)apObjects;
Event.apObjects[0] = pNewObj->m_pObject;
Event.apObjects[1] = (pOldObj?pOldObj->m_pObject:NULL);
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);
pEventObj->Release();
SysFreeString(Event.wsz2);
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)
{
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 ( pList != NULL )
{
hr = pList->AddRequest( m_pNamespace, pReq );
if ( FAILED(hr) )
{
delete pReq;
}
}
else
{
hr = WBEM_E_OUT_OF_MEMORY;
}
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(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(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 v;
VariantInit(&v);
pClass->Get(L"__CLASS", 0, &v, NULL, NULL);
pNewInfo->m_wszClassName = CloneWstr(V_BSTR(&v));
if(pNewInfo->m_wszClassName == NULL)
{
delete pNewInfo;
return WBEM_E_OUT_OF_MEMORY;
}
VariantClear(&v);
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());
}