Leaked source code of windows server 2003
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.

2422 lines
78 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. Handles incoming notifications and requests for consumers and providers
  7. Author:
  8. 16-Jan-1997 AlanWar
  9. Revision History:
  10. --*/
  11. #include <nt.h>
  12. #include "wmiump.h"
  13. #include "evntrace.h"
  14. #include "ntcsrdll.h"
  15. #include "trcapi.h"
  16. #include <strsafe.h>
  17. //
  18. // These globals are essentially parameters passed from the thread crating
  19. // the event pump. The creating thread will alloc all of these resources
  20. // so that it can know whether the pump thread will be successful or not.
  21. // If we ever wanted to be able to have multiple pump threads then we'd
  22. // need to move the globals into a structure and pass the struructure to the
  23. // pump thread.
  24. //
  25. HANDLE EtwpEventDeviceHandle;
  26. HANDLE EtwpPumpCommandEvent;
  27. HANDLE EtwpMyProcessHandle;
  28. OVERLAPPED EtwpOverlapped1, EtwpOverlapped2;
  29. PUCHAR EtwpEventBuffer1, EtwpEventBuffer2;
  30. ULONG EtwpEventBufferSize1, EtwpEventBufferSize2;
  31. //
  32. // How long to wait before the event pump thread times out. On checked
  33. // builds we want to stress the event pump and so we timeout almost
  34. // right away. On free builds we want to be more cautious so we timeout
  35. // after 5 minutes.
  36. //
  37. #if DBG
  38. #define EVENT_NOTIFICATION_WAIT 1
  39. #else
  40. #define EVENT_NOTIFICATION_WAIT (5 * 60 * 1000)
  41. #endif
  42. ULONG EtwpEventNotificationWait = EVENT_NOTIFICATION_WAIT;
  43. typedef enum
  44. {
  45. EVENT_PUMP_ZERO, // Pump thread has not been started yet
  46. EVENT_PUMP_IDLE, // Pump thread was started, but then exited
  47. EVENT_PUMP_RUNNING, // Pump thread is running
  48. EVENT_PUMP_STOPPING // Pump thread is in process of stopping
  49. } EVENTPUMPSTATE, *PEVENTPUMPSTATE;
  50. EVENTPUMPSTATE EtwpPumpState = EVENT_PUMP_ZERO;
  51. BOOLEAN EtwpNewPumpThreadPending;
  52. #define EtwpSendPumpCommand() EtwpSetEvent(EtwpPumpCommandEvent);
  53. #define EtwpIsPumpStopping() \
  54. ((EtwpPumpState == EVENT_PUMP_STOPPING) ? TRUE : FALSE)
  55. #if DBG
  56. PCHAR GuidToStringA(
  57. PCHAR s,
  58. ULONG szBuf,
  59. LPGUID piid
  60. )
  61. {
  62. GUID XGuid;
  63. if ( (s == NULL) || (piid == NULL) || (szBuf == 0) ) {
  64. return NULL;
  65. }
  66. XGuid = *piid;
  67. StringCchPrintf(s, szBuf, "%x-%x-%x-%x%x%x%x%x%x%x%x",
  68. XGuid.Data1, XGuid.Data2,
  69. XGuid.Data3,
  70. XGuid.Data4[0], XGuid.Data4[1],
  71. XGuid.Data4[2], XGuid.Data4[3],
  72. XGuid.Data4[4], XGuid.Data4[5],
  73. XGuid.Data4[6], XGuid.Data4[7]);
  74. return(s);
  75. }
  76. #endif
  77. ULONG EtwpEventPumpFromKernel(
  78. PVOID Param
  79. );
  80. void EtwpExternalNotification(
  81. NOTIFICATIONCALLBACK Callback,
  82. ULONG_PTR Context,
  83. PWNODE_HEADER Wnode
  84. )
  85. /*++
  86. Routine Description:
  87. This routine dispatches an event to the appropriate callback
  88. routine. This process only receives events from the WMI service that
  89. need to be dispatched within this process. The callback address for the
  90. specific event is passed by the wmi service in Wnode->Linkage.
  91. Arguments:
  92. Callback is address to callback
  93. Context is the context to callback with
  94. Wnode has event to deliver
  95. Return Value:
  96. --*/
  97. {
  98. try
  99. {
  100. (*Callback)(Wnode, Context);
  101. } except(EXCEPTION_EXECUTE_HANDLER) {
  102. EtwpDebugPrint(("NotificationCallbackRoutine threw exception %d\n",
  103. GetExceptionCode()));
  104. }
  105. }
  106. #ifdef MEMPHIS
  107. ULONG EtwpExternalNotificationThread(
  108. PNOTIFDELIVERYCTX NDContext
  109. )
  110. /*++
  111. Routine Description:
  112. This routine is the thread function used to deliver events to event
  113. consumers on memphis.
  114. Arguments:
  115. NDContext specifies the information about how to callback the application
  116. with the event.
  117. Return Value:
  118. --*/
  119. {
  120. EtwpExternalNotification(NDContext->Callback,
  121. NDContext->Context,
  122. NDContext->Wnode);
  123. EtwpFree(NDContext);
  124. return(0);
  125. }
  126. #endif
  127. void
  128. EtwpProcessExternalEvent(
  129. PWNODE_HEADER Wnode,
  130. ULONG WnodeSize,
  131. PVOID DeliveryInfo,
  132. ULONG_PTR DeliveryContext,
  133. ULONG NotificationFlags
  134. )
  135. {
  136. HANDLE ThreadHandle;
  137. PNOTIFDELIVERYCTX NDContext;
  138. PWNODE_HEADER *WnodePtr;
  139. BOOLEAN WnodePtrOk;
  140. PWNODE_HEADER WnodeCopy;
  141. DWORD ThreadId;
  142. BOOLEAN PostOk;
  143. ULONG Status;
  144. PVOID NotificationAddress;
  145. PVOID NotificationContext;
  146. NotificationAddress = DeliveryInfo;
  147. NotificationContext = (PVOID)DeliveryContext;
  148. if (NotificationFlags & NOTIFICATION_FLAG_CALLBACK_DIRECT)
  149. {
  150. //
  151. // Callback notifications can happen in this thread or a new
  152. // thread. It is up to the server to decide.
  153. #ifdef MEMPHIS
  154. if (NotificationFlags & DCREF_FLAG_NO_EXTRA_THREAD)
  155. {
  156. EtwpExternalNotification(
  157. (NOTIFICATIONCALLBACK)NotificationAddress,
  158. (ULONG_PTR)NotificationContext,
  159. Wnode);
  160. } else {
  161. NDContext = EtwpAlloc(FIELD_OFFSET(NOTIFDELIVERYCTX,
  162. WnodeBuffer) + WnodeSize);
  163. if (NDContext != NULL)
  164. {
  165. NDContext->Callback = (NOTIFICATIONCALLBACK)NotificationAddress;
  166. NDContext->Context = (ULONG_PTR)NotificationContext;
  167. WnodeCopy = (PWNODE_HEADER)NDContext->WnodeBuffer;
  168. memcpy(WnodeCopy, Wnode, WnodeSize);
  169. NDContext->Wnode = WnodeCopy;
  170. ThreadHandle = EtwpCreateThread(NULL,
  171. 0,
  172. EtwpExternalNotificationThread,
  173. NDContext,
  174. 0,
  175. &ThreadId);
  176. if (ThreadHandle != NULL)
  177. {
  178. EtwpCloseHandle(ThreadHandle);
  179. } else {
  180. EtwpDebugPrint(("WMI: Event dropped due to thread creation failure\n"));
  181. }
  182. } else {
  183. EtwpDebugPrint(("WMI: Event dropped due to lack of memory\n"));
  184. }
  185. }
  186. #else
  187. //
  188. // On NT we deliver events in this thread since
  189. // the service is using async rpc.
  190. EtwpExternalNotification(
  191. (NOTIFICATIONCALLBACK)NotificationAddress,
  192. (ULONG_PTR)NotificationContext,
  193. Wnode);
  194. #endif
  195. }
  196. }
  197. void
  198. EtwpEnableDisableGuid(
  199. PWNODE_HEADER Wnode,
  200. ULONG RequestCode,
  201. BOOLEAN bDelayEnable
  202. )
  203. {
  204. ULONG ActionCode;
  205. PUCHAR Buffer = (PUCHAR)Wnode;
  206. PGUIDNOTIFICATION GNEntry = NULL;
  207. ULONG i;
  208. PVOID DeliveryInfo = NULL;
  209. ULONG_PTR DeliveryContext1;
  210. WMIDPREQUEST WmiDPRequest;
  211. PVOID RequestAddress;
  212. PVOID RequestContext;
  213. ULONG Status;
  214. ULONG BufferSize = Wnode->BufferSize;
  215. GNEntry = EtwpFindAndLockGuidNotification(&Wnode->Guid, !bDelayEnable);
  216. if (GNEntry != NULL) {
  217. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  218. {
  219. EtwpEnterPMCritSection();
  220. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  221. DeliveryContext1 = GNEntry->Notifyee[i].DeliveryContext;
  222. EtwpLeavePMCritSection();
  223. if ((DeliveryInfo != NULL) &&
  224. (DeliveryContext1 != 0) &&
  225. (! EtwpIsNotifyeePendingClose(&(GNEntry->Notifyee[i] ))))
  226. {
  227. PTRACE_REG_INFO pTraceRegInfo;
  228. pTraceRegInfo = (PTRACE_REG_INFO) DeliveryContext1;
  229. WmiDPRequest = (WMIDPREQUEST) pTraceRegInfo->NotifyRoutine;
  230. RequestContext = pTraceRegInfo->NotifyContext;
  231. //
  232. // If this Enable is for a PrivateLogger and it is not up
  233. // then we need save the state and return.
  234. //
  235. if (RequestCode == WMI_ENABLE_EVENTS) {
  236. PTRACE_ENABLE_CONTEXT pContext = (PTRACE_ENABLE_CONTEXT)
  237. &Wnode->HistoricalContext;
  238. if (bDelayEnable) {
  239. if (pTraceRegInfo->EnabledState) {
  240. Wnode->HistoricalContext = pTraceRegInfo->LoggerContext;
  241. }
  242. else {
  243. continue;
  244. }
  245. }
  246. else {
  247. pTraceRegInfo->LoggerContext = Wnode->HistoricalContext;
  248. pTraceRegInfo->EnabledState = TRUE;
  249. if ( pContext->InternalFlag &
  250. EVENT_TRACE_INTERNAL_FLAG_PRIVATE ) {
  251. if (!EtwpIsPrivateLoggerOn()) {
  252. continue; // Need this for every Notifyee
  253. }
  254. }
  255. }
  256. }
  257. else if (RequestCode == WMI_DISABLE_EVENTS) {
  258. pTraceRegInfo->EnabledState = FALSE;
  259. pTraceRegInfo->LoggerContext = 0;
  260. }
  261. try
  262. {
  263. if (*WmiDPRequest != NULL) {
  264. Status = (*WmiDPRequest)(Wnode->ProviderId,
  265. RequestContext,
  266. &BufferSize,
  267. Buffer);
  268. }
  269. else
  270. Status = ERROR_WMI_DP_NOT_FOUND;
  271. } except (EXCEPTION_EXECUTE_HANDLER) {
  272. #if DBG
  273. Status = GetExceptionCode();
  274. EtwpDebugPrint(("WMI: EnableCB exception %d\n",
  275. Status));
  276. #endif
  277. Status = ERROR_WMI_DP_FAILED;
  278. }
  279. }
  280. }
  281. //
  282. // We are done with the callbacks. Go ahead
  283. // and unlock the GNEntry to indicate All clear for
  284. // unregistering, unloading etc.,
  285. //
  286. if (!bDelayEnable) {
  287. EtwpUnlockCB(GNEntry);
  288. }
  289. EtwpDereferenceGNEntry(GNEntry);
  290. }
  291. }
  292. void EtwpInternalNotification(
  293. IN PWNODE_HEADER Wnode,
  294. IN NOTIFICATIONCALLBACK Callback,
  295. IN ULONG_PTR DeliveryContext,
  296. IN BOOLEAN IsAnsi
  297. )
  298. {
  299. ULONG ActionCode;
  300. PUCHAR Buffer = (PUCHAR)Wnode;
  301. PGUIDNOTIFICATION GNEntry = NULL;
  302. ULONG i;
  303. PVOID DeliveryInfo = NULL;
  304. ULONG_PTR DeliveryContext1;
  305. //
  306. // This is an internal event, which is really a callback
  307. // from kernel mode
  308. //
  309. ActionCode = Wnode->ProviderId;
  310. // if this is a trace guid enable/disable call use the cookie
  311. // to get the address
  312. if ( (Wnode->Flags & WNODE_FLAG_TRACED_GUID) || (ActionCode == WmiMBRequest) )
  313. {
  314. switch (ActionCode) {
  315. case WmiEnableEvents:
  316. case WmiDisableEvents:
  317. {
  318. WMIDPREQUEST WmiDPRequest;
  319. PVOID RequestAddress;
  320. PVOID RequestContext;
  321. ULONG Status;
  322. ULONG BufferSize = Wnode->BufferSize;
  323. if (Wnode->BufferSize >= (sizeof(WNODE_HEADER) + sizeof(ULONG64)) ) {
  324. PULONG64 pLoggerContext = (PULONG64)(Buffer + sizeof(WNODE_HEADER));
  325. Wnode->HistoricalContext = *pLoggerContext;
  326. }
  327. else {
  328. EtwpSetLastError(ERROR_WMI_DP_FAILED);
  329. #if DBG
  330. EtwpDebugPrint(("WMI: Small Wnode %d for notifications\n",
  331. Wnode->BufferSize));
  332. #endif
  333. return;
  334. }
  335. EtwpEnableDisableGuid(Wnode, ActionCode, FALSE);
  336. break;
  337. }
  338. case WmiMBRequest:
  339. {
  340. PWMI_LOGGER_INFORMATION LoggerInfo;
  341. if (Wnode->BufferSize < (sizeof(WNODE_HEADER) + sizeof(WMI_LOGGER_INFORMATION)) )
  342. {
  343. #if DBG
  344. EtwpSetLastError(ERROR_WMI_DP_FAILED);
  345. EtwpDebugPrint(("WMI: WmiMBRequest with invalid buffer size %d\n",
  346. Wnode->BufferSize));
  347. #endif
  348. return;
  349. }
  350. LoggerInfo = (PWMI_LOGGER_INFORMATION) ((PUCHAR)Wnode +
  351. sizeof(WNODE_HEADER));
  352. GNEntry = EtwpFindAndLockGuidNotification(
  353. &LoggerInfo->Wnode.Guid,
  354. TRUE);
  355. if (GNEntry != NULL)
  356. {
  357. EtwpEnterPMCritSection();
  358. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  359. {
  360. if ((GNEntry->Notifyee[i].DeliveryInfo != NULL) &&
  361. (! EtwpIsNotifyeePendingClose(&(GNEntry->Notifyee[i]))))
  362. {
  363. //
  364. // Since only one ProcessPrivate logger is
  365. // allowed, we need to just find one entry.
  366. //
  367. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  368. DeliveryContext1 = GNEntry->Notifyee[i].DeliveryContext;
  369. break;
  370. }
  371. }
  372. EtwpLeavePMCritSection();
  373. if (DeliveryInfo != NULL)
  374. {
  375. LoggerInfo->Wnode.CountLost = Wnode->CountLost;
  376. EtwpProcessUMRequest(LoggerInfo,
  377. DeliveryInfo,
  378. Wnode->Version
  379. );
  380. }
  381. EtwpUnlockCB(GNEntry);
  382. EtwpDereferenceGNEntry(GNEntry);
  383. }
  384. break;
  385. }
  386. default:
  387. {
  388. #if DBG
  389. EtwpSetLastError(ERROR_WMI_DP_FAILED);
  390. EtwpDebugPrint(("WMI: WmiMBRequest failed. Delivery Info not found\n" ));
  391. #endif
  392. }
  393. }
  394. } else if (IsEqualGUID(&Wnode->Guid, &GUID_MOF_RESOURCE_ADDED_NOTIFICATION) ||
  395. IsEqualGUID(&Wnode->Guid, &GUID_MOF_RESOURCE_REMOVED_NOTIFICATION) )
  396. {
  397. switch (ActionCode)
  398. {
  399. case MOFEVENT_ACTION_IMAGE_PATH:
  400. case MOFEVENT_ACTION_REGISTRY_PATH:
  401. {
  402. //
  403. // We got a MOF resource added or removed notification. We have
  404. // to convert from regpath to imagepath and then get the list
  405. // of MUI image paths
  406. //
  407. EtwpProcessMofAddRemoveEvent((PWNODE_SINGLE_INSTANCE)Wnode,
  408. Callback,
  409. DeliveryContext,
  410. IsAnsi);
  411. break;
  412. }
  413. case MOFEVENT_ACTION_LANGUAGE_CHANGE:
  414. {
  415. //
  416. // This is a notification for adding or removing a language
  417. // from the system. We need to figure out which language is
  418. // coming or going and then build a list of the affected mof
  419. // resources and send mof added or removed notifications for
  420. // all mof resources
  421. //
  422. EtwpProcessLanguageAddRemoveEvent((PWNODE_SINGLE_INSTANCE)Wnode,
  423. Callback,
  424. DeliveryContext,
  425. IsAnsi);
  426. break;
  427. }
  428. default:
  429. {
  430. EtwpAssert(FALSE);
  431. }
  432. }
  433. }
  434. }
  435. void EtwpConvertEventToAnsi(
  436. PWNODE_HEADER Wnode
  437. )
  438. {
  439. PWCHAR WPtr;
  440. if (Wnode->Flags & WNODE_FLAG_ALL_DATA)
  441. {
  442. EtwpConvertWADToAnsi((PWNODE_ALL_DATA)Wnode);
  443. } else if ((Wnode->Flags & WNODE_FLAG_SINGLE_INSTANCE) ||
  444. (Wnode->Flags & WNODE_FLAG_SINGLE_ITEM)) {
  445. WPtr = (PWCHAR)OffsetToPtr(Wnode,
  446. ((PWNODE_SINGLE_INSTANCE)Wnode)->OffsetInstanceName);
  447. EtwpCountedUnicodeToCountedAnsi(WPtr, (PCHAR)WPtr);
  448. }
  449. Wnode->Flags |= WNODE_FLAG_ANSI_INSTANCENAMES;
  450. }
  451. void EtwpDeliverAllEvents(
  452. PUCHAR Buffer,
  453. ULONG BufferSize
  454. )
  455. /*++
  456. Routine Description:
  457. Arguments:
  458. Return Value:
  459. --*/
  460. {
  461. PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer;
  462. ULONG Linkage = 1;
  463. ULONG CompositeFlags;
  464. ULONG i;
  465. PGUIDNOTIFICATION GNEntry;
  466. ULONG Flags;
  467. PVOID DeliveryInfo;
  468. ULONG_PTR DeliveryContext;
  469. ULONG WnodeSize;
  470. ULONG CurrentOffset;
  471. #if DBG
  472. PWNODE_HEADER LastWnode;
  473. #endif
  474. CurrentOffset = 0;
  475. while (Linkage != 0)
  476. {
  477. //
  478. // External notifications are handled here
  479. Linkage = Wnode->Linkage;
  480. Wnode->Linkage = 0;
  481. if (Wnode->Flags & WNODE_FLAG_INTERNAL)
  482. {
  483. //
  484. // This is an internal event, which is really a callback
  485. // from kernel mode
  486. //
  487. EtwpInternalNotification(Wnode,
  488. NULL,
  489. 0,
  490. FALSE);
  491. } else {
  492. //
  493. // This is a plain old event, figure out who owns it and'
  494. // go deliver it
  495. //
  496. GNEntry = EtwpFindAndLockGuidNotification(&Wnode->Guid, TRUE);
  497. if (GNEntry != NULL)
  498. {
  499. CompositeFlags = 0;
  500. WnodeSize = Wnode->BufferSize;
  501. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  502. {
  503. EtwpEnterPMCritSection();
  504. Flags = GNEntry->Notifyee[i].Flags;
  505. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  506. DeliveryContext = GNEntry->Notifyee[i].DeliveryContext;
  507. EtwpLeavePMCritSection();
  508. if ((DeliveryInfo != NULL) &&
  509. ((Flags & NOTIFICATION_FLAG_PENDING_CLOSE) == 0) &&
  510. ((Flags & DCREF_FLAG_ANSI) == 0))
  511. {
  512. EtwpProcessExternalEvent(Wnode,
  513. WnodeSize,
  514. DeliveryInfo,
  515. DeliveryContext,
  516. Flags);
  517. }
  518. CompositeFlags |= Flags;
  519. }
  520. //
  521. // If there is any demand for ANSI events then convert
  522. // event to ansi and send it off
  523. if (CompositeFlags & DCREF_FLAG_ANSI)
  524. {
  525. //
  526. // Caller wants ansi notification - convert
  527. // instance names
  528. //
  529. EtwpConvertEventToAnsi(Wnode);
  530. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  531. {
  532. EtwpEnterPMCritSection();
  533. Flags = GNEntry->Notifyee[i].Flags;
  534. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  535. DeliveryContext = GNEntry->Notifyee[i].DeliveryContext;
  536. EtwpLeavePMCritSection();
  537. if ((DeliveryInfo != NULL) &&
  538. ((Flags & NOTIFICATION_FLAG_PENDING_CLOSE) == 0) &&
  539. (Flags & DCREF_FLAG_ANSI))
  540. {
  541. EtwpProcessExternalEvent(Wnode,
  542. WnodeSize,
  543. DeliveryInfo,
  544. DeliveryContext,
  545. Flags);
  546. }
  547. }
  548. }
  549. EtwpUnlockCB(GNEntry);
  550. EtwpDereferenceGNEntry(GNEntry);
  551. }
  552. }
  553. #if DBG
  554. LastWnode = Wnode;
  555. #endif
  556. Wnode = (PWNODE_HEADER)OffsetToPtr(Wnode, Linkage);
  557. CurrentOffset += Linkage;
  558. if (CurrentOffset >= BufferSize)
  559. {
  560. EtwpDebugPrint(("WMI: Invalid linkage field 0x%x in WNODE %p. Buffer %p, Length 0x%x\n",
  561. Linkage, LastWnode, Buffer, BufferSize));
  562. Linkage = 0;
  563. }
  564. }
  565. }
  566. LIST_ENTRY EtwpGNHead = {&EtwpGNHead, &EtwpGNHead};
  567. PLIST_ENTRY EtwpGNHeadPtr = &EtwpGNHead;
  568. BOOLEAN
  569. EtwpDereferenceGNEntry(
  570. PGUIDNOTIFICATION GNEntry
  571. )
  572. {
  573. ULONG RefCount;
  574. BOOLEAN IsFreed;
  575. #if DBG
  576. ULONG i;
  577. #endif
  578. EtwpEnterPMCritSection();
  579. RefCount = InterlockedDecrement(&GNEntry->RefCount);
  580. if (RefCount == 0)
  581. {
  582. RemoveEntryList(&GNEntry->GNList);
  583. EtwpLeavePMCritSection();
  584. #if DBG
  585. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  586. {
  587. EtwpAssert(GNEntry->Notifyee[i].DeliveryInfo == NULL);
  588. }
  589. #endif
  590. if (GNEntry->NotifyeeCount != STATIC_NOTIFYEE_COUNT)
  591. {
  592. EtwpFree(GNEntry->Notifyee);
  593. }
  594. EtwpFreeGNEntry(GNEntry);
  595. IsFreed = TRUE;
  596. } else {
  597. IsFreed = FALSE;
  598. EtwpLeavePMCritSection();
  599. }
  600. return(IsFreed);
  601. }
  602. PGUIDNOTIFICATION
  603. EtwpFindAndLockGuidNotification(
  604. LPGUID Guid,
  605. BOOLEAN bLock
  606. )
  607. /*++
  608. Routine Description:
  609. This routine finds the GUIDNOTIFICATION entry for the given Guid.
  610. The bLock argument is used to synchronize Unregistering threads
  611. with the Callback or Pump threads. We want to avoid the situation
  612. where the Unregistering thread unloading the callback code before
  613. the callback function is called. This is done by blocking the
  614. Unregister call whenever there is a callback function in progress.
  615. If the bLock is TRUE, the InProgressEvent will be reset. This will
  616. block any other threads trying to cleanup the GNEntry.
  617. Note: If bLock is TRUE, then the caller must set the InProgressEvent
  618. when it is safe to do so. (ie., after a callback).
  619. Arguments:
  620. Return Value:
  621. --*/
  622. {
  623. PLIST_ENTRY GNList;
  624. PGUIDNOTIFICATION GNEntry;
  625. EtwpEnterPMCritSection();
  626. GNList = EtwpGNHead.Flink;
  627. while (GNList != &EtwpGNHead)
  628. {
  629. GNEntry = (PGUIDNOTIFICATION)CONTAINING_RECORD(GNList,
  630. GUIDNOTIFICATION,
  631. GNList);
  632. if (IsEqualGUID(Guid, &GNEntry->Guid))
  633. {
  634. EtwpAssert(GNEntry->RefCount > 0);
  635. EtwpReferenceGNEntry(GNEntry);
  636. //
  637. // If bLock is TRUE, then we need to reset the
  638. // event so that any other thread looking up the event
  639. // blocks. The caller of this routine is responsible
  640. // for setting the Event once the callback is done.
  641. //
  642. if (bLock) {
  643. EtwpLockCB(GNEntry);
  644. }
  645. EtwpLeavePMCritSection();
  646. return(GNEntry);
  647. }
  648. GNList = GNList->Flink;
  649. }
  650. EtwpLeavePMCritSection();
  651. return(NULL);
  652. }
  653. ULONG
  654. EtwpAddToGNList(
  655. LPGUID Guid,
  656. PVOID DeliveryInfo,
  657. ULONG_PTR DeliveryContext,
  658. ULONG Flags,
  659. HANDLE GuidHandle
  660. )
  661. {
  662. PGUIDNOTIFICATION GNEntry;
  663. ULONG NewCount;
  664. PNOTIFYEE NewNotifyee;
  665. BOOLEAN AllFull;
  666. ULONG EmptySlot = 0;
  667. ULONG i;
  668. #if DBG
  669. CHAR s[MAX_PATH];
  670. #endif
  671. EtwpEnterPMCritSection();
  672. GNEntry = EtwpFindAndLockGuidNotification(Guid, FALSE);
  673. if (GNEntry == NULL)
  674. {
  675. GNEntry = EtwpAllocGNEntry();
  676. if (GNEntry == NULL)
  677. {
  678. EtwpLeavePMCritSection();
  679. return(ERROR_NOT_ENOUGH_MEMORY);
  680. }
  681. memset(GNEntry, 0, sizeof(GUIDNOTIFICATION));
  682. GNEntry->Guid = *Guid;
  683. GNEntry->RefCount = 1;
  684. GNEntry->NotifyeeCount = STATIC_NOTIFYEE_COUNT;
  685. GNEntry->Notifyee = GNEntry->StaticNotifyee;
  686. InsertHeadList(&EtwpGNHead, &GNEntry->GNList);
  687. }
  688. //
  689. // We have got a GUIDNOTIFICATION by newly allocating one or by finding
  690. // an existing one.
  691. AllFull = TRUE;
  692. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  693. {
  694. if ((GNEntry->Notifyee[i].DeliveryInfo == DeliveryInfo) &&
  695. (! EtwpIsNotifyeePendingClose(&GNEntry->Notifyee[i])))
  696. {
  697. EtwpDebugPrint(("WMI: Duplicate Notification Enable for guid %s, 0x%x\n",
  698. GuidToStringA(s, MAX_PATH, Guid), DeliveryInfo));
  699. EtwpLeavePMCritSection();
  700. EtwpDereferenceGNEntry(GNEntry);
  701. return(ERROR_WMI_ALREADY_ENABLED);
  702. } else if (AllFull && (GNEntry->Notifyee[i].DeliveryInfo == NULL)) {
  703. EmptySlot = i;
  704. AllFull = FALSE;
  705. }
  706. }
  707. if (! AllFull)
  708. {
  709. GNEntry->Notifyee[EmptySlot].DeliveryInfo = DeliveryInfo;
  710. GNEntry->Notifyee[EmptySlot].DeliveryContext = DeliveryContext;
  711. GNEntry->Notifyee[EmptySlot].Flags = Flags;
  712. GNEntry->Notifyee[EmptySlot].GuidHandle = GuidHandle;
  713. EtwpLeavePMCritSection();
  714. return(ERROR_SUCCESS);
  715. }
  716. //
  717. // All Notifyee structs are full so allocate a new chunk
  718. NewCount = GNEntry->NotifyeeCount * 2;
  719. NewNotifyee = EtwpAlloc(NewCount * sizeof(NOTIFYEE));
  720. if (NewNotifyee == NULL)
  721. {
  722. EtwpLeavePMCritSection();
  723. EtwpDereferenceGNEntry(GNEntry);
  724. return(ERROR_NOT_ENOUGH_MEMORY);
  725. }
  726. memset(NewNotifyee, 0, NewCount * sizeof(NOTIFYEE));
  727. memcpy(NewNotifyee, GNEntry->Notifyee,
  728. GNEntry->NotifyeeCount * sizeof(NOTIFYEE));
  729. if (GNEntry->NotifyeeCount != STATIC_NOTIFYEE_COUNT)
  730. {
  731. EtwpFree(GNEntry->Notifyee);
  732. }
  733. GNEntry->Notifyee = NewNotifyee;
  734. GNEntry->NotifyeeCount = NewCount;
  735. GNEntry->Notifyee[i].DeliveryInfo = DeliveryInfo;
  736. GNEntry->Notifyee[i].DeliveryContext = DeliveryContext;
  737. GNEntry->Notifyee[i].Flags = Flags;
  738. GNEntry->Notifyee[i].GuidHandle = GuidHandle;
  739. EtwpLeavePMCritSection();
  740. return(ERROR_SUCCESS);
  741. }
  742. BOOLEAN EtwpCloseNotifyee(
  743. PNOTIFYEE Notifyee,
  744. PGUIDNOTIFICATION GuidNotification
  745. )
  746. {
  747. //
  748. // This routine assumes the PM Criticial Section is held
  749. //
  750. EtwpCloseHandle(Notifyee->GuidHandle);
  751. Notifyee->DeliveryInfo = NULL;
  752. Notifyee->Flags = 0;
  753. return(EtwpDereferenceGNEntry(GuidNotification));
  754. }
  755. void EtwpMarkPendingCloseNotifyee(
  756. PNOTIFYEE Notifyee
  757. #if DBG
  758. , LPGUID Guid
  759. #endif
  760. )
  761. {
  762. WMIMARKASCLOSED MarkAsClosed;
  763. ULONG ReturnSize;
  764. NTSTATUS Status;
  765. #if DBG
  766. char s[MAX_PATH];
  767. #endif
  768. //
  769. // This routine assumes the PM Critical Section is held
  770. //
  771. //
  772. // The pump thread is running we need to
  773. // sync with it. Mark the handle as pending
  774. // closure. Call into the kernel and inform it
  775. // that the handle should no longer receive
  776. // events. The pump thread will do the dirty
  777. // work of closing the handle. Also
  778. // the pump thread will unreference the GNEntry so that
  779. // it doesn't go away until after the handle is closed.
  780. // Lastly the pump thread needs to reset the
  781. // DeliveryInfo memory to NULL so that the slot is not
  782. // reused.
  783. //
  784. Notifyee->Flags |= NOTIFICATION_FLAG_PENDING_CLOSE;
  785. WmipSetHandle3264(MarkAsClosed.Handle, Notifyee->GuidHandle);
  786. Status = EtwpSendWmiKMRequest(NULL,
  787. IOCTL_WMI_MARK_HANDLE_AS_CLOSED,
  788. &MarkAsClosed,
  789. sizeof(MarkAsClosed),
  790. NULL,
  791. 0,
  792. &ReturnSize,
  793. NULL);
  794. //
  795. // Only enable this for testing. If the request fails then it is not a
  796. // fatal situaion
  797. //
  798. // EtwpAssert(Status == ERROR_SUCCESS);
  799. }
  800. ULONG
  801. EtwpRemoveFromGNList(
  802. LPGUID Guid,
  803. PVOID DeliveryInfo
  804. )
  805. {
  806. PGUIDNOTIFICATION GNEntry;
  807. ULONG i;
  808. ULONG Count;
  809. ULONG Status;
  810. GNEntry = EtwpFindAndLockGuidNotification(Guid, FALSE);
  811. if (GNEntry != NULL)
  812. {
  813. Status = ERROR_INVALID_PARAMETER;
  814. Count = 0;
  815. EtwpEnterPMCritSection();
  816. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  817. {
  818. if (GNEntry->Notifyee[i].DeliveryInfo != NULL)
  819. {
  820. if ((GNEntry->Notifyee[i].DeliveryInfo == DeliveryInfo) &&
  821. ( ! EtwpIsNotifyeePendingClose(&GNEntry->Notifyee[i])) &&
  822. (Status != ERROR_SUCCESS))
  823. {
  824. if ((EtwpPumpState == EVENT_PUMP_ZERO) ||
  825. (EtwpPumpState == EVENT_PUMP_IDLE) )
  826. {
  827. //
  828. // If the pump thread is not running then we
  829. // don't need to worry about synchronizing with
  830. // it. We can go ahead and close the handle and
  831. // clean up the GNLIST
  832. //
  833. EtwpCloseNotifyee(&GNEntry->Notifyee[i],
  834. GNEntry);
  835. } else {
  836. //
  837. // Since the pump thread is running we need to
  838. // postpone the actual handle closure to the
  839. // pump thread.
  840. //
  841. EtwpMarkPendingCloseNotifyee(&GNEntry->Notifyee[i]
  842. #if DBG
  843. , Guid
  844. #endif
  845. );
  846. }
  847. Status = ERROR_SUCCESS;
  848. break;
  849. } else if (! EtwpIsNotifyeePendingClose(&GNEntry->Notifyee[i])) {
  850. Count++;
  851. }
  852. }
  853. }
  854. //
  855. // This hack will allow removal from the GNLIST in the case that the
  856. // passed DeliveryInfo does not match the DeliveryInfo in the GNEntry.
  857. // This is allowed only when there is only one NOTIFYEE in the GNENTRY
  858. // In the past we only supported one notifyee per guid in a process
  859. // and so we allowed the caller not to pass a valid DeliveryInfo when
  860. // unrefistering.
  861. if ((Status != ERROR_SUCCESS) &&
  862. (GNEntry->NotifyeeCount == STATIC_NOTIFYEE_COUNT) &&
  863. (Count == 1))
  864. {
  865. if ((GNEntry->Notifyee[0].DeliveryInfo != NULL) &&
  866. ( ! EtwpIsNotifyeePendingClose(&GNEntry->Notifyee[0])))
  867. {
  868. if ((EtwpPumpState == EVENT_PUMP_ZERO) ||
  869. (EtwpPumpState == EVENT_PUMP_IDLE) )
  870. {
  871. EtwpCloseNotifyee(&GNEntry->Notifyee[0],
  872. GNEntry);
  873. } else {
  874. //
  875. // Since the pump thread is running we need to
  876. // postpone the actual handle closure to the
  877. // pump thread.
  878. //
  879. EtwpMarkPendingCloseNotifyee(&GNEntry->Notifyee[0]
  880. #if DBG
  881. , Guid
  882. #endif
  883. );
  884. }
  885. Status = ERROR_SUCCESS;
  886. } else if ((GNEntry->Notifyee[1].DeliveryInfo != NULL) &&
  887. ( ! EtwpIsNotifyeePendingClose(&GNEntry->Notifyee[1]))) {
  888. if ((EtwpPumpState == EVENT_PUMP_ZERO) ||
  889. (EtwpPumpState == EVENT_PUMP_IDLE) )
  890. {
  891. EtwpCloseNotifyee(&GNEntry->Notifyee[1],
  892. GNEntry);
  893. } else {
  894. //
  895. // Since the pump thread is running we need to
  896. // postpone the actual handle closure to the
  897. // pump thread.
  898. //
  899. EtwpMarkPendingCloseNotifyee(&GNEntry->Notifyee[1]
  900. #if DBG
  901. , Guid
  902. #endif
  903. );
  904. }
  905. Status = ERROR_SUCCESS;
  906. }
  907. }
  908. EtwpLeavePMCritSection();
  909. //
  910. // Before Dereferencing the GNEntry make sure there is no
  911. // callback in progress. If this Event is set then, it is safe
  912. // to exit. If it is not set we need to wait for the callback thread
  913. // to finish the callback and set this event.
  914. //
  915. if (GNEntry->bInProgress) {
  916. #if DBG
  917. EtwpDebugPrint(("WMI: Waiting on GNEntry %x %s %d\n",
  918. GNEntry, __FILE__, __LINE__));
  919. #endif
  920. NtWaitForSingleObject(EtwpCBInProgressEvent, 0, NULL);
  921. #if DBG
  922. EtwpDebugPrint(("WMI: Done Waiting for GNEntry %x %s %d\n",
  923. GNEntry, __FILE__, __LINE__));
  924. #endif
  925. }
  926. EtwpDereferenceGNEntry(GNEntry);
  927. } else {
  928. Status = ERROR_WMI_ALREADY_DISABLED;
  929. }
  930. return(Status);
  931. }
  932. PVOID EtwpAllocDontFail(
  933. ULONG SizeNeeded,
  934. BOOLEAN *HoldCritSect
  935. )
  936. {
  937. PVOID Buffer;
  938. do
  939. {
  940. Buffer = EtwpAlloc(SizeNeeded);
  941. if (Buffer != NULL)
  942. {
  943. return(Buffer);
  944. }
  945. //
  946. // Out of memory so we'll EtwpSleep and hope that things will get
  947. // better later
  948. //
  949. if (*HoldCritSect)
  950. {
  951. //
  952. // If we are holding the PM critical section then we need
  953. // to release it. The caller is going to need to check if
  954. // the critical section was released and if so then deal
  955. // with it
  956. //
  957. *HoldCritSect = FALSE;
  958. EtwpLeavePMCritSection();
  959. }
  960. EtwpSleep(250);
  961. } while (1);
  962. }
  963. void EtwpProcessEventBuffer(
  964. PUCHAR Buffer,
  965. ULONG ReturnSize,
  966. PUCHAR *PrimaryBuffer,
  967. ULONG *PrimaryBufferSize,
  968. PUCHAR *BackupBuffer,
  969. ULONG *BackupBufferSize,
  970. BOOLEAN ReallocateBuffers
  971. )
  972. {
  973. PWNODE_TOO_SMALL WnodeTooSmall;
  974. ULONG SizeNeeded;
  975. BOOLEAN HoldCritSection;
  976. WnodeTooSmall = (PWNODE_TOO_SMALL)Buffer;
  977. if ((ReturnSize == sizeof(WNODE_TOO_SMALL)) &&
  978. (WnodeTooSmall->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL))
  979. {
  980. //
  981. // The buffer passed to kernel mode was too small
  982. // so we need to make it larger and then try the
  983. // request again.
  984. //
  985. if (ReallocateBuffers)
  986. {
  987. //
  988. // Only do this if the caller is prepared for us to
  989. // allocate a new set of buffers
  990. //
  991. SizeNeeded = WnodeTooSmall->SizeNeeded;
  992. EtwpAssert(*PrimaryBuffer != NULL);
  993. EtwpFree(*PrimaryBuffer);
  994. HoldCritSection = FALSE;
  995. *PrimaryBuffer = EtwpAllocDontFail(SizeNeeded, &HoldCritSection);
  996. *PrimaryBufferSize = SizeNeeded;
  997. EtwpAssert(*BackupBuffer != NULL);
  998. EtwpFree(*BackupBuffer);
  999. HoldCritSection = FALSE;
  1000. *BackupBuffer = EtwpAllocDontFail(SizeNeeded, &HoldCritSection);
  1001. *BackupBufferSize = SizeNeeded;
  1002. }
  1003. } else if (ReturnSize >= sizeof(WNODE_HEADER)) {
  1004. //
  1005. // The buffer return from kernel looks good so go and
  1006. // deliver the events returned
  1007. //
  1008. EtwpDeliverAllEvents(Buffer, ReturnSize);
  1009. } else {
  1010. //
  1011. // If this completes successfully then we expect a decent size, but
  1012. // we didn't get one
  1013. //
  1014. EtwpDebugPrint(("WMI: Bad size 0x%x returned for notification query %p\n",
  1015. ReturnSize, Buffer));
  1016. EtwpAssert(FALSE);
  1017. }
  1018. }
  1019. ULONG
  1020. EtwpReceiveNotifications(
  1021. IN ULONG HandleCount,
  1022. IN HANDLE *HandleList,
  1023. IN NOTIFICATIONCALLBACK Callback,
  1024. IN ULONG_PTR DeliveryContext,
  1025. IN BOOLEAN IsAnsi,
  1026. IN ULONG Action,
  1027. IN PUSER_THREAD_START_ROUTINE UserModeCallback,
  1028. IN HANDLE ProcessHandle
  1029. )
  1030. {
  1031. ULONG Status;
  1032. ULONG ReturnSize;
  1033. PWMIRECEIVENOTIFICATION RcvNotification;
  1034. ULONG RcvNotificationSize;
  1035. PUCHAR Buffer;
  1036. ULONG BufferSize;
  1037. PWNODE_TOO_SMALL WnodeTooSmall;
  1038. PWNODE_HEADER Wnode;
  1039. ULONG i;
  1040. ULONG Linkage;
  1041. EtwpInitProcessHeap();
  1042. if (HandleCount == 0)
  1043. {
  1044. EtwpSetLastError(ERROR_INVALID_PARAMETER);
  1045. return(ERROR_INVALID_PARAMETER);
  1046. }
  1047. RcvNotificationSize = sizeof(WMIRECEIVENOTIFICATION) +
  1048. ((HandleCount-1) * sizeof(HANDLE3264));
  1049. RcvNotification = EtwpAlloc(RcvNotificationSize);
  1050. if (RcvNotification != NULL)
  1051. {
  1052. Status = ERROR_SUCCESS;
  1053. RcvNotification->Action = Action;
  1054. WmipSetPVoid3264(RcvNotification->UserModeCallback, UserModeCallback);
  1055. WmipSetHandle3264(RcvNotification->UserModeProcess, ProcessHandle);
  1056. RcvNotification->HandleCount = HandleCount;
  1057. for (i = 0; i < HandleCount; i++)
  1058. {
  1059. try
  1060. {
  1061. RcvNotification->Handles[i].Handle = HandleList[i];
  1062. } except(EXCEPTION_EXECUTE_HANDLER) {
  1063. Status = ERROR_INVALID_PARAMETER;
  1064. break;
  1065. }
  1066. }
  1067. BufferSize = 0x1000;
  1068. Status = ERROR_INSUFFICIENT_BUFFER;
  1069. while (Status == ERROR_INSUFFICIENT_BUFFER)
  1070. {
  1071. Buffer = EtwpAlloc(BufferSize);
  1072. if (Buffer != NULL)
  1073. {
  1074. Status = EtwpSendWmiKMRequest(NULL,
  1075. IOCTL_WMI_RECEIVE_NOTIFICATIONS,
  1076. RcvNotification,
  1077. RcvNotificationSize,
  1078. Buffer,
  1079. BufferSize,
  1080. &ReturnSize,
  1081. NULL);
  1082. if (Status == ERROR_SUCCESS)
  1083. {
  1084. WnodeTooSmall = (PWNODE_TOO_SMALL)Buffer;
  1085. if ((ReturnSize == sizeof(WNODE_TOO_SMALL)) &&
  1086. (WnodeTooSmall->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL))
  1087. {
  1088. //
  1089. // The buffer passed to kernel mode was too small
  1090. // so we need to make it larger and then try the
  1091. // request again
  1092. //
  1093. BufferSize = WnodeTooSmall->SizeNeeded;
  1094. Status = ERROR_INSUFFICIENT_BUFFER;
  1095. } else {
  1096. //
  1097. // We got a buffer of notifications so lets go
  1098. // process them and callback the caller
  1099. //
  1100. Wnode = (PWNODE_HEADER)Buffer;
  1101. do
  1102. {
  1103. Linkage = Wnode->Linkage;
  1104. Wnode->Linkage = 0;
  1105. if (Wnode->Flags & WNODE_FLAG_INTERNAL)
  1106. {
  1107. //
  1108. // Go and process the internal
  1109. // notification
  1110. //
  1111. EtwpInternalNotification(Wnode,
  1112. Callback,
  1113. DeliveryContext,
  1114. IsAnsi);
  1115. } else {
  1116. if (IsAnsi)
  1117. {
  1118. //
  1119. // Caller wants ansi notification - convert
  1120. // instance names
  1121. //
  1122. EtwpConvertEventToAnsi(Wnode);
  1123. }
  1124. //
  1125. // Now go and deliver this event
  1126. //
  1127. EtwpExternalNotification(Callback,
  1128. DeliveryContext,
  1129. Wnode);
  1130. }
  1131. Wnode = (PWNODE_HEADER)OffsetToPtr(Wnode, Linkage);
  1132. } while (Linkage != 0);
  1133. }
  1134. }
  1135. EtwpFree(Buffer);
  1136. } else {
  1137. Status = ERROR_NOT_ENOUGH_MEMORY;
  1138. }
  1139. }
  1140. EtwpFree(RcvNotification);
  1141. } else {
  1142. Status = ERROR_NOT_ENOUGH_MEMORY;
  1143. }
  1144. EtwpSetLastError(Status);
  1145. return(Status);
  1146. }
  1147. void EtwpMakeEventCallbacks(
  1148. IN PWNODE_HEADER Wnode,
  1149. IN NOTIFICATIONCALLBACK Callback,
  1150. IN ULONG_PTR DeliveryContext,
  1151. IN BOOLEAN IsAnsi
  1152. )
  1153. {
  1154. EtwpAssert((Wnode->Flags & WNODE_FLAG_INTERNAL) == 0);
  1155. if (Callback == NULL)
  1156. {
  1157. //
  1158. // This event needs to be sent to all consumers
  1159. //
  1160. EtwpDeliverAllEvents((PUCHAR)Wnode,
  1161. Wnode->BufferSize);
  1162. } else {
  1163. //
  1164. // This event is targetted at a specific consumer
  1165. //
  1166. if (IsAnsi)
  1167. {
  1168. //
  1169. // Caller wants ansi notification - convert
  1170. // instance names
  1171. //
  1172. EtwpConvertEventToAnsi(Wnode);
  1173. }
  1174. //
  1175. // Now go and deliver this event
  1176. //
  1177. EtwpExternalNotification(Callback,
  1178. DeliveryContext,
  1179. Wnode);
  1180. }
  1181. }
  1182. void EtwpClosePendingHandles(
  1183. )
  1184. {
  1185. PLIST_ENTRY GuidNotificationList, GuidNotificationListNext;
  1186. PGUIDNOTIFICATION GuidNotification;
  1187. ULONG i;
  1188. PNOTIFYEE Notifyee;
  1189. EtwpEnterPMCritSection();
  1190. GuidNotificationList = EtwpGNHead.Flink;
  1191. while (GuidNotificationList != &EtwpGNHead)
  1192. {
  1193. GuidNotification = CONTAINING_RECORD(GuidNotificationList,
  1194. GUIDNOTIFICATION,
  1195. GNList);
  1196. GuidNotificationListNext = GuidNotificationList->Flink;
  1197. for (i = 0; i < GuidNotification->NotifyeeCount; i++)
  1198. {
  1199. Notifyee = &GuidNotification->Notifyee[i];
  1200. if ((Notifyee->DeliveryInfo != NULL) &&
  1201. (EtwpIsNotifyeePendingClose(Notifyee)))
  1202. {
  1203. //
  1204. // This notifyee is pending closure so we clean it up
  1205. // now. We need to close the handle, reset the
  1206. // DeliveryInfo field and unreference the
  1207. // GuidNotification. Note that unreferencing may cause
  1208. // the GuidNotification to go away
  1209. //
  1210. if (EtwpCloseNotifyee(Notifyee,
  1211. GuidNotification))
  1212. {
  1213. //
  1214. // GuidNotification has been removed from the list.
  1215. // We jump out of all processing of this
  1216. // GuidNotification and move onto the next one
  1217. //
  1218. break;
  1219. }
  1220. }
  1221. }
  1222. GuidNotificationList = GuidNotificationListNext;
  1223. }
  1224. EtwpLeavePMCritSection();
  1225. }
  1226. void EtwpBuildReceiveNotification(
  1227. PUCHAR *BufferPtr,
  1228. ULONG *BufferSizePtr,
  1229. ULONG *RequestSize,
  1230. ULONG Action,
  1231. HANDLE ProcessHandle
  1232. )
  1233. {
  1234. ULONG GuidCount;
  1235. PUCHAR Buffer;
  1236. ULONG BufferSize;
  1237. PLIST_ENTRY GuidNotificationList;
  1238. PGUIDNOTIFICATION GuidNotification;
  1239. PWMIRECEIVENOTIFICATION ReceiveNotification;
  1240. ULONG SizeNeeded;
  1241. ULONG i;
  1242. PNOTIFYEE Notifyee;
  1243. ULONG ReturnSize;
  1244. ULONG Status;
  1245. BOOLEAN HoldCritSection;
  1246. BOOLEAN HaveGroupHandle;
  1247. Buffer = *BufferPtr;
  1248. BufferSize = *BufferSizePtr;
  1249. ReceiveNotification = (PWMIRECEIVENOTIFICATION)Buffer;
  1250. TryAgain:
  1251. GuidCount = 0;
  1252. SizeNeeded = FIELD_OFFSET(WMIRECEIVENOTIFICATION, Handles);
  1253. //
  1254. // Loop over all guid notifications and build an ioctl request for
  1255. // all of them
  1256. //
  1257. EtwpEnterPMCritSection();
  1258. GuidNotificationList = EtwpGNHead.Flink;
  1259. while (GuidNotificationList != &EtwpGNHead)
  1260. {
  1261. GuidNotification = CONTAINING_RECORD(GuidNotificationList,
  1262. GUIDNOTIFICATION,
  1263. GNList);
  1264. HaveGroupHandle = FALSE;
  1265. for (i = 0; i < GuidNotification->NotifyeeCount; i++)
  1266. {
  1267. Notifyee = &GuidNotification->Notifyee[i];
  1268. if ((Notifyee->DeliveryInfo != NULL) &&
  1269. ( ! EtwpIsNotifyeePendingClose(Notifyee)))
  1270. {
  1271. if (((! HaveGroupHandle) ||
  1272. ((Notifyee->Flags & NOTIFICATION_FLAG_GROUPED_EVENT) == 0)))
  1273. {
  1274. //
  1275. // If there is an active handle in the notifyee slot
  1276. // and we either have not already inserted the group
  1277. // handle for this guid or the slot is not part of the
  1278. // guid group, then we insert the handle into the list
  1279. //
  1280. SizeNeeded += sizeof(HANDLE3264);
  1281. if (SizeNeeded > BufferSize)
  1282. {
  1283. //
  1284. // We need to grow the size of the buffer. Alloc a
  1285. // bigger buffer, copy over
  1286. //
  1287. BufferSize *= 2;
  1288. HoldCritSection = TRUE;
  1289. Buffer = EtwpAllocDontFail(BufferSize, &HoldCritSection);
  1290. memcpy(Buffer, ReceiveNotification, *BufferSizePtr);
  1291. EtwpFree(*BufferPtr);
  1292. *BufferPtr = Buffer;
  1293. *BufferSizePtr = BufferSize;
  1294. ReceiveNotification = (PWMIRECEIVENOTIFICATION)Buffer;
  1295. if (! HoldCritSection)
  1296. {
  1297. //
  1298. // Critical section was released within
  1299. // EtwpAllocDontFail since we had to block. So
  1300. // everything could have changed. We need to go
  1301. // back and start over again
  1302. //
  1303. goto TryAgain;
  1304. }
  1305. }
  1306. WmipSetHandle3264(ReceiveNotification->Handles[GuidCount],
  1307. Notifyee->GuidHandle);
  1308. GuidCount++;
  1309. if (Notifyee->Flags & NOTIFICATION_FLAG_GROUPED_EVENT)
  1310. {
  1311. //
  1312. // This was a guid group handle and we did insert
  1313. // it into the list so we don't want to insert it
  1314. // again
  1315. //
  1316. HaveGroupHandle = TRUE;
  1317. }
  1318. }
  1319. }
  1320. }
  1321. GuidNotificationList = GuidNotificationList->Flink;
  1322. }
  1323. EtwpLeavePMCritSection();
  1324. ReceiveNotification->HandleCount = GuidCount;
  1325. ReceiveNotification->Action = Action;
  1326. WmipSetPVoid3264(ReceiveNotification->UserModeCallback, EtwpEventPumpFromKernel);
  1327. WmipSetHandle3264(ReceiveNotification->UserModeProcess, ProcessHandle);
  1328. *RequestSize = SizeNeeded;
  1329. }
  1330. #if _MSC_FULL_VER >= 13008827
  1331. #pragma warning(push)
  1332. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  1333. #endif
  1334. ULONG EtwpEventPump(
  1335. PVOID Param
  1336. )
  1337. {
  1338. LPOVERLAPPED ActiveOverlapped, DeadOverlapped;
  1339. LPOVERLAPPED PrimaryOverlapped;
  1340. LPOVERLAPPED BackupOverlapped;
  1341. PUCHAR ActiveBuffer, DeadBuffer;
  1342. ULONG ActiveBufferSize, DeadBufferSize;
  1343. PUCHAR PrimaryBuffer, BackupBuffer;
  1344. ULONG PrimaryBufferSize, BackupBufferSize;
  1345. ULONG ReturnSize=0;
  1346. ULONG DeadReturnSize=0;
  1347. ULONG Status, WaitStatus;
  1348. HANDLE HandleArray[2];
  1349. ULONG RequestSize;
  1350. //
  1351. // We need to hold off on letting the thread into the routine until
  1352. // the previous pump thread has had a chance to finish. This could
  1353. // occur if a GN is added/removed while the previous thread is
  1354. // finishing up or if an event is received as the previous thread
  1355. // is finishing up.
  1356. //
  1357. while (EtwpIsPumpStopping())
  1358. {
  1359. //
  1360. // wait 50ms for the previous thread to finish up
  1361. //
  1362. EtwpSleep(50);
  1363. }
  1364. //
  1365. // Next thing to do is to make sure that another pump thread isn't
  1366. // already running. This can happen in the case that both a GN is
  1367. // added or removed and an event reaches kernel and the kernel
  1368. // creates a new thread too. Right here we only let one of them
  1369. // win.
  1370. //
  1371. EtwpEnterPMCritSection();
  1372. if ((EtwpPumpState != EVENT_PUMP_IDLE) &&
  1373. (EtwpPumpState != EVENT_PUMP_ZERO))
  1374. {
  1375. EtwpLeavePMCritSection();
  1376. EtwpExitThread(0);
  1377. } else {
  1378. EtwpPumpState = EVENT_PUMP_RUNNING;
  1379. EtwpNewPumpThreadPending = FALSE;
  1380. EtwpLeavePMCritSection();
  1381. }
  1382. //
  1383. // Make sure we have all resources we'll need to pump out events
  1384. // since there is no way that we can return an error to the original
  1385. // caller since we are on a new thread
  1386. //
  1387. EtwpAssert(EtwpEventDeviceHandle != NULL);
  1388. EtwpAssert(EtwpPumpCommandEvent != NULL);
  1389. EtwpAssert(EtwpMyProcessHandle != NULL);
  1390. EtwpAssert(EtwpEventBuffer1 != NULL);
  1391. EtwpAssert(EtwpEventBuffer2 != NULL);
  1392. EtwpAssert(EtwpOverlapped1.hEvent != NULL);
  1393. EtwpAssert(EtwpOverlapped2.hEvent != NULL);
  1394. ActiveOverlapped = NULL;
  1395. PrimaryOverlapped = &EtwpOverlapped1;
  1396. PrimaryBuffer = EtwpEventBuffer1;
  1397. PrimaryBufferSize = EtwpEventBufferSize1;
  1398. BackupOverlapped = &EtwpOverlapped2;
  1399. BackupBuffer = EtwpEventBuffer2;
  1400. BackupBufferSize = EtwpEventBufferSize2;
  1401. HandleArray[0] = EtwpPumpCommandEvent;
  1402. while(TRUE)
  1403. {
  1404. //
  1405. // Build request to receive events for all guids that are
  1406. // registered
  1407. //
  1408. EtwpEnterPMCritSection();
  1409. if (IsListEmpty(&EtwpGNHead))
  1410. {
  1411. //
  1412. // There are no events to be received so we cancel any
  1413. // outstanding requests and quietly exit this thread. Note
  1414. // that once we leave the critsec there could be another
  1415. // pump thread running so all we can do after that is exit.
  1416. //
  1417. EtwpCancelIo(EtwpEventDeviceHandle);
  1418. //
  1419. // Enter the idle state which implies that all of the
  1420. // pump resources stay allocated when the thread is not
  1421. // running
  1422. //
  1423. EtwpEventBuffer1 = PrimaryBuffer;
  1424. EtwpEventBufferSize1 = PrimaryBufferSize;
  1425. EtwpEventBuffer2 = BackupBuffer;
  1426. EtwpEventBufferSize2 = BackupBufferSize;
  1427. EtwpPumpState = EVENT_PUMP_IDLE;
  1428. EtwpLeavePMCritSection();
  1429. EtwpExitThread(0);
  1430. }
  1431. EtwpLeavePMCritSection();
  1432. if (ActiveOverlapped != NULL)
  1433. {
  1434. //
  1435. // If there was a previously outstanding request then
  1436. // we remember it and switch to the backup overlapped and
  1437. // and data buffer
  1438. //
  1439. DeadOverlapped = ActiveOverlapped;
  1440. DeadBuffer = ActiveBuffer;
  1441. DeadBufferSize = ActiveBufferSize;
  1442. //
  1443. // The request being mooted should be the current primary
  1444. //
  1445. EtwpAssert(DeadOverlapped == PrimaryOverlapped);
  1446. EtwpAssert(DeadBuffer == PrimaryBuffer);
  1447. //
  1448. // Use the backup request as the new primary
  1449. //
  1450. EtwpAssert(BackupOverlapped != NULL);
  1451. EtwpAssert(BackupBuffer != NULL);
  1452. PrimaryOverlapped = BackupOverlapped;
  1453. PrimaryBuffer = BackupBuffer;
  1454. PrimaryBufferSize = BackupBufferSize;
  1455. BackupOverlapped = NULL;
  1456. BackupBuffer = NULL;
  1457. } else {
  1458. //
  1459. // If there is no outstanding request then we don't worry about
  1460. // it
  1461. //
  1462. DeadOverlapped = NULL;
  1463. }
  1464. //
  1465. // Build and send the request down to kernel to receive events
  1466. //
  1467. RebuildRequest:
  1468. //
  1469. // Make sure any handles that are pending closure are closed
  1470. //
  1471. EtwpClosePendingHandles();
  1472. EtwpBuildReceiveNotification(&PrimaryBuffer,
  1473. &PrimaryBufferSize,
  1474. &RequestSize,
  1475. EtwpIsPumpStopping() ? RECEIVE_ACTION_CREATE_THREAD :
  1476. RECEIVE_ACTION_NONE,
  1477. EtwpMyProcessHandle);
  1478. ActiveOverlapped = PrimaryOverlapped;
  1479. ActiveBuffer = PrimaryBuffer;
  1480. ActiveBufferSize = PrimaryBufferSize;
  1481. Status = EtwpSendWmiKMRequest(EtwpEventDeviceHandle,
  1482. IOCTL_WMI_RECEIVE_NOTIFICATIONS,
  1483. ActiveBuffer,
  1484. RequestSize,
  1485. ActiveBuffer,
  1486. ActiveBufferSize,
  1487. &ReturnSize,
  1488. ActiveOverlapped);
  1489. if (DeadOverlapped != NULL)
  1490. {
  1491. if ((Status != ERROR_SUCCESS) &&
  1492. (Status != ERROR_IO_PENDING) &&
  1493. (Status != ERROR_OPERATION_ABORTED))
  1494. {
  1495. //
  1496. // There was a previous request which won't be cleared
  1497. // unless the new request returns pending, cancelled
  1498. // or success. So if the new request returns something
  1499. // else then we need to retry the request
  1500. //
  1501. EtwpDebugPrint(("WMI: Event Poll error %d\n", Status));
  1502. EtwpSleep(100);
  1503. goto RebuildRequest;
  1504. }
  1505. //
  1506. // The new request should have caused the old one to
  1507. // be completed
  1508. //
  1509. if (EtwpGetOverlappedResult(EtwpEventDeviceHandle,
  1510. DeadOverlapped,
  1511. &DeadReturnSize,
  1512. TRUE))
  1513. {
  1514. //
  1515. // The dead request did succeed and was not failed by
  1516. // the receipt of the new request. This is a unlikely
  1517. // race condition where the requests crossed paths. So we
  1518. // need to process the events returned in the dead request.
  1519. // Now if the buffer returned was a WNODE_TOO_SMALL we want
  1520. // to ignore it at this point since we are not at a
  1521. // good position to reallocate the buffers - the
  1522. // primary buffer is already attached to the new
  1523. // request. That request is also going to return a
  1524. // WNODE_TOO_SMALL and in the processing of that one we will
  1525. // grow the buffers. So it is safe to ignore here.
  1526. // However we will still need to dispatch any real
  1527. // events received as they have been purged from KM.
  1528. //
  1529. if (DeadReturnSize != 0)
  1530. {
  1531. EtwpProcessEventBuffer(DeadBuffer,
  1532. DeadReturnSize,
  1533. &PrimaryBuffer,
  1534. &PrimaryBufferSize,
  1535. &BackupBuffer,
  1536. &BackupBufferSize,
  1537. FALSE);
  1538. } else {
  1539. EtwpAssert(EtwpIsPumpStopping());
  1540. }
  1541. }
  1542. //
  1543. // Now make the completed request the backup request
  1544. //
  1545. EtwpAssert(BackupOverlapped == NULL);
  1546. EtwpAssert(BackupBuffer == NULL);
  1547. BackupOverlapped = DeadOverlapped;
  1548. BackupBuffer = DeadBuffer;
  1549. BackupBufferSize = DeadBufferSize;
  1550. }
  1551. if (Status == ERROR_IO_PENDING)
  1552. {
  1553. //
  1554. // if the ioctl pended then we wait until either an event
  1555. // is returned or a command needs processed
  1556. //
  1557. HandleArray[1] = ActiveOverlapped->hEvent;
  1558. WaitStatus = EtwpWaitForMultipleObjectsEx(2,
  1559. HandleArray,
  1560. FALSE,
  1561. EtwpEventNotificationWait,
  1562. TRUE);
  1563. } else {
  1564. //
  1565. // the ioctl completed immediately so we fake out the wait
  1566. //
  1567. WaitStatus = WAIT_OBJECT_0 + 1;
  1568. }
  1569. if (WaitStatus == WAIT_OBJECT_0 + 1)
  1570. {
  1571. if (Status == ERROR_IO_PENDING)
  1572. {
  1573. if (EtwpGetOverlappedResult(EtwpEventDeviceHandle,
  1574. ActiveOverlapped,
  1575. &ReturnSize,
  1576. TRUE))
  1577. {
  1578. Status = ERROR_SUCCESS;
  1579. } else {
  1580. Status = EtwpGetLastError();
  1581. }
  1582. }
  1583. if (Status == ERROR_SUCCESS)
  1584. {
  1585. //
  1586. // We received some events from KM so we want to go and
  1587. // process them. If we got a WNODE_TOO_SMALL then the
  1588. // primary and backup buffers will get reallocated with
  1589. // the new size that is needed.
  1590. //
  1591. if (ReturnSize != 0)
  1592. {
  1593. EtwpProcessEventBuffer(ActiveBuffer,
  1594. ReturnSize,
  1595. &PrimaryBuffer,
  1596. &PrimaryBufferSize,
  1597. &BackupBuffer,
  1598. &BackupBufferSize,
  1599. TRUE);
  1600. //
  1601. // In the case that we are shutting down the event
  1602. // pump and the buffer passed to clear out all of
  1603. // the events was too small we need to call back
  1604. // down to the kernel to get the rest of the events
  1605. // since we cannot exit the thread with events that
  1606. // are not delivered. The kernel will not set the
  1607. // flag that a new thread is needed unless the irp
  1608. // clears all outstanding events
  1609. //
  1610. } else {
  1611. EtwpAssert(EtwpIsPumpStopping());
  1612. if (EtwpIsPumpStopping())
  1613. {
  1614. //
  1615. // The irp just completed should have not only
  1616. // just cleared all events out of kernel mode
  1617. // but also setup flags that new events should
  1618. // cause a new pump thread to be created. So
  1619. // there may be a new pump thread already created
  1620. // Also note there could be yet
  1621. // another event pump thread that was created
  1622. // if a GN was added or removed. Once we set
  1623. // the pump state to IDLE we are off to the
  1624. // races (See code at top of function)
  1625. //
  1626. EtwpEnterPMCritSection();
  1627. EtwpPumpState = EVENT_PUMP_IDLE;
  1628. EtwpEventBuffer1 = PrimaryBuffer;
  1629. EtwpEventBufferSize1 = PrimaryBufferSize;
  1630. EtwpEventBuffer2 = BackupBuffer;
  1631. EtwpEventBufferSize2 = BackupBufferSize;
  1632. //
  1633. // Before shutting down the pump we need to
  1634. // close any handles that are pending closure
  1635. //
  1636. EtwpClosePendingHandles();
  1637. EtwpLeavePMCritSection();
  1638. EtwpExitThread(0);
  1639. }
  1640. }
  1641. } else {
  1642. //
  1643. // For some reason the request failed. All we can do is
  1644. // wait a bit and hope that the problem will clear up.
  1645. // If we are stopping the thread we still need to wait
  1646. // and try again as all events may not have been
  1647. // cleared from the kernel. We really don't know if the
  1648. // irp even made it to the kernel.
  1649. //
  1650. EtwpDebugPrint(("WMI: [%x - %x] Error %d from Ioctl\n",
  1651. EtwpGetCurrentProcessId(), EtwpGetCurrentThreadId(),
  1652. Status));
  1653. EtwpSleep(250);
  1654. }
  1655. //
  1656. // Flag that there is no longer a request outstanding
  1657. //
  1658. ActiveOverlapped = NULL;
  1659. } else if (WaitStatus == STATUS_TIMEOUT) {
  1660. //
  1661. // The wait for events timed out so we go into the thread
  1662. // stopping state to indicate that we are going to terminate
  1663. // the thread once all events are cleared out of kernel. At
  1664. // this point we are commited to stopping the thread. If any
  1665. // GN are added/removed after going into the stopping state,
  1666. // a new (and suspended) thread will be created. Right
  1667. // before exiting we check if that thread is pending and if
  1668. // so resume it.
  1669. //
  1670. EtwpEnterPMCritSection();
  1671. EtwpPumpState = EVENT_PUMP_STOPPING;
  1672. EtwpLeavePMCritSection();
  1673. }
  1674. }
  1675. //
  1676. // Should never break out of infinite loop
  1677. //
  1678. EtwpAssert(FALSE);
  1679. EtwpExitThread(0);
  1680. }
  1681. #if _MSC_FULL_VER >= 13008827
  1682. #pragma warning(pop)
  1683. #endif
  1684. ULONG EtwpEventPumpFromKernel(
  1685. PVOID Param
  1686. )
  1687. {
  1688. //
  1689. // Note that we MUST call ExitThread when we want to shutdown the
  1690. // thread and not return() since the thread has been created by
  1691. // kernel mode and there is nothing on the stack to return to, so
  1692. // we'd just AV
  1693. //
  1694. //
  1695. // Call into ntdll so that it marks our thread as a CSR thread
  1696. //
  1697. CsrNewThread();
  1698. EtwpEnterPMCritSection();
  1699. if ((EtwpNewPumpThreadPending == FALSE) &&
  1700. (EtwpPumpState == EVENT_PUMP_IDLE) ||
  1701. (EtwpPumpState == EVENT_PUMP_STOPPING))
  1702. {
  1703. //
  1704. // If the pump is currently idle or stopping and there is not
  1705. // another pump thread that is pending we want our thread
  1706. // to be the one that gets the pump going again. We mark the
  1707. // that there is a pump thread pending which means that no more
  1708. // pump threads will be created when adding/removing GN
  1709. // and any pump threads created by kernel will just exit quickly
  1710. //
  1711. EtwpNewPumpThreadPending = TRUE;
  1712. EtwpLeavePMCritSection();
  1713. //
  1714. // ISSUE: We cannot call EtwpEventPump with Param (ie, the
  1715. // parameter that is passed to this function) because when the
  1716. // thread is created by a Win64 kernel on a x86 app running
  1717. // under win64, Param is not actually passed on the stack since
  1718. // the code that creates the context forgets to do so
  1719. //
  1720. EtwpExitThread(EtwpEventPump(0));
  1721. }
  1722. EtwpLeavePMCritSection();
  1723. EtwpExitThread(0);
  1724. return(0);
  1725. }
  1726. ULONG EtwpEstablishEventPump(
  1727. )
  1728. {
  1729. #if DBG
  1730. #define INITIALEVENTBUFFERSIZE 0x38
  1731. #else
  1732. #define INITIALEVENTBUFFERSIZE 0x1000
  1733. #endif
  1734. HANDLE ThreadHandle;
  1735. CLIENT_ID ClientId;
  1736. ULONG Status;
  1737. BOOL b;
  1738. #if DBG
  1739. //
  1740. // On checked builds update the length of time to wait before a
  1741. // pump thread times out
  1742. //
  1743. EtwpGetRegistryValue(PumpTimeoutRegValueText,
  1744. &EtwpEventNotificationWait);
  1745. #endif
  1746. //
  1747. // Make sure the event pump thread is running. We check both the
  1748. // pump state and that the device handle is not created since there
  1749. // is a window after the handle is created and the thread starts
  1750. // running and changes the pump state
  1751. //
  1752. EtwpEnterPMCritSection();
  1753. if ((EtwpPumpState == EVENT_PUMP_ZERO) &&
  1754. (EtwpEventDeviceHandle == NULL))
  1755. {
  1756. //
  1757. // Not only is pump not running, but the resources for it
  1758. // haven't been allocated
  1759. //
  1760. EtwpAssert(EtwpPumpCommandEvent == NULL);
  1761. EtwpAssert(EtwpMyProcessHandle == NULL);
  1762. EtwpAssert(EtwpOverlapped1.hEvent == NULL);
  1763. EtwpAssert(EtwpOverlapped2.hEvent == NULL);
  1764. EtwpAssert(EtwpEventBuffer1 == NULL);
  1765. EtwpAssert(EtwpEventBuffer2 == NULL);
  1766. //
  1767. // Preallocate all of the resources that the event pump will need
  1768. // so that it has no excuse to fail
  1769. //
  1770. EtwpEventDeviceHandle = EtwpCreateFileW(WMIDataDeviceName_W,
  1771. GENERIC_READ | GENERIC_WRITE,
  1772. 0,
  1773. NULL,
  1774. OPEN_EXISTING,
  1775. FILE_ATTRIBUTE_NORMAL |
  1776. FILE_FLAG_OVERLAPPED,
  1777. NULL);
  1778. if (EtwpEventDeviceHandle == INVALID_HANDLE_VALUE)
  1779. {
  1780. Status = EtwpGetLastError();
  1781. goto Cleanup;
  1782. }
  1783. EtwpPumpCommandEvent = EtwpCreateEventW(NULL, FALSE, FALSE, NULL);
  1784. if (EtwpPumpCommandEvent == NULL)
  1785. {
  1786. Status = EtwpGetLastError();
  1787. goto Cleanup;
  1788. }
  1789. b = EtwpDuplicateHandle(EtwpGetCurrentProcess(),
  1790. EtwpGetCurrentProcess(),
  1791. EtwpGetCurrentProcess(),
  1792. &EtwpMyProcessHandle,
  1793. 0,
  1794. FALSE,
  1795. DUPLICATE_SAME_ACCESS);
  1796. if (! b)
  1797. {
  1798. Status = EtwpGetLastError();
  1799. goto Cleanup;
  1800. }
  1801. EtwpOverlapped1.hEvent = EtwpCreateEventW(NULL, FALSE, FALSE, NULL);
  1802. if (EtwpOverlapped1.hEvent == NULL)
  1803. {
  1804. Status = EtwpGetLastError();
  1805. goto Cleanup;
  1806. }
  1807. EtwpOverlapped2.hEvent = EtwpCreateEventW(NULL, FALSE, FALSE, NULL);
  1808. if (EtwpOverlapped2.hEvent == NULL)
  1809. {
  1810. Status = EtwpGetLastError();
  1811. goto Cleanup;
  1812. }
  1813. EtwpEventBuffer1 = EtwpAlloc(INITIALEVENTBUFFERSIZE);
  1814. if (EtwpEventBuffer1 == NULL)
  1815. {
  1816. Status = ERROR_NOT_ENOUGH_MEMORY;
  1817. goto Cleanup;
  1818. }
  1819. EtwpEventBufferSize1 = INITIALEVENTBUFFERSIZE;
  1820. EtwpEventBuffer2 = EtwpAlloc(INITIALEVENTBUFFERSIZE);
  1821. if (EtwpEventBuffer2 == NULL)
  1822. {
  1823. Status = ERROR_NOT_ENOUGH_MEMORY;
  1824. goto Cleanup;
  1825. }
  1826. EtwpEventBufferSize2 = INITIALEVENTBUFFERSIZE;
  1827. ThreadHandle = EtwpCreateThread(NULL,
  1828. 0,
  1829. EtwpEventPump,
  1830. NULL,
  1831. 0,
  1832. (LPDWORD)&ClientId);
  1833. if (ThreadHandle != NULL)
  1834. {
  1835. EtwpNewPumpThreadPending = TRUE;
  1836. EtwpCloseHandle(ThreadHandle);
  1837. } else {
  1838. //
  1839. // Since we were able to allocate all of our pump
  1840. // resources, but didn't get the pump thread started,
  1841. // we will hang onto our resources and move the pump
  1842. // state to idle. In this way when the pump is started
  1843. // again we do not have to reallocate our resources
  1844. //
  1845. EtwpPumpState = EVENT_PUMP_IDLE;
  1846. Status = EtwpGetLastError();
  1847. goto Done;
  1848. }
  1849. EtwpLeavePMCritSection();
  1850. return(ERROR_SUCCESS);
  1851. } else {
  1852. //
  1853. // Pump resources should already be allocated
  1854. //
  1855. EtwpAssert(EtwpPumpCommandEvent != NULL);
  1856. EtwpAssert(EtwpMyProcessHandle != NULL);
  1857. EtwpAssert(EtwpOverlapped1.hEvent != NULL);
  1858. EtwpAssert(EtwpOverlapped2.hEvent != NULL);
  1859. EtwpAssert(EtwpEventBuffer1 != NULL);
  1860. EtwpAssert(EtwpEventBuffer2 != NULL);
  1861. if ((EtwpNewPumpThreadPending == FALSE) &&
  1862. (EtwpPumpState == EVENT_PUMP_STOPPING) ||
  1863. (EtwpPumpState == EVENT_PUMP_IDLE))
  1864. {
  1865. //
  1866. // If pump is stopping or is idle then we need to fire up a
  1867. // new thread
  1868. //
  1869. ThreadHandle = EtwpCreateThread(NULL,
  1870. 0,
  1871. EtwpEventPump,
  1872. NULL,
  1873. 0,
  1874. (LPDWORD)&ClientId);
  1875. if (ThreadHandle != NULL)
  1876. {
  1877. EtwpNewPumpThreadPending = TRUE;
  1878. EtwpCloseHandle(ThreadHandle);
  1879. } else {
  1880. Status = EtwpGetLastError();
  1881. goto Done;
  1882. }
  1883. } else {
  1884. EtwpAssert((EtwpPumpState == EVENT_PUMP_RUNNING) ||
  1885. (EtwpNewPumpThreadPending == TRUE));
  1886. }
  1887. EtwpLeavePMCritSection();
  1888. return(ERROR_SUCCESS);
  1889. }
  1890. Cleanup:
  1891. if (EtwpEventDeviceHandle != NULL)
  1892. {
  1893. EtwpCloseHandle(EtwpEventDeviceHandle);
  1894. EtwpEventDeviceHandle = NULL;
  1895. }
  1896. if (EtwpPumpCommandEvent != NULL)
  1897. {
  1898. EtwpCloseHandle(EtwpPumpCommandEvent);
  1899. EtwpPumpCommandEvent = NULL;
  1900. }
  1901. if (EtwpMyProcessHandle != NULL)
  1902. {
  1903. EtwpCloseHandle(EtwpMyProcessHandle);
  1904. EtwpMyProcessHandle = NULL;
  1905. }
  1906. if (EtwpOverlapped1.hEvent != NULL)
  1907. {
  1908. EtwpCloseHandle(EtwpOverlapped1.hEvent);
  1909. EtwpOverlapped1.hEvent = NULL;
  1910. }
  1911. if (EtwpOverlapped2.hEvent != NULL)
  1912. {
  1913. EtwpCloseHandle(EtwpOverlapped2.hEvent);
  1914. EtwpOverlapped2.hEvent = NULL;
  1915. }
  1916. if (EtwpEventBuffer1 != NULL)
  1917. {
  1918. EtwpFree(EtwpEventBuffer1);
  1919. EtwpEventBuffer1 = NULL;
  1920. }
  1921. if (EtwpEventBuffer2 != NULL)
  1922. {
  1923. EtwpFree(EtwpEventBuffer2);
  1924. EtwpEventBuffer2 = NULL;
  1925. }
  1926. Done:
  1927. EtwpLeavePMCritSection();
  1928. return(Status);
  1929. }
  1930. ULONG EtwpAddHandleToEventPump(
  1931. LPGUID Guid,
  1932. PVOID DeliveryInfo,
  1933. ULONG_PTR DeliveryContext,
  1934. ULONG NotificationFlags,
  1935. HANDLE GuidHandle
  1936. )
  1937. {
  1938. ULONG Status;
  1939. Status = EtwpAddToGNList(Guid,
  1940. DeliveryInfo,
  1941. DeliveryContext,
  1942. NotificationFlags,
  1943. GuidHandle);
  1944. if (Status == ERROR_SUCCESS)
  1945. {
  1946. Status = EtwpEstablishEventPump();
  1947. if (Status == ERROR_SUCCESS)
  1948. {
  1949. EtwpSendPumpCommand();
  1950. } else {
  1951. //
  1952. // If we couldn't establish the event pump we want to
  1953. // remove the handle from the GNList and propogate back the
  1954. // error
  1955. //
  1956. EtwpRemoveFromGNList(Guid,
  1957. DeliveryInfo);
  1958. }
  1959. } else {
  1960. //
  1961. // If handle could not be added to the lists then we need to
  1962. // close the handle to prevent leaks.
  1963. //
  1964. EtwpCloseHandle(GuidHandle);
  1965. }
  1966. return(Status);
  1967. }
  1968. ULONG
  1969. EtwpNotificationRegistration(
  1970. IN LPGUID InGuid,
  1971. IN BOOLEAN Enable,
  1972. IN PVOID DeliveryInfo,
  1973. IN ULONG_PTR DeliveryContext,
  1974. IN ULONG64 LoggerContext,
  1975. IN ULONG Flags,
  1976. IN BOOLEAN IsAnsi
  1977. )
  1978. {
  1979. HANDLE GuidHandle;
  1980. GUID Guid;
  1981. PVOID NotificationDeliveryContext;
  1982. PVOID NotificationDeliveryInfo;
  1983. ULONG NotificationFlags;
  1984. ULONG Status;
  1985. HANDLE ThreadHandle;
  1986. DWORD ThreadId;
  1987. ULONG ReturnSize;
  1988. EtwpInitProcessHeap();
  1989. //
  1990. // Validate input parameters and flags
  1991. //
  1992. if (InGuid == NULL)
  1993. {
  1994. EtwpSetLastError(ERROR_INVALID_PARAMETER);
  1995. return(ERROR_INVALID_PARAMETER);
  1996. }
  1997. try
  1998. {
  1999. Guid = *InGuid;
  2000. } except(EXCEPTION_EXECUTE_HANDLER) {
  2001. EtwpSetLastError(ERROR_INVALID_PARAMETER);
  2002. return(ERROR_INVALID_PARAMETER);
  2003. }
  2004. if (Flags == NOTIFICATION_CHECK_ACCESS)
  2005. {
  2006. //
  2007. // Caller just wants to check that he has does have permission
  2008. // to enable the notification
  2009. //
  2010. #ifdef MEMPHIS
  2011. return(ERROR_SUCCESS);
  2012. #else
  2013. Status = EtwpCheckGuidAccess(&Guid, WMIGUID_NOTIFICATION);
  2014. EtwpSetLastError(Status);
  2015. return(Status);
  2016. #endif
  2017. }
  2018. //
  2019. // Validate that flags are correct
  2020. //
  2021. if (Enable)
  2022. {
  2023. if ((Flags != NOTIFICATION_TRACE_FLAG) &&
  2024. (Flags != NOTIFICATION_CALLBACK_DIRECT))
  2025. {
  2026. //
  2027. // Invalid Flags were passed
  2028. Status = ERROR_INVALID_PARAMETER;
  2029. } else if (Flags == NOTIFICATION_TRACE_FLAG) {
  2030. Status = ERROR_SUCCESS;
  2031. } else if ((Flags == NOTIFICATION_CALLBACK_DIRECT) &&
  2032. (DeliveryInfo == NULL)) {
  2033. //
  2034. // Not a valid callback function
  2035. Status = ERROR_INVALID_PARAMETER;
  2036. } else {
  2037. Status = ERROR_SUCCESS;
  2038. }
  2039. if (Status != ERROR_SUCCESS)
  2040. {
  2041. EtwpSetLastError(Status);
  2042. return(Status);
  2043. }
  2044. }
  2045. NotificationDeliveryInfo = (PVOID)DeliveryInfo;
  2046. NotificationDeliveryContext = (PVOID)DeliveryContext;
  2047. NotificationFlags = IsAnsi ? DCREF_FLAG_ANSI : 0;
  2048. if (Flags & NOTIFICATION_TRACE_FLAG)
  2049. {
  2050. //
  2051. // This is a tracelog enable/disable request so send it down the
  2052. // fast lane to KM so it can be processed.
  2053. //
  2054. WMITRACEENABLEDISABLEINFO TraceEnableInfo;
  2055. TraceEnableInfo.Guid = Guid;
  2056. TraceEnableInfo.LoggerContext = LoggerContext;
  2057. TraceEnableInfo.Enable = Enable;
  2058. Status = EtwpSendWmiKMRequest(NULL,
  2059. IOCTL_WMI_ENABLE_DISABLE_TRACELOG,
  2060. &TraceEnableInfo,
  2061. sizeof(WMITRACEENABLEDISABLEINFO),
  2062. NULL,
  2063. 0,
  2064. &ReturnSize,
  2065. NULL);
  2066. } else {
  2067. //
  2068. // This is a WMI event enable/disable event request so fixup the
  2069. // flags and send a request off to the event pump thread.
  2070. //
  2071. if (Flags & NOTIFICATION_CALLBACK_DIRECT) {
  2072. NotificationFlags |= NOTIFICATION_FLAG_CALLBACK_DIRECT;
  2073. } else {
  2074. NotificationFlags |= Flags;
  2075. }
  2076. if (Enable)
  2077. {
  2078. //
  2079. // Since we are enabling, make sure we have access to the
  2080. // guid and then make sure we can get the notification pump
  2081. // thread running.
  2082. //
  2083. Status = EtwpOpenKernelGuid(&Guid,
  2084. WMIGUID_NOTIFICATION,
  2085. &GuidHandle,
  2086. IOCTL_WMI_OPEN_GUID_FOR_EVENTS);
  2087. if (Status == ERROR_SUCCESS)
  2088. {
  2089. Status = EtwpAddHandleToEventPump(&Guid,
  2090. DeliveryInfo,
  2091. DeliveryContext,
  2092. NotificationFlags |
  2093. NOTIFICATION_FLAG_GROUPED_EVENT,
  2094. GuidHandle);
  2095. }
  2096. } else {
  2097. Status = EtwpRemoveFromGNList(&Guid,
  2098. DeliveryInfo);
  2099. if (Status == ERROR_SUCCESS)
  2100. {
  2101. EtwpSendPumpCommand();
  2102. }
  2103. if (Status == ERROR_INVALID_PARAMETER)
  2104. {
  2105. CHAR s[MAX_PATH];
  2106. EtwpDebugPrint(("WMI: Invalid DeliveryInfo %x passed to unregister for notification %s\n",
  2107. DeliveryInfo,
  2108. GuidToStringA(s, MAX_PATH, &Guid)));
  2109. Status = ERROR_WMI_ALREADY_DISABLED;
  2110. }
  2111. }
  2112. }
  2113. EtwpSetLastError(Status);
  2114. return(Status);
  2115. }