Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1385 lines
37 KiB

  1. //******************************************************************************
  2. //
  3. // POLLER.CPP
  4. //
  5. // Copyright (C) 1996-1999 Microsoft Corporation
  6. //
  7. //******************************************************************************
  8. #include "precomp.h"
  9. #include <stdio.h>
  10. #include "ess.h"
  11. #include <cominit.h>
  12. #include <callsec.h>
  13. #include "Quota.h"
  14. long g_lNumPollingCachedObjects = 0;
  15. long g_lNumPollingInstructions = 0;
  16. // {2ECF39D0-2B26-11d2-AEC8-00C04FB68820}
  17. const GUID IID_IWbemCallSecurity = {
  18. 0x2ecf39d0, 0x2b26, 0x11d2, {0xae, 0xc8, 0x0, 0xc0, 0x4f, 0xb6, 0x88, 0x20}};
  19. void CBasePollingInstruction::AddRef()
  20. {
  21. InterlockedIncrement(&m_lRef);
  22. }
  23. void CBasePollingInstruction::Release()
  24. {
  25. if(InterlockedDecrement(&m_lRef) == 0)
  26. {
  27. if(DeleteTimer())
  28. {
  29. delete this;
  30. }
  31. else
  32. {
  33. //
  34. // Deep trouble --- cannot delete timer, so it will execute again.
  35. // This means I must leak this instruction (to prevent a crash)
  36. //
  37. }
  38. }
  39. }
  40. CBasePollingInstruction::CBasePollingInstruction(CEssNamespace* pNamespace)
  41. : m_pNamespace(pNamespace), m_strLanguage(NULL), m_strQuery(NULL),
  42. m_pSecurity(NULL), m_lRef(0), m_bUsedQuota(false),
  43. m_bCancelled(false), m_hTimer(NULL)
  44. {
  45. pNamespace->AddRef();
  46. }
  47. CBasePollingInstruction::~CBasePollingInstruction()
  48. {
  49. Destroy();
  50. }
  51. void CBasePollingInstruction::Destroy()
  52. {
  53. //
  54. // The timer is guaranteed to have been deleted by the Release
  55. //
  56. _DBG_ASSERT(m_hTimer == NULL);
  57. if(m_pNamespace)
  58. m_pNamespace->Release();
  59. SysFreeString(m_strLanguage);
  60. SysFreeString(m_strQuery);
  61. if (m_bUsedQuota)
  62. {
  63. if (m_pSecurity)
  64. m_pSecurity->ImpersonateClient();
  65. g_quotas.DecrementQuotaIndex(
  66. ESSQ_POLLING_INSTRUCTIONS,
  67. NULL,
  68. 1);
  69. if (m_pSecurity)
  70. m_pSecurity->RevertToSelf();
  71. }
  72. if(m_pSecurity)
  73. m_pSecurity->Release();
  74. }
  75. bool CBasePollingInstruction::DeleteTimer()
  76. {
  77. HANDLE hTimer = NULL;
  78. {
  79. CInCritSec ics(&m_cs);
  80. hTimer = m_hTimer;
  81. m_bCancelled = true;
  82. }
  83. if(hTimer)
  84. {
  85. if(!DeleteTimerQueueTimer(NULL, hTimer, INVALID_HANDLE_VALUE))
  86. {
  87. return false;
  88. }
  89. m_hTimer = NULL; // no need for cs --- it's cancelled!
  90. }
  91. return true;
  92. }
  93. CWbemTime CBasePollingInstruction::GetNextFiringTime(CWbemTime LastFiringTime,
  94. OUT long* plFiringCount) const
  95. {
  96. *plFiringCount = 1;
  97. CWbemTime Next = LastFiringTime + m_Interval;
  98. if(Next < CWbemTime::GetCurrentTime())
  99. {
  100. // We missed a poll. No problem --- reschedule for later
  101. // =====================================================
  102. return CWbemTime::GetCurrentTime() + m_Interval;
  103. }
  104. else
  105. {
  106. return Next;
  107. }
  108. }
  109. CWbemTime CBasePollingInstruction::GetFirstFiringTime() const
  110. {
  111. // The first time is a random function of the interval
  112. // ===================================================
  113. double dblFrac = (double)rand() / RAND_MAX;
  114. return CWbemTime::GetCurrentTime() + m_Interval * dblFrac;
  115. }
  116. HRESULT CBasePollingInstruction::Initialize(LPCWSTR wszLanguage,
  117. LPCWSTR wszQuery, DWORD dwMsInterval,
  118. bool bAffectsQuota)
  119. {
  120. m_strLanguage = SysAllocString(wszLanguage);
  121. m_strQuery = SysAllocString(wszQuery);
  122. if(m_strLanguage == NULL || m_strQuery == NULL)
  123. return WBEM_E_OUT_OF_MEMORY;
  124. m_Interval.SetMilliseconds(dwMsInterval);
  125. //
  126. // Retrieve the current security object. Even though it is ours, we cannot
  127. // keep it, since it is shared by other threads
  128. //
  129. HRESULT hres = WBEM_S_NO_ERROR;
  130. m_pSecurity = CWbemCallSecurity::MakeInternalCopyOfThread();
  131. if (bAffectsQuota)
  132. {
  133. if ( m_pSecurity )
  134. {
  135. hres = m_pSecurity->ImpersonateClient();
  136. if ( FAILED(hres) )
  137. {
  138. ERRORTRACE((LOG_ESS, "Polling instruction for query %S failed "
  139. "to impersonate client during initialization.\n",
  140. wszQuery ));
  141. return hres;
  142. }
  143. }
  144. hres =
  145. g_quotas.IncrementQuotaIndex(
  146. ESSQ_POLLING_INSTRUCTIONS,
  147. NULL,
  148. 1);
  149. if (m_pSecurity)
  150. m_pSecurity->RevertToSelf();
  151. if (SUCCEEDED(hres))
  152. m_bUsedQuota = true;
  153. }
  154. return hres;
  155. }
  156. void CBasePollingInstruction::staticTimerCallback(void* pParam, BOOLEAN)
  157. {
  158. CBasePollingInstruction* pInst = (CBasePollingInstruction*)pParam;
  159. try
  160. {
  161. pInst->ExecQuery();
  162. }
  163. catch( CX_MemoryException )
  164. {
  165. }
  166. //
  167. // Reschedule the timer, if needed
  168. //
  169. {
  170. CInCritSec ics(&pInst->m_cs);
  171. //
  172. // First, check if the instruction has been cancelled
  173. //
  174. if(pInst->m_bCancelled)
  175. return;
  176. //
  177. // Delete ourselves
  178. //
  179. _DBG_ASSERT(pInst->m_hTimer != NULL);
  180. DeleteTimerQueueTimer(NULL, pInst->m_hTimer, NULL);
  181. CreateTimerQueueTimer(&pInst->m_hTimer, NULL,
  182. (WAITORTIMERCALLBACK)&staticTimerCallback,
  183. pParam,
  184. pInst->m_Interval.GetMilliseconds(),
  185. 0,
  186. WT_EXECUTELONGFUNCTION);
  187. }
  188. }
  189. HRESULT CBasePollingInstruction::Fire(long lNumTimes, CWbemTime NextFiringTime)
  190. {
  191. return ExecQuery();
  192. }
  193. void CBasePollingInstruction::Cancel()
  194. {
  195. m_bCancelled = true;
  196. }
  197. HRESULT CBasePollingInstruction::ExecQuery()
  198. {
  199. HRESULT hres;
  200. // Impersonate
  201. // ===========
  202. if(m_pSecurity)
  203. {
  204. hres = m_pSecurity->ImpersonateClient();
  205. if(FAILED(hres) && (hres != E_NOTIMPL))
  206. {
  207. ERRORTRACE((LOG_ESS, "Impersonation failed with error code %X for "
  208. "polling query %S. Will retry at next polling interval\n",
  209. hres, m_strQuery));
  210. return hres;
  211. }
  212. }
  213. // Execute the query synchrnously (TBD: async would be better)
  214. // ==============================
  215. IWbemServices* pServices = NULL;
  216. hres = m_pNamespace->GetNamespacePointer(&pServices);
  217. if(FAILED(hres))
  218. return hres;
  219. CReleaseMe rm1(pServices);
  220. DEBUGTRACE((LOG_ESS, "Executing polling query '%S' in namespace '%S'\n",
  221. m_strQuery, m_pNamespace->GetName()));
  222. IEnumWbemClassObject* pEnum;
  223. hres = pServices->ExecQuery(m_strLanguage, m_strQuery,
  224. WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY |
  225. WBEM_FLAG_KEEP_SHAPE,
  226. GetCurrentEssContext(), &pEnum);
  227. if(m_pSecurity)
  228. m_pSecurity->RevertToSelf();
  229. if(FAILED(hres))
  230. {
  231. ERRORTRACE((LOG_ESS, "Polling query %S failed with error code %X. "
  232. "Will retry at next polling interval\n", m_strQuery, hres));
  233. return hres;
  234. }
  235. CReleaseMe rm2(pEnum);
  236. // Get the results into an array
  237. // =============================
  238. IWbemClassObject* aBuffer[100];
  239. DWORD dwNumRet;
  240. while(1)
  241. {
  242. hres = pEnum->Next(1000, 100, aBuffer, &dwNumRet);
  243. if(FAILED(hres))
  244. break;
  245. bool bDone = false;
  246. if(hres == WBEM_S_FALSE)
  247. bDone = true;
  248. //
  249. // Check if this query has been cancelled
  250. //
  251. if(m_bCancelled)
  252. {
  253. DEBUGTRACE((LOG_ESS, "Aborting polling query '%S' because its "
  254. "subscription is cancelled\n", m_strQuery));
  255. return WBEM_E_CALL_CANCELLED;
  256. }
  257. for(DWORD dw = 0; dw < dwNumRet; dw++)
  258. {
  259. _IWmiObject* pObj = NULL;
  260. aBuffer[dw]->QueryInterface(IID__IWmiObject, (void**)&pObj);
  261. CReleaseMe rm(pObj);
  262. hres = ProcessObject(pObj);
  263. if(FAILED(hres))
  264. break;
  265. }
  266. for( dw=0; dw < dwNumRet; dw++ )
  267. {
  268. aBuffer[dw]->Release();
  269. }
  270. if(dw < dwNumRet || FAILED(hres))
  271. break;
  272. if(bDone)
  273. break;
  274. }
  275. ProcessQueryDone(hres, NULL);
  276. if(FAILED(hres))
  277. {
  278. ERRORTRACE((LOG_ESS, "Polling query '%S' failed with error code 0x%X. "
  279. "Will retry at next polling interval\n", m_strQuery, hres));
  280. return hres;
  281. }
  282. else
  283. {
  284. DEBUGTRACE((LOG_ESS, "Polling query '%S' done\n", m_strQuery));
  285. }
  286. return WBEM_S_NO_ERROR;
  287. }
  288. BOOL CBasePollingInstruction::CompareTo(CBasePollingInstruction* pOther)
  289. {
  290. if(wcscmp(pOther->m_strLanguage, m_strLanguage)) return FALSE;
  291. if(wcscmp(pOther->m_strQuery, m_strQuery)) return FALSE;
  292. if(pOther->m_Interval.GetMilliseconds() != m_Interval.GetMilliseconds())
  293. return FALSE;
  294. return TRUE;
  295. }
  296. //***************************************************************************
  297. //***************************************************************************
  298. //***************************************************************************
  299. CPollingInstruction::CCachedObject::CCachedObject(_IWmiObject* pObject)
  300. : m_pObject(pObject), m_strPath(NULL)
  301. {
  302. g_lNumPollingCachedObjects++;
  303. // Extract the path
  304. // ================
  305. VARIANT v;
  306. VariantInit(&v);
  307. if (SUCCEEDED(pObject->Get(L"__RELPATH", 0, &v, NULL, NULL)))
  308. m_strPath = V_BSTR(&v);
  309. // Variant intentionally not cleared
  310. pObject->AddRef();
  311. }
  312. CPollingInstruction::CCachedObject::~CCachedObject()
  313. {
  314. g_lNumPollingCachedObjects--;
  315. if(m_pObject)
  316. m_pObject->Release();
  317. SysFreeString(m_strPath);
  318. }
  319. int __cdecl CPollingInstruction::CCachedObject::compare(const void* pelem1,
  320. const void* pelem2)
  321. {
  322. CCachedObject* p1 = *(CCachedObject**)pelem1;
  323. CCachedObject* p2 = *(CCachedObject**)pelem2;
  324. return wbem_wcsicmp(p1->m_strPath, p2->m_strPath);
  325. }
  326. CPollingInstruction::CPollingInstruction(CEssNamespace* pNamespace)
  327. : CBasePollingInstruction(pNamespace), m_papCurrentObjects(NULL),
  328. m_dwEventMask(0), m_pDest(NULL), m_papPrevObjects(NULL), m_pUser(NULL)
  329. {
  330. g_lNumPollingInstructions++;
  331. }
  332. CPollingInstruction::~CPollingInstruction()
  333. {
  334. g_lNumPollingInstructions--;
  335. SubtractMemory(m_papCurrentObjects);
  336. delete m_papCurrentObjects;
  337. ResetPrevious();
  338. if(m_pDest)
  339. m_pDest->Release();
  340. if(m_pUser)
  341. g_quotas.FreeUser(m_pUser);
  342. }
  343. // This class represents a postponed request to execute a query
  344. class CPostponedQuery : public CPostponedRequest
  345. {
  346. protected:
  347. CPollingInstruction* m_pInst;
  348. public:
  349. CPostponedQuery(CPollingInstruction* pInst) : m_pInst(pInst)
  350. {
  351. m_pInst->AddRef();
  352. }
  353. ~CPostponedQuery()
  354. {
  355. m_pInst->Release();
  356. }
  357. HRESULT Execute(CEssNamespace* pNamespace)
  358. {
  359. return m_pInst->FirstExecute();
  360. }
  361. };
  362. HRESULT CPollingInstruction::FirstExecute()
  363. {
  364. //
  365. // Check if our filter has any hope
  366. //
  367. if(FAILED(m_pDest->GetPollingError()))
  368. {
  369. DEBUGTRACE((LOG_ESS, "Polling query '%S' will not be attempted as \n"
  370. "another polling query related to the same subscription has failed "
  371. "to start with error code 0x%X, deactivating subscription\n",
  372. m_strQuery, m_pDest->GetPollingError()));
  373. return m_pDest->GetPollingError();
  374. }
  375. if(m_bCancelled)
  376. {
  377. DEBUGTRACE((LOG_ESS, "Aborting polling query '%S' because its "
  378. "subscription is cancelled\n", m_strQuery));
  379. return WBEM_E_CALL_CANCELLED;
  380. }
  381. // note that if this function fails, then it will be destroyed when
  382. // the postponed query releases it reference. If this function succeedes
  383. // then tss will hold onto a reference and keep it alive.
  384. //
  385. m_papCurrentObjects = _new CCachedArray;
  386. if( m_papCurrentObjects == NULL )
  387. {
  388. return WBEM_E_OUT_OF_MEMORY;
  389. }
  390. HRESULT hres = ExecQuery();
  391. if ( FAILED(hres) )
  392. {
  393. ERRORTRACE((LOG_ESS, "Polling query '%S' failed on the first try with "
  394. "error code 0x%X.\nDeactivating subscription\n", m_strQuery, hres));
  395. m_pDest->SetPollingError(hres);
  396. return hres;
  397. }
  398. //
  399. // add this instruction to the scheduler
  400. //
  401. if(!CreateTimerQueueTimer(&m_hTimer, NULL,
  402. (WAITORTIMERCALLBACK)&staticTimerCallback,
  403. (void*)(CBasePollingInstruction*)this,
  404. m_Interval.GetMilliseconds(),
  405. 0,
  406. WT_EXECUTELONGFUNCTION))
  407. {
  408. long lRes = GetLastError();
  409. ERRORTRACE((LOG_ESS, "ESS is unable to schedule a timer instruction "
  410. "with the system (error code %d). This operation will be "
  411. "aborted.\n", lRes));
  412. return WBEM_E_FAILED;
  413. }
  414. return WBEM_S_NO_ERROR;
  415. }
  416. HRESULT CPollingInstruction::Initialize(LPCWSTR wszLanguage, LPCWSTR wszQuery,
  417. DWORD dwMsInterval, DWORD dwEventMask,
  418. CEventFilter* pDest)
  419. {
  420. HRESULT hres;
  421. hres = CBasePollingInstruction::Initialize(wszLanguage, wszQuery,
  422. dwMsInterval);
  423. if(FAILED(hres))
  424. return hres;
  425. m_dwEventMask = dwEventMask;
  426. m_pDest = pDest;
  427. pDest->AddRef();
  428. hres = g_quotas.FindUser(pDest, &m_pUser);
  429. if(FAILED(hres))
  430. return hres;
  431. return WBEM_S_NO_ERROR;
  432. }
  433. HRESULT CPollingInstruction::ProcessObject(_IWmiObject* pObj)
  434. {
  435. HRESULT hres;
  436. //
  437. // Make sure that the current object list exists
  438. //
  439. if(m_papCurrentObjects == NULL)
  440. {
  441. m_papCurrentObjects = new CCachedArray;
  442. if(m_papCurrentObjects == NULL)
  443. return WBEM_E_OUT_OF_MEMORY;
  444. }
  445. //
  446. // Check if this query has been cancelled
  447. //
  448. if(m_bCancelled)
  449. {
  450. DEBUGTRACE((LOG_ESS, "Aborting polling query '%S' because its "
  451. "subscription is cancelled\n", m_strQuery));
  452. return WBEM_E_CALL_CANCELLED;
  453. }
  454. //
  455. // Check quotas
  456. //
  457. DWORD dwSize = ComputeObjectMemory(pObj);
  458. hres = g_quotas.IncrementQuotaIndexByUser(ESSQ_POLLING_MEMORY,
  459. m_pUser, dwSize);
  460. if(FAILED(hres))
  461. {
  462. ERRORTRACE((LOG_ESS, "Aborting polling query '%S' because the quota "
  463. "for memory used by polling is exceeded\n", m_strQuery));
  464. return hres;
  465. }
  466. //
  467. // Add the object to the current list
  468. //
  469. CCachedObject* pRecord = _new CCachedObject(pObj);
  470. if(pRecord == NULL || !pRecord->IsValid())
  471. {
  472. delete pRecord;
  473. return WBEM_E_OUT_OF_MEMORY;
  474. }
  475. if(m_papCurrentObjects->Add(pRecord) < 0)
  476. {
  477. delete pRecord;
  478. return WBEM_E_OUT_OF_MEMORY;
  479. }
  480. return WBEM_S_NO_ERROR;
  481. }
  482. DWORD CPollingInstruction::ComputeObjectMemory(_IWmiObject* pObj)
  483. {
  484. DWORD dwSize = 0;
  485. HRESULT hres = pObj->GetObjectMemory( NULL, 0, &dwSize );
  486. if (FAILED(hres) && hres != WBEM_E_BUFFER_TOO_SMALL )
  487. {
  488. return hres;
  489. }
  490. return dwSize;
  491. }
  492. HRESULT CPollingInstruction::ProcessQueryDone( HRESULT hresQuery,
  493. IWbemClassObject* pErrorObj)
  494. {
  495. HRESULT hres;
  496. if(FAILED(hresQuery))
  497. {
  498. //
  499. // If the query failed, retain the previous poll
  500. // result --- that's the best we can do
  501. //
  502. SubtractMemory(m_papCurrentObjects);
  503. delete m_papCurrentObjects;
  504. m_papCurrentObjects = NULL;
  505. //
  506. // Report subscription error
  507. //
  508. return WBEM_S_FALSE;
  509. }
  510. else if ( m_papCurrentObjects == NULL )
  511. {
  512. //
  513. // Query came back empty --- emulate by creating an empty
  514. // m_papCurrentObjects
  515. //
  516. m_papCurrentObjects = new CCachedArray;
  517. if(m_papCurrentObjects == NULL)
  518. return WBEM_E_OUT_OF_MEMORY;
  519. }
  520. //
  521. // Sort the objects by path
  522. //
  523. qsort((void*)m_papCurrentObjects->GetArrayPtr(),
  524. m_papCurrentObjects->GetSize(),
  525. sizeof(CCachedObject*), CCachedObject::compare);
  526. //
  527. // At this point, m_papCurrentObjects contains the sorted results of the
  528. // current query. If this is not the first time, m_papPrevObjects
  529. // contains the previous result. If first time, then all done for now.
  530. //
  531. if( m_papPrevObjects == NULL )
  532. {
  533. m_papPrevObjects = m_papCurrentObjects;
  534. m_papCurrentObjects = NULL;
  535. return WBEM_S_NO_ERROR;
  536. }
  537. //
  538. // Now is the time to compare
  539. //
  540. long lOldIndex = 0, lNewIndex = 0;
  541. while(lNewIndex < m_papCurrentObjects->GetSize() &&
  542. lOldIndex < m_papPrevObjects->GetSize())
  543. {
  544. int nCompare = wbem_wcsicmp(
  545. m_papCurrentObjects->GetAt(lNewIndex)->m_strPath,
  546. m_papPrevObjects->GetAt(lOldIndex)->m_strPath);
  547. if(nCompare < 0)
  548. {
  549. // The _new object is not in the old array --- object created
  550. // =========================================================
  551. if(m_dwEventMask & (1 << e_EventTypeInstanceCreation))
  552. {
  553. RaiseCreationEvent(m_papCurrentObjects->GetAt(lNewIndex));
  554. }
  555. lNewIndex++;
  556. }
  557. else if(nCompare > 0)
  558. {
  559. // The old object is not in the _new array --- object deleted
  560. // =========================================================
  561. if(m_dwEventMask & (1 << e_EventTypeInstanceDeletion))
  562. {
  563. RaiseDeletionEvent(m_papPrevObjects->GetAt(lOldIndex));
  564. }
  565. lOldIndex++;
  566. }
  567. else
  568. {
  569. if(m_dwEventMask & (1 << e_EventTypeInstanceModification))
  570. {
  571. // Compare the objects themselves
  572. // ==============================
  573. hres = m_papCurrentObjects->GetAt(lNewIndex)->m_pObject->
  574. CompareTo(
  575. WBEM_FLAG_IGNORE_CLASS | WBEM_FLAG_IGNORE_OBJECT_SOURCE,
  576. m_papPrevObjects->GetAt(lOldIndex)->m_pObject);
  577. if(hres != S_OK)
  578. {
  579. // The objects are not the same --- object changed
  580. // ===============================================
  581. RaiseModificationEvent(
  582. m_papCurrentObjects->GetAt(lNewIndex),
  583. m_papPrevObjects->GetAt(lOldIndex));
  584. }
  585. }
  586. lOldIndex++; lNewIndex++;
  587. }
  588. }
  589. if(m_dwEventMask & (1 << e_EventTypeInstanceDeletion))
  590. {
  591. while(lOldIndex < m_papPrevObjects->GetSize())
  592. {
  593. RaiseDeletionEvent(m_papPrevObjects->GetAt(lOldIndex));
  594. lOldIndex++;
  595. }
  596. }
  597. if(m_dwEventMask & (1 << e_EventTypeInstanceCreation))
  598. {
  599. while(lNewIndex < m_papCurrentObjects->GetSize())
  600. {
  601. RaiseCreationEvent(m_papCurrentObjects->GetAt(lNewIndex));
  602. lNewIndex++;
  603. }
  604. }
  605. // Replace the cached array with the new one
  606. // =========================================
  607. ResetPrevious();
  608. m_papPrevObjects = m_papCurrentObjects;
  609. m_papCurrentObjects = NULL;
  610. return S_OK;
  611. }
  612. HRESULT CPollingInstruction::RaiseCreationEvent(CCachedObject* pNewObj)
  613. {
  614. IWbemClassObject* _pObj = pNewObj->m_pObject;
  615. CEventRepresentation Event;
  616. Event.type = e_EventTypeInstanceCreation;
  617. Event.wsz1 = (LPWSTR)m_pNamespace->GetName();
  618. Event.wsz2 = GetObjectClass(pNewObj);
  619. Event.wsz3 = NULL;
  620. Event.nObjects = 1;
  621. Event.apObjects = &_pObj;
  622. IWbemEvent* pEventObj;
  623. if(FAILED(Event.MakeWbemObject(m_pNamespace, &pEventObj)))
  624. return WBEM_E_OUT_OF_MEMORY;
  625. // BUGBUG: context
  626. HRESULT hres = m_pDest->Indicate(1, &pEventObj, NULL);
  627. pEventObj->Release();
  628. SysFreeString(Event.wsz2);
  629. return hres;
  630. }
  631. HRESULT CPollingInstruction::RaiseDeletionEvent(CCachedObject* pOldObj)
  632. {
  633. IWbemClassObject* _pObj = pOldObj->m_pObject;
  634. CEventRepresentation Event;
  635. Event.type = e_EventTypeInstanceDeletion;
  636. Event.wsz1 = (LPWSTR)m_pNamespace->GetName();
  637. Event.wsz2 = GetObjectClass(pOldObj);
  638. Event.wsz3 = NULL;
  639. Event.nObjects = 1;
  640. Event.apObjects = &_pObj;
  641. IWbemEvent* pEventObj;
  642. if(FAILED(Event.MakeWbemObject(m_pNamespace, &pEventObj)))
  643. return WBEM_E_OUT_OF_MEMORY;
  644. // BUGBUG: context
  645. HRESULT hres = m_pDest->Indicate(1, &pEventObj, NULL);
  646. pEventObj->Release();
  647. SysFreeString(Event.wsz2);
  648. return hres;
  649. }
  650. HRESULT CPollingInstruction::RaiseModificationEvent(CCachedObject* pNewObj,
  651. CCachedObject* pOldObj)
  652. {
  653. IWbemClassObject* apObjects[2];
  654. CEventRepresentation Event;
  655. Event.type = e_EventTypeInstanceModification;
  656. Event.wsz1 = (LPWSTR)m_pNamespace->GetName();
  657. Event.wsz2 = GetObjectClass(pNewObj);
  658. Event.wsz3 = NULL;
  659. Event.nObjects = 2;
  660. Event.apObjects = (IWbemClassObject**)apObjects;
  661. Event.apObjects[0] = pNewObj->m_pObject;
  662. Event.apObjects[1] = (pOldObj?pOldObj->m_pObject:NULL);
  663. IWbemEvent* pEventObj;
  664. if(FAILED(Event.MakeWbemObject(m_pNamespace, &pEventObj)))
  665. return WBEM_E_OUT_OF_MEMORY;
  666. // BUGBUG: context
  667. HRESULT hres = m_pDest->Indicate(1, &pEventObj, NULL);
  668. pEventObj->Release();
  669. SysFreeString(Event.wsz2);
  670. return hres;
  671. }
  672. HRESULT CPollingInstruction::ResetPrevious()
  673. {
  674. HRESULT hres;
  675. SubtractMemory(m_papPrevObjects);
  676. delete m_papPrevObjects;
  677. m_papPrevObjects = NULL;
  678. return S_OK;
  679. }
  680. HRESULT CPollingInstruction::SubtractMemory(CCachedArray* pArray)
  681. {
  682. HRESULT hres;
  683. if(pArray == NULL)
  684. return S_FALSE;
  685. for(int i = 0; i < pArray->GetSize(); i++)
  686. {
  687. _IWmiObject* pObj = pArray->GetAt(i)->m_pObject;
  688. DWORD dwSize = ComputeObjectMemory(pObj);
  689. hres = g_quotas.DecrementQuotaIndexByUser(ESSQ_POLLING_MEMORY,
  690. m_pUser, dwSize);
  691. if(FAILED(hres))
  692. return hres;
  693. }
  694. return S_OK;
  695. }
  696. SYSFREE_ME BSTR CPollingInstruction::GetObjectClass(CCachedObject* pObj)
  697. {
  698. VARIANT v;
  699. VariantInit(&v);
  700. if ( FAILED( pObj->m_pObject->Get(L"__CLASS", 0, &v, NULL, NULL) ) )
  701. {
  702. return NULL;
  703. }
  704. return V_BSTR(&v);
  705. }
  706. //*****************************************************************************
  707. //*****************************************************************************
  708. //
  709. // P o l l e r
  710. //
  711. //*****************************************************************************
  712. //*****************************************************************************
  713. CPoller::CPoller(CEssNamespace* pNamespace)
  714. : m_pNamespace(pNamespace), m_bInResync(FALSE)
  715. {
  716. }
  717. CPoller::~CPoller()
  718. {
  719. }
  720. void CPoller::Clear()
  721. {
  722. CInstructionMap::iterator it = m_mapInstructions.begin();
  723. while(it != m_mapInstructions.end())
  724. {
  725. // Release the refcount this holds on the instructioin
  726. // ===================================================
  727. it->first->Cancel();
  728. it->first->DeleteTimer();
  729. it->first->Release();
  730. it = m_mapInstructions.erase(it);
  731. }
  732. }
  733. HRESULT CPoller::ActivateFilter(CEventFilter* pDest,
  734. LPCWSTR wszQuery, QL_LEVEL_1_RPN_EXPRESSION* pExpr)
  735. {
  736. // Check what kind of events it is looking for
  737. // ===========================================
  738. DWORD dwEventMask = CEventRepresentation::GetTypeMaskFromName(
  739. pExpr->bsClassName);
  740. if((dwEventMask &
  741. ((1 << e_EventTypeInstanceCreation) |
  742. (1 << e_EventTypeInstanceDeletion) |
  743. (1 << e_EventTypeInstanceModification)
  744. )
  745. ) == 0
  746. )
  747. {
  748. // This registration does not involve instance-related events and
  749. // therefore there is no polling involved
  750. // ==============================================================
  751. return WBEM_S_FALSE;
  752. }
  753. // The query is looking for instance-change events. See what classes
  754. // of objects it is interested in.
  755. // =================================================================
  756. CClassInfoArray* paInfos;
  757. HRESULT hres = m_Analyser.GetPossibleInstanceClasses(pExpr, paInfos);
  758. if(FAILED(hres)) return hres;
  759. CDeleteMe<CClassInfoArray> dm2(paInfos);
  760. if(!paInfos->IsLimited())
  761. {
  762. // Analyser could not find any limits on the possible classes.
  763. // Rephrase that as all children of ""
  764. // ===========================================================
  765. CClassInformation* pNewInfo = _new CClassInformation;
  766. if(pNewInfo == NULL)
  767. return WBEM_E_OUT_OF_MEMORY;
  768. pNewInfo->m_wszClassName = NULL;
  769. pNewInfo->m_bIncludeChildren = TRUE;
  770. paInfos->AddClass(pNewInfo);
  771. paInfos->SetLimited(TRUE);
  772. }
  773. // See if it is looking for any dynamic classes.
  774. // =============================================
  775. for(int i = 0; i < paInfos->GetNumClasses(); i++)
  776. {
  777. CClassInfoArray aNonProvided;
  778. hres = ListNonProvidedClasses(
  779. paInfos->GetClass(i), dwEventMask, aNonProvided);
  780. if(FAILED(hres))
  781. {
  782. ERRORTRACE((LOG_ESS,"Failed searching for classes to poll.\n"
  783. "Class name: %S, Error code: %X\n\n",
  784. paInfos->GetClass(i)->m_wszClassName, hres));
  785. return hres;
  786. }
  787. // Increment our quotas if necessary.
  788. DWORD nClasses = aNonProvided.GetNumClasses();
  789. if (nClasses)
  790. {
  791. if (FAILED(hres = g_quotas.IncrementQuotaIndex(
  792. ESSQ_POLLING_INSTRUCTIONS, pDest, nClasses)))
  793. {
  794. return hres;
  795. }
  796. }
  797. // Institute polling for each class
  798. // ================================
  799. for(int j = 0; j < nClasses; j++)
  800. {
  801. // We have an instance-change event registration where dynamic
  802. // instances are involved. Check if tolerance is specified
  803. // ===========================================================
  804. if(pExpr->Tolerance.m_bExact ||
  805. pExpr->Tolerance.m_fTolerance == 0)
  806. {
  807. return WBEMESS_E_REGISTRATION_TOO_PRECISE;
  808. }
  809. // Tolerance is there. Get the right query for this class
  810. // ======================================================
  811. LPWSTR wszThisQuery = NULL;
  812. hres = m_Analyser.GetLimitingQueryForInstanceClass(
  813. pExpr, *aNonProvided.GetClass(j), wszThisQuery);
  814. CVectorDeleteMe<WCHAR> vdm1(wszThisQuery);
  815. if(FAILED(hres))
  816. {
  817. ERRORTRACE((LOG_ESS,"ERROR: Limiting query extraction failed.\n"
  818. "Original query: %S\nClass: %S\nError code: %X\n",
  819. wszQuery, aNonProvided.GetClass(j)->m_wszClassName, hres));
  820. return hres;
  821. }
  822. DEBUGTRACE((LOG_ESS,"Instituting polling query %S to satisfy event"
  823. " query %S\n", wszThisQuery, wszQuery));
  824. DWORD dwMs = pExpr->Tolerance.m_fTolerance * 1000;
  825. CWbemPtr<CPollingInstruction> pInst;
  826. pInst = _new CPollingInstruction(m_pNamespace);
  827. if(pInst == NULL)
  828. return WBEM_E_OUT_OF_MEMORY;
  829. hres = pInst->Initialize( L"WQL",
  830. wszThisQuery,
  831. dwMs,
  832. aNonProvided.GetClass(j)->m_dwEventMask,
  833. pDest);
  834. if ( SUCCEEDED(hres) )
  835. {
  836. hres = AddInstruction( (DWORD_PTR)pDest, pInst );
  837. }
  838. if ( FAILED(hres) )
  839. {
  840. ERRORTRACE((LOG_ESS,
  841. "ERROR: Polling instruction initialization failed\n"
  842. "Query: %S\nError code: %X\n\n", wszThisQuery, hres));
  843. return hres;
  844. }
  845. }
  846. }
  847. return WBEM_S_NO_ERROR;
  848. }
  849. HRESULT CPoller::AddInstruction( DWORD_PTR dwKey, CPollingInstruction* pInst )
  850. {
  851. HRESULT hr;
  852. CInCritSec ics(&m_cs);
  853. if( m_bInResync )
  854. {
  855. // Search for the instruction in the map
  856. // =====================================
  857. CInstructionMap::iterator it;
  858. for( it=m_mapInstructions.begin(); it != m_mapInstructions.end(); it++)
  859. {
  860. //
  861. // if the filter key is the same and the instructions have the
  862. // same queries, then there is a match. It is not enough to
  863. // do just the filter key, since there can be multiple instructions
  864. // per filter, and it is not enough to do just the instruction
  865. // comparison since multiple filters can have the same polling
  866. // instruction queries. Since there can never be multiple
  867. // instructions with the same query for the same filter,
  868. // comparing both works.
  869. //
  870. if( it->second.m_dwFilterId == dwKey &&
  871. it->first->CompareTo( pInst ) )
  872. {
  873. //
  874. // Found it, set to active but DO NOT add to the generator.
  875. // it is already there
  876. //
  877. it->second.m_bActive = TRUE;
  878. return WBEM_S_FALSE;
  879. }
  880. }
  881. }
  882. //
  883. // add to the instruction to the map.
  884. //
  885. FilterInfo Info;
  886. Info.m_dwFilterId = dwKey;
  887. Info.m_bActive = TRUE;
  888. try
  889. {
  890. m_mapInstructions[pInst] = Info;
  891. }
  892. catch(CX_MemoryException)
  893. {
  894. return WBEM_E_OUT_OF_MEMORY;
  895. }
  896. pInst->AddRef();
  897. //
  898. // Postpone the first execution of the query.
  899. // 1. Execution may not be done here, because the namespace is
  900. // locked
  901. // 2. Execution may not be done asynchronously, because we
  902. // must get a baseline reading before returning to the
  903. // client.
  904. //
  905. CPostponedList* pList = GetCurrentPostponedList();
  906. _DBG_ASSERT( pList != NULL );
  907. CPostponedQuery* pReq = new CPostponedQuery( pInst );
  908. if ( pList != NULL )
  909. {
  910. hr = pList->AddRequest( m_pNamespace, pReq );
  911. if ( FAILED(hr) )
  912. {
  913. delete pReq;
  914. }
  915. }
  916. else
  917. {
  918. hr = WBEM_E_OUT_OF_MEMORY;
  919. }
  920. if ( FAILED(hr) )
  921. {
  922. pInst->Release();
  923. m_mapInstructions.erase( pInst );
  924. }
  925. return hr;
  926. }
  927. HRESULT CPoller::DeactivateFilter(CEventFilter* pDest)
  928. {
  929. CInCritSec ics(&m_cs);
  930. DWORD_PTR dwKey = (DWORD_PTR)pDest;
  931. // Remove it from the map
  932. // ======================
  933. CInstructionMap::iterator it = m_mapInstructions.begin();
  934. DWORD nItems = 0;
  935. while(it != m_mapInstructions.end())
  936. {
  937. if(it->second.m_dwFilterId == dwKey)
  938. {
  939. CBasePollingInstruction* pInst = it->first;
  940. //
  941. // First, cancel the instruction so that if it is executing, it will
  942. // abort at the earliest convenience
  943. //
  944. pInst->Cancel();
  945. //
  946. // Then, deactivate the timer. This will block until the
  947. // instruction has finished executing, if it is currently doing so
  948. //
  949. pInst->DeleteTimer();
  950. //
  951. // Now we are safe --- release the instruction.
  952. //
  953. it = m_mapInstructions.erase(it);
  954. pInst->Release();
  955. nItems++;
  956. }
  957. else it++;
  958. }
  959. // Release our quotas if needed.
  960. if (nItems)
  961. g_quotas.DecrementQuotaIndex(ESSQ_POLLING_INSTRUCTIONS, pDest, nItems);
  962. return WBEM_S_NO_ERROR;
  963. }
  964. HRESULT CPoller::ListNonProvidedClasses(IN CClassInformation* pInfo,
  965. IN DWORD dwDesiredMask,
  966. OUT CClassInfoArray& aNonProvided)
  967. {
  968. HRESULT hres;
  969. aNonProvided.Clear();
  970. // Get the class itself
  971. // ====================
  972. IWbemServices* pNamespace;
  973. hres = m_pNamespace->GetNamespacePointer(&pNamespace);
  974. if(FAILED(hres))
  975. return hres;
  976. CReleaseMe rm0(pNamespace);
  977. IWbemClassObject* pClass = NULL;
  978. hres = pNamespace->GetObject(pInfo->m_wszClassName, 0,
  979. GetCurrentEssContext(), &pClass, NULL);
  980. if(FAILED(hres))
  981. return hres;
  982. CReleaseMe rm1(pClass);
  983. if(IsClassDynamic(pClass))
  984. {
  985. AddDynamicClass(pClass, dwDesiredMask, aNonProvided);
  986. return WBEM_S_NO_ERROR;
  987. }
  988. // Enumerate all its descendants
  989. // =============================
  990. IEnumWbemClassObject* pEnum;
  991. hres = pNamespace->CreateClassEnum(pInfo->m_wszClassName,
  992. WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY |
  993. ((pInfo->m_bIncludeChildren)?WBEM_FLAG_DEEP:WBEM_FLAG_SHALLOW),
  994. GetCurrentEssContext(), &pEnum);
  995. if(FAILED(hres)) return hres;
  996. CReleaseMe rm3(pEnum);
  997. IWbemClassObject* pChild = NULL;
  998. DWORD dwNumRet;
  999. while(SUCCEEDED(pEnum->Next(INFINITE, 1, &pChild, &dwNumRet)) && dwNumRet > 0)
  1000. {
  1001. // Check if this one is dynamic
  1002. // ============================
  1003. if(IsClassDynamic(pChild))
  1004. {
  1005. AddDynamicClass(pChild, dwDesiredMask, aNonProvided);
  1006. }
  1007. pChild->Release();
  1008. pChild = NULL;
  1009. }
  1010. return WBEM_S_NO_ERROR;
  1011. }
  1012. BOOL CPoller::AddDynamicClass(IWbemClassObject* pClass, DWORD dwDesiredMask,
  1013. OUT CClassInfoArray& aNonProvided)
  1014. {
  1015. // Check to see if all desired events are provided
  1016. // ===============================================
  1017. DWORD dwProvidedMask = m_pNamespace->GetProvidedEventMask(pClass);
  1018. DWORD dwRemainingMask = ((~dwProvidedMask) & dwDesiredMask);
  1019. if(dwRemainingMask)
  1020. {
  1021. // Add it to the array of classes to poll
  1022. // ======================================
  1023. CClassInformation* pNewInfo = _new CClassInformation;
  1024. if(pNewInfo == NULL)
  1025. return WBEM_E_OUT_OF_MEMORY;
  1026. VARIANT v;
  1027. VariantInit(&v);
  1028. pClass->Get(L"__CLASS", 0, &v, NULL, NULL);
  1029. pNewInfo->m_wszClassName = CloneWstr(V_BSTR(&v));
  1030. if(pNewInfo->m_wszClassName == NULL)
  1031. {
  1032. delete pNewInfo;
  1033. return WBEM_E_OUT_OF_MEMORY;
  1034. }
  1035. VariantClear(&v);
  1036. pNewInfo->m_bIncludeChildren = FALSE;
  1037. pNewInfo->m_dwEventMask = dwRemainingMask;
  1038. pNewInfo->m_pClass = pClass;
  1039. pClass->AddRef();
  1040. if(!aNonProvided.AddClass(pNewInfo))
  1041. {
  1042. delete pNewInfo;
  1043. return WBEM_E_OUT_OF_MEMORY;
  1044. }
  1045. return TRUE;
  1046. }
  1047. return FALSE;
  1048. }
  1049. BOOL CPoller::IsClassDynamic(IWbemClassObject* pClass)
  1050. {
  1051. HRESULT hres;
  1052. IWbemQualifierSet* pSet;
  1053. hres = pClass->GetQualifierSet(&pSet);
  1054. if(FAILED(hres))
  1055. return TRUE;
  1056. VARIANT v;
  1057. VariantInit(&v);
  1058. hres = pSet->Get(L"dynamic", 0, &v, NULL);
  1059. pSet->Release();
  1060. if(FAILED(hres)) return FALSE;
  1061. BOOL bRes = V_BOOL(&v);
  1062. VariantClear(&v);
  1063. return bRes;
  1064. }
  1065. HRESULT CPoller::VirtuallyStopPolling()
  1066. {
  1067. CInCritSec ics(&m_cs);
  1068. // Mark all polling instructions in the map with the key of 0xFFFFFFFF
  1069. // This will not stop them from working, but will separate them from the
  1070. // new ones.
  1071. // =====================================================================
  1072. for(CInstructionMap::iterator it = m_mapInstructions.begin();
  1073. it != m_mapInstructions.end(); it++)
  1074. {
  1075. it->second.m_bActive = FALSE;
  1076. }
  1077. m_bInResync = TRUE;
  1078. return WBEM_S_NO_ERROR;
  1079. }
  1080. HRESULT CPoller::CancelUnnecessaryPolling()
  1081. {
  1082. CInCritSec ics(&m_cs);
  1083. // Remove it from the map
  1084. // ======================
  1085. CInstructionMap::iterator it = m_mapInstructions.begin();
  1086. while(it != m_mapInstructions.end())
  1087. {
  1088. if( !it->second.m_bActive )
  1089. {
  1090. CBasePollingInstruction* pInst = it->first;
  1091. //
  1092. // First, cancel the instruction so that if it is executing, it will
  1093. // abort at the earliest convenience
  1094. //
  1095. pInst->Cancel();
  1096. //
  1097. // Then, deactivate the timer. This will block until the
  1098. // instruction has finished executing, if it is currently doing so
  1099. //
  1100. pInst->DeleteTimer();
  1101. //
  1102. // Now we are safe --- release the instruction.
  1103. //
  1104. it = m_mapInstructions.erase(it);
  1105. pInst->Release();
  1106. }
  1107. else it++;
  1108. }
  1109. m_bInResync = FALSE;
  1110. return WBEM_S_NO_ERROR;
  1111. }
  1112. void CPoller::DumpStatistics(FILE* f, long lFlags)
  1113. {
  1114. fprintf(f, "%d polling instructions\n", m_mapInstructions.size());
  1115. }