1386 lines
37 KiB
C++
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());
|
|
}
|